diff --git a/doc/hotspot-style.html b/doc/hotspot-style.html index 8f421de54f0ecb305625f13655dab6842decb24f..fe72cbbdf80c1cca160571c8e4b9cd51e122c9f1 100644 --- a/doc/hotspot-style.html +++ b/doc/hotspot-style.html @@ -50,6 +50,7 @@
  • nullptr
  • <atomic>
  • Uniform Initialization
  • +
  • Local Function Objects
  • Additional Permitted Features
  • Excluded Features
  • Undecided Features
  • @@ -254,8 +255,7 @@ while ( test_foo(args...) ) { // No, excess spaces around control

    Function argument deduction. This is always permitted, and indeed encouraged. It is nearly always better to allow the type of a function template argument to be deduced rather than explicitly specified.

  • auto variable declarations (n1984)
    For local variables, this can be used to make the code clearer by eliminating type information that is obvious or irrelevant. Excessive use can make code much harder to understand.

  • Function return type deduction (n3638)
    Only use if the function body has a very small number of return statements, and generally relatively little other code.

  • -
  • Generic lambdas. Lambdas are not (yet) permitted.

  • -
  • Lambda init captures. Lambdas are not (yet) permitted.

  • +
  • Also see lambda expressions.

  • Expression SFINAE

    Substitution Failure Is Not An Error (SFINAE) is a template metaprogramming technique that makes use of template parameter substitution failures to make compile-time decisions.

    @@ -288,6 +288,121 @@ while ( test_foo(args...) ) { // No, excess spaces around controlaggregate initialization

    Although related, the use of std::initializer_list remains forbidden, as part of the avoidance of the C++ Standard Library in HotSpot code.

    +

    Local Function Objects

    + +

    Single-use function objects can be defined locally within a function, directly at the point of use. This is an alternative to having a function or function object class defined at class or namespace scope.

    +

    This usage was somewhat limited by C++03, which does not permit such a class to be used as a template parameter. That restriction was removed by C++11 (n2657). Use of this feature is permitted.

    +

    Many HotSpot protocols involve "function-like" objects that involve some named member function rather than a call operator. For example, a function that performs some action on all threads might be written as

    +
    void do_something() {
    +  struct DoSomething : public ThreadClosure {
    +    virtual void do_thread(Thread* t) {
    +      ... do something with t ...
    +    }
    +  } closure;
    +  Threads::threads_do(&closure);
    +}
    +

    HotSpot code has historically usually placed the DoSomething class at namespace (or sometimes class) scope. This separates the function's code from its use, often to the detriment of readability. It requires giving the class a globally unique name (if at namespace scope). It also loses the information that the class is intended for use in exactly one place, and does not have any subclasses. (However, the latter can now be indicated by declaring it final.) Often, for simplicity, a local class will skip things like access control and accessor functions, giving the enclosing function direct access to the implementation and eliminating some boilerplate that might be provided if the class is in some outer (more accessible) scope. On the other hand, if there is a lot of surrounding code in the function body or the local class is of significant size, defining it locally can increase clutter and reduce readability.

    +

    C++11 added lambda expressions as a new way to write a function object. Simple lambda expressions can be significantly more concise than a function object, eliminating a lot of boiler-plate. On the other hand, a complex lambda expression may not provide much, if any, readability benefit compared to an ordinary function object. Also, while a lambda can encapsulate a call to a "function-like" object, it cannot be used in place of such.

    +

    A common use for local functions is as one-use RAII objects. The amount of boilerplate for a function object class (local or not) makes such usage somewhat clumsy and verbose. But with the help of a small amount of supporting utility code, lambdas work particularly well for this use case.

    +

    Another use for local functions is partial application. Again here, lambdas are typically much simpler and less verbose than function object classes.

    +

    Because of these benefits, lambda expressions are permitted in HotSpot code, with some restrictions and usage guidance. An anonymous lambda is one which is passed directly as an argument. A named lambda is the value of a variable, which is its name.

    +

    Lambda expressions should only be passed downward. In particular, a lambda should not be returned from a function or stored in a global variable, whether directly or as the value of a member of some other object. Lambda capture is syntactically subtle (by design), and propagating a lambda in such ways can easily pass references to captured values to places where they are no longer valid. In particular, members of the enclosing this object are effectively captured by reference, even if the default capture is by-value. For such uses-cases a function object class should be used to make the desired value capturing and propagation explicit.

    +

    Limiting the capture list to [&] (implicitly capture by reference) is a simplifying restriction that still provides good support for HotSpot usage, while reducing the cases a reader must recognize and understand.

    + +

    The use of mutable lambda expressions is forbidden because there don't seem to be many, if any, good use-cases for them in HotSpot. A lambda expression needs to be mutable in order to modify a by-value captured value. But with only downward lambdas, such usage seems likely to be rare and complicated. It is better to use a function object class in any such cases that arise, rather than requiring all HotSpot developers to understand this relatively obscure feature.

    +

    While it is possible to directly invoke an anonymous lambda expression, that feature should not be used, as such a form can be confusing to readers. Instead, name the lambda and call it by name.

    +

    Some reasons to prefer a named lambda instead of an anonymous lambda are

    + +

    Lambda expressions, and particularly anonymous lambda expressions, should be simple and compact. One-liners are good. Anonymous lambdas should usually be limited to a couple lines of body code. More complex lambdas should be named. A named lambda should not clutter the enclosing function and make it long and complex; do continue to break up large functions via the use of separate helper functions.

    +

    An anonymous lambda expression should either be a one-liner in a one-line expression, or isolated in its own set of lines. Don't place part of a lambda expression on the same line as other arguments to a function. The body of a multi-line lambda argument should be indented from the start of the capture list, as if that were the start of an ordinary function definition. The body of a multi-line named lambda should be indented one step from the variable's indentation.

    +

    Some examples:

    +
      +
    1. foo([&] { ++counter; });
    2. +
    3. foo(x, [&] { ++counter; });
    4. +
    5. foo([&] { if (predicate) ++counter; });
    6. +
    7. foo([&] { auto tmp = process(x); tmp.f(); return tmp.g(); })
    8. +
    9. Separate one-line lambda from other arguments:

      +
      foo(c.begin(), c.end(),
      +    [&] (const X& x) { do_something(x); return x.value(); });
    10. +
    11. Indentation for multi-line lambda:

      +
      c.do_entries([&] (const X& x) {
      +               do_something(x, a);
      +               do_something1(x, b);
      +               do_something2(x, c);
      +             });
    12. +
    13. Separate multi-line lambda from other arguments:

      +
      foo(c.begin(), c.end(),
      +    [&] (const X& x) {
      +      do_something(x, a);
      +      do_something1(x, b);
      +      do_something2(x, c);
      +    });
    14. +
    15. Multi-line named lambda:

      +
      auto do_entry = [&] (const X& x) {
      +  do_something(x, a);
      +  do_something1(x, b);
      +  do_something2(x, c);
      +};
    16. +
    +

    Item 4, and especially items 6 and 7, are pushing the simplicity limits for anonymous lambdas. Item 6 might be better written using a named lambda:

    +
    c.do_entries(do_entry);
    +

    Note that C++11 also added bind expressions as a way to write a function object for partial application, using std::bind and related facilities from the Standard Library. std::bind generalizes and replaces some of the binders from C++03. Bind expressions are not permitted in HotSpot code. They don't provide enough benefit over lambdas or local function classes in the cases where bind expressions are applicable to warrant the introduction of yet another mechanism in this space into HotSpot code.

    +

    References:

    + +

    References from C++17

    + +

    References from C++20

    + +

    References from C++23

    +

    Additional Permitted Features

    Excluded Features

    @@ -337,7 +451,6 @@ while ( test_foo(args...) ) { // No, excess spaces around control

    Member initializers and aggregates (n3653)

  • [[noreturn]] attribute (n2761)

  • Rvalue references and move semantics

  • -
  • Lambdas

  • diff --git a/doc/hotspot-style.md b/doc/hotspot-style.md index f02954347edfdc6315dc0d332c568e7a44af5922..6d167cad9d6c20a2c7e6713aca40994de6da9a3f 100644 --- a/doc/hotspot-style.md +++ b/doc/hotspot-style.md @@ -596,9 +596,7 @@ use can make code much harder to understand. Only use if the function body has a very small number of `return` statements, and generally relatively little other code. -* Generic lambdas. Lambdas are not (yet) permitted. - -* Lambda init captures. Lambdas are not (yet) permitted. +* Also see [lambda expressions](#lambdaexpressions). ### Expression SFINAE @@ -703,6 +701,273 @@ Some relevant sections from cppreference.com: Although related, the use of `std::initializer_list` remains forbidden, as part of the avoidance of the C++ Standard Library in HotSpot code. +### Local Function Objects + +* Local function objects, including lambda expressions, may be used. +* Lambda expressions must only be used as a downward value. +* Prefer `[&]` as the capture list of a lambda expression. +* Return type deduction for lambda expressions is permitted, and indeed encouraged. +* An empty parameter list for a lambda expression may be elided. +* A lambda expression must not be `mutable`. +* Generic lambda expressions are permitted. +* Lambda expressions should be relatively simple. +* Anonymous lambda expressions should not overly clutter the enclosing expression. +* An anonymous lambda expression must not be directly invoked. +* Bind expressions are forbidden. + +Single-use function objects can be defined locally within a function, +directly at the point of use. This is an alternative to having a function +or function object class defined at class or namespace scope. + +This usage was somewhat limited by C++03, which does not permit such a class +to be used as a template parameter. That restriction was removed by C++11 +([n2657]). Use of this feature is permitted. + +Many HotSpot protocols involve "function-like" objects that involve some +named member function rather than a call operator. For example, a function +that performs some action on all threads might be written as + +``` +void do_something() { + struct DoSomething : public ThreadClosure { + virtual void do_thread(Thread* t) { + ... do something with t ... + } + } closure; + Threads::threads_do(&closure); +} +``` + +HotSpot code has historically usually placed the DoSomething class at +namespace (or sometimes class) scope. This separates the function's code +from its use, often to the detriment of readability. It requires giving the +class a globally unique name (if at namespace scope). It also loses the +information that the class is intended for use in exactly one place, and +does not have any subclasses. (However, the latter can now be indicated by +declaring it `final`.) Often, for simplicity, a local class will skip +things like access control and accessor functions, giving the enclosing +function direct access to the implementation and eliminating some +boilerplate that might be provided if the class is in some outer (more +accessible) scope. On the other hand, if there is a lot of surrounding code +in the function body or the local class is of significant size, defining it +locally can increase clutter and reduce readability. + + +C++11 added _lambda expressions_ as a new way to write a function object. +Simple lambda expressions can be significantly more concise than a function +object, eliminating a lot of boiler-plate. On the other hand, a complex +lambda expression may not provide much, if any, readability benefit compared +to an ordinary function object. Also, while a lambda can encapsulate a call +to a "function-like" object, it cannot be used in place of such. + +A common use for local functions is as one-use [RAII] objects. The amount +of boilerplate for a function object class (local or not) makes such usage +somewhat clumsy and verbose. But with the help of a small amount of +supporting utility code, lambdas work particularly well for this use case. + +Another use for local functions is [partial application][PARTIALAPP]. Again +here, lambdas are typically much simpler and less verbose than function +object classes. + +Because of these benefits, lambda expressions are permitted in HotSpot code, +with some restrictions and usage guidance. An anonymous lambda is one which +is passed directly as an argument. A named lambda is the value of a +variable, which is its name. + +Lambda expressions should only be passed downward. In particular, a lambda +should not be returned from a function or stored in a global variable, +whether directly or as the value of a member of some other object. Lambda +capture is syntactically subtle (by design), and propagating a lambda in +such ways can easily pass references to captured values to places where they +are no longer valid. In particular, members of the enclosing `this` object +are effectively captured by reference, even if the default capture is +by-value. For such uses-cases a function object class should be used to +make the desired value capturing and propagation explicit. + +Limiting the capture list to `[&]` (implicitly capture by reference) is a +simplifying restriction that still provides good support for HotSpot usage, +while reducing the cases a reader must recognize and understand. + +* Many common lambda uses require reference capture. Not permitting it +would substantially reduce the utility of lambdas. + +* Referential transparency. Implicit reference capture makes variable +references in the lambda body have the same meaning they would have in the +enclosing code. There isn't a semantic barrier across which the meaning of +a variable changes. + +* Explicit reference capture introduces significant clutter, especially when +lambda expressions are relatively small and simple, as they should be in +HotSpot code. + +* There are a number of reasons why by-value capture might be used, but for +the most part they don't apply to HotSpot code, given other usage restrictions. + + * A primary use-case for by-value capture is to support escaping uses, + where values captured by-reference might become invalid. That use-case + doesn't apply if only downward lambdas are used. + + * By-value capture can also make a lambda-local copy for mutation, which + requires making the lambda `mutable`; see below. + + * By-value capture might be viewed as an optimization, avoiding any + overhead for reference capture of cheap to copy values. But the + compiler can often eliminate any such overhead. + + * By-value capture by a non-`mutable` lambda makes the captured values + const, preventing any modification by the lambda and making the captured + value unaffected by modifications to the outer variable. But this only + applies to captured auto variables, not member variables, and is + inconsistent with referential transparency. + +* Non-capturing lambdas (with an empty capture list - `[]`) have limited +utility. There are cases where no captures are required (pure functions, +for example), but if the function is small and simple then that's obvious +anyway. + +* Capture initializers (a C++14 feature - [N3649]) are not permitted. +Capture initializers inherently increase the complexity of the capture list, +and provide little benefit over an additional in-scope local variable. + +The use of `mutable` lambda expressions is forbidden because there don't +seem to be many, if any, good use-cases for them in HotSpot. A lambda +expression needs to be mutable in order to modify a by-value captured value. +But with only downward lambdas, such usage seems likely to be rare and +complicated. It is better to use a function object class in any such cases +that arise, rather than requiring all HotSpot developers to understand this +relatively obscure feature. + +While it is possible to directly invoke an anonymous lambda expression, that +feature should not be used, as such a form can be confusing to readers. +Instead, name the lambda and call it by name. + +Some reasons to prefer a named lambda instead of an anonymous lambda are + +* The body contains non-trivial control flow or declarations or other nested +constructs. + +* Its role in an argument list is hard to guess without examining the +function declaration. Give it a name that indicates its purpose. + +* It has an unusual capture list. + +* It has a complex explicit return type or parameter types. + +Lambda expressions, and particularly anonymous lambda expressions, should be +simple and compact. One-liners are good. Anonymous lambdas should usually +be limited to a couple lines of body code. More complex lambdas should be +named. A named lambda should not clutter the enclosing function and make it +long and complex; do continue to break up large functions via the use of +separate helper functions. + +An anonymous lambda expression should either be a one-liner in a one-line +expression, or isolated in its own set of lines. Don't place part of a +lambda expression on the same line as other arguments to a function. The +body of a multi-line lambda argument should be indented from the start of +the capture list, as if that were the start of an ordinary function +definition. The body of a multi-line named lambda should be indented one +step from the variable's indentation. + +Some examples: + +1. `foo([&] { ++counter; });` +2. `foo(x, [&] { ++counter; });` +3. `foo([&] { if (predicate) ++counter; });` +4. `foo([&] { auto tmp = process(x); tmp.f(); return tmp.g(); })` +5. Separate one-line lambda from other arguments: + + ``` + foo(c.begin(), c.end(), + [&] (const X& x) { do_something(x); return x.value(); }); + ``` +6. Indentation for multi-line lambda: + + ``` + c.do_entries([&] (const X& x) { + do_something(x, a); + do_something1(x, b); + do_something2(x, c); + }); + ``` +7. Separate multi-line lambda from other arguments: + + ``` + foo(c.begin(), c.end(), + [&] (const X& x) { + do_something(x, a); + do_something1(x, b); + do_something2(x, c); + }); + ``` +8. Multi-line named lambda: + + ``` + auto do_entry = [&] (const X& x) { + do_something(x, a); + do_something1(x, b); + do_something2(x, c); + }; + ``` + +Item 4, and especially items 6 and 7, are pushing the simplicity limits for +anonymous lambdas. Item 6 might be better written using a named lambda: +``` +c.do_entries(do_entry); +``` + +Note that C++11 also added _bind expressions_ as a way to write a function +object for partial application, using `std::bind` and related facilities +from the Standard Library. `std::bind` generalizes and replaces some of the +binders from C++03. Bind expressions are not permitted in HotSpot code. +They don't provide enough benefit over lambdas or local function classes in +the cases where bind expressions are applicable to warrant the introduction +of yet another mechanism in this space into HotSpot code. + +References: + +* Local and unnamed types as template parameters ([n2657]) +* New wording for C++0x lambdas ([n2927]) +* Generalized lambda capture (init-capture) ([N3648]) +* Generic (polymorphic) lambda expressions ([N3649]) + +[n2657]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm +[n2927]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2927.pdf +[N3648]: https://isocpp.org/files/papers/N3648.html +[N3649]: https://isocpp.org/files/papers/N3649.html + +References from C++17 + +* Wording for constexpr lambda ([p0170r1]) +* Lambda capture of *this by Value ([p0018r3]) + +[p0170r1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0170r1.pdf +[p0018r3]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0018r3.html + +References from C++20 + +* Allow lambda capture [=, this] ([p0409r2]) +* Familiar template syntax for generic lambdas ([p0428r2]) +* Simplifying implicit lambda capture ([p0588r1]) +* Default constructible and assignable stateless lambdas ([p0624r2]) +* Lambdas in unevaluated contexts ([p0315r4]) +* Allow pack expansion in lambda init-capture ([p0780r2]) ([p2095r0]) +* Deprecate implicit capture of this via [=] ([p0806r2]) + +[p0409r2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0409r2.html +[p0428r2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0428r2.pdf +[p0588r1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0588r1.html +[p0624r2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0624r2.pdf +[p0315r4]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0315r4.pdf +[p0780r2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0780r2.html +[p2095r0]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2095r0.html +[p0806r2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0806r2.html + +References from C++23 + +* Make () more optional for lambdas ([p1102r2]) + +[p1102r2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1102r2.html + ### Additional Permitted Features * `constexpr` @@ -757,9 +1022,6 @@ part of the avoidance of the C++ Standard Library in HotSpot code. ([n3206](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3206.htm)), ([n3272](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3272.htm)) -* Local and unnamed types as template parameters -([n2657](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm)) - * Range-based `for` loops ([n2930](http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2930.html)) ([range-for](https://en.cppreference.com/w/cpp/language/range-for)) @@ -837,9 +1099,6 @@ features that have not yet been discussed. * Rvalue references and move semantics -* Lambdas - - [ADL]: https://en.cppreference.com/w/cpp/language/adl "Argument Dependent Lookup" @@ -854,3 +1113,6 @@ features that have not yet been discussed. [SFINAE]: https://en.cppreference.com/w/cpp/language/sfinae "Substitution Failure Is Not An Error" + +[PARTIALAPP]: https://en.wikipedia.org/wiki/Partial_application + "Partial Application" diff --git a/make/CreateJmods.gmk b/make/CreateJmods.gmk index 6edc69397a51fd6b96a354dc3e45567d67b9f6c1..d9e5f415fe787c5d0b7721464f15a339b6d15ead 100644 --- a/make/CreateJmods.gmk +++ b/make/CreateJmods.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -196,6 +196,11 @@ else # not java.base endif endif +# Set main class of jdk.httpserver module +ifeq ($(MODULE), jdk.httpserver) + JMOD_FLAGS += --main-class sun.net.httpserver.simpleserver.Main +endif + # Changes to the jmod tool itself should also trigger a rebuild of all jmods. # The variable JMOD_CMD could contain an environment variable assignment before # the actual command. Filter that out using wildcard before adding to DEPS. diff --git a/make/Hsdis.gmk b/make/Hsdis.gmk new file mode 100644 index 0000000000000000000000000000000000000000..2253da906797293b3089c450688a76760c15df0d --- /dev/null +++ b/make/Hsdis.gmk @@ -0,0 +1,118 @@ +# +# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +default: all + +include $(SPEC) +include MakeBase.gmk +include JdkNativeCompilation.gmk + +################################################################################ +# This makefile compiles and installs the hsdis library +# +################################################################################ + +HSDIS_OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/hsdis + +ifeq ($(call isTargetOs, windows), true) + INSTALLED_HSDIS_DIR := $(JDK_OUTPUTDIR)/bin + + # On windows, we need to "fake" a completely different toolchain using gcc + # instead of the normal microsoft toolchain. This is quite hacky... + + MINGW_BASE := x86_64-w64-mingw32 + + $(eval $(call DefineNativeToolchain, TOOLCHAIN_MINGW, \ + CC := $(MINGW_BASE)-gcc, \ + LD := $(MINGW_BASE)-ld, \ + OBJCOPY := $(MINGW_BASE)-objcopy, \ + RC := $(RC), \ + SYSROOT_CFLAGS := --sysroot=/usr/$(MINGW_BASE)/sys-root, \ + SYSROOT_LDFLAGS := --sysroot=/usr/$(MINGW_BASE)/sys-root, \ + )) + + TOOLCHAIN_TYPE := gcc + OPENJDK_TARGET_OS := linux + CC_OUT_OPTION := -o$(SPACE) + LD_OUT_OPTION := -o$(SPACE) + GENDEPS_FLAGS := -MMD -MF + CFLAGS_DEBUG_SYMBOLS := -g + DISABLED_WARNINGS := + DISABLE_WARNING_PREFIX := -Wno- + CFLAGS_WARNINGS_ARE_ERRORS := -Werror + SHARED_LIBRARY_FLAGS := -shared + + HSDIS_TOOLCHAIN := TOOLCHAIN_MINGW + HSDIS_TOOLCHAIN_CFLAGS := + HSDIS_TOOLCHAIN_LDFLAGS := -L/usr/lib/gcc/$(MINGW_BASE)/9.2.0 \ + -L/usr/$(MINGW_BASE)/sys-root/mingw/lib + MINGW_DLLCRT := /usr/$(MINGW_BASE)/sys-root/mingw/lib/dllcrt2.o + HSDIS_TOOLCHAIN_LIBS := $(MINGW_DLLCRT) -lmingw32 -lgcc -lgcc_eh -lmoldname \ + -lmingwex -lmsvcrt -lpthread -ladvapi32 -lshell32 -luser32 -lkernel32 +else + INSTALLED_HSDIS_DIR := $(JDK_OUTPUTDIR)/lib + + HSDIS_TOOLCHAIN := TOOLCHAIN_DEFAULT + HSDIS_TOOLCHAIN_CFLAGS := $(CFLAGS_JDKLIB) + HSDIS_TOOLCHAIN_LDFLAGS := $(LDFLAGS_JDKLIB) + HSDIS_TOOLCHAIN_LIBS := -ldl +endif + + +$(eval $(call SetupJdkLibrary, BUILD_HSDIS, \ + NAME := hsdis, \ + SRC := $(TOPDIR)/src/utils/hsdis, \ + TOOLCHAIN := $(HSDIS_TOOLCHAIN), \ + OUTPUT_DIR := $(HSDIS_OUTPUT_DIR), \ + OBJECT_DIR := $(HSDIS_OUTPUT_DIR), \ + DISABLED_WARNINGS_gcc := undef format-nonliteral sign-compare, \ + DISABLED_WARNINGS_clang := undef format-nonliteral, \ + CFLAGS := $(HSDIS_TOOLCHAIN_CFLAGS) $(HSDIS_CFLAGS), \ + LDFLAGS := $(HSDIS_TOOLCHAIN_LDFLAGS) $(SHARED_LIBRARY_FLAGS), \ + LIBS := $(HSDIS_LIBS) $(HSDIS_TOOLCHAIN_LIBS), \ +)) + +build: $(BUILD_HSDIS) + +TARGETS += build + +INSTALLED_HSDIS_NAME := hsdis-$(OPENJDK_TARGET_CPU_LEGACY_LIB)$(SHARED_LIBRARY_SUFFIX) + +INSTALLED_HSDIS := $(INSTALLED_HSDIS_DIR)/$(INSTALLED_HSDIS_NAME) + +$(INSTALLED_HSDIS): $(BUILD_HSDIS_TARGET) + $(call LogWarn, NOTE: The resulting build might not be redistributable. Seek legal advice before distibuting.) + $(install-file) + + +install: $(INSTALLED_HSDIS) + +TARGETS += install + +################################################################################ + +all: $(TARGETS) + +.PHONY: all default build install diff --git a/make/Main.gmk b/make/Main.gmk index cad3ecb3203b71c6e22c3e8ee65963c700e732d8..75eee65ba84afddd1f787ddbac7d13c2fd205fd8 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -526,6 +526,18 @@ $(eval $(call SetupTarget, update-x11wrappers, \ DEPS := java.base-copy buildtools-jdk, \ )) +ifneq ($(HSDIS_BACKEND), none) + $(eval $(call SetupTarget, build-hsdis, \ + MAKEFILE := Hsdis, \ + TARGET := build, \ + )) + + $(eval $(call SetupTarget, install-hsdis, \ + MAKEFILE := Hsdis, \ + TARGET := install, \ + )) +endif + ################################################################################ # Cross compilation support diff --git a/make/RunTests.gmk b/make/RunTests.gmk index f1da577de6a4b3ea71a77527ea68d37b129b6819..a2c8ea8101eac3fbdca236ec0a3f087acae94cc0 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -200,9 +200,10 @@ $(eval $(call SetTestOpt,FAILURE_HANDLER_TIMEOUT,JTREG)) $(eval $(call ParseKeywordVariable, JTREG, \ SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \ TEST_MODE ASSERT VERBOSE RETAIN MAX_MEM RUN_PROBLEM_LISTS \ - RETRY_COUNT MAX_OUTPUT, \ + RETRY_COUNT MAX_OUTPUT $(CUSTOM_JTREG_SINGLE_KEYWORDS), \ STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \ - EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS, \ + EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \ + $(CUSTOM_JTREG_STRING_KEYWORDS), \ )) ifneq ($(JTREG), ) @@ -832,6 +833,8 @@ define SetupRunJtregTestBody endif endif + $$(eval $$(call SetupRunJtregTestCustom, $1)) + clean-workdir-$1: $$(RM) -r $$($1_TEST_SUPPORT_DIR) diff --git a/make/ToolsJdk.gmk b/make/ToolsJdk.gmk index 99e8bd9727c61321f2971c425dc1eeb551675dc1..395a78602f626a83e7ecdd2fb45e980e6caf7119 100644 --- a/make/ToolsJdk.gmk +++ b/make/ToolsJdk.gmk @@ -75,6 +75,8 @@ TOOL_MAKEJAVASECURITY = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_clas build.tools.makejavasecurity.MakeJavaSecurity TOOL_GENERATECACERTS = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ + -Dkeystore.pkcs12.certProtectionAlgorithm=NONE \ + -Dkeystore.pkcs12.macAlgorithm=NONE \ build.tools.generatecacerts.GenerateCacerts TOOL_GENERATEEMOJIDATA = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 0faec69738e185af9d454b48d37ae798fef548d7..29ed3f206aa46722b6b50bbb8d3ed047c764a214 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -249,6 +249,7 @@ JDKOPT_EXCLUDE_TRANSLATIONS JDKOPT_ENABLE_DISABLE_MANPAGES JDKOPT_ENABLE_DISABLE_CDS_ARCHIVE JDKOPT_ENABLE_DISABLE_COMPATIBLE_CDS_ALIGNMENT +JDKOPT_SETUP_HSDIS ############################################################################### # diff --git a/make/autoconf/help.m4 b/make/autoconf/help.m4 index 924cc42560986e1d4ceece7e07555a4a44658c90..f36aa2819dfcc943e80509ec32113bf14eb7908a 100644 --- a/make/autoconf/help.m4 +++ b/make/autoconf/help.m4 @@ -80,6 +80,14 @@ cygwin_help() { PKGHANDLER_COMMAND="( cd && cmd /c setup -q -P make )" HELP_MSG="You might be able to fix this by running '$PKGHANDLER_COMMAND'." ;; + i686-w64-mingw32-gcc) + PKGHANDLER_COMMAND="( cd && cmd /c setup -q -P gcc-core i686-w64-mingw32-gcc-core mingw64-i686-glib2.0 )" + HELP_MSG="You might be able to fix this by running '$PKGHANDLER_COMMAND'." + ;; + x86_64-w64-mingw32-gcc) + PKGHANDLER_COMMAND="( cd && cmd /c setup -q -P gcc-core x86_64-w64-mingw32-gcc-core mingw64-x86_64-glib2.0 )" + HELP_MSG="You might be able to fix this by running '$PKGHANDLER_COMMAND'." + ;; esac } diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 299f76bd1e63c480bf6396b24967f78d9be982d1..c937101c2c71af49c93377beb8cf4f7e35e50d6a 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -704,3 +704,145 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_REPRODUCIBLE_BUILD], AC_SUBST(SOURCE_DATE) AC_SUBST(ENABLE_REPRODUCIBLE_BUILD) ]) + +################################################################################ +# +# Helper function to build binutils from source. +# +AC_DEFUN([JDKOPT_BUILD_BINUTILS], +[ + BINUTILS_SRC="$with_binutils_src" + UTIL_FIXUP_PATH(BINUTILS_SRC) + + if ! test -d $BINUTILS_SRC; then + AC_MSG_ERROR([--with-binutils-src is not pointing to a directory]) + fi + if ! test -x $BINUTILS_SRC/configure; then + AC_MSG_ERROR([--with-binutils-src does not look like a binutils source directory]) + fi + + if test -e $BINUTILS_SRC/bfd/libbfd.a && \ + test -e $BINUTILS_SRC/opcodes/libopcodes.a && \ + test -e $BINUTILS_SRC/libiberty/libiberty.a && \ + test -e $BINUTILS_SRC/zlib/libz.a; then + AC_MSG_NOTICE([Found binutils binaries in binutils source directory -- not building]) + else + # On Windows, we cannot build with the normal Microsoft CL, but must instead use + # a separate mingw toolchain. + if test "x$OPENJDK_BUILD_OS" = xwindows; then + if test "x$OPENJDK_TARGET_CPU" = "xx86"; then + target_base="i686-w64-mingw32" + else + target_base="$OPENJDK_TARGET_CPU-w64-mingw32" + fi + binutils_cc="$target_base-gcc" + binutils_target="--host=$target_base --target=$target_base" + # Somehow the uint typedef is not included when building with mingw + binutils_cflags="-Duint=unsigned" + compiler_version=`$binutils_cc --version 2>&1` + if ! [ [[ "$compiler_version" =~ GCC ]] ]; then + AC_MSG_NOTICE([Could not find correct mingw compiler $binutils_cc.]) + HELP_MSG_MISSING_DEPENDENCY([$binutils_cc]) + AC_MSG_ERROR([Cannot continue. $HELP_MSG]) + else + AC_MSG_NOTICE([Using compiler $binutils_cc with version $compiler_version]) + fi + elif test "x$OPENJDK_BUILD_OS" = xmacosx; then + if test "x$OPENJDK_TARGET_CPU" = "xaarch64"; then + binutils_target="--enable-targets=aarch64-darwin" + else + binutils_target="" + fi + else + binutils_cc="$CC $SYSROOT_CFLAGS" + binutils_target="" + fi + binutils_cflags="$binutils_cflags $MACHINE_FLAG $JVM_PICFLAG $C_O_FLAG_NORM" + + AC_MSG_NOTICE([Running binutils configure]) + AC_MSG_NOTICE([configure command line: ./configure --disable-nls CFLAGS="$binutils_cflags" CC="$binutils_cc" $binutils_target]) + saved_dir=`pwd` + cd "$BINUTILS_SRC" + ./configure --disable-nls CFLAGS="$binutils_cflags" CC="$binutils_cc" $binutils_target + if test $? -ne 0 || ! test -e $BINUTILS_SRC/Makefile; then + AC_MSG_NOTICE([Automatic building of binutils failed on configure. Try building it manually]) + AC_MSG_ERROR([Cannot continue]) + fi + AC_MSG_NOTICE([Running binutils make]) + $MAKE all-opcodes + if test $? -ne 0; then + AC_MSG_NOTICE([Automatic building of binutils failed on make. Try building it manually]) + AC_MSG_ERROR([Cannot continue]) + fi + cd $saved_dir + AC_MSG_NOTICE([Building of binutils done]) + fi + + BINUTILS_DIR="$BINUTILS_SRC" +]) + +################################################################################ +# +# Determine if hsdis should be built, and if so, with which backend. +# +AC_DEFUN_ONCE([JDKOPT_SETUP_HSDIS], +[ + AC_ARG_WITH([hsdis], [AS_HELP_STRING([--with-hsdis], + [what hsdis backend to use ('none', 'binutils') @<:@none@:>@])]) + + AC_ARG_WITH([binutils], [AS_HELP_STRING([--with-binutils], + [where to find the binutils files needed for hsdis/binutils])]) + + AC_ARG_WITH([binutils-src], [AS_HELP_STRING([--with-binutils-src], + [where to find the binutils source for building])]) + + AC_MSG_CHECKING([what hsdis backend to use]) + + if test "x$with_hsdis" = xyes; then + AC_MSG_ERROR([--with-hsdis must have a value]) + elif test "x$with_hsdis" = xnone || test "x$with_hsdis" = xno || test "x$with_hsdis" = x; then + HSDIS_BACKEND=none + AC_MSG_RESULT(['none', hsdis will not be built]) + elif test "x$with_hsdis" = xbinutils; then + HSDIS_BACKEND=binutils + AC_MSG_RESULT(['binutils']) + + # We need the binutils static libs and includes. + if test "x$with_binutils_src" != x; then + # Try building the source first. If it succeeds, it sets $BINUTILS_DIR. + JDKOPT_BUILD_BINUTILS + fi + + if test "x$with_binutils" != x; then + BINUTILS_DIR="$with_binutils" + fi + + AC_MSG_CHECKING([for binutils to use with hsdis]) + if test "x$BINUTILS_DIR" != x; then + if test -e $BINUTILS_DIR/bfd/libbfd.a && \ + test -e $BINUTILS_DIR/opcodes/libopcodes.a && \ + test -e $BINUTILS_DIR/libiberty/libiberty.a; then + AC_MSG_RESULT([$BINUTILS_DIR]) + HSDIS_CFLAGS="-I$BINUTILS_DIR/include -I$BINUTILS_DIR/bfd -DLIBARCH_$OPENJDK_TARGET_CPU_LEGACY_LIB" + HSDIS_LIBS="$BINUTILS_DIR/bfd/libbfd.a $BINUTILS_DIR/opcodes/libopcodes.a $BINUTILS_DIR/libiberty/libiberty.a $BINUTILS_DIR/zlib/libz.a" + else + AC_MSG_RESULT([invalid]) + AC_MSG_ERROR([$BINUTILS_DIR does not contain a proper binutils installation]) + fi + else + AC_MSG_RESULT([missing]) + AC_MSG_NOTICE([--with-hsdis=binutils requires specifying a binutils installation.]) + AC_MSG_NOTICE([Download binutils from https://www.gnu.org/software/binutils and unpack it,]) + AC_MSG_NOTICE([and point --with-binutils-src to the resulting directory, or use]) + AC_MSG_NOTICE([--with-binutils to point to a pre-built binutils installation.]) + AC_MSG_ERROR([Cannot continue]) + fi + else + AC_MSG_RESULT([invalid]) + AC_MSG_ERROR([Incorrect hsdis backend "$with_hsdis"]) + fi + + AC_SUBST(HSDIS_BACKEND) + AC_SUBST(HSDIS_CFLAGS) + AC_SUBST(HSDIS_LIBS) +]) diff --git a/make/autoconf/jvm-features.m4 b/make/autoconf/jvm-features.m4 index a4d0bf62ec2c235b5fac891d8c65ce871309dcec..1f76c323129fd366a358b72f1e67c6a0a63b7568 100644 --- a/make/autoconf/jvm-features.m4 +++ b/make/autoconf/jvm-features.m4 @@ -357,6 +357,13 @@ AC_DEFUN_ONCE([JVM_FEATURES_CHECK_ZGC], AC_MSG_RESULT([no, $OPENJDK_TARGET_OS-$OPENJDK_TARGET_CPU]) AVAILABLE=false fi + elif test "x$OPENJDK_TARGET_CPU" = "xppc64le"; then + if test "x$OPENJDK_TARGET_OS" = "xlinux"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no, $OPENJDK_TARGET_OS-$OPENJDK_TARGET_CPU]) + AVAILABLE=false + fi else AC_MSG_RESULT([no, $OPENJDK_TARGET_OS-$OPENJDK_TARGET_CPU]) AVAILABLE=false diff --git a/make/autoconf/lib-ffi.m4 b/make/autoconf/lib-ffi.m4 index 6aec7bc3bd25e7f9405d65048672175a89ecc129..0905c3cd2255af758917184de61b1dfa44e350db 100644 --- a/make/autoconf/lib-ffi.m4 +++ b/make/autoconf/lib-ffi.m4 @@ -149,7 +149,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBFFI], else AC_MSG_ERROR([Could not locate libffi.so.? for bundling]) fi - elif test "x${OPENJDK_TARGET_CPU}" = "xx86_64"; then + elif test "x${OPENJDK_TARGET_CPU}" = "xx86_64" || test "x${OPENJDK_TARGET_CPU}" = "xaarch64"; then if test -e ${SYSROOT}/usr/lib64/libffi.so.? ; then LIBFFI_LIB_FILE="${SYSROOT}/usr/lib64/libffi.so.?" elif test -e ${SYSROOT}/usr/lib/x86_64-linux-gnu/libffi.so.? ; then diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index bb7d63346f4425ffd624f51bf425c5ae02fa3e90..72be922f1036f0b31ad6fc4ccd347f1e5eb69871 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -359,6 +359,10 @@ ENABLE_COMPATIBLE_CDS_ALIGNMENT := @ENABLE_COMPATIBLE_CDS_ALIGNMENT@ ALLOW_ABSOLUTE_PATHS_IN_OUTPUT := @ALLOW_ABSOLUTE_PATHS_IN_OUTPUT@ +HSDIS_BACKEND := @HSDIS_BACKEND@ +HSDIS_CFLAGS := @HSDIS_CFLAGS@ +HSDIS_LIBS := @HSDIS_LIBS@ + # The boot jdk to use. This is overridden in bootcycle-spec.gmk. Make sure to keep # it in sync. BOOT_JDK:=@BOOT_JDK@ diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 7ae3fb6012f3d1537a8b44a0516add5fd4b91a60..5fc212aa45223aef983b978641bc97864184255f 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -394,13 +394,8 @@ var getJibProfilesCommon = function (input, data) { }; }; - if (input.build_os == 'macosx' && input.build_cpu == 'aarch64') { - common.boot_jdk_version = "17"; - common.boot_jdk_build_number = "24"; - } else { - common.boot_jdk_version = "16"; - common.boot_jdk_build_number = "36"; - } + common.boot_jdk_version = "17"; + common.boot_jdk_build_number = "35"; common.boot_jdk_home = input.get("boot_jdk", "install_path") + "/jdk-" + common.boot_jdk_version + (input.build_os == "macosx" ? ".jdk/Contents/Home" : ""); @@ -564,7 +559,7 @@ var getJibProfilesProfiles = function (input, common, data) { "ANT_HOME": input.get("ant", "home_path") } }; - [ "linux-x64", "macosx-x64", "windows-x64"] + [ "linux-x64", "macosx-x64", "windows-x64", "linux-aarch64"] .forEach(function (name) { var maketestName = name + "-testmake"; profiles[maketestName] = concatObjects(profiles[name], testmakeBase); @@ -593,6 +588,17 @@ var getJibProfilesProfiles = function (input, common, data) { ]) }, + "linux-aarch64-zero": { + target_os: "linux", + target_cpu: "aarch64", + dependencies: ["devkit", "gtest"], + configure_args: concat(common.configure_args_64bit, [ + "--with-zlib=system", + "--with-jvm-variants=zero", + "--enable-libffi-bundling" + ]) + }, + "linux-x86-zero": { target_os: "linux", target_cpu: "x86", @@ -638,7 +644,7 @@ var getJibProfilesProfiles = function (input, common, data) { // Bootcycle profiles runs the build with itself as the boot jdk. This can // be done in two ways. Either using the builtin bootcycle target in the // build system. Or by supplying the main jdk build as bootjdk to configure. - [ "linux-x64", "macosx-x64", "windows-x64" ] + [ "linux-x64", "macosx-x64", "windows-x64", "linux-aarch64" ] .forEach(function (name) { var bootcycleName = name + "-bootcycle"; var bootcyclePrebuiltName = name + "-bootcycle-prebuilt"; @@ -1073,11 +1079,7 @@ var getJibProfilesDependencies = function (input, common) { } var boot_jdk_os = input.build_os; if (input.build_os == "macosx") { - if (input.build_cpu == "aarch64") { - boot_jdk_os = "macos"; - } else { - boot_jdk_os = "osx"; - } + boot_jdk_os = "macos"; } var boot_jdk_platform = boot_jdk_os + "-" + input.build_cpu; var boot_jdk_ext = (input.build_os == "windows" ? ".zip" : ".tar.gz") @@ -1141,9 +1143,9 @@ var getJibProfilesDependencies = function (input, common) { jtreg: { server: "jpg", product: "jtreg", - version: "6", + version: "6.1", build_number: "1", - file: "bundles/jtreg-6+1.zip", + file: "bundles/jtreg-6.1+1.zip", environment_name: "JT_HOME", environment_path: input.get("jtreg", "home_path") + "/bin", configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"), diff --git a/make/conf/test-dependencies b/make/conf/test-dependencies index 4619ab744471c1805bfc79c1c964f7c5389f165f..1e53b8f10f1435e961aebad20dbfe08cd9a30682 100644 --- a/make/conf/test-dependencies +++ b/make/conf/test-dependencies @@ -25,19 +25,19 @@ # Versions and download locations for dependencies used by pre-submit testing. -BOOT_JDK_VERSION=16 -JTREG_VERSION=6 +BOOT_JDK_VERSION=17 +JTREG_VERSION=6.1 JTREG_BUILD=1 GTEST_VERSION=1.8.1 -LINUX_X64_BOOT_JDK_FILENAME=openjdk-16_linux-x64_bin.tar.gz -LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk16/7863447f0ab643c585b9bdebf67c69db/36/GPL/openjdk-16_linux-x64_bin.tar.gz -LINUX_X64_BOOT_JDK_SHA256=e952958f16797ad7dc7cd8b724edd69ec7e0e0434537d80d6b5165193e33b931 +LINUX_X64_BOOT_JDK_FILENAME=openjdk-17_linux-x64_bin.tar.gz +LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_linux-x64_bin.tar.gz +LINUX_X64_BOOT_JDK_SHA256=aef49cc7aa606de2044302e757fa94c8e144818e93487081c4fd319ca858134b -WINDOWS_X64_BOOT_JDK_FILENAME=openjdk-16_windows-x64_bin.zip -WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk16/7863447f0ab643c585b9bdebf67c69db/36/GPL/openjdk-16_windows-x64_bin.zip -WINDOWS_X64_BOOT_JDK_SHA256=a78bdeaad186297601edac6772d931224d7af6f682a43372e693c37020bd37d6 +WINDOWS_X64_BOOT_JDK_FILENAME=openjdk-17_windows-x64_bin.zip +WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_windows-x64_bin.zip +WINDOWS_X64_BOOT_JDK_SHA256=e88b0df00021c9d266bb435c9a95fdc67d1948cce4518daf85c234907bd393c5 -MACOS_X64_BOOT_JDK_FILENAME=openjdk-16_osx-x64_bin.tar.gz -MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk16/7863447f0ab643c585b9bdebf67c69db/36/GPL/openjdk-16_osx-x64_bin.tar.gz -MACOS_X64_BOOT_JDK_SHA256=16f3e39a31e86f3f51b0b4035a37494a47ed3c4ead760eafc6afd7afdf2ad9f2 +MACOS_X64_BOOT_JDK_FILENAME=openjdk-17_macos-x64_bin.tar.gz +MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_macos-x64_bin.tar.gz +MACOS_X64_BOOT_JDK_SHA256=18e11cf9bbc6f584031e801b11ae05a233c32086f8e1b84eb8a1e9bb8e1f5d90 diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 1a5484311827dca122ddc78b93e63ee3caba2542..2d12d668a71051fad460bebc8a7a6027a2706e9f 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -37,6 +37,6 @@ DEFAULT_VERSION_DATE=2022-03-15 DEFAULT_VERSION_CLASSFILE_MAJOR=62 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 -DEFAULT_ACCEPTABLE_BOOT_VERSIONS="16 17 18" +DEFAULT_ACCEPTABLE_BOOT_VERSIONS="17 18" DEFAULT_JDK_SOURCE_TARGET_VERSION=18 DEFAULT_PROMOTED_VERSION_PRE=ea diff --git a/make/data/currency/CurrencyData.properties b/make/data/currency/CurrencyData.properties index 1a92ef5b8673690c313cad89d8c5368d7a6e646a..236e544feaf65543bac0ca05d69e55946dd31996 100644 --- a/make/data/currency/CurrencyData.properties +++ b/make/data/currency/CurrencyData.properties @@ -32,7 +32,7 @@ formatVersion=3 # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=169 +dataVersion=170 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -54,7 +54,7 @@ all=ADP020-AED784-AFA004-AFN971-ALL008-AMD051-ANG532-AOA973-ARS032-ATS040-AUD036 SBD090-SCR690-SDD736-SDG938-SEK752-SGD702-SHP654-SIT705-SKK703-SLL694-SOS706-\ SRD968-SRG740-SSP728-STD678-STN930-SVC222-SYP760-SZL748-THB764-TJS972-TMM795-TMT934-TND788-TOP776-\ TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\ - UYU858-UZS860-VEB862-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ + UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ XBB956-XBC957-XBD958-XCD951-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\ XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWL932-\ ZWN942-ZWR935 diff --git a/make/data/hotspot-symbols/symbols-unix b/make/data/hotspot-symbols/symbols-unix index a34732739d35b9b64433cee4feff724c77af5017..d735f61b3a456bc9878949861043ebb995eceed7 100644 --- a/make/data/hotspot-symbols/symbols-unix +++ b/make/data/hotspot-symbols/symbols-unix @@ -183,6 +183,7 @@ JVM_ReferenceRefersTo JVM_RegisterLambdaProxyClassForArchiving JVM_RegisterSignal JVM_ReleaseUTF +JVM_ReportFinalizationComplete JVM_ResumeThread JVM_SetArrayElement JVM_SetClassSigners diff --git a/make/data/jdwp/jdwp.spec b/make/data/jdwp/jdwp.spec index b55a286aaa1ed47ab34ceddd838d91ebb3006853..51fe13cd9ef7f075763e5a2b82fe373284997e25 100644 --- a/make/data/jdwp/jdwp.spec +++ b/make/data/jdwp/jdwp.spec @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,9 +134,9 @@ JDWP "Java(tm) Debug Wire Protocol" "
      " "
    • All event requests are cancelled. " "
    • All threads suspended by the thread-level " - "resume command " + "suspend command " "or the VM-level " - "resume command " + "suspend command " "are resumed as many times as necessary for them to run. " "
    • Garbage collection is re-enabled in all cases where it was " "disabled " diff --git a/make/data/tzdata/VERSION b/make/data/tzdata/VERSION index 71632a7bb61312e54d153f4879953b81c3083305..b5c971d897937678e10650d567dc9926e84e2e5a 100644 --- a/make/data/tzdata/VERSION +++ b/make/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2021a +tzdata2021c diff --git a/make/data/tzdata/africa b/make/data/tzdata/africa index 5de2e5f4ab1b0b97a6b029bd0e38733e8d1a41e0..0f367713ea900d457b14a23963854bc94c1c1414 100644 --- a/make/data/tzdata/africa +++ b/make/data/tzdata/africa @@ -53,9 +53,6 @@ # Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94. # https://www.jstor.org/stable/1774359 # -# A reliable and entertaining source about time zones is -# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). -# # European-style abbreviations are commonly used along the Mediterranean. # For sub-Saharan Africa abbreviations were less standardized. # Previous editions of this database used WAT, CAT, SAT, and EAT @@ -176,8 +173,9 @@ Zone Africa/Ndjamena 1:00:12 - LMT 1912 # N'Djamena # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Abidjan -0:16:08 - LMT 1912 0:00 - GMT +Link Africa/Abidjan Africa/Accra # Ghana Link Africa/Abidjan Africa/Bamako # Mali -Link Africa/Abidjan Africa/Banjul # Gambia +Link Africa/Abidjan Africa/Banjul # The Gambia Link Africa/Abidjan Africa/Conakry # Guinea Link Africa/Abidjan Africa/Dakar # Senegal Link Africa/Abidjan Africa/Freetown # Sierra Leone @@ -404,93 +402,8 @@ Zone Africa/Cairo 2:05:09 - LMT 1900 Oct # Gabon # See Africa/Lagos. -# Gambia -# See Africa/Abidjan. - +# The Gambia # Ghana - -# From P Chan (2020-11-20): -# Interpretation Amendment Ordinance, 1915 (No.24 of 1915) [1915-11-02] -# Ordinances of the Gold Coast, Ashanti, Northern Territories 1915, p 69-71 -# https://books.google.com/books?id=ErA-AQAAIAAJ&pg=PA70 -# This Ordinance added "'Time' shall mean Greenwich Mean Time" to the -# Interpretation Ordinance, 1876. -# -# Determination of the Time Ordinance, 1919 (No. 18 of 1919) [1919-11-24] -# Ordinances of the Gold Coast, Ashanti, Northern Territories 1919, p 75-76 -# https://books.google.com/books?id=MbA-AQAAIAAJ&pg=PA75 -# This Ordinance removed the previous definition of time and introduced DST. -# -# Time Determination Ordinance (Cap. 214) -# The Laws of the Gold Coast (including Togoland Under British Mandate) -# Vol. II (1937), p 2328 -# https://books.google.com/books?id=Z7M-AQAAIAAJ&pg=PA2328 -# Revised edition of the 1919 Ordinance. -# -# Time Determination (Amendment) Ordinance, 1940 (No. 9 of 1940) [1940-04-06] -# Annual Volume of the Laws of the Gold Coast: -# Containing All Legislation Enacted During Year 1940, p 22 -# https://books.google.com/books?id=1ao-AQAAIAAJ&pg=PA22 -# This Ordinance changed the forward transition from September to May. -# -# Defence (Time Determination Ordinance Amendment) Regulations, 1942 -# (Regulations No. 6 of 1942) [1942-01-31, commenced on 1942-02-08] -# Annual Volume of the Laws of the Gold Coast: -# Containing All Legislation Enacted During Year 1942, p 48 -# https://books.google.com/books?id=Das-AQAAIAAJ&pg=PA48 -# These regulations advanced the [standard] time by thirty minutes. -# -# Defence (Time Determination Ordinance Amendment (No.2)) Regulations, -# 1942 (Regulations No. 28 of 1942) [1942-04-25] -# Annual Volume of the Laws of the Gold Coast: -# Containing All Legislation Enacted During Year 1942, p 87 -# https://books.google.com/books?id=Das-AQAAIAAJ&pg=PA87 -# These regulations abolished DST and changed the time to GMT+0:30. -# -# Defence (Revocation) (No.4) Regulations, 1945 (Regulations No. 45 of -# 1945) [1945-10-24, commenced on 1946-01-06] -# Annual Volume of the Laws of the Gold Coast: -# Containing All Legislation Enacted During Year 1945, p 256 -# https://books.google.com/books?id=9as-AQAAIAAJ&pg=PA256 -# These regulations revoked the previous two sets of Regulations. -# -# Time Determination (Amendment) Ordinance, 1945 (No. 18 of 1945) [1946-01-06] -# Annual Volume of the Laws of the Gold Coast: -# Containing All Legislation Enacted During Year 1945, p 69 -# https://books.google.com/books?id=9as-AQAAIAAJ&pg=PA69 -# This Ordinance abolished DST. -# -# Time Determination (Amendment) Ordinance, 1950 (No. 26 of 1950) [1950-07-22] -# Annual Volume of the Laws of the Gold Coast: -# Containing All Legislation Enacted During Year 1950, p 35 -# https://books.google.com/books?id=e60-AQAAIAAJ&pg=PA35 -# This Ordinance restored DST but with thirty minutes offset. -# -# Time Determination Ordinance (Cap. 264) -# The Laws of the Gold Coast, Vol. V (1954), p 380 -# https://books.google.com/books?id=Mqc-AQAAIAAJ&pg=PA380 -# Revised edition of the Time Determination Ordinance. -# -# Time Determination (Amendment) Ordinance, 1956 (No. 21 of 1956) [1956-08-29] -# Annual Volume of the Ordinances of the Gold Coast Enacted During the -# Year 1956, p 83 -# https://books.google.com/books?id=VLE-AQAAIAAJ&pg=PA83 -# This Ordinance abolished DST. - -# Rule NAME FROM TO - IN ON AT SAVE LETTER/S -Rule Ghana 1919 only - Nov 24 0:00 0:20 +0020 -Rule Ghana 1920 1942 - Jan 1 2:00 0 GMT -Rule Ghana 1920 1939 - Sep 1 2:00 0:20 +0020 -Rule Ghana 1940 1941 - May 1 2:00 0:20 +0020 -Rule Ghana 1950 1955 - Sep 1 2:00 0:30 +0030 -Rule Ghana 1951 1956 - Jan 1 2:00 0 GMT - -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Africa/Accra -0:00:52 - LMT 1915 Nov 2 - 0:00 Ghana %s 1942 Feb 8 - 0:30 - +0030 1946 Jan 6 - 0:00 Ghana %s - # Guinea # See Africa/Abidjan. @@ -755,7 +668,7 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # See Africa/Nairobi. # Morocco -# See the 'europe' file for Spanish Morocco (Africa/Ceuta). +# See Africa/Ceuta for Spanish Morocco. # From Alex Krivenyshev (2008-05-09): # Here is an article that Morocco plan to introduce Daylight Saving Time between @@ -1405,23 +1318,21 @@ Zone Africa/Lagos 0:13:35 - LMT 1905 Jul 1 0:13:35 - LMT 1914 Jan 1 0:30 - +0030 1919 Sep 1 1:00 - WAT -Link Africa/Lagos Africa/Bangui # Central African Republic -Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo -Link Africa/Lagos Africa/Douala # Cameroon -Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west) -Link Africa/Lagos Africa/Libreville # Gabon -Link Africa/Lagos Africa/Luanda # Angola -Link Africa/Lagos Africa/Malabo # Equatorial Guinea -Link Africa/Lagos Africa/Niamey # Niger -Link Africa/Lagos Africa/Porto-Novo # Benin +Link Africa/Lagos Africa/Bangui # Central African Republic +Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo +Link Africa/Lagos Africa/Douala # Cameroon +Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west) +Link Africa/Lagos Africa/Libreville # Gabon +Link Africa/Lagos Africa/Luanda # Angola +Link Africa/Lagos Africa/Malabo # Equatorial Guinea +Link Africa/Lagos Africa/Niamey # Niger +Link Africa/Lagos Africa/Porto-Novo # Benin # Réunion # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Indian/Reunion 3:41:52 - LMT 1911 Jun # Saint-Denis 4:00 - +04 # -# Crozet Islands also observes Réunion time; see the 'antarctica' file. -# # Scattered Islands (Îles Éparses) administered from Réunion are as follows. # The following information about them is taken from # Îles Éparses (, 1997-07-22, @@ -1513,8 +1424,8 @@ Rule SA 1943 1944 - Mar Sun>=15 2:00 0 - Zone Africa/Johannesburg 1:52:00 - LMT 1892 Feb 8 1:30 - SAST 1903 Mar 2:00 SA SAST -Link Africa/Johannesburg Africa/Maseru # Lesotho -Link Africa/Johannesburg Africa/Mbabane # Eswatini +Link Africa/Johannesburg Africa/Maseru # Lesotho +Link Africa/Johannesburg Africa/Mbabane # Eswatini # # Marion and Prince Edward Is # scientific station since 1947 @@ -1550,12 +1461,13 @@ Zone Africa/Khartoum 2:10:08 - LMT 1931 3:00 - EAT 2017 Nov 1 2:00 - CAT +# South Sudan + # From Steffen Thorsen (2021-01-18): # "South Sudan will change its time zone by setting the clock back 1 # hour on February 1, 2021...." # from https://eyeradio.org/south-sudan-adopts-new-time-zone-makuei/ -# South Sudan # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Juba 2:06:28 - LMT 1931 2:00 Sudan CA%sT 2000 Jan 15 12:00 @@ -1660,7 +1572,7 @@ Rule Tunisia 2005 only - Sep 30 1:00s 0 - Rule Tunisia 2006 2008 - Mar lastSun 2:00s 1:00 S Rule Tunisia 2006 2008 - Oct lastSun 2:00s 0 - -# See Europe/Paris for PMT-related transitions. +# See Europe/Paris commentary for PMT-related transitions. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Tunis 0:40:44 - LMT 1881 May 12 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time diff --git a/make/data/tzdata/antarctica b/make/data/tzdata/antarctica index 509fadc29a9644cb9d2d714e54588382a1ee3b16..13f024ef9bc4c6544b8fa11e5a1fb8ab80cf14fa 100644 --- a/make/data/tzdata/antarctica +++ b/make/data/tzdata/antarctica @@ -171,7 +171,7 @@ Zone Antarctica/Mawson 0 - -00 1954 Feb 13 # # Alfred Faure, Possession Island, Crozet Islands, -462551+0515152, since 1964; # sealing & whaling stations operated variously 1802/1911+; -# see Indian/Reunion. +# see Asia/Dubai. # # Martin-de-Viviès, Amsterdam Island, -374105+0773155, since 1950 # Port-aux-Français, Kerguelen Islands, -492110+0701303, since 1951; @@ -185,17 +185,7 @@ Zone Indian/Kerguelen 0 - -00 1950 # Port-aux-Français 5:00 - +05 # # year-round base in the main continent -# Dumont d'Urville, Île des Pétrels, -6640+14001, since 1956-11 -# (2005-12-05) -# -# Another base at Port-Martin, 50km east, began operation in 1947. -# It was destroyed by fire on 1952-01-14. -# -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Antarctica/DumontDUrville 0 - -00 1947 - 10:00 - +10 1952 Jan 14 - 0 - -00 1956 Nov - 10:00 - +10 +# Dumont d'Urville - see Pacific/Port_Moresby. # France & Italy - year-round base # Concordia, -750600+1232000, since 2005 @@ -211,20 +201,7 @@ Zone Antarctica/DumontDUrville 0 - -00 1947 # Zuchelli, Terra Nova Bay, -744140+1640647, since 1986 # Japan - year-round bases -# Syowa (also known as Showa), -690022+0393524, since 1957 -# -# From Hideyuki Suzuki (1999-02-06): -# In all Japanese stations, +0300 is used as the standard time. -# -# Syowa station, which is the first antarctic station of Japan, -# was established on 1957-01-29. Since Syowa station is still the main -# station of Japan, it's appropriate for the principal location. -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Antarctica/Syowa 0 - -00 1957 Jan 29 - 3:00 - +03 -# See: -# NIPR Antarctic Research Activities (1999-08-17) -# http://www.nipr.ac.jp/english/ara01.html +# See Asia/Riyadh. # S Korea - year-round base # Jang Bogo, Terra Nova Bay, -743700+1641205 since 2014 diff --git a/make/data/tzdata/asia b/make/data/tzdata/asia index 143d8e8fdc3876ef3346a6a18ea5026c1e171a88..a5cee81a42ee963fe7f1e8d0fef8a67afb828adc 100644 --- a/make/data/tzdata/asia +++ b/make/data/tzdata/asia @@ -57,9 +57,6 @@ # Byalokoz EL. New Counting of Time in Russia since July 1, 1919. # (See the 'europe' file for a fuller citation.) # -# A reliable and entertaining source about time zones is -# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). -# # The following alphabetic abbreviations appear in these tables # (corrections are welcome): # std dst @@ -2257,6 +2254,14 @@ Zone Asia/Tokyo 9:18:59 - LMT 1887 Dec 31 15:00u # From Paul Eggert (2013-12-11): # As Steffen suggested, consider the past 21-month experiment to be DST. +# From Steffen Thorsen (2021-09-24): +# The Jordanian Government announced yesterday that they will start DST +# in February instead of March: +# https://petra.gov.jo/Include/InnerPage.jsp?ID=37683&lang=en&name=en_news (English) +# https://petra.gov.jo/Include/InnerPage.jsp?ID=189969&lang=ar&name=news (Arabic) +# From the Arabic version, it seems to say it would be at midnight +# (assume 24:00) on the last Thursday in February, starting from 2022. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Jordan 1973 only - Jun 6 0:00 1:00 S Rule Jordan 1973 1975 - Oct 1 0:00 0 - @@ -2287,8 +2292,9 @@ Rule Jordan 2004 only - Oct 15 0:00s 0 - Rule Jordan 2005 only - Sep lastFri 0:00s 0 - Rule Jordan 2006 2011 - Oct lastFri 0:00s 0 - Rule Jordan 2013 only - Dec 20 0:00 0 - -Rule Jordan 2014 max - Mar lastThu 24:00 1:00 S +Rule Jordan 2014 2021 - Mar lastThu 24:00 1:00 S Rule Jordan 2014 max - Oct lastFri 0:00s 0 - +Rule Jordan 2022 max - Feb lastThu 24:00 1:00 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Amman 2:23:44 - LMT 1931 2:00 Jordan EE%sT @@ -2763,7 +2769,8 @@ Rule NBorneo 1935 1941 - Dec 14 0:00 0 - # # peninsular Malaysia # taken from Mok Ly Yng (2003-10-30) -# http://www.math.nus.edu.sg/aslaksen/teaching/timezone.html +# https://web.archive.org/web/20190822231045/http://www.math.nus.edu.sg/~mathelmr/teaching/timezone.html +# This agrees with Singapore since 1905-06-01. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Kuala_Lumpur 6:46:46 - LMT 1901 Jan 1 6:55:25 - SMT 1905 Jun 1 # Singapore M.T. @@ -3523,6 +3530,12 @@ Zone Asia/Hebron 2:20:23 - LMT 1900 Oct # influence of the sources. There is no current abbreviation for DST, # so use "PDT", the usual American style. +# From P Chan (2021-05-10): +# Here's a fairly comprehensive article in Japanese: +# https://wiki.suikawiki.org/n/Philippine%20Time +# From Paul Eggert (2021-05-10): +# The info in the Japanese table has not been absorbed (yet) below. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Phil 1936 only - Nov 1 0:00 1:00 D Rule Phil 1937 only - Feb 1 0:00 0 S @@ -3589,12 +3602,13 @@ Link Asia/Qatar Asia/Bahrain # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14 3:00 - +03 +Link Asia/Riyadh Antarctica/Syowa Link Asia/Riyadh Asia/Aden # Yemen Link Asia/Riyadh Asia/Kuwait # Singapore # taken from Mok Ly Yng (2003-10-30) -# http://www.math.nus.edu.sg/aslaksen/teaching/timezone.html +# https://web.archive.org/web/20190822231045/http://www.math.nus.edu.sg/~mathelmr/teaching/timezone.html # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 6:55:25 - SMT 1905 Jun 1 # Singapore M.T. diff --git a/make/data/tzdata/australasia b/make/data/tzdata/australasia index e28538e0c84e032d8911c0c526deaf081d5b3847..af72f11e5aede93d24f6b62b72de744abab66fd0 100644 --- a/make/data/tzdata/australasia +++ b/make/data/tzdata/australasia @@ -487,7 +487,7 @@ Link Pacific/Guam Pacific/Saipan # N Mariana Is # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Tarawa 11:32:04 - LMT 1901 # Bairiki 12:00 - +12 -Zone Pacific/Enderbury -11:24:20 - LMT 1901 +Zone Pacific/Kanton 0 - -00 1937 Aug 31 -12:00 - -12 1979 Oct -11:00 - -11 1994 Dec 31 13:00 - +13 @@ -620,13 +620,46 @@ Link Pacific/Auckland Antarctica/McMurdo # was probably like Pacific/Auckland # Cook Is -# From Shanks & Pottenger: +# +# From Alexander Krivenyshev (2021-03-24): +# In 1899 the Cook Islands celebrated Christmas twice to correct the calendar. +# According to the old books, missionaries were unaware of +# the International Date line, when they came from Sydney. +# Thus the Cook Islands were one day ahead.... +# http://nzetc.victoria.ac.nz/tm/scholarly/tei-KloDisc-t1-body-d18.html +# ... Appendix to the Journals of the House of Representatives, 1900 +# https://atojs.natlib.govt.nz/cgi-bin/atojs?a=d&d=AJHR1900-I.2.1.2.3 +# (page 20) +# +# From Michael Deckers (2021-03-24): +# ... in the Cook Island Act of 1915-10-11, online at +# http://www.paclii.org/ck/legis/ck-nz_act/cia1915132/ +# "651. The hour of the day shall in each of the islands included in the +# Cook Islands be determined in accordance with the meridian of that island." +# so that local (mean?) time was still used in Rarotonga (and Niue) in 1915. +# This was changed in the Cook Island Amendment Act of 1952-10-16 ... +# http://www.paclii.org/ck/legis/ck-nz_act/ciaa1952212/ +# "651 (1) The hour of the day in each of the islands included in the Cook +# Islands, other than Niue, shall be determined as if each island were +# situated on the meridian one hundred and fifty-seven degrees thirty minutes +# West of Greenwich. (2) The hour of the day in the Island of Niue shall be +# determined as if that island were situated on the meridian one hundred and +# seventy degrees West of Greenwich." +# This act does not state when it takes effect, so one has to assume it +# applies since 1952-10-16. But there is the possibility that the act just +# legalized prior existing practice, as we had seen with the Guernsey law of +# 1913-06-18 for the switch in 1909-04-19. +# +# From Paul Eggert (2021-03-24): +# Transitions after 1952 are from Shanks & Pottenger. +# # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Cook 1978 only - Nov 12 0:00 0:30 - Rule Cook 1979 1991 - Mar Sun>=1 0:00 0 - Rule Cook 1979 1990 - Oct lastSun 0:00 0:30 - # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Pacific/Rarotonga -10:39:04 - LMT 1901 # Avarua +Zone Pacific/Rarotonga 13:20:56 - LMT 1899 Dec 26 # Avarua + -10:39:04 - LMT 1952 Oct 16 -10:30 - -1030 1978 Nov 12 -10:00 Cook -10/-0930 @@ -634,10 +667,18 @@ Zone Pacific/Rarotonga -10:39:04 - LMT 1901 # Avarua # Niue +# See Pacific/Raratonga comments for 1952 transition. +# +# From Tim Parenti (2021-09-13): +# Consecutive contemporaneous editions of The Air Almanac listed -11:20 for +# Niue as of Apr 1964 but -11 as of Aug 1964: +# Apr 1964: https://books.google.com/books?id=_1So677Y5vUC&pg=SL1-PA23 +# Aug 1964: https://books.google.com/books?id=MbJloqd-zyUC&pg=SL1-PA23 +# Without greater specificity, guess 1964-07-01 for this transition. + # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Pacific/Niue -11:19:40 - LMT 1901 # Alofi - -11:20 - -1120 1951 - -11:30 - -1130 1978 Oct 1 +Zone Pacific/Niue -11:19:40 - LMT 1952 Oct 16 # Alofi + -11:20 - -1120 1964 Jul -11:00 - -11 # Norfolk @@ -661,6 +702,7 @@ Zone Pacific/Palau -15:02:04 - LMT 1844 Dec 31 # Koror Zone Pacific/Port_Moresby 9:48:40 - LMT 1880 9:48:32 - PMMT 1895 # Port Moresby Mean Time 10:00 - +10 +Link Pacific/Port_Moresby Antarctica/DumontDUrville # # From Paul Eggert (2014-10-13): # Base the Bougainville entry on the Arawa-Kieta region, which appears to have @@ -765,13 +807,17 @@ Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands # From Paul Eggert (2014-07-08): # That web page currently lists transitions for 2012/3 and 2013/4. # Assume the pattern instituted in 2012 will continue indefinitely. +# +# From Geoffrey D. Bennett (2021-09-20): +# https://www.mcil.gov.ws/storage/2021/09/MCIL-Scan_20210920_120553.pdf +# DST has been cancelled for this year. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule WS 2010 only - Sep lastSun 0:00 1 - Rule WS 2011 only - Apr Sat>=1 4:00 0 - Rule WS 2011 only - Sep lastSat 3:00 1 - -Rule WS 2012 max - Apr Sun>=1 4:00 0 - -Rule WS 2012 max - Sep lastSun 3:00 1 - +Rule WS 2012 2021 - Apr Sun>=1 4:00 0 - +Rule WS 2012 2020 - Sep lastSun 3:00 1 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Apia 12:33:04 - LMT 1892 Jul 5 -11:26:56 - LMT 1911 @@ -818,8 +864,8 @@ Rule Tonga 2001 2002 - Jan lastSun 2:00 0 - Rule Tonga 2016 only - Nov Sun>=1 2:00 1:00 - Rule Tonga 2017 only - Jan Sun>=15 3:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Pacific/Tongatapu 12:19:20 - LMT 1901 - 12:20 - +1220 1941 +Zone Pacific/Tongatapu 12:19:12 - LMT 1945 Sep 10 + 12:20 - +1220 1961 13:00 - +13 1999 13:00 Tonga +13/+14 @@ -1761,6 +1807,23 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # One source for this is page 202 of: Bartky IR. One Time Fits All: # The Campaigns for Global Uniformity (2007). +# Kanton + +# From Paul Eggert (2021-05-27): +# Kiribati's +13 timezone is represented by Kanton, its only populated +# island. (It was formerly spelled "Canton", but Gilbertese lacks "C".) +# Kanton was settled on 1937-08-31 by two British radio operators +# ; +# Americans came the next year and built an airfield, partly to +# establish airline service and perhaps partly anticipating the +# next war. Aside from the war, the airfield was used by commercial +# airlines until long-range jets became standard; although currently +# for emergency use only, China says it is considering rebuilding the +# airfield for high-end niche tourism. Kanton has about two dozen +# people, caretakers who rotate in from the rest of Kiribati in 2-5 +# year shifts, and who use some of the leftover structures +# . + # Kwajalein # From an AP article (1993-08-22): @@ -2044,6 +2107,17 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # Tonga +# From Paul Eggert (2021-03-04): +# In 1943 "The standard time kept is 12 hrs. 19 min. 12 sec. fast +# on Greenwich mean time." according to the Admiralty's Hydrographic +# Dept., Pacific Islands Pilot, Vol. II, 7th ed., 1943, p 360. + +# From Michael Deckers (2021-03-03): +# [Ian R Bartky: "One Time Fits All: The Campaigns for Global Uniformity". +# Stanford University Press. 2007. p. 255]: +# On 10 September 1945 Tonga adopted a standard time 12 hours, +# 20 minutes in advance of Greenwich. + # From Paul Eggert (1996-01-22): # Today's _Wall Street Journal_ (p 1) reports that "Tonga has been plotting # to sneak ahead of [New Zealanders] by introducing daylight-saving time." @@ -2072,9 +2146,26 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # The Crown Prince, presented an unanswerable argument: "Remember that # on the World Day of Prayer, you would be the first people on Earth # to say your prayers in the morning." - -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger say the transition was on 1968-10-01; go with Mundell. +# +# From Tim Parenti (2021-09-13), per Paul Eggert (2006-03-22) and Michael +# Deckers (2021-03-03): +# Mundell places the transition from +12:20 to +13 in 1941, while Shanks & +# Pottenger say the transition was on 1968-10-01. +# +# The Air Almanac published contemporaneous tables of standard times, +# which listed +12:20 as of Nov 1960 and +13 as of Mar 1961: +# Nov 1960: https://books.google.com/books?id=bVgtWM6kPZUC&pg=SL1-PA19 +# Mar 1961: https://books.google.com/books?id=W2nItAul4g0C&pg=SL1-PA19 +# (Thanks to P Chan for pointing us toward these sources.) +# This agrees with Bartky, who writes that "since 1961 [Tonga's] official time +# has been thirteen hours in advance of Greenwich time" (p. 202) and further +# writes in an endnote that this was because "the legislation was amended" on +# 1960-10-19. (p. 255) +# +# Without greater specificity, presume that Bartky and the Air Almanac point to +# a 1961-01-01 transition, as Tāufaʻāhau Tupou IV was still Crown Prince in +# 1961 and this still jives with the gist of Mundell's telling, and go with +# this over Shanks & Pottenger. # From Eric Ulevik (1999-05-03): # Tonga's director of tourism, who is also secretary of the National Millennium diff --git a/make/data/tzdata/backward b/make/data/tzdata/backward index 48482b74d301a0401a7545177afec1869e33801a..59c125623e2a15af64134b2f356a5751dca2764e 100644 --- a/make/data/tzdata/backward +++ b/make/data/tzdata/backward @@ -26,8 +26,10 @@ # This file is in the public domain, so clarified as of # 2009-05-17 by Arthur David Olson. -# This file provides links between current names for timezones -# and their old names. Many names changed in late 1993. +# This file provides links from old or merged timezone names to current ones. +# Many names changed in late 1993. Several of these names are +# also present in the file 'backzone', which has data important only +# for pre-1970 timestamps and so is out of scope for tzdb proper. # Link TARGET LINK-NAME Link Africa/Nairobi Africa/Asmera @@ -36,7 +38,7 @@ Link America/Argentina/Catamarca America/Argentina/ComodRivadavia Link America/Adak America/Atka Link America/Argentina/Buenos_Aires America/Buenos_Aires Link America/Argentina/Catamarca America/Catamarca -Link America/Atikokan America/Coral_Harbour +Link America/Panama America/Coral_Harbour Link America/Argentina/Cordoba America/Cordoba Link America/Tijuana America/Ensenada Link America/Indiana/Indianapolis America/Fort_Wayne @@ -51,7 +53,7 @@ Link America/Rio_Branco America/Porto_Acre Link America/Argentina/Cordoba America/Rosario Link America/Tijuana America/Santa_Isabel Link America/Denver America/Shiprock -Link America/Port_of_Spain America/Virgin +Link America/Puerto_Rico America/Virgin Link Pacific/Auckland Antarctica/South_Pole Link Asia/Ashgabat Asia/Ashkhabad Link Asia/Kolkata Asia/Calcutta @@ -126,6 +128,7 @@ Link Pacific/Auckland NZ Link Pacific/Chatham NZ-CHAT Link America/Denver Navajo Link Asia/Shanghai PRC +Link Pacific/Kanton Pacific/Enderbury Link Pacific/Honolulu Pacific/Johnston Link Pacific/Pohnpei Pacific/Ponape Link Pacific/Pago_Pago Pacific/Samoa diff --git a/make/data/tzdata/europe b/make/data/tzdata/europe index eb9056e92d58437f39dd03bde54608dfff292a7c..87f9a19f7acbad9586b3053d25803db33ef5a31e 100644 --- a/make/data/tzdata/europe +++ b/make/data/tzdata/europe @@ -91,7 +91,6 @@ # 0:00 GMT BST BDST Greenwich, British Summer # 0:00 GMT IST Greenwich, Irish Summer # 0:00 WET WEST WEMT Western Europe -# 0:19:32.13 AMT* NST* Amsterdam, Netherlands Summer (1835-1937) # 1:00 BST British Standard (1968-1971) # 1:00 IST GMT Irish Standard (1968-) with winter DST # 1:00 CET CEST CEMT Central Europe @@ -1823,6 +1822,10 @@ Zone Europe/Rome 0:49:56 - LMT 1866 Dec 12 1:00 Italy CE%sT 1980 1:00 EU CE%sT +# Kosovo +# See Europe/Belgrade. + + Link Europe/Rome Europe/Vatican Link Europe/Rome Europe/San_Marino @@ -2173,6 +2176,10 @@ Zone Europe/Monaco 0:29:32 - LMT 1892 Jun 1 # The data entries before 1945 are taken from # https://www.staff.science.uu.nl/~gent0113/wettijd/wettijd.htm +# From Paul Eggert (2021-05-09): +# I invented the abbreviations AMT for Amsterdam Mean Time and NST for +# Netherlands Summer Time, used in the Netherlands from 1835 to 1937. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Neth 1916 only - May 1 0:00 1:00 NST # Netherlands Summer Time Rule Neth 1916 only - Oct 1 0:00 0 AMT # Amsterdam Mean Time @@ -2399,12 +2406,10 @@ Rule Port 1943 1945 - Aug Sat>=25 22:00s 1:00 S Rule Port 1944 1945 - Apr Sat>=21 22:00s 2:00 M Rule Port 1946 only - Apr Sat>=1 23:00s 1:00 S Rule Port 1946 only - Oct Sat>=1 23:00s 0 - -Rule Port 1947 1949 - Apr Sun>=1 2:00s 1:00 S -Rule Port 1947 1949 - Oct Sun>=1 2:00s 0 - -# Shanks & Pottenger say DST was observed in 1950; go with Whitman. +# Whitman says DST was not observed in 1950; go with Shanks & Pottenger. # Whitman gives Oct lastSun for 1952 on; go with Shanks & Pottenger. -Rule Port 1951 1965 - Apr Sun>=1 2:00s 1:00 S -Rule Port 1951 1965 - Oct Sun>=1 2:00s 0 - +Rule Port 1947 1965 - Apr Sun>=1 2:00s 1:00 S +Rule Port 1947 1965 - Oct Sun>=1 2:00s 0 - Rule Port 1977 only - Mar 27 0:00s 1:00 S Rule Port 1977 only - Sep 25 0:00s 0 - Rule Port 1978 1979 - Apr Sun>=1 0:00s 1:00 S @@ -3706,6 +3711,9 @@ Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C. # # Source: The newspaper "Dagens Nyheter", 1916-10-01, page 7 upper left. +# An extra-special abbreviation style is SET for Swedish Time (svensk +# normaltid) 1879-1899, 3° west of the Stockholm Observatory. + # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Europe/Stockholm 1:12:12 - LMT 1879 Jan 1 1:00:14 - SET 1900 Jan 1 # Swedish Time diff --git a/make/data/tzdata/leapseconds b/make/data/tzdata/leapseconds index 6f1941601d3b97dd4061ec9f0168088c38758bd0..cc514561ff177924dd0741b5b5f9f37c3b8526d2 100644 --- a/make/data/tzdata/leapseconds +++ b/make/data/tzdata/leapseconds @@ -95,11 +95,11 @@ Leap 2016 Dec 31 23:59:60 + S # Any additional leap seconds will come after this. # This Expires line is commented out for now, # so that pre-2020a zic implementations do not reject this file. -#Expires 2021 Dec 28 00:00:00 +#Expires 2022 Jun 28 00:00:00 # POSIX timestamps for the data in this file: #updated 1467936000 (2016-07-08 00:00:00 UTC) -#expires 1640649600 (2021-12-28 00:00:00 UTC) +#expires 1656374400 (2022-06-28 00:00:00 UTC) -# Updated through IERS Bulletin C61 -# File expires on: 28 December 2021 +# Updated through IERS Bulletin C62 +# File expires on: 28 June 2022 diff --git a/make/data/tzdata/northamerica b/make/data/tzdata/northamerica index 610c606c01a10b1cb4ec9afebb5406dfd9e8c1f0..ddd4929b1d4c6912d42ced6ed9ccfdbde9f9a83b 100644 --- a/make/data/tzdata/northamerica +++ b/make/data/tzdata/northamerica @@ -752,7 +752,11 @@ Zone America/Adak 12:13:22 - LMT 1867 Oct 19 12:44:35 -11:00 US B%sT 1983 Oct 30 2:00 -10:00 US AH%sT 1983 Nov 30 -10:00 US H%sT -# The following switches don't quite make our 1970 cutoff. +# The following switches don't make our 1970 cutoff. +# +# Kiska observed Tokyo date and time during Japanese occupation from +# 1942-06-06 to 1943-07-29, and similarly for Attu from 1942-06-07 to +# 1943-05-29 (all dates American). Both islands are now uninhabited. # # Shanks writes that part of southwest Alaska (e.g. Aniak) # switched from -11:00 to -10:00 on 1968-09-22 at 02:00, @@ -848,6 +852,8 @@ Zone America/Phoenix -7:28:18 - LMT 1883 Nov 18 11:31:42 -7:00 - MST 1967 -7:00 US M%sT 1968 Mar 21 -7:00 - MST +Link America/Phoenix America/Creston + # From Arthur David Olson (1988-02-13): # A writer from the Inter Tribal Council of Arizona, Inc., # notes in private correspondence dated 1987-12-28 that "Presently, only the @@ -1616,24 +1622,7 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 # From Paul Eggert (2020-01-10): # See America/Toronto for most of Quebec, including Montreal. # See America/Halifax for the Îles de la Madeleine and the Listuguj reserve. -# -# Matthews and Vincent (1998) also write that Quebec east of the -63 -# meridian is supposed to observe AST, but residents as far east as -# Natashquan use EST/EDT, and residents east of Natashquan use AST. -# The Quebec department of justice writes in -# "The situation in Minganie and Basse-Côte-Nord" -# https://www.justice.gouv.qc.ca/en/department/ministre/functions-and-responsabilities/legal-time-in-quebec/the-situation-in-minganie-and-basse-cote-nord/ -# that the coastal strip from just east of Natashquan to Blanc-Sablon -# observes Atlantic standard time all year round. -# This common practice was codified into law as of 2007; see Legal Time Act, -# CQLR c T-5.1 . -# For lack of better info, guess this practice began around 1970, contra to -# Shanks & Pottenger who have this region observing AST/ADT. - -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone America/Blanc-Sablon -3:48:28 - LMT 1884 - -4:00 Canada A%sT 1970 - -4:00 - AST +# See America/Puerto_Rico for east of Natashquan. # Ontario @@ -1672,54 +1661,6 @@ Zone America/Blanc-Sablon -3:48:28 - LMT 1884 # time became a comic failure in Orillia. Toronto Star 2017-07-08. # https://www.thestar.com/news/insight/2017/07/08/bold-attempt-at-daylight-saving-time-became-a-comic-failure-in-orillia.html -# From Paul Eggert (1997-10-17): -# Mark Brader writes that an article in the 1997-10-14 Toronto Star -# says that Atikokan, Ontario currently does not observe DST, -# but will vote on 11-10 whether to use EST/EDT. -# He also writes that the Ontario Time Act (1990, Chapter T.9) -# http://www.gov.on.ca/MBS/english/publications/statregs/conttext.html -# says that Ontario east of 90W uses EST/EDT, and west of 90W uses CST/CDT. -# Officially Atikokan is therefore on CST/CDT, and most likely this report -# concerns a non-official time observed as a matter of local practice. -# -# From Paul Eggert (2000-10-02): -# Matthews and Vincent (1998) write that Atikokan, Pickle Lake, and -# New Osnaburgh observe CST all year, that Big Trout Lake observes -# CST/CDT, and that Upsala and Shebandowan observe EST/EDT, all in -# violation of the official Ontario rules. -# -# From Paul Eggert (2006-07-09): -# Chris Walton (2006-07-06) mentioned an article by Stephanie MacLellan in the -# 2005-07-21 Chronicle-Journal, which said: -# -# The clocks in Atikokan stay set on standard time year-round. -# This means they spend about half the time on central time and -# the other half on eastern time. -# -# For the most part, the system works, Mayor Dennis Brown said. -# -# "The majority of businesses in Atikokan deal more with Eastern -# Canada, but there are some that deal with Western Canada," he -# said. "I don't see any changes happening here." -# -# Walton also writes "Supposedly Pickle Lake and Mishkeegogamang -# [New Osnaburgh] follow the same practice." - -# From Garry McKinnon (2006-07-14) via Chris Walton: -# I chatted with a member of my board who has an outstanding memory -# and a long history in Atikokan (and in the telecom industry) and he -# can say for certain that Atikokan has been practicing the current -# time keeping since 1952, at least. - -# From Paul Eggert (2006-07-17): -# Shanks & Pottenger say that Atikokan has agreed with Rainy River -# ever since standard time was introduced, but the information from -# McKinnon sounds more authoritative. For now, assume that Atikokan -# switched to EST immediately after WWII era daylight saving time -# ended. This matches the old (less-populous) America/Coral_Harbour -# entry since our cutoff date of 1970, so we can move -# America/Coral_Harbour to the 'backward' file. - # From Mark Brader (2010-03-06): # # Currently the database has: @@ -1850,6 +1791,7 @@ Zone America/Toronto -5:17:32 - LMT 1895 -5:00 Canada E%sT 1946 -5:00 Toronto E%sT 1974 -5:00 Canada E%sT +Link America/Toronto America/Nassau Zone America/Thunder_Bay -5:57:00 - LMT 1895 -6:00 - CST 1910 -5:00 - EST 1942 @@ -1865,11 +1807,7 @@ Zone America/Rainy_River -6:18:16 - LMT 1895 -6:00 Canada C%sT 1940 Sep 29 -6:00 1:00 CDT 1942 Feb 9 2:00s -6:00 Canada C%sT -Zone America/Atikokan -6:06:28 - LMT 1895 - -6:00 Canada C%sT 1940 Sep 29 - -6:00 1:00 CDT 1942 Feb 9 2:00s - -6:00 Canada C%sT 1945 Sep 30 2:00 - -5:00 - EST +# For Atikokan see America/Panama. # Manitoba @@ -2060,60 +1998,6 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # Shanks & Pottenger write that since 1970 most of this region has # been like Vancouver. # Dawson Creek uses MST. Much of east BC is like Edmonton. -# Matthews and Vincent (1998) write that Creston is like Dawson Creek. - -# It seems though that (re: Creston) is not entirely correct: - -# From Chris Walton (2011-12-01): -# There are two areas within the Canadian province of British Columbia -# that do not currently observe daylight saving: -# a) The Creston Valley (includes the town of Creston and surrounding area) -# b) The eastern half of the Peace River Regional District -# (includes the cities of Dawson Creek and Fort St. John) - -# Earlier this year I stumbled across a detailed article about the time -# keeping history of Creston; it was written by Tammy Hardwick who is the -# manager of the Creston & District Museum. The article was written in May 2009. -# http://www.ilovecreston.com/?p=articles&t=spec&ar=260 -# According to the article, Creston has not changed its clocks since June 1918. -# i.e. Creston has been stuck on UT-7 for 93 years. -# Dawson Creek, on the other hand, changed its clocks as recently as April 1972. - -# Unfortunately the exact date for the time change in June 1918 remains -# unknown and will be difficult to ascertain. I e-mailed Tammy a few months -# ago to ask if Sunday June 2 was a reasonable guess. She said it was just -# as plausible as any other date (in June). She also said that after writing -# the article she had discovered another time change in 1916; this is the -# subject of another article which she wrote in October 2010. -# http://www.creston.museum.bc.ca/index.php?module=comments&uop=view_comment&cm+id=56 - -# Here is a summary of the three clock change events in Creston's history: -# 1. 1884 or 1885: adoption of Mountain Standard Time (GMT-7) -# Exact date unknown -# 2. Oct 1916: switch to Pacific Standard Time (GMT-8) -# Exact date in October unknown; Sunday October 1 is a reasonable guess. -# 3. June 1918: switch to Pacific Daylight Time (GMT-7) -# Exact date in June unknown; Sunday June 2 is a reasonable guess. -# note 1: -# On Oct 27/1918 when daylight saving ended in the rest of Canada, -# Creston did not change its clocks. -# note 2: -# During WWII when the Federal Government legislated a mandatory clock change, -# Creston did not oblige. -# note 3: -# There is no guarantee that Creston will remain on Mountain Standard Time -# (UTC-7) forever. -# The subject was debated at least once this year by the town Council. -# http://www.bclocalnews.com/kootenay_rockies/crestonvalleyadvance/news/116760809.html - -# During a period WWII, summer time (Daylight saying) was mandatory in Canada. -# In Creston, that was handled by shifting the area to PST (-8:00) then applying -# summer time to cause the offset to be -7:00, the same as it had been before -# the change. It can be argued that the timezone abbreviation during this -# period should be PDT rather than MST, but that doesn't seem important enough -# (to anyone) to further complicate the rules. - -# The transition dates (and times) are guesses. # From Matt Johnson (2015-09-21): # Fort Nelson, BC, Canada will cancel DST this year. So while previously they @@ -2167,10 +2051,7 @@ Zone America/Fort_Nelson -8:10:47 - LMT 1884 -8:00 Vanc P%sT 1987 -8:00 Canada P%sT 2015 Mar 8 2:00 -7:00 - MST -Zone America/Creston -7:46:04 - LMT 1884 - -7:00 - MST 1916 Oct 1 - -8:00 - PST 1918 Jun 2 - -7:00 - MST +# For Creston see America/Phoenix. # Northwest Territories, Nunavut, Yukon @@ -2952,64 +2833,61 @@ Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 0:11:56 # Anguilla # Antigua and Barbuda -# See America/Port_of_Spain. +# See America/Puerto_Rico. -# Bahamas -# -# For 1899 Milne gives -5:09:29.5; round that. -# -# From P Chan (2020-11-27, corrected on 2020-12-02): -# There were two periods of DST observed in 1942-1945: 1942-05-01 -# midnight to 1944-12-31 midnight and 1945-02-01 to 1945-10-17 midnight. -# "midnight" should mean 24:00 from the context. -# -# War Time Order 1942 [1942-05-01] and War Time (No. 2) Order 1942 [1942-09-29] -# Appendix to the Statutes of 7 George VI. and the Year 1942. p 34, 43 -# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA3-PA34 -# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA3-PA43 -# -# War Time Order 1943 [1943-03-31] and War Time Order 1944 [1943-12-29] -# Appendix to the Statutes of 8 George VI. and the Year 1943. p 9-10, 28-29 -# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA4-PA9 -# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA4-PA28 -# -# War Time Order 1945 [1945-01-31] and the Order which revoke War Time Order -# 1945 [1945-10-16] Appendix to the Statutes of 9 George VI. and the Year -# 1945. p 160, 247-248 -# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA6-PA160 -# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA6-PA247 -# -# From Sue Williams (2006-12-07): -# The Bahamas announced about a month ago that they plan to change their DST -# rules to sync with the U.S. starting in 2007.... -# http://www.jonesbahamas.com/?c=45&a=10412 +# The Bahamas +# See America/Toronto. -# Rule NAME FROM TO - IN ON AT SAVE LETTER/S -Rule Bahamas 1942 only - May 1 24:00 1:00 W -Rule Bahamas 1944 only - Dec 31 24:00 0 S -Rule Bahamas 1945 only - Feb 1 0:00 1:00 W -Rule Bahamas 1945 only - Aug 14 23:00u 1:00 P # Peace -Rule Bahamas 1945 only - Oct 17 24:00 0 S -Rule Bahamas 1964 1975 - Oct lastSun 2:00 0 S -Rule Bahamas 1964 1975 - Apr lastSun 2:00 1:00 D -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone America/Nassau -5:09:30 - LMT 1912 Mar 2 - -5:00 Bahamas E%sT 1976 - -5:00 US E%sT # Barbados # For 1899 Milne gives -3:58:29.2; round that. +# From P Chan (2020-12-09 and 2020-12-11): +# Standard time of GMT-4 was adopted in 1911. +# Definition of Time Act, 1911 (1911-7) [1911-08-28] +# 1912, Laws of Barbados (5 v.), OCLC Number: 919801291, Vol. 4, Image No. 522 +# 1944, Laws of Barbados (5 v.), OCLC Number: 84548697, Vol. 4, Image No. 122 +# http://llmc.com/browse.aspx?type=2&coll=85&div=297 +# +# DST was observed in 1942-44. +# Defence (Daylight Saving) Regulations, 1942, 1942-04-13 +# Defence (Daylight Saving) (Repeal) Regulations, 1942, 1942-08-22 +# Defence (Daylight Saving) Regulations, 1943, 1943-04-16 +# Defence (Daylight Saving) (Repeal) Regulations, 1943, 1943-09-01 +# Defence (Daylight Saving) Regulations, 1944, 1944-03-21 +# [Defence (Daylight Saving) (Amendment) Regulations 1944, 1944-03-28] +# Defence (Daylight Saving) (Repeal) Regulations, 1944, 1944-08-30 +# +# 1914-, Subsidiary Legis., Annual Vols. OCLC Number: 226290591 +# 1942: Image Nos. 527-528, 555-556 +# 1943: Image Nos. 178-179, 198 +# 1944: Image Nos. 113-115, 129 +# http://llmc.com/titledescfull.aspx?type=2&coll=85&div=297&set=98437 +# +# From Tim Parenti (2021-02-20): +# The transitions below are derived from P Chan's sources, except that the 1977 +# through 1980 transitions are from Shanks & Pottenger since we have no better +# data there. Of particular note, the 1944 DST regulation only advanced the +# time to "exactly three and a half hours later than Greenwich mean time", as +# opposed to "three hours" in the 1942 and 1943 regulations. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S +Rule Barb 1942 only - Apr 19 5:00u 1:00 D +Rule Barb 1942 only - Aug 31 6:00u 0 S +Rule Barb 1943 only - May 2 5:00u 1:00 D +Rule Barb 1943 only - Sep 5 6:00u 0 S +Rule Barb 1944 only - Apr 10 5:00u 0:30 - +Rule Barb 1944 only - Sep 10 6:00u 0 S Rule Barb 1977 only - Jun 12 2:00 1:00 D Rule Barb 1977 1978 - Oct Sun>=1 2:00 0 S Rule Barb 1978 1980 - Apr Sun>=15 2:00 1:00 D Rule Barb 1979 only - Sep 30 2:00 0 S Rule Barb 1980 only - Sep 25 2:00 0 S # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone America/Barbados -3:58:29 - LMT 1924 # Bridgetown - -3:58:29 - BMT 1932 # Bridgetown Mean Time +Zone America/Barbados -3:58:29 - LMT 1911 Aug 28 # Bridgetown + -4:00 Barb A%sT 1944 + -4:00 Barb AST/-0330 1945 -4:00 Barb A%sT # Belize @@ -3171,6 +3049,9 @@ Zone Atlantic/Bermuda -4:19:18 - LMT 1890 # Hamilton -4:00 Canada A%sT 1976 -4:00 US A%sT +# Caribbean Netherlands +# See America/Puerto_Rico. + # Cayman Is # See America/Panama. @@ -3399,7 +3280,7 @@ Zone America/Havana -5:29:28 - LMT 1890 -5:00 Cuba C%sT # Dominica -# See America/Port_of_Spain. +# See America/Puerto_Rico. # Dominican Republic @@ -3451,7 +3332,7 @@ Zone America/El_Salvador -5:56:48 - LMT 1921 # San Salvador # Guadeloupe # St Barthélemy # St Martin (French part) -# See America/Port_of_Spain. +# See America/Puerto_Rico. # Guatemala # @@ -3638,7 +3519,7 @@ Zone America/Martinique -4:04:20 - LMT 1890 # Fort-de-France -4:00 - AST # Montserrat -# See America/Port_of_Spain. +# See America/Puerto_Rico. # Nicaragua # @@ -3710,6 +3591,7 @@ Zone America/Managua -5:45:08 - LMT 1890 Zone America/Panama -5:18:08 - LMT 1890 -5:19:36 - CMT 1908 Apr 22 # Colón Mean Time -5:00 - EST +Link America/Panama America/Atikokan Link America/Panama America/Cayman # Puerto Rico @@ -3719,10 +3601,29 @@ Zone America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12:00 # San Juan -4:00 - AST 1942 May 3 -4:00 US A%sT 1946 -4:00 - AST +Link America/Puerto_Rico America/Anguilla +Link America/Puerto_Rico America/Antigua +Link America/Puerto_Rico America/Aruba +Link America/Puerto_Rico America/Curacao +Link America/Puerto_Rico America/Blanc-Sablon # Quebec (Lower North Shore) +Link America/Puerto_Rico America/Dominica +Link America/Puerto_Rico America/Grenada +Link America/Puerto_Rico America/Guadeloupe +Link America/Puerto_Rico America/Kralendijk # Caribbean Netherlands +Link America/Puerto_Rico America/Lower_Princes # Sint Maarten +Link America/Puerto_Rico America/Marigot # St Martin (French part) +Link America/Puerto_Rico America/Montserrat +Link America/Puerto_Rico America/Port_of_Spain # Trinidad & Tobago +Link America/Puerto_Rico America/St_Barthelemy # St Barthélemy +Link America/Puerto_Rico America/St_Kitts # St Kitts & Nevis +Link America/Puerto_Rico America/St_Lucia +Link America/Puerto_Rico America/St_Thomas # Virgin Islands (US) +Link America/Puerto_Rico America/St_Vincent +Link America/Puerto_Rico America/Tortola # Virgin Islands (UK) # St Kitts-Nevis # St Lucia -# See America/Port_of_Spain. +# See America/Puerto_Rico. # St Pierre and Miquelon # There are too many St Pierres elsewhere, so we'll use 'Miquelon'. @@ -3733,7 +3634,10 @@ Zone America/Miquelon -3:44:40 - LMT 1911 May 15 # St Pierre -3:00 Canada -03/-02 # St Vincent and the Grenadines -# See America/Port_of_Spain. +# See America/Puerto_Rico. + +# Sint Maarten +# See America/Puerto_Rico. # Turks and Caicos # @@ -3804,8 +3708,8 @@ Zone America/Grand_Turk -4:44:32 - LMT 1890 -5:00 US E%sT # British Virgin Is -# Virgin Is -# See America/Port_of_Spain. +# US Virgin Is +# See America/Puerto_Rico. # Local Variables: diff --git a/make/data/tzdata/southamerica b/make/data/tzdata/southamerica index 566dabfadb46e94ddf3237b88199968ea2288d70..503ed65f58036589e0a49de694077ca769a25a9e 100644 --- a/make/data/tzdata/southamerica +++ b/make/data/tzdata/southamerica @@ -597,7 +597,7 @@ Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31 -3:00 - -03 # Aruba -Link America/Curacao America/Aruba +# See America/Puerto_Rico. # Bolivia # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -1392,35 +1392,14 @@ Zone America/Bogota -4:56:16 - LMT 1884 Mar 13 # no information; probably like America/Bogota # Curaçao - -# Milne gives 4:35:46.9 for Curaçao mean time; round to nearest. -# -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger say that The Bottom and Philipsburg have been at -# -4:00 since standard time was introduced on 1912-03-02; and that -# Kralendijk and Rincon used Kralendijk Mean Time (-4:33:08) from -# 1912-02-02 to 1965-01-01. The former is dubious, since S&P also say -# Saba Island has been like Curaçao. -# This all predates our 1970 cutoff, though. -# -# By July 2007 Curaçao and St Maarten are planned to become -# associated states within the Netherlands, much like Aruba; -# Bonaire, Saba and St Eustatius would become directly part of the -# Netherlands as Kingdom Islands. This won't affect their time zones -# though, as far as we know. +# See America/Puerto_Rico. # -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone America/Curacao -4:35:47 - LMT 1912 Feb 12 # Willemstad - -4:30 - -0430 1965 - -4:00 - AST - # From Arthur David Olson (2011-06-15): # use links for places with new iso3166 codes. # The name "Lower Prince's Quarter" is both longer than fourteen characters -# and contains an apostrophe; use "Lower_Princes" below. - -Link America/Curacao America/Lower_Princes # Sint Maarten -Link America/Curacao America/Kralendijk # Caribbean Netherlands +# and contains an apostrophe; use "Lower_Princes".... +# From Paul Eggert (2021-09-29): +# These backward-compatibility links now are in the 'northamerica' file. # Ecuador # @@ -1563,11 +1542,40 @@ Zone America/Cayenne -3:29:20 - LMT 1911 Jul -3:00 - -03 # Guyana + +# From P Chan (2020-11-27): +# https://books.google.com/books?id=5-5CAQAAMAAJ&pg=SA1-PA547 +# The Official Gazette of British Guiana. (New Series.) Vol. XL. July to +# December, 1915, p 1547, lists as several notes: +# "Local Mean Time 3 hours 52 mins. 39 secs. slow of Greenwich Mean Time +# (Georgetown.) From 1st August, 1911, British Guiana Standard Mean Time 4 +# hours slow of Greenwich Mean Time, by notice in Official Gazette on 1st July, +# 1911. From 1st March, 1915, British Guiana Standard Mean Time 3 hours 45 +# mins. 0 secs. slow of Greenwich Mean Time, by notice in Official Gazette on +# 23rd January, 1915." +# +# https://parliament.gov.gy/documents/acts/10923-act_no._27_of_1975_-_interpretation_and_general_clauses_(amendment)_act_1975.pdf +# Interpretation and general clauses (Amendment) Act 1975 (Act No. 27 of 1975) +# [dated 1975-07-31] +# "This Act...shall come into operation on 1st August, 1975." +# "...where any expression of time occurs...the time referred to shall signify +# the standard time of Guyana which shall be three hours behind Greenwich Mean +# Time." +# +# Circular No. 10/1992 dated 1992-03-20 +# https://dps.gov.gy/wp-content/uploads/2018/12/1992-03-20-Circular-010.pdf +# "...cabinet has decided that with effect from Sunday 29th March, 1992, Guyana +# Standard Time would be re-established at 01:00 hours by adjusting the hands +# of the clock back to 24:00 hours." +# Legislated in the Interpretation and general clauses (Amendment) Act 1992 +# (Act No. 6 of 1992) [passed 1992-03-27, published 1992-04-18] +# https://parliament.gov.gy/documents/acts/5885-6_of_1992_interpretation_and_general_clauses_(amendment)_act_1992.pdf + # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone America/Guyana -3:52:40 - LMT 1915 Mar # Georgetown - -3:45 - -0345 1975 Jul 31 - -3:00 - -03 1991 -# IATA SSIM (1996-06) says -4:00. Assume a 1991 switch. +Zone America/Guyana -3:52:39 - LMT 1911 Aug 1 # Georgetown + -4:00 - -04 1915 Mar 1 + -3:45 - -0345 1975 Aug 1 + -3:00 - -03 1992 Mar 29 1:00 -4:00 - -04 # Paraguay @@ -1708,24 +1716,7 @@ Zone America/Paramaribo -3:40:40 - LMT 1911 -3:00 - -03 # Trinidad and Tobago -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone America/Port_of_Spain -4:06:04 - LMT 1912 Mar 2 - -4:00 - AST - -# These all agree with Trinidad and Tobago since 1970. -Link America/Port_of_Spain America/Anguilla -Link America/Port_of_Spain America/Antigua -Link America/Port_of_Spain America/Dominica -Link America/Port_of_Spain America/Grenada -Link America/Port_of_Spain America/Guadeloupe -Link America/Port_of_Spain America/Marigot # St Martin (French part) -Link America/Port_of_Spain America/Montserrat -Link America/Port_of_Spain America/St_Barthelemy # St Barthélemy -Link America/Port_of_Spain America/St_Kitts # St Kitts & Nevis -Link America/Port_of_Spain America/St_Lucia -Link America/Port_of_Spain America/St_Thomas # Virgin Islands (US) -Link America/Port_of_Spain America/St_Vincent -Link America/Port_of_Spain America/Tortola # Virgin Islands (UK) +# See America/Puerto_Rico. # Uruguay # From Paul Eggert (1993-11-18): diff --git a/make/data/tzdata/zone.tab b/make/data/tzdata/zone.tab index 28db0745e08be412d2b4fca9e2d3517907e29f2c..0420a6934c92b2bda79ae9487a9060bce72ac628 100644 --- a/make/data/tzdata/zone.tab +++ b/make/data/tzdata/zone.tab @@ -26,7 +26,7 @@ # This file is in the public domain, so clarified as of # 2009-05-17 by Arthur David Olson. # -# From Paul Eggert (2018-06-27): +# From Paul Eggert (2021-09-20): # This file is intended as a backward-compatibility aid for older programs. # New programs should use zone1970.tab. This file is like zone1970.tab (see # zone1970.tab's comments), but with the following additional restrictions: @@ -39,6 +39,9 @@ # clocks have agreed since 1970; this is a narrower definition than # that of zone1970.tab. # +# Unlike zone1970.tab, a row's third column can be a Link from +# 'backward' instead of a Zone. +# # This table is intended as an aid for users, to help them select timezones # appropriate for their practical needs. It is not intended to take or # endorse any position on legal or territorial claims. @@ -251,7 +254,7 @@ KE -0117+03649 Africa/Nairobi KG +4254+07436 Asia/Bishkek KH +1133+10455 Asia/Phnom_Penh KI +0125+17300 Pacific/Tarawa Gilbert Islands -KI -0308-17105 Pacific/Enderbury Phoenix Islands +KI -0247-17143 Pacific/Kanton Phoenix Islands KI +0152-15720 Pacific/Kiritimati Line Islands KM -1141+04316 Indian/Comoro KN +1718-06243 America/St_Kitts @@ -414,7 +417,7 @@ TK -0922-17114 Pacific/Fakaofo TL -0833+12535 Asia/Dili TM +3757+05823 Asia/Ashgabat TN +3648+01011 Africa/Tunis -TO -2110-17510 Pacific/Tongatapu +TO -210800-1751200 Pacific/Tongatapu TR +4101+02858 Europe/Istanbul TT +1039-06131 America/Port_of_Spain TV -0831+17913 Pacific/Funafuti diff --git a/make/hotspot/gensrc/GensrcAdlc.gmk b/make/hotspot/gensrc/GensrcAdlc.gmk index ba8165c2ff036d979f3dbc8c6a742c2cdcefe0e5..f9f1bb3868879e9fe5a95fa551e142ca7a6f4416 100644 --- a/make/hotspot/gensrc/GensrcAdlc.gmk +++ b/make/hotspot/gensrc/GensrcAdlc.gmk @@ -155,6 +155,7 @@ ifeq ($(call check-jvm-feature, compiler2), true) ifeq ($(call check-jvm-feature, zgc), true) AD_SRC_FILES += $(call uniq, $(wildcard $(foreach d, $(AD_SRC_ROOTS), \ $d/cpu/$(HOTSPOT_TARGET_CPU_ARCH)/gc/z/z_$(HOTSPOT_TARGET_CPU).ad \ + $d/cpu/$(HOTSPOT_TARGET_CPU_ARCH)/gc/z/z_$(HOTSPOT_TARGET_CPU_ARCH).ad \ ))) endif diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index 03c4de783cd9445ac07de15193124167d2604198..cb2bbccc1686aa4a28a8f8557523eee75d0b80ca 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -101,7 +101,7 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBJVM, \ CFLAGS_windows := -EHsc, \ CFLAGS_macosx := -DGTEST_OS_MAC=1, \ DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc) \ - undef, \ + undef stringop-overflow, \ DISABLED_WARNINGS_clang := $(DISABLED_WARNINGS_clang) \ undef switch format-nonliteral tautological-undefined-compare \ self-assign-overloaded, \ diff --git a/make/hotspot/lib/JvmFlags.gmk b/make/hotspot/lib/JvmFlags.gmk index 0c292ad866ca3f749f93e6f5fe3457bf49d3ee65..1588c19fac39f6d89664412e67b5f18356969274 100644 --- a/make/hotspot/lib/JvmFlags.gmk +++ b/make/hotspot/lib/JvmFlags.gmk @@ -74,8 +74,8 @@ ifeq ($(DEBUG_LEVEL), release) endif else ifeq ($(DEBUG_LEVEL), fastdebug) JVM_CFLAGS_DEBUGLEVEL := -DASSERT - ifeq ($(call isTargetOs, windows aix), false) - # NOTE: Old build did not define CHECK_UNHANDLED_OOPS on Windows and AIX. + ifeq ($(call isTargetOs, aix), false) + # NOTE: Old build did not define CHECK_UNHANDLED_OOPS on AIX. JVM_CFLAGS_DEBUGLEVEL += -DCHECK_UNHANDLED_OOPS endif else ifeq ($(DEBUG_LEVEL), slowdebug) diff --git a/make/hotspot/lib/JvmMapfile.gmk b/make/hotspot/lib/JvmMapfile.gmk index 5cba93178c744feb0d1c0286634a40def232eca2..1c343d2b5039333a12502609d58b59f56262f131 100644 --- a/make/hotspot/lib/JvmMapfile.gmk +++ b/make/hotspot/lib/JvmMapfile.gmk @@ -99,9 +99,26 @@ else ifeq ($(call isTargetOs, aix), true) else ifeq ($(call isTargetOs, windows), true) DUMP_SYMBOLS_CMD := $(DUMPBIN) -symbols *.obj + + # The following lines create a list of vftable symbols to be filtered out of + # the mapfile. Removing this line causes the linker to complain about too many + # (> 64K) symbols, so the _guess_ is that this line is here to keep down the + # number of exported symbols below that limit. + # + # Some usages of C++ lambdas require the vftable symbol of classes that use + # the lambda type as a template parameter. The usage of those classes won't + # link if their vftable symbols are removed. That's why there's an exception + # for vftable symbols containing the string 'lambda'. + # + # A very simple example of a lambda usage that fails if the lambda vftable + # symbols are missing in the mapfile: + # + # #include + # std::function f = [](){} + FILTER_SYMBOLS_AWK_SCRIPT := \ '{ \ - if ($$7 ~ /??_7.*@@6B@/ && $$7 !~ /type_info/) print $$7; \ + if ($$7 ~ /??_7.*@@6B@/ && $$7 !~ /type_info/ && $$7 !~ /lambda/) print $$7; \ }' else diff --git a/make/jdk/src/classes/build/tools/generatecacerts/GenerateCacerts.java b/make/jdk/src/classes/build/tools/generatecacerts/GenerateCacerts.java index aa769b47b78dcc1d483961d219caea809efe6b4a..d7c809090a1b9916bc1c9cae40bca1791fa02927 100644 --- a/make/jdk/src/classes/build/tools/generatecacerts/GenerateCacerts.java +++ b/make/jdk/src/classes/build/tools/generatecacerts/GenerateCacerts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,21 +25,15 @@ package build.tools.generatecacerts; -import java.io.DataOutputStream; import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Path; -import java.security.DigestOutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; +import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -51,33 +45,16 @@ import java.util.stream.Collectors; public class GenerateCacerts { public static void main(String[] args) throws Exception { try (FileOutputStream fos = new FileOutputStream(args[1])) { - store(args[0], fos, "changeit".toCharArray()); + store(args[0], fos); } } - // The following code are copied from JavaKeyStore.java. + public static void store(String dir, OutputStream stream) throws Exception { - private static final int MAGIC = 0xfeedfeed; - private static final int VERSION_2 = 0x02; + CertificateFactory cf = CertificateFactory.getInstance("X.509"); - // This method is a simplified version of JavaKeyStore::engineStore. - // A new "dir" argument is added. All cert names in "dir" is collected into - // a sorted array. Each cert is stored with a creation date set to its - // notBefore value. Thus the output is determined as long as the certs - // are the same. - public static void store(String dir, OutputStream stream, char[] password) - throws IOException, NoSuchAlgorithmException, CertificateException - { - byte[] encoded; // the certificate encoding - CertificateFactory cf = CertificateFactory.getInstance("X509"); - - MessageDigest md = getPreKeyedHash(password); - DataOutputStream dos - = new DataOutputStream(new DigestOutputStream(stream, md)); - - dos.writeInt(MAGIC); - // always write the latest version - dos.writeInt(VERSION_2); + KeyStore ks = KeyStore.getInstance("pkcs12"); + ks.load(null, null); // All file names in dir sorted. // README is excluded. Name starting with "." excluded. @@ -88,61 +65,15 @@ public class GenerateCacerts { entries.sort(String::compareTo); - dos.writeInt(entries.size()); - for (String entry : entries) { - String alias = entry + " [jdk]"; X509Certificate cert; try (InputStream fis = Files.newInputStream(Path.of(dir, entry))) { cert = (X509Certificate) cf.generateCertificate(fis); } - - dos.writeInt(2); - - // Write the alias - dos.writeUTF(alias); - - // Write the (entry creation) date, which is notBefore of the cert - dos.writeLong(cert.getNotBefore().getTime()); - - // Write the trusted certificate - encoded = cert.getEncoded(); - dos.writeUTF(cert.getType()); - dos.writeInt(encoded.length); - dos.write(encoded); + ks.setCertificateEntry(alias, cert); } - /* - * Write the keyed hash which is used to detect tampering with - * the keystore (such as deleting or modifying key or - * certificate entries). - */ - byte[] digest = md.digest(); - - dos.write(digest); - dos.flush(); - } - - private static MessageDigest getPreKeyedHash(char[] password) - throws NoSuchAlgorithmException, UnsupportedEncodingException - { - - MessageDigest md = MessageDigest.getInstance("SHA"); - byte[] passwdBytes = convertToBytes(password); - md.update(passwdBytes); - Arrays.fill(passwdBytes, (byte) 0x00); - md.update("Mighty Aphrodite".getBytes("UTF8")); - return md; - } - - private static byte[] convertToBytes(char[] password) { - int i, j; - byte[] passwdBytes = new byte[password.length * 2]; - for (i=0, j=0; i> 8); - passwdBytes[j++] = (byte)password[i]; - } - return passwdBytes; + ks.store(stream, null); } } diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java index 71ff01f94dd8f9ff46160d2dcaf30b1436a040f3..1f439e1c29e081bd23174c1a44f6cebb10c56841 100644 --- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java +++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java @@ -123,6 +123,7 @@ import com.sun.tools.classfile.InnerClasses_attribute; import com.sun.tools.classfile.InnerClasses_attribute.Info; import com.sun.tools.classfile.Method; import com.sun.tools.classfile.MethodParameters_attribute; +import com.sun.tools.classfile.ModuleMainClass_attribute; import com.sun.tools.classfile.ModuleResolution_attribute; import com.sun.tools.classfile.ModuleTarget_attribute; import com.sun.tools.classfile.Module_attribute; @@ -928,6 +929,12 @@ public class CreateSymbols { attributes.put(Attribute.ModuleTarget, new ModuleTarget_attribute(attrIdx, targetIdx)); } + if (header.moduleMainClass != null) { + int attrIdx = addString(cp, Attribute.ModuleMainClass); + int targetIdx = addString(cp, header.moduleMainClass); + attributes.put(Attribute.ModuleMainClass, + new ModuleMainClass_attribute(attrIdx, targetIdx)); + } int attrIdx = addString(cp, Attribute.Module); attributes.put(Attribute.Module, new Module_attribute(attrIdx, @@ -2294,6 +2301,13 @@ public class CreateSymbols { chd.isSealed = true; break; } + case Attribute.ModuleMainClass: { + ModuleMainClass_attribute moduleMainClass = (ModuleMainClass_attribute) attr; + assert feature instanceof ModuleHeaderDescription; + ModuleHeaderDescription mhd = (ModuleHeaderDescription) feature; + mhd.moduleMainClass = moduleMainClass.getMainClassName(cf.constant_pool); + break; + } default: throw new IllegalStateException("Unhandled attribute: " + attrName); @@ -2731,6 +2745,7 @@ public class CreateSymbols { List provides = new ArrayList<>(); Integer moduleResolution; String moduleTarget; + String moduleMainClass; @Override public int hashCode() { @@ -2743,6 +2758,7 @@ public class CreateSymbols { hash = 83 * hash + Objects.hashCode(this.provides); hash = 83 * hash + Objects.hashCode(this.moduleResolution); hash = 83 * hash + Objects.hashCode(this.moduleTarget); + hash = 83 * hash + Objects.hashCode(this.moduleMainClass); return hash; } @@ -2781,6 +2797,10 @@ public class CreateSymbols { other.moduleResolution)) { return false; } + if (!Objects.equals(this.moduleMainClass, + other.moduleMainClass)) { + return false; + } return true; } @@ -2818,6 +2838,8 @@ public class CreateSymbols { output.append(" resolution " + quote(Integer.toHexString(moduleResolution), true)); + if (moduleMainClass != null) + output.append(" moduleMainClass " + quote(moduleMainClass, true)); writeAttributes(output); output.append("\n"); writeInnerClasses(output, baselineVersion, version); @@ -2862,6 +2884,8 @@ public class CreateSymbols { moduleResolution = Integer.parseInt(resolutionFlags, 16); } + moduleMainClass = reader.attributes.get("moduleMainClass"); + readAttributes(reader); reader.moveNext(); readInnerClasses(reader); diff --git a/make/modules/jdk.httpserver/Gensrc.gmk b/make/modules/jdk.httpserver/Gensrc.gmk new file mode 100644 index 0000000000000000000000000000000000000000..6e90917db337f1b297ad9389b2dbd43ff482cd86 --- /dev/null +++ b/make/modules/jdk.httpserver/Gensrc.gmk @@ -0,0 +1,41 @@ +# +# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +include GensrcCommonJdk.gmk +include GensrcProperties.gmk +include Modules.gmk + +################################################################################ + +# Use wildcard so as to avoid getting non-existing directories back +SIMPLESERVER_RESOURCES_DIRS := $(wildcard $(addsuffix /sun/net/httpserver/simpleserver/resources, \ + $(call FindModuleSrcDirs, jdk.httpserver))) + +$(eval $(call SetupCompileProperties, SIMPLESERVER_PROPERTIES, \ + SRC_DIRS := $(SIMPLESERVER_RESOURCES_DIRS), \ + CLASS := ListResourceBundle, \ +)) + +TARGETS += $(SIMPLESERVER_PROPERTIES) diff --git a/make/test/BuildTestLib.gmk b/make/test/BuildTestLib.gmk index dff446eed3b3c3cca8b99b60628c551898da2459..f677d255dda579611bef9f91eece0eff9e133a26 100644 --- a/make/test/BuildTestLib.gmk +++ b/make/test/BuildTestLib.gmk @@ -36,9 +36,10 @@ TEST_LIB_SUPPORT := $(SUPPORT_OUTPUTDIR)/test/lib $(eval $(call SetupJavaCompilation, BUILD_WB_JAR, \ TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK_UPGRADED), \ - SRC := $(TEST_LIB_SOURCE_DIR)/sun, \ + SRC := $(TEST_LIB_SOURCE_DIR)/sun $(TEST_LIB_SOURCE_DIR)/jdk/test/whitebox/parser, \ BIN := $(TEST_LIB_SUPPORT)/wb_classes, \ JAR := $(TEST_LIB_SUPPORT)/wb.jar, \ + DISABLED_WARNINGS := deprecation removal, \ )) TARGETS += $(BUILD_WB_JAR) @@ -50,7 +51,7 @@ $(eval $(call SetupJavaCompilation, BUILD_TEST_LIB_JAR, \ BIN := $(TEST_LIB_SUPPORT)/test-lib_classes, \ HEADERS := $(TEST_LIB_SUPPORT)/test-lib_headers, \ JAR := $(TEST_LIB_SUPPORT)/test-lib.jar, \ - DISABLED_WARNINGS := try deprecation rawtypes unchecked serial cast, \ + DISABLED_WARNINGS := try deprecation rawtypes unchecked serial cast removal, \ )) TARGETS += $(BUILD_TEST_LIB_JAR) diff --git a/src/demo/share/java2d/J2DBench/Makefile b/src/demo/share/java2d/J2DBench/Makefile index 16cbe5cd86ea2b3a6eb5fa2c912a2cfd1f7b60c0..04b0818a2c35b795534e19ff31f90ce31810193d 100644 --- a/src/demo/share/java2d/J2DBench/Makefile +++ b/src/demo/share/java2d/J2DBench/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -80,10 +80,10 @@ SCM_DIRs = .hg .svn CVS RCS SCCS Codemgr_wsdata deleted_files all: mkdirs J2DBench.jar J2DAnalyzer.jar run: mkdirs J2DBench.jar - java -jar J2DBench.jar + java -jar $(DIST)/J2DBench.jar analyze: mkdirs J2DAnalyzer.jar - java -jar J2DAnalyzer.jar + java -jar $(DIST)/J2DAnalyzer.jar J2DBench.jar: \ $(J2DBENCH_CLASSES) $(J2DBENCH_RESOURCES) \ diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 6fcbb24536dbfee841452cd41b56ab8963fe8a29..8e155b926af175d8117901d444509e94f84b341a 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2401,9 +2401,6 @@ const bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType // Special cases switch (opcode) { case Op_VectorMaskCmp: - // We don't have VectorReinterpret with bit_size less than 64 support for - // now, even for byte type. To be refined with fully VectorCast support. - case Op_VectorReinterpret: if (vlen < 2 || bit_size < 64) { return false; } @@ -2421,23 +2418,6 @@ const bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType return false; } break; - // Some types of VectorCast are not implemented for now. - case Op_VectorCastI2X: - if (bt == T_BYTE) { - return false; - } - break; - case Op_VectorCastS2X: - if (vlen < 4 || bit_size < 64) { - return false; - } - break; - case Op_VectorCastF2X: - case Op_VectorCastD2X: - if (bt == T_INT || bt == T_SHORT || bt == T_BYTE || bt == T_LONG) { - return false; - } - break; case Op_LoadVectorGather: case Op_StoreVectorScatter: return false; @@ -10893,7 +10873,6 @@ instruct msubI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2, iRegIorL2I src3) instruct mnegI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2, immI0 zero) %{ match(Set dst (MulI (SubI zero src1) src2)); - match(Set dst (MulI src1 (SubI zero src2))); ins_cost(INSN_COST * 3); format %{ "mneg $dst, $src1, $src2" %} @@ -10945,7 +10924,6 @@ instruct msubL(iRegLNoSp dst, iRegL src1, iRegL src2, iRegL src3) %{ instruct mnegL(iRegLNoSp dst, iRegL src1, iRegL src2, immL0 zero) %{ match(Set dst (MulL (SubL zero src1) src2)); - match(Set dst (MulL src1 (SubL zero src2))); ins_cost(INSN_COST * 5); format %{ "mneg $dst, $src1, $src2" %} @@ -10995,7 +10973,6 @@ instruct smsubL(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2, iRegLNoSp src3) instruct smnegL(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2, immL0 zero) %{ match(Set dst (MulL (SubL zero (ConvI2L src1)) (ConvI2L src2))); - match(Set dst (MulL (ConvI2L src1) (SubL zero (ConvI2L src2)))); ins_cost(INSN_COST * 3); format %{ "smnegl $dst, $src1, $src2" %} @@ -16864,6 +16841,7 @@ instruct encode_iso_array(iRegP_R2 src, iRegP_R1 dst, iRegI_R3 len, vRegD_V2 Vtmp3, vRegD_V3 Vtmp4, iRegI_R0 result, rFlagsReg cr) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(USE_KILL src, USE_KILL dst, USE_KILL len, KILL Vtmp1, KILL Vtmp2, KILL Vtmp3, KILL Vtmp4, KILL cr); diff --git a/src/hotspot/cpu/aarch64/aarch64_neon.ad b/src/hotspot/cpu/aarch64/aarch64_neon.ad index bb617aeb379bead5d09ee8993782048e2a8d3896..0b59686238a5459b06a23bbf8c0862be3c45132d 100644 --- a/src/hotspot/cpu/aarch64/aarch64_neon.ad +++ b/src/hotspot/cpu/aarch64/aarch64_neon.ad @@ -150,16 +150,12 @@ instruct reinterpretD2X(vecX dst, vecD src) n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 8); match(Set dst (VectorReinterpret src)); ins_cost(INSN_COST); - format %{ " # reinterpret $dst,$src\t# D2X" %} + format %{ " # reinterpret $dst,$src\t# D to X" %} ins_encode %{ - // If registers are the same, no register move is required - the - // upper 64 bits of 'src' are expected to have been initialized - // to zero. - if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { - __ orr(as_FloatRegister($dst$$reg), __ T8B, - as_FloatRegister($src$$reg), - as_FloatRegister($src$$reg)); - } + // The higher 64-bits of the "dst" register must be cleared to zero. + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); %} ins_pipe(vlogical64); %} @@ -170,10 +166,9 @@ instruct reinterpretX2D(vecD dst, vecX src) n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 16); match(Set dst (VectorReinterpret src)); ins_cost(INSN_COST); - format %{ " # reinterpret $dst,$src\t# X2D" %} + format %{ " # reinterpret $dst,$src\t# X to D" %} ins_encode %{ - // Resize the vector from 128-bits to 64-bits. The higher 64-bits of - // the "dst" register must be cleared to zero. + // The higher 64-bits of the "dst" register must be cleared to zero. __ orr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg), as_FloatRegister($src$$reg)); @@ -181,19 +176,64 @@ instruct reinterpretX2D(vecD dst, vecX src) ins_pipe(vlogical64); %} -// ------------------------------ Vector cast ------------------------------- +instruct reinterpretS2X(vecX dst, vecD src) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == 16 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 4); + match(Set dst (VectorReinterpret src)); + ins_cost(INSN_COST); + format %{ " # reinterpret $dst,$src\t# S to X" %} + ins_encode %{ + // The higher bits of the "dst" register must be cleared to zero. + __ dup(as_FloatRegister($dst$$reg), __ S, as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_slow); +%} -instruct vcvt4Bto4S(vecD dst, vecD src) +instruct reinterpretX2S(vecD dst, vecX src) %{ - predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); - match(Set dst (VectorCastB2X src)); - format %{ "sxtl $dst, T8H, $src, T8B\t# convert 4B to 4S vector" %} + predicate(n->bottom_type()->is_vect()->length_in_bytes() == 4 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 16); + match(Set dst (VectorReinterpret src)); + ins_cost(INSN_COST); + format %{ " # reinterpret $dst,$src\t# X to S" %} ins_encode %{ - __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + // The higher bits of the "dst" register must be cleared to zero. + __ dup(as_FloatRegister($dst$$reg), __ S, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(pipe_slow); +%} + +instruct reinterpretS2D(vecD dst, vecD src) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == 8 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 4); + match(Set dst (VectorReinterpret src)); + ins_cost(INSN_COST); + format %{ " # reinterpret $dst,$src\t# S to D" %} + ins_encode %{ + // The higher bits of the "dst" register must be cleared to zero. + __ dup(as_FloatRegister($dst$$reg), __ S, as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct reinterpretD2S(vecD dst, vecD src) +%{ + predicate(n->bottom_type()->is_vect()->length_in_bytes() == 4 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 8); + match(Set dst (VectorReinterpret src)); + ins_cost(INSN_COST); + format %{ " # reinterpret $dst,$src\t# D to S" %} + ins_encode %{ + // The higher bits of the "dst" register must be cleared to zero. + __ dup(as_FloatRegister($dst$$reg), __ S, as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_slow); %} +// ------------------------------ Vector cast ------------------------------- + instruct vcvt8Bto8S(vecX dst, vecD src) %{ predicate(n->as_Vector()->length() == 8 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); @@ -205,13 +245,13 @@ instruct vcvt8Bto8S(vecX dst, vecD src) ins_pipe(pipe_class_default); %} -instruct vcvt4Sto4B(vecD dst, vecD src) +instruct vcvt4Bto4S(vecD dst, vecD src) %{ - predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); - match(Set dst (VectorCastS2X src)); - format %{ "xtn $dst, T8B, $src, T8H\t# convert 4S to 4B vector" %} + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorCastB2X src)); + format %{ "sxtl $dst, T8H, $src, T8B\t# convert 4B to 4S vector" %} ins_encode %{ - __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg), __ T8H); + __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); %} ins_pipe(pipe_class_default); %} @@ -227,6 +267,17 @@ instruct vcvt8Sto8B(vecD dst, vecX src) ins_pipe(pipe_class_default); %} +instruct vcvt4Sto4B(vecD dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorCastS2X src)); + format %{ "xtn $dst, T8B, $src, T8H\t# convert 4S to 4B vector" %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg), __ T8H); + %} + ins_pipe(pipe_class_default); +%} + instruct vcvt4Sto4I(vecX dst, vecD src) %{ predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); @@ -271,6 +322,20 @@ instruct vcvt2Lto2I(vecD dst, vecX src) ins_pipe(pipe_class_default); %} +instruct vcvt4Ito4B(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorCastI2X src)); + format %{ "xtn $dst, T4H, $src, T4S\n\t" + "xtn $dst, T8B, $dst, T8H\t# convert 4I to 4B vector" + %} + ins_encode %{ + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($src$$reg), __ T4S); + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + %} + ins_pipe(pipe_class_default); +%} + instruct vcvt4Bto4I(vecX dst, vecD src) %{ predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); @@ -282,37 +347,54 @@ instruct vcvt4Bto4I(vecX dst, vecD src) __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); __ sxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); %} - ins_pipe(pipe_slow); + ins_pipe(pipe_class_default); %} -instruct vcvt4Ito4B(vecD dst, vecX src) +instruct vcvt2Lto2F(vecD dst, vecX src) %{ - predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); - match(Set dst (VectorCastI2X src)); - format %{ "xtn $dst, T4H, $src, T4S\n\t" - "xtn $dst, T8B, $dst, T8H\t# convert 4I to 4B vector" + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastL2X src)); + format %{ "scvtfv T2D, $dst, $src\n\t" + "fcvtn $dst, T2S, $dst, T2D\t# convert 2L to 2F vector" %} ins_encode %{ - __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($src$$reg), __ T4S); - __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + __ fcvtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($dst$$reg), __ T2D); %} ins_pipe(pipe_slow); %} -instruct vcvt4Bto4F(vecX dst, vecD src) +instruct vcvt2Ito2F(vecD dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastI2X src)); + format %{ "scvtfv T2S, $dst, $src\t# convert 2I to 2F vector" %} + ins_encode %{ + __ scvtfv(__ T2S, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt4Ito4F(vecX dst, vecX src) %{ predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); - match(Set dst (VectorCastB2X src)); - format %{ "sxtl $dst, T8H, $src, T8B\n\t" - "sxtl $dst, T4S, $dst, T4H\n\t" - "scvtfv T4S, $dst, $dst\t# convert 4B to 4F vector" + match(Set dst (VectorCastI2X src)); + format %{ "scvtfv T4S, $dst, $src\t# convert 4I to 4F vector" %} + ins_encode %{ + __ scvtfv(__ T4S, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); %} + ins_pipe(pipe_class_default); +%} + +instruct vcvt2Lto2D(vecX dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); + match(Set dst (VectorCastL2X src)); + format %{ "scvtfv T2D, $dst, $src\t# convert 2L to 2D vector" %} ins_encode %{ - __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); - __ sxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); - __ scvtfv(__ T4S, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_slow); + ins_pipe(pipe_class_default); %} instruct vcvt4Sto4F(vecX dst, vecD src) @@ -343,39 +425,113 @@ instruct vcvt2Ito2D(vecX dst, vecD src) ins_pipe(pipe_slow); %} -instruct vcvt2Ito2F(vecD dst, vecD src) +instruct vcvt4Bto4F(vecX dst, vecD src) %{ - predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); - match(Set dst (VectorCastI2X src)); - format %{ "scvtfv T2S, $dst, $src\t# convert 2I to 2F vector" %} + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastB2X src)); + format %{ "sxtl $dst, T8H, $src, T8B\n\t" + "sxtl $dst, T4S, $dst, T4H\n\t" + "scvtfv T4S, $dst, $dst\t# convert 4B to 4F vector" + %} ins_encode %{ - __ scvtfv(__ T2S, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + __ sxtl(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), __ T8B); + __ sxtl(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($dst$$reg), __ T4H); + __ scvtfv(__ T4S, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt2Fto2L(vecX dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorCastF2X src)); + format %{ "fcvtl $dst, T2D, $src, T2S\n\t" + "fcvtzs $dst, T2D, $dst\t# convert 2F to 2L vector" + %} + ins_encode %{ + __ fcvtl(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg), __ T2S); + __ fcvtzs(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt2Fto2I(vecD dst, vecD src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorCastF2X src)); + format %{ "fcvtzs $dst, T2S, $src\t# convert 2F to 2I vector" %} + ins_encode %{ + __ fcvtzs(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg)); %} ins_pipe(pipe_class_default); %} -instruct vcvt4Ito4F(vecX dst, vecX src) +instruct vcvt4Fto4I(vecX dst, vecX src) %{ - predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); - match(Set dst (VectorCastI2X src)); - format %{ "scvtfv T4S, $dst, $src\t# convert 4I to 4F vector" %} + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorCastF2X src)); + format %{ "fcvtzs $dst, T4S, $src\t# convert 4F to 4I vector" %} ins_encode %{ - __ scvtfv(__ T4S, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + __ fcvtzs(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg)); %} ins_pipe(pipe_class_default); %} -instruct vcvt2Lto2D(vecX dst, vecX src) +instruct vcvt2Dto2L(vecX dst, vecX src) %{ - predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); - match(Set dst (VectorCastL2X src)); - format %{ "scvtfv T2D, $dst, $src\t# convert 2L to 2D vector" %} + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorCastD2X src)); + format %{ "fcvtzs $dst, T2D, $src\t# convert 2D to 2L vector" %} ins_encode %{ - __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + __ fcvtzs(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg)); %} ins_pipe(pipe_class_default); %} +instruct vcvt4Fto4S(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_SHORT); + match(Set dst (VectorCastF2X src)); + format %{ "fcvtzs $dst, T4S, $src\n\t" + "xtn $dst, T4H, $dst, T4S\t# convert 4F to 4S vector" + %} + ins_encode %{ + __ fcvtzs(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg)); + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($dst$$reg), __ T4S); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt2Dto2I(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_INT); + match(Set dst (VectorCastD2X src)); + format %{ "fcvtzs $dst, T2D, $src\n\t" + "xtn $dst, T2S, $dst, T2D\t# convert 2D to 2I vector" + %} + ins_encode %{ + __ fcvtzs(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg)); + __ xtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($dst$$reg), __ T2D); + %} + ins_pipe(pipe_slow); +%} + +instruct vcvt4Fto4B(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorCastF2X src)); + format %{ "fcvtzs $dst, T4S, $src\n\t" + "xtn $dst, T4H, $dst, T4S\n\t" + "xtn $dst, T8B, $dst, T8H\t# convert 4F to 4B vector" + %} + ins_encode %{ + __ fcvtzs(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg)); + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($dst$$reg), __ T4S); + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + %} + ins_pipe(pipe_slow); +%} + instruct vcvt2Fto2D(vecX dst, vecD src) %{ predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_DOUBLE); @@ -398,20 +554,6 @@ instruct vcvt2Dto2F(vecD dst, vecX src) ins_pipe(pipe_class_default); %} -instruct vcvt2Lto2F(vecD dst, vecX src) -%{ - predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); - match(Set dst (VectorCastL2X src)); - format %{ "scvtfv T2D, $dst, $src\n\t" - "fcvtn $dst, T2S, $dst, T2D\t# convert 2L to 2F vector" - %} - ins_encode %{ - __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); - __ fcvtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($dst$$reg), __ T2D); - %} - ins_pipe(pipe_slow); -%} - // ------------------------------ Reduction ------------------------------- instruct reduce_add8B(iRegINoSp dst, iRegIorL2I isrc, vecD vsrc, vecD tmp) @@ -1887,7 +2029,7 @@ instruct vmul2L(vecX dst, vecX src1, vecX src2, iRegLNoSp tmp1, iRegLNoSp tmp2) "umov $tmp1, $src1, D, 1\n\t" "umov $tmp2, $src2, D, 1\n\t" "mul $tmp2, $tmp2, $tmp1\n\t" - "mov $dst, T2D, 1, $tmp2\t# insert into vector(2L)\n\t" + "mov $dst, T2D, 1, $tmp2\t# insert into vector(2L)" %} ins_encode %{ __ umov($tmp1$$Register, as_FloatRegister($src1$$reg), __ D, 0); @@ -4019,7 +4161,7 @@ instruct vmuladdS2I(vecX dst, vecX src1, vecX src2, vecX tmp) %{ effect(TEMP_DEF dst, TEMP tmp); format %{ "smullv $tmp, $src1, $src2\t# vector (4H)\n\t" "smullv $dst, $src1, $src2\t# vector (8H)\n\t" - "addpv $dst, $tmp, $dst\t# vector (4S)\n\t" %} + "addpv $dst, $tmp, $dst\t# vector (4S)" %} ins_encode %{ __ smullv(as_FloatRegister($tmp$$reg), __ T4H, as_FloatRegister($src1$$reg), diff --git a/src/hotspot/cpu/aarch64/aarch64_neon_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_neon_ad.m4 index a0f980e44cb11880c8369e75b05457546132962c..4d947b8141c612780f4b3cefd880fce176f40424 100644 --- a/src/hotspot/cpu/aarch64/aarch64_neon_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_neon_ad.m4 @@ -95,43 +95,46 @@ dnl $1 $2 REINTERPRET(D, 8) REINTERPRET(X, 16) dnl - -instruct reinterpretD2X(vecX dst, vecD src) +define(`REINTERPRET_DX', ` +instruct reinterpret$1`'2$2`'(vec$2 dst, vec$1 src) %{ - predicate(n->bottom_type()->is_vect()->length_in_bytes() == 16 && - n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 8); + predicate(n->bottom_type()->is_vect()->length_in_bytes() == $3 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == $4); match(Set dst (VectorReinterpret src)); ins_cost(INSN_COST); - format %{ " # reinterpret $dst,$src\t# D2X" %} + format %{ " # reinterpret $dst,$src\t# $1 to $2" %} ins_encode %{ - // If registers are the same, no register move is required - the - // upper 64 bits of 'src' are expected to have been initialized - // to zero. - if (as_FloatRegister($dst$$reg) != as_FloatRegister($src$$reg)) { - __ orr(as_FloatRegister($dst$$reg), __ T8B, - as_FloatRegister($src$$reg), - as_FloatRegister($src$$reg)); - } + // The higher 64-bits of the "dst" register must be cleared to zero. + __ orr(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); %} ins_pipe(vlogical64); -%} - -instruct reinterpretX2D(vecD dst, vecX src) +%}')dnl +dnl $1 $2 $3 $4 +REINTERPRET_DX(D, X, 16, 8) +REINTERPRET_DX(X, D, 8, 16) +dnl +define(`REINTERPRET_SX', ` +instruct reinterpret$1`'2$2`'(vec$3 dst, vec$4 src) %{ - predicate(n->bottom_type()->is_vect()->length_in_bytes() == 8 && - n->in(1)->bottom_type()->is_vect()->length_in_bytes() == 16); + predicate(n->bottom_type()->is_vect()->length_in_bytes() == $5 && + n->in(1)->bottom_type()->is_vect()->length_in_bytes() == $6); match(Set dst (VectorReinterpret src)); ins_cost(INSN_COST); - format %{ " # reinterpret $dst,$src\t# X2D" %} + format %{ " # reinterpret $dst,$src\t# $1 to $2" %} ins_encode %{ - // Resize the vector from 128-bits to 64-bits. The higher 64-bits of - // the "dst" register must be cleared to zero. - __ orr(as_FloatRegister($dst$$reg), __ T8B, - as_FloatRegister($src$$reg), - as_FloatRegister($src$$reg)); + // The higher bits of the "dst" register must be cleared to zero. + __ dup(as_FloatRegister($dst$$reg), __ S, as_FloatRegister($src$$reg)); %} - ins_pipe(vlogical64); -%} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 +REINTERPRET_SX(S, X, X, D, 16, 4) +REINTERPRET_SX(X, S, D, X, 4, 16) +REINTERPRET_SX(S, D, D, D, 8, 4) +REINTERPRET_SX(D, S, D, D, 4, 8) +dnl // ------------------------------ Vector cast ------------------------------- dnl @@ -147,39 +150,89 @@ instruct vcvt$1$2to$1$3`'(vec$4 dst, vec$5 src) ins_pipe(pipe_class_default); %}')dnl dnl $1 $2 $3 $4 $5 $6 $7 $8 -VECTOR_CAST_I2I(4, B, S, D, D, sxtl, 8B, 8H) VECTOR_CAST_I2I(8, B, S, X, D, sxtl, 8B, 8H) -VECTOR_CAST_I2I(4, S, B, D, D, xtn, 8H, 8B) +VECTOR_CAST_I2I(4, B, S, D, D, sxtl, 8B, 8H) VECTOR_CAST_I2I(8, S, B, D, X, xtn, 8H, 8B) +VECTOR_CAST_I2I(4, S, B, D, D, xtn, 8H, 8B) VECTOR_CAST_I2I(4, S, I, X, D, sxtl, 4H, 4S) VECTOR_CAST_I2I(4, I, S, D, X, xtn, 4S, 4H) VECTOR_CAST_I2I(2, I, L, X, D, sxtl, 2S, 2D) VECTOR_CAST_I2I(2, L, I, D, X, xtn, 2D, 2S) dnl -define(`VECTOR_CAST_B2I', ` -instruct vcvt4$1to4$2`'(vec$3 dst, vec$4 src) +define(`VECTOR_CAST_I2I_L', ` +instruct vcvt$1$2to$1$3`'(vec$4 dst, vec$5 src) %{ - predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($2)); - match(Set dst (VectorCast$1`'2X src)); - format %{ "$5 $dst, T$7, $src, T$6\n\t" - "$5 $dst, T$9, $dst, T$8\t# convert 4$1 to 4$2 vector" + predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorCast$2`'2X src)); + format %{ "$6 $dst, T$8, $src, T$7\n\t" + "$6 $dst, T$10, $dst, T$9\t# convert $1$2 to $1$3 vector" %} ins_encode %{ - __ $5(as_FloatRegister($dst$$reg), __ T$7, as_FloatRegister($src$$reg), __ T$6); - __ $5(as_FloatRegister($dst$$reg), __ T$9, as_FloatRegister($dst$$reg), __ T$8); + __ $6(as_FloatRegister($dst$$reg), __ T$8, as_FloatRegister($src$$reg), __ T$7); + __ $6(as_FloatRegister($dst$$reg), __ T$10, as_FloatRegister($dst$$reg), __ T$9); + %} + ins_pipe(pipe_class_default); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 +VECTOR_CAST_I2I_L(4, I, B, D, X, xtn, 4S, 4H, 8H, 8B) +VECTOR_CAST_I2I_L(4, B, I, X, D, sxtl, 8B, 8H, 4H, 4S) +dnl + +instruct vcvt2Lto2F(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); + match(Set dst (VectorCastL2X src)); + format %{ "scvtfv T2D, $dst, $src\n\t" + "fcvtn $dst, T2S, $dst, T2D\t# convert 2L to 2F vector" + %} + ins_encode %{ + __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + __ fcvtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($dst$$reg), __ T2D); %} ins_pipe(pipe_slow); +%} +dnl +define(`VECTOR_CAST_I2F', ` +instruct vcvt$1$2to$1$3`'(vec$4 dst, vec$4 src) +%{ + predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorCast$2`'2X src)); + format %{ "scvtfv T$5, $dst, $src\t# convert $1$2 to $1$3 vector" %} + ins_encode %{ + __ scvtfv(__ T$5, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + %} + ins_pipe(pipe_class_default); %}')dnl -dnl $1 $2 $3 $4 $5 $6 $7 $8 $9 -VECTOR_CAST_B2I(B, I, X, D, sxtl, 8B, 8H, 4H, 4S) -VECTOR_CAST_B2I(I, B, D, X, xtn, 4S, 4H, 8H, 8B) +dnl $1 $2 $3 $4 $5 +VECTOR_CAST_I2F(2, I, F, D, 2S) +VECTOR_CAST_I2F(4, I, F, X, 4S) +VECTOR_CAST_I2F(2, L, D, X, 2D) +dnl +define(`VECTOR_CAST_I2F_L', ` +instruct vcvt$1$2to$1$3`'(vec$4 dst, vec$5 src) +%{ + predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorCast$2`'2X src)); + format %{ "sxtl $dst, T$7, $src, T$6\n\t" + "scvtfv T$7, $dst, $dst\t# convert $1$2 to $1$3 vector" + %} + ins_encode %{ + __ sxtl(as_FloatRegister($dst$$reg), __ T$7, as_FloatRegister($src$$reg), __ T$6); + __ scvtfv(__ T$7, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 +VECTOR_CAST_I2F_L(4, S, F, X, D, 4H, 4S) +VECTOR_CAST_I2F_L(2, I, D, X, D, 2S, 2D) +dnl instruct vcvt4Bto4F(vecX dst, vecD src) %{ predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); match(Set dst (VectorCastB2X src)); - format %{ "sxtl $dst, T8H, $src, T8B\n\t" - "sxtl $dst, T4S, $dst, T4H\n\t" + format %{ "sxtl $dst, T8H, $src, T8B\n\t" + "sxtl $dst, T4S, $dst, T4H\n\t" "scvtfv T4S, $dst, $dst\t# convert 4B to 4F vector" %} ins_encode %{ @@ -189,40 +242,71 @@ instruct vcvt4Bto4F(vecX dst, vecD src) %} ins_pipe(pipe_slow); %} -dnl -define(`VECTOR_CAST_I2F_L', ` -instruct vcvt$1$2to$1$3`'(vecX dst, vecD src) + +instruct vcvt2Fto2L(vecX dst, vecD src) %{ - predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); - match(Set dst (VectorCast$2`'2X src)); - format %{ "sxtl $dst, T$5, $src, T$4\n\t" - "scvtfv T$5, $dst, $dst\t# convert $1$2 to $1$3 vector" + predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_LONG); + match(Set dst (VectorCastF2X src)); + format %{ "fcvtl $dst, T2D, $src, T2S\n\t" + "fcvtzs $dst, T2D, $dst\t# convert 2F to 2L vector" %} ins_encode %{ - __ sxtl(as_FloatRegister($dst$$reg), __ T$5, as_FloatRegister($src$$reg), __ T$4); - __ scvtfv(__ T$5, as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); + __ fcvtl(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg), __ T2S); + __ fcvtzs(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($dst$$reg)); %} ins_pipe(pipe_slow); -%}')dnl -dnl $1 $2 $3 $4 $5 -VECTOR_CAST_I2F_L(4, S, F, 4H, 4S) -VECTOR_CAST_I2F_L(2, I, D, 2S, 2D) +%} dnl -define(`VECTOR_CAST_I2F', ` +define(`VECTOR_CAST_F2I', ` instruct vcvt$1$2to$1$3`'(vec$4 dst, vec$4 src) %{ predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); match(Set dst (VectorCast$2`'2X src)); - format %{ "scvtfv T$5, $dst, $src\t# convert $1$2 to $1$3 vector" %} + format %{ "fcvtzs $dst, T$5, $src\t# convert $1$2 to $1$3 vector" %} ins_encode %{ - __ scvtfv(__ T$5, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); + __ fcvtzs(as_FloatRegister($dst$$reg), __ T$5, as_FloatRegister($src$$reg)); %} ins_pipe(pipe_class_default); %}')dnl dnl $1 $2 $3 $4 $5 -VECTOR_CAST_I2F(2, I, F, D, 2S) -VECTOR_CAST_I2F(4, I, F, X, 4S) -VECTOR_CAST_I2F(2, L, D, X, 2D) +VECTOR_CAST_F2I(2, F, I, D, 2S) +VECTOR_CAST_F2I(4, F, I, X, 4S) +VECTOR_CAST_F2I(2, D, L, X, 2D) +dnl +define(`VECTOR_CAST_F2I_L', ` +instruct vcvt$1$2to$1$3`'(vec$4 dst, vec$5 src) +%{ + predicate(n->as_Vector()->length() == $1 && n->bottom_type()->is_vect()->element_basic_type() == T_`'TYPE2DATATYPE($3)); + match(Set dst (VectorCast$2`'2X src)); + format %{ "fcvtzs $dst, T$6, $src\n\t" + "xtn $dst, T$7, $dst, T$6\t# convert $1$2 to $1$3 vector" + %} + ins_encode %{ + __ fcvtzs(as_FloatRegister($dst$$reg), __ T$6, as_FloatRegister($src$$reg)); + __ xtn(as_FloatRegister($dst$$reg), __ T$7, as_FloatRegister($dst$$reg), __ T$6); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl $1 $2 $3 $4 $5 $6 $7 +VECTOR_CAST_F2I_L(4, F, S, D, X, 4S, 4H) +VECTOR_CAST_F2I_L(2, D, I, D, X, 2D, 2S) +dnl + +instruct vcvt4Fto4B(vecD dst, vecX src) +%{ + predicate(n->as_Vector()->length() == 4 && n->bottom_type()->is_vect()->element_basic_type() == T_BYTE); + match(Set dst (VectorCastF2X src)); + format %{ "fcvtzs $dst, T4S, $src\n\t" + "xtn $dst, T4H, $dst, T4S\n\t" + "xtn $dst, T8B, $dst, T8H\t# convert 4F to 4B vector" + %} + ins_encode %{ + __ fcvtzs(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg)); + __ xtn(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($dst$$reg), __ T4S); + __ xtn(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($dst$$reg), __ T8H); + %} + ins_pipe(pipe_slow); +%} dnl define(`VECTOR_CAST_F2F', ` instruct vcvt2$1to2$2`'(vec$3 dst, vec$4 src) @@ -240,20 +324,6 @@ VECTOR_CAST_F2F(F, D, X, D, fcvtl, 2S, 2D) VECTOR_CAST_F2F(D, F, D, X, fcvtn, 2D, 2S) dnl -instruct vcvt2Lto2F(vecD dst, vecX src) -%{ - predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); - match(Set dst (VectorCastL2X src)); - format %{ "scvtfv T2D, $dst, $src\n\t" - "fcvtn $dst, T2S, $dst, T2D\t# convert 2L to 2F vector" - %} - ins_encode %{ - __ scvtfv(__ T2D, as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); - __ fcvtn(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($dst$$reg), __ T2D); - %} - ins_pipe(pipe_slow); -%} - // ------------------------------ Reduction ------------------------------- dnl define(`REDUCE_ADD_BORS', ` @@ -909,7 +979,7 @@ instruct vmul2L(vecX dst, vecX src1, vecX src2, iRegLNoSp tmp1, iRegLNoSp tmp2) "umov $tmp1, $src1, D, 1\n\t" "umov $tmp2, $src2, D, 1\n\t" "mul $tmp2, $tmp2, $tmp1\n\t" - "mov $dst, T2D, 1, $tmp2\t# insert into vector(2L)\n\t" + "mov $dst, T2D, 1, $tmp2\t# insert into vector(2L)" %} ins_encode %{ __ umov($tmp1$$Register, as_FloatRegister($src1$$reg), __ D, 0); @@ -1782,7 +1852,7 @@ instruct vmuladdS2I(vecX dst, vecX src1, vecX src2, vecX tmp) %{ effect(TEMP_DEF dst, TEMP tmp); format %{ "smullv $tmp, $src1, $src2\t# vector (4H)\n\t" "smullv $dst, $src1, $src2\t# vector (8H)\n\t" - "addpv $dst, $tmp, $dst\t# vector (4S)\n\t" %} + "addpv $dst, $tmp, $dst\t# vector (4S)" %} ins_encode %{ __ smullv(as_FloatRegister($tmp$$reg), __ T4H, as_FloatRegister($src1$$reg), diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index abdc424062de4d860df7d914ed975f5faa27f114..c5f65f2bc5a92ac0a705a35840dc095324530f37 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -2823,6 +2823,16 @@ public: f(0b000001, 15, 10), rf(Vn, 5), rf(Vd, 0); } + // Advanced SIMD scalar copy + void dup(FloatRegister Vd, SIMD_RegVariant T, FloatRegister Vn, int index = 0) + { + starti; + assert(T != Q, "invalid size"); + f(0b01011110000, 31, 21); + f((1 << T) | (index << (T + 1)), 20, 16); + f(0b000001, 15, 10), rf(Vn, 5), rf(Vd, 0); + } + // AdvSIMD ZIP/UZP/TRN #define INSN(NAME, opcode) \ void NAME(FloatRegister Vd, SIMD_Arrangement T, FloatRegister Vn, FloatRegister Vm) { \ @@ -2901,6 +2911,7 @@ public: INSN(frintn, 0, 0b00, 0b01, 0b11000); INSN(frintm, 0, 0b00, 0b01, 0b11001); INSN(frintp, 0, 0b10, 0b01, 0b11000); + INSN(fcvtzs, 0, 0b10, 0b01, 0b11011); #undef ASSERTION #define ASSERTION (T == T8B || T == T16B || T == T4H || T == T8H || T == T2S || T == T4S) diff --git a/src/hotspot/cpu/aarch64/atomic_aarch64.hpp b/src/hotspot/cpu/aarch64/atomic_aarch64.hpp index 6f9425e43ac1464458fd39bb3db5b50f960e4bdb..f1e1f04c2442261ec991d97a591cec4a1579c113 100644 --- a/src/hotspot/cpu/aarch64/atomic_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/atomic_aarch64.hpp @@ -37,6 +37,8 @@ typedef uint64_t (*aarch64_atomic_stub_t)(volatile void *ptr, uint64_t arg1, uin // Pointers to stubs extern aarch64_atomic_stub_t aarch64_atomic_fetch_add_4_impl; extern aarch64_atomic_stub_t aarch64_atomic_fetch_add_8_impl; +extern aarch64_atomic_stub_t aarch64_atomic_fetch_add_4_relaxed_impl; +extern aarch64_atomic_stub_t aarch64_atomic_fetch_add_8_relaxed_impl; extern aarch64_atomic_stub_t aarch64_atomic_xchg_4_impl; extern aarch64_atomic_stub_t aarch64_atomic_xchg_8_impl; extern aarch64_atomic_stub_t aarch64_atomic_cmpxchg_1_impl; diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp index bc3fc6355d0183b4cad5ca7ff506c7afa1d6ffb1..a4a2b14203976333a707fa734ba02aedf4dca30a 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp @@ -276,7 +276,7 @@ void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) { __ load_method_holder_cld(rscratch1, rmethod); // Is it a strong CLD? - __ ldr(rscratch2, Address(rscratch1, ClassLoaderData::keep_alive_offset())); + __ ldrw(rscratch2, Address(rscratch1, ClassLoaderData::keep_alive_offset())); __ cbnz(rscratch2, method_live); // Is it a weak but alive CLD? diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index de228bfeb6f9ca54d74812143b14ad35fb0658f3..5c9b1fc327d09af0d73b39e40e8b54bb106a1b90 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -1855,15 +1855,6 @@ void MacroAssembler::increment(Address dst, int value) str(rscratch1, dst); } - -void MacroAssembler::pusha() { - push(0x7fffffff, sp); -} - -void MacroAssembler::popa() { - pop(0x7fffffff, sp); -} - // Push lots of registers in the bit set supplied. Don't push sp. // Return the number of words pushed int MacroAssembler::push(unsigned int bitset, Register stack) { @@ -2505,7 +2496,7 @@ void MacroAssembler::pop_call_clobbered_registers_except(RegSet exclude) { void MacroAssembler::push_CPU_state(bool save_vectors, bool use_sve, int sve_vector_size_in_bytes) { - push(0x3fffffff, sp); // integer registers except lr & sp + push(RegSet::range(r0, r29), sp); // integer registers except lr & sp if (save_vectors && use_sve && sve_vector_size_in_bytes > 16) { sub(sp, sp, sve_vector_size_in_bytes * FloatRegisterImpl::number_of_registers); for (int i = 0; i < FloatRegisterImpl::number_of_registers; i++) { @@ -2543,7 +2534,14 @@ void MacroAssembler::pop_CPU_state(bool restore_vectors, bool use_sve, reinitialize_ptrue(); } - pop(0x3fffffff, sp); // integer registers except lr & sp + // integer registers except lr & sp + pop(RegSet::range(r0, r17), sp); +#ifdef R18_RESERVED + ldp(zr, r19, Address(post(sp, 2 * wordSize))); + pop(RegSet::range(r20, r29), sp); +#else + pop(RegSet::range(r18_tls, r29), sp); +#endif } /** diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index dae0a206656361208bc97f794d64be9425955962..3287f153ab8f846c74619b0e774dab5484896201 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1106,10 +1106,6 @@ public: void push(Register src); void pop(Register dst); - // push all registers onto the stack - void pusha(); - void popa(); - void repne_scan(Register addr, Register value, Register count, Register scratch); void repne_scanw(Register addr, Register value, Register count, diff --git a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp index e5bee7990a6f50316bcad301374d62b0ae688bd6..e0bb39f9e5ffcf61690572f368aa84dc39796cff 100644 --- a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp @@ -56,7 +56,7 @@ static const bool supports_generic_vector_operands = false; // No support for 48 extra htbl entries in aes-gcm intrinsic - static const int htbl_entries = -1; + static const int htbl_entries = 0; static constexpr bool isSimpleConstant64(jlong value) { // Will one (StoreL ConL) be cheaper than two (StoreI ConI)?. @@ -163,4 +163,7 @@ return true; } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = false; + #endif // CPU_AARCH64_MATCHER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 8c7ae234d042d4406a6d555f3f6cbb11c83b2995..0388bb73b919b912162fdf80aadeef45658d398e 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -3094,7 +3094,8 @@ class StubGenerator: public StubCodeGenerator { // key = c_rarg4 // state = c_rarg5 - GHASH.state // subkeyHtbl = c_rarg6 - powers of H - // counter = c_rarg7 - pointer to 16 bytes of CTR + // subkeyHtbl_48_entries = c_rarg7 (not used) + // counter = [sp, #0] pointer to 16 bytes of CTR // return - number of processed bytes address generate_galoisCounterMode_AESCrypt() { address ghash_polynomial = __ pc(); @@ -3107,6 +3108,8 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", "galoisCounterMode_AESCrypt"); address start = __ pc(); + __ enter(); + const Register in = c_rarg0; const Register len = c_rarg1; const Register ct = c_rarg2; @@ -3118,10 +3121,12 @@ class StubGenerator: public StubCodeGenerator { const Register subkeyHtbl = c_rarg6; + // Pointer to CTR is passed on the stack before the (fp, lr) pair. + const Address counter_mem(sp, 2 * wordSize); const Register counter = c_rarg7; + __ ldr(counter, counter_mem); const Register keylen = r10; - __ enter(); // Save state before entering routine __ sub(sp, sp, 4 * 16); __ st1(v12, v13, v14, v15, __ T16B, Address(sp)); @@ -4834,18 +4839,6 @@ class StubGenerator: public StubCodeGenerator { return entry; } - // code for comparing 16 bytes of strings with same encoding - void compare_string_16_bytes_same(Label &DIFF1, Label &DIFF2) { - Register result = r0, str1 = r1, cnt1 = r2, str2 = r3, tmp1 = r10, tmp2 = r11; - __ ldr(rscratch1, Address(__ post(str1, 8))); - __ eor(rscratch2, tmp1, tmp2); - __ ldr(cnt1, Address(__ post(str2, 8))); - __ cbnz(rscratch2, DIFF1); - __ ldr(tmp1, Address(__ post(str1, 8))); - __ eor(rscratch2, rscratch1, cnt1); - __ ldr(tmp2, Address(__ post(str2, 8))); - __ cbnz(rscratch2, DIFF2); - } // code for comparing 16 characters of strings with Latin1 and Utf16 encoding void compare_string_16_x_LU(Register tmpL, Register tmpU, Label &DIFF1, @@ -5052,15 +5045,18 @@ class StubGenerator: public StubCodeGenerator { : "compare_long_string_same_encoding UU"); address entry = __ pc(); Register result = r0, str1 = r1, cnt1 = r2, str2 = r3, cnt2 = r4, - tmp1 = r10, tmp2 = r11; - Label SMALL_LOOP, LARGE_LOOP_PREFETCH, CHECK_LAST, DIFF2, TAIL, - LENGTH_DIFF, DIFF, LAST_CHECK_AND_LENGTH_DIFF, - DIFF_LAST_POSITION, DIFF_LAST_POSITION2; + tmp1 = r10, tmp2 = r11, tmp1h = rscratch1, tmp2h = rscratch2; + + Label LARGE_LOOP_PREFETCH, LOOP_COMPARE16, DIFF, LESS16, LESS8, CAL_DIFFERENCE, LENGTH_DIFF; + // exit from large loop when less than 64 bytes left to read or we're about // to prefetch memory behind array border int largeLoopExitCondition = MAX2(64, SoftwarePrefetchHintDistance)/(isLL ? 1 : 2); - // cnt1/cnt2 contains amount of characters to compare. cnt1 can be re-used - // update cnt2 counter with already loaded 8 bytes + + // before jumping to stub, pre-load 8 bytes already, so do comparison directly + __ eor(rscratch2, tmp1, tmp2); + __ cbnz(rscratch2, CAL_DIFFERENCE); + __ sub(cnt2, cnt2, wordSize/(isLL ? 1 : 2)); // update pointers, because of previous read __ add(str1, str1, wordSize); @@ -5069,80 +5065,88 @@ class StubGenerator: public StubCodeGenerator { __ bind(LARGE_LOOP_PREFETCH); __ prfm(Address(str1, SoftwarePrefetchHintDistance)); __ prfm(Address(str2, SoftwarePrefetchHintDistance)); - compare_string_16_bytes_same(DIFF, DIFF2); - compare_string_16_bytes_same(DIFF, DIFF2); + + __ align(OptoLoopAlignment); + for (int i = 0; i < 4; i++) { + __ ldp(tmp1, tmp1h, Address(str1, i * 16)); + __ ldp(tmp2, tmp2h, Address(str2, i * 16)); + __ cmp(tmp1, tmp2); + __ ccmp(tmp1h, tmp2h, 0, Assembler::EQ); + __ br(Assembler::NE, DIFF); + } __ sub(cnt2, cnt2, isLL ? 64 : 32); - compare_string_16_bytes_same(DIFF, DIFF2); + __ add(str1, str1, 64); + __ add(str2, str2, 64); __ subs(rscratch2, cnt2, largeLoopExitCondition); - compare_string_16_bytes_same(DIFF, DIFF2); - __ br(__ GT, LARGE_LOOP_PREFETCH); - __ cbz(cnt2, LAST_CHECK_AND_LENGTH_DIFF); // no more chars left? + __ br(Assembler::GE, LARGE_LOOP_PREFETCH); + __ cbz(cnt2, LENGTH_DIFF); // no more chars left? } - // less than 16 bytes left? - __ subs(cnt2, cnt2, isLL ? 16 : 8); - __ br(__ LT, TAIL); + + __ subs(rscratch1, cnt2, isLL ? 16 : 8); + __ br(Assembler::LE, LESS16); __ align(OptoLoopAlignment); - __ bind(SMALL_LOOP); - compare_string_16_bytes_same(DIFF, DIFF2); - __ subs(cnt2, cnt2, isLL ? 16 : 8); - __ br(__ GE, SMALL_LOOP); - __ bind(TAIL); - __ adds(cnt2, cnt2, isLL ? 16 : 8); - __ br(__ EQ, LAST_CHECK_AND_LENGTH_DIFF); + __ bind(LOOP_COMPARE16); + __ ldp(tmp1, tmp1h, Address(__ post(str1, 16))); + __ ldp(tmp2, tmp2h, Address(__ post(str2, 16))); + __ cmp(tmp1, tmp2); + __ ccmp(tmp1h, tmp2h, 0, Assembler::EQ); + __ br(Assembler::NE, DIFF); + __ sub(cnt2, cnt2, isLL ? 16 : 8); + __ subs(rscratch2, cnt2, isLL ? 16 : 8); + __ br(Assembler::LT, LESS16); + + __ ldp(tmp1, tmp1h, Address(__ post(str1, 16))); + __ ldp(tmp2, tmp2h, Address(__ post(str2, 16))); + __ cmp(tmp1, tmp2); + __ ccmp(tmp1h, tmp2h, 0, Assembler::EQ); + __ br(Assembler::NE, DIFF); + __ sub(cnt2, cnt2, isLL ? 16 : 8); + __ subs(rscratch2, cnt2, isLL ? 16 : 8); + __ br(Assembler::GE, LOOP_COMPARE16); + __ cbz(cnt2, LENGTH_DIFF); + + __ bind(LESS16); + // each 8 compare __ subs(cnt2, cnt2, isLL ? 8 : 4); - __ br(__ LE, CHECK_LAST); - __ eor(rscratch2, tmp1, tmp2); - __ cbnz(rscratch2, DIFF); + __ br(Assembler::LE, LESS8); __ ldr(tmp1, Address(__ post(str1, 8))); __ ldr(tmp2, Address(__ post(str2, 8))); + __ eor(rscratch2, tmp1, tmp2); + __ cbnz(rscratch2, CAL_DIFFERENCE); __ sub(cnt2, cnt2, isLL ? 8 : 4); - __ bind(CHECK_LAST); + + __ bind(LESS8); // directly load last 8 bytes if (!isLL) { - __ add(cnt2, cnt2, cnt2); // now in bytes + __ add(cnt2, cnt2, cnt2); } + __ ldr(tmp1, Address(str1, cnt2)); + __ ldr(tmp2, Address(str2, cnt2)); __ eor(rscratch2, tmp1, tmp2); - __ cbnz(rscratch2, DIFF); - __ ldr(rscratch1, Address(str1, cnt2)); - __ ldr(cnt1, Address(str2, cnt2)); - __ eor(rscratch2, rscratch1, cnt1); __ cbz(rscratch2, LENGTH_DIFF); - // Find the first different characters in the longwords and - // compute their difference. - __ bind(DIFF2); - __ rev(rscratch2, rscratch2); - __ clz(rscratch2, rscratch2); - __ andr(rscratch2, rscratch2, isLL ? -8 : -16); - __ lsrv(rscratch1, rscratch1, rscratch2); - if (isLL) { - __ lsrv(cnt1, cnt1, rscratch2); - __ uxtbw(rscratch1, rscratch1); - __ uxtbw(cnt1, cnt1); - } else { - __ lsrv(cnt1, cnt1, rscratch2); - __ uxthw(rscratch1, rscratch1); - __ uxthw(cnt1, cnt1); - } - __ subw(result, rscratch1, cnt1); - __ b(LENGTH_DIFF); + __ b(CAL_DIFFERENCE); + __ bind(DIFF); + __ cmp(tmp1, tmp2); + __ csel(tmp1, tmp1, tmp1h, Assembler::NE); + __ csel(tmp2, tmp2, tmp2h, Assembler::NE); + // reuse rscratch2 register for the result of eor instruction + __ eor(rscratch2, tmp1, tmp2); + + __ bind(CAL_DIFFERENCE); __ rev(rscratch2, rscratch2); __ clz(rscratch2, rscratch2); __ andr(rscratch2, rscratch2, isLL ? -8 : -16); __ lsrv(tmp1, tmp1, rscratch2); + __ lsrv(tmp2, tmp2, rscratch2); if (isLL) { - __ lsrv(tmp2, tmp2, rscratch2); __ uxtbw(tmp1, tmp1); __ uxtbw(tmp2, tmp2); } else { - __ lsrv(tmp2, tmp2, rscratch2); __ uxthw(tmp1, tmp1); __ uxthw(tmp2, tmp2); } __ subw(result, tmp1, tmp2); - __ b(LENGTH_DIFF); - __ bind(LAST_CHECK_AND_LENGTH_DIFF); - __ eor(rscratch2, tmp1, tmp2); - __ cbnz(rscratch2, DIFF); + __ bind(LENGTH_DIFF); __ ret(lr); return entry; @@ -6205,10 +6209,16 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); } - void gen_ldaddal_entry(Assembler::operand_size size) { + void gen_ldadd_entry(Assembler::operand_size size, atomic_memory_order order) { Register prev = r2, addr = c_rarg0, incr = c_rarg1; - __ ldaddal(size, incr, prev, addr); - __ membar(Assembler::StoreStore|Assembler::StoreLoad); + // If not relaxed, then default to conservative. Relaxed is the only + // case we use enough to be worth specializing. + if (order == memory_order_relaxed) { + __ ldadd(size, incr, prev, addr); + } else { + __ ldaddal(size, incr, prev, addr); + __ membar(Assembler::StoreStore|Assembler::StoreLoad); + } if (size == Assembler::xword) { __ mov(r0, prev); } else { @@ -6238,12 +6248,21 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, "StubRoutines", "atomic entry points"); address first_entry = __ pc(); - // All memory_order_conservative + // ADD, memory_order_conservative AtomicStubMark mark_fetch_add_4(_masm, &aarch64_atomic_fetch_add_4_impl); - gen_ldaddal_entry(Assembler::word); + gen_ldadd_entry(Assembler::word, memory_order_conservative); AtomicStubMark mark_fetch_add_8(_masm, &aarch64_atomic_fetch_add_8_impl); - gen_ldaddal_entry(Assembler::xword); + gen_ldadd_entry(Assembler::xword, memory_order_conservative); + + // ADD, memory_order_relaxed + AtomicStubMark mark_fetch_add_4_relaxed + (_masm, &aarch64_atomic_fetch_add_4_relaxed_impl); + gen_ldadd_entry(MacroAssembler::word, memory_order_relaxed); + AtomicStubMark mark_fetch_add_8_relaxed + (_masm, &aarch64_atomic_fetch_add_8_relaxed_impl); + gen_ldadd_entry(MacroAssembler::xword, memory_order_relaxed); + // XCHG, memory_order_conservative AtomicStubMark mark_xchg_4(_masm, &aarch64_atomic_xchg_4_impl); gen_swpal_entry(Assembler::word); AtomicStubMark mark_xchg_8_impl(_masm, &aarch64_atomic_xchg_8_impl); @@ -7443,6 +7462,8 @@ void StubGenerator_generate(CodeBuffer* code, bool all) { DEFAULT_ATOMIC_OP(fetch_add, 4, ) DEFAULT_ATOMIC_OP(fetch_add, 8, ) +DEFAULT_ATOMIC_OP(fetch_add, 4, _relaxed) +DEFAULT_ATOMIC_OP(fetch_add, 8, _relaxed) DEFAULT_ATOMIC_OP(xchg, 4, ) DEFAULT_ATOMIC_OP(xchg, 8, ) DEFAULT_ATOMIC_OP(cmpxchg, 1, ) diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index db253fe5c2cd07a3a0cbc894525e5885a75594c0..e20cffd57670b49e7ee7372a5432f1a0c1249e2b 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -1397,11 +1397,12 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ cmp(rscratch1, (u1)StackOverflow::stack_guard_yellow_reserved_disabled); __ br(Assembler::NE, no_reguard); - __ pusha(); // XXX only save smashed registers + __ push_call_clobbered_registers(); __ mov(c_rarg0, rthread); __ mov(rscratch2, CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); __ blr(rscratch2); - __ popa(); // XXX only restore smashed registers + __ pop_call_clobbered_registers(); + __ bind(no_reguard); } diff --git a/src/hotspot/cpu/arm/matcher_arm.hpp b/src/hotspot/cpu/arm/matcher_arm.hpp index b7a9a3f50425ea74d877ef7fbb883bb60e18d728..6254f4b33991d66069d47cf02aee04e1e7ed4e7d 100644 --- a/src/hotspot/cpu/arm/matcher_arm.hpp +++ b/src/hotspot/cpu/arm/matcher_arm.hpp @@ -155,4 +155,7 @@ return false; } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = false; + #endif // CPU_ARM_MATCHER_ARM_HPP diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index d6d58262de5aa960c2dbd5d0a7c8715b71ff1f64..2f4287a9553d1bbff29a87f77bf94e7bbff9dd12 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -47,6 +47,9 @@ class Address { Address(Register b, address d = 0) : _base(b), _index(noreg), _disp((intptr_t)d) {} + Address(Register b, ByteSize d) + : _base(b), _index(noreg), _disp((intptr_t)d) {} + Address(Register b, intptr_t d) : _base(b), _index(noreg), _disp(d) {} diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp index 800b34e4ba73663e206807a0c0ff7b489612c296..3758cc2fcf7627a618571efeb88589494092d0e5 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp @@ -111,16 +111,28 @@ void BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, } } +// Generic implementation. GCs can provide an optimized one. void BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2, MacroAssembler::PreservationLevel preservation_level) { - Label done; + Label done, not_weak, verify; __ cmpdi(CCR0, value, 0); __ beq(CCR0, done); // Use NULL as-is. - __ clrrdi(tmp1, value, JNIHandles::weak_tag_size); - __ ld(value, 0, tmp1); // Resolve (untagged) jobject. + __ andi_(tmp1, value, JNIHandles::weak_tag_mask); + __ beq(CCR0, not_weak); // Test for jweak tag. + // Resolve (untagged) jobject. + __ clrrdi(value, value, JNIHandles::weak_tag_size); + load_at(masm, IN_NATIVE | ON_PHANTOM_OOP_REF, T_OBJECT, + value, (intptr_t)0, value, tmp1, tmp2, preservation_level); + __ b(verify); + + __ bind(not_weak); + load_at(masm, IN_NATIVE, T_OBJECT, + value, (intptr_t)0, value, tmp1, tmp2, preservation_level); + + __ bind(verify); __ verify_oop(value, FILE_AND_LINE); __ bind(done); } @@ -178,7 +190,7 @@ void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler *masm, Register tmp1, __ ld(tmp1_class_loader_data, in_bytes(InstanceKlass::class_loader_data_offset()), tmp1); // Fast path: If class loader is strong, the holder cannot be unloaded. - __ ld(tmp2, in_bytes(ClassLoaderData::keep_alive_offset()), tmp1_class_loader_data); + __ lwz(tmp2, in_bytes(ClassLoaderData::keep_alive_offset()), tmp1_class_loader_data); __ cmpdi(CCR0, tmp2, 0); __ bne(CCR0, skip_barrier); diff --git a/src/hotspot/cpu/ppc/gc/shared/modRefBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/modRefBarrierSetAssembler_ppc.cpp index ed66c5f892918383b26305bb0a46bf47170dd373..1d1f923108f2ae96b6bae1e6e0d9a082119c81e5 100644 --- a/src/hotspot/cpu/ppc/gc/shared/modRefBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/modRefBarrierSetAssembler_ppc.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "gc/shared/modRefBarrierSetAssembler.hpp" +#include "runtime/jniHandles.hpp" #define __ masm-> @@ -74,3 +75,17 @@ void ModRefBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet deco preservation_level); } } + +void ModRefBarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level) { + Label done; + __ cmpdi(CCR0, value, 0); + __ beq(CCR0, done); // Use NULL as-is. + + __ clrrdi(tmp1, value, JNIHandles::weak_tag_size); + __ ld(value, 0, tmp1); // Resolve (untagged) jobject. + + __ verify_oop(value, FILE_AND_LINE); + __ bind(done); +} diff --git a/src/hotspot/cpu/ppc/gc/shared/modRefBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shared/modRefBarrierSetAssembler_ppc.hpp index eec826212803ca1b9b97e5c094977593d52381f4..5d105f6c0484f7fd0901a23ed6e6e55260796b3b 100644 --- a/src/hotspot/cpu/ppc/gc/shared/modRefBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shared/modRefBarrierSetAssembler_ppc.hpp @@ -57,6 +57,10 @@ public: Register base, RegisterOrConstant ind_or_offs, Register val, Register tmp1, Register tmp2, Register tmp3, MacroAssembler::PreservationLevel preservation_level); + + virtual void resolve_jobject(MacroAssembler* masm, Register value, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level); }; #endif // CPU_PPC_GC_SHARED_MODREFBARRIERSETASSEMBLER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26c3bf371f3fed342304fe676cd8d207fc0758fd --- /dev/null +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "asm/register.hpp" +#include "precompiled.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "code/codeBlob.hpp" +#include "code/vmreg.inline.hpp" +#include "gc/z/zBarrier.inline.hpp" +#include "gc/z/zBarrierSet.hpp" +#include "gc/z/zBarrierSetAssembler.hpp" +#include "gc/z/zBarrierSetRuntime.hpp" +#include "gc/z/zThreadLocalData.hpp" +#include "memory/resourceArea.hpp" +#include "register_ppc.hpp" +#include "runtime/sharedRuntime.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" +#ifdef COMPILER1 +#include "c1/c1_LIRAssembler.hpp" +#include "c1/c1_MacroAssembler.hpp" +#include "gc/z/c1/zBarrierSetC1.hpp" +#endif // COMPILER1 +#ifdef COMPILER2 +#include "gc/z/c2/zBarrierSetC2.hpp" +#endif // COMPILER2 + +#undef __ +#define __ masm-> + +void ZBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register base, RegisterOrConstant ind_or_offs, Register dst, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level, Label *L_handle_null) { + __ block_comment("load_at (zgc) {"); + + // Check whether a special gc barrier is required for this particular load + // (e.g. whether it's a reference load or not) + if (!ZBarrierSet::barrier_needed(decorators, type)) { + BarrierSetAssembler::load_at(masm, decorators, type, base, ind_or_offs, dst, + tmp1, tmp2, preservation_level, L_handle_null); + return; + } + + if (ind_or_offs.is_register()) { + assert_different_registers(base, ind_or_offs.as_register(), tmp1, tmp2, R0, noreg); + assert_different_registers(dst, ind_or_offs.as_register(), tmp1, tmp2, R0, noreg); + } else { + assert_different_registers(base, tmp1, tmp2, R0, noreg); + assert_different_registers(dst, tmp1, tmp2, R0, noreg); + } + + /* ==== Load the pointer using the standard implementation for the actual heap access + and the decompression of compressed pointers ==== */ + // Result of 'load_at' (standard implementation) will be written back to 'dst'. + // As 'base' is required for the C-call, it must be reserved in case of a register clash. + Register saved_base = base; + if (base == dst) { + __ mr(tmp2, base); + saved_base = tmp2; + } + + BarrierSetAssembler::load_at(masm, decorators, type, base, ind_or_offs, dst, + tmp1, noreg, preservation_level, L_handle_null); + + /* ==== Check whether pointer is dirty ==== */ + Label skip_barrier; + + // Load bad mask into scratch register. + __ ld(tmp1, (intptr_t) ZThreadLocalData::address_bad_mask_offset(), R16_thread); + + // The color bits of the to-be-tested pointer do not have to be equivalent to the 'bad_mask' testing bits. + // A pointer is classified as dirty if any of the color bits that also match the bad mask is set. + // Conversely, it follows that the logical AND of the bad mask and the pointer must be zero + // if the pointer is not dirty. + // Only dirty pointers must be processed by this barrier, so we can skip it in case the latter condition holds true. + __ and_(tmp1, tmp1, dst); + __ beq(CCR0, skip_barrier); + + /* ==== Invoke barrier ==== */ + int nbytes_save = 0; + + const bool needs_frame = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR; + const bool preserve_gp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS; + const bool preserve_fp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS; + + const bool preserve_R3 = dst != R3_ARG1; + + if (needs_frame) { + if (preserve_gp_registers) { + nbytes_save = (preserve_fp_registers + ? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs + : MacroAssembler::num_volatile_gp_regs) * BytesPerWord; + nbytes_save -= preserve_R3 ? 0 : BytesPerWord; + __ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers, preserve_R3); + } + + __ save_LR_CR(tmp1); + __ push_frame_reg_args(nbytes_save, tmp1); + } + + // Setup arguments + if (saved_base != R3_ARG1) { + __ mr_if_needed(R3_ARG1, dst); + __ add(R4_ARG2, ind_or_offs, saved_base); + } else if (dst != R4_ARG2) { + __ add(R4_ARG2, ind_or_offs, saved_base); + __ mr(R3_ARG1, dst); + } else { + __ add(R0, ind_or_offs, saved_base); + __ mr(R3_ARG1, dst); + __ mr(R4_ARG2, R0); + } + + __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators)); + + Register result = R3_RET; + if (needs_frame) { + __ pop_frame(); + __ restore_LR_CR(tmp1); + + if (preserve_R3) { + __ mr(R0, R3_RET); + result = R0; + } + + if (preserve_gp_registers) { + __ restore_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers, preserve_R3); + } + } + __ mr_if_needed(dst, result); + + __ bind(skip_barrier); + __ block_comment("} load_at (zgc)"); +} + +#ifdef ASSERT +// The Z store barrier only verifies the pointers it is operating on and is thus a sole debugging measure. +void ZBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register base, RegisterOrConstant ind_or_offs, Register val, + Register tmp1, Register tmp2, Register tmp3, + MacroAssembler::PreservationLevel preservation_level) { + __ block_comment("store_at (zgc) {"); + + // If the 'val' register is 'noreg', the to-be-stored value is a null pointer. + if (is_reference_type(type) && val != noreg) { + __ ld(tmp1, in_bytes(ZThreadLocalData::address_bad_mask_offset()), R16_thread); + __ and_(tmp1, tmp1, val); + __ asm_assert_eq("Detected dirty pointer on the heap in Z store barrier"); + } + + // Store value + BarrierSetAssembler::store_at(masm, decorators, type, base, ind_or_offs, val, tmp1, tmp2, tmp3, preservation_level); + + __ block_comment("} store_at (zgc)"); +} +#endif // ASSERT + +void ZBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, DecoratorSet decorators, BasicType component_type, + Register src, Register dst, Register count, + Register preserve1, Register preserve2) { + __ block_comment("arraycopy_prologue (zgc) {"); + + /* ==== Check whether a special gc barrier is required for this particular load ==== */ + if (!is_reference_type(component_type)) { + return; + } + + Label skip_barrier; + + // Fast path: Array is of length zero + __ cmpdi(CCR0, count, 0); + __ beq(CCR0, skip_barrier); + + /* ==== Ensure register sanity ==== */ + Register tmp_R11 = R11_scratch1; + + assert_different_registers(src, dst, count, tmp_R11, noreg); + if (preserve1 != noreg) { + // Not technically required, but unlikely being intended. + assert_different_registers(preserve1, preserve2); + } + + /* ==== Invoke barrier (slowpath) ==== */ + int nbytes_save = 0; + + { + assert(!noreg->is_volatile(), "sanity"); + + if (preserve1->is_volatile()) { + __ std(preserve1, -BytesPerWord * ++nbytes_save, R1_SP); + } + + if (preserve2->is_volatile() && preserve1 != preserve2) { + __ std(preserve2, -BytesPerWord * ++nbytes_save, R1_SP); + } + + __ std(src, -BytesPerWord * ++nbytes_save, R1_SP); + __ std(dst, -BytesPerWord * ++nbytes_save, R1_SP); + __ std(count, -BytesPerWord * ++nbytes_save, R1_SP); + + __ save_LR_CR(tmp_R11); + __ push_frame_reg_args(nbytes_save, tmp_R11); + } + + // ZBarrierSetRuntime::load_barrier_on_oop_array_addr(src, count) + if (count == R3_ARG1) { + if (src == R4_ARG2) { + // Arguments are provided in reverse order + __ mr(tmp_R11, count); + __ mr(R3_ARG1, src); + __ mr(R4_ARG2, tmp_R11); + } else { + __ mr(R4_ARG2, count); + __ mr(R3_ARG1, src); + } + } else { + __ mr_if_needed(R3_ARG1, src); + __ mr_if_needed(R4_ARG2, count); + } + + __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_array_addr()); + + __ pop_frame(); + __ restore_LR_CR(tmp_R11); + + { + __ ld(count, -BytesPerWord * nbytes_save--, R1_SP); + __ ld(dst, -BytesPerWord * nbytes_save--, R1_SP); + __ ld(src, -BytesPerWord * nbytes_save--, R1_SP); + + if (preserve2->is_volatile() && preserve1 != preserve2) { + __ ld(preserve2, -BytesPerWord * nbytes_save--, R1_SP); + } + + if (preserve1->is_volatile()) { + __ ld(preserve1, -BytesPerWord * nbytes_save--, R1_SP); + } + } + + __ bind(skip_barrier); + + __ block_comment("} arraycopy_prologue (zgc)"); +} + +void ZBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, + Register obj, Register tmp, Label& slowpath) { + __ block_comment("try_resolve_jobject_in_native (zgc) {"); + + assert_different_registers(jni_env, obj, tmp); + + // Resolve the pointer using the standard implementation for weak tag handling and pointer verfication. + BarrierSetAssembler::try_resolve_jobject_in_native(masm, dst, jni_env, obj, tmp, slowpath); + + // Check whether pointer is dirty. + __ ld(tmp, + in_bytes(ZThreadLocalData::address_bad_mask_offset() - JavaThread::jni_environment_offset()), + jni_env); + + __ and_(tmp, obj, tmp); + __ bne(CCR0, slowpath); + + __ block_comment("} try_resolve_jobject_in_native (zgc)"); +} + +#undef __ + +#ifdef COMPILER1 +#define __ ce->masm()-> + +// Code emitted by LIR node "LIR_OpZLoadBarrierTest" which in turn is emitted by ZBarrierSetC1::load_barrier. +// The actual compare and branch instructions are represented as stand-alone LIR nodes. +void ZBarrierSetAssembler::generate_c1_load_barrier_test(LIR_Assembler* ce, + LIR_Opr ref) const { + __ block_comment("load_barrier_test (zgc) {"); + + __ ld(R0, in_bytes(ZThreadLocalData::address_bad_mask_offset()), R16_thread); + __ andr(R0, R0, ref->as_pointer_register()); + __ cmpdi(CCR5 /* as mandated by LIR node */, R0, 0); + + __ block_comment("} load_barrier_test (zgc)"); +} + +// Code emitted by code stub "ZLoadBarrierStubC1" which in turn is emitted by ZBarrierSetC1::load_barrier. +// Invokes the runtime stub which is defined just below. +void ZBarrierSetAssembler::generate_c1_load_barrier_stub(LIR_Assembler* ce, + ZLoadBarrierStubC1* stub) const { + __ block_comment("c1_load_barrier_stub (zgc) {"); + + __ bind(*stub->entry()); + + /* ==== Determine relevant data registers and ensure register sanity ==== */ + Register ref = stub->ref()->as_register(); + Register ref_addr = noreg; + + // Determine reference address + if (stub->tmp()->is_valid()) { + // 'tmp' register is given, so address might have an index or a displacement. + ce->leal(stub->ref_addr(), stub->tmp()); + ref_addr = stub->tmp()->as_pointer_register(); + } else { + // 'tmp' register is not given, so address must have neither an index nor a displacement. + // The address' base register is thus usable as-is. + assert(stub->ref_addr()->as_address_ptr()->disp() == 0, "illegal displacement"); + assert(!stub->ref_addr()->as_address_ptr()->index()->is_valid(), "illegal index"); + + ref_addr = stub->ref_addr()->as_address_ptr()->base()->as_pointer_register(); + } + + assert_different_registers(ref, ref_addr, R0, noreg); + + /* ==== Invoke stub ==== */ + // Pass arguments via stack. The stack pointer will be bumped by the stub. + __ std(ref, (intptr_t) -1 * BytesPerWord, R1_SP); + __ std(ref_addr, (intptr_t) -2 * BytesPerWord, R1_SP); + + __ load_const_optimized(R0, stub->runtime_stub()); + __ call_stub(R0); + + // The runtime stub passes the result via the R0 register, overriding the previously-loaded stub address. + __ mr_if_needed(ref, R0); + __ b(*stub->continuation()); + + __ block_comment("} c1_load_barrier_stub (zgc)"); +} + +#undef __ +#define __ sasm-> + +// Code emitted by runtime code stub which in turn is emitted by ZBarrierSetC1::generate_c1_runtime_stubs. +void ZBarrierSetAssembler::generate_c1_load_barrier_runtime_stub(StubAssembler* sasm, + DecoratorSet decorators) const { + __ block_comment("c1_load_barrier_runtime_stub (zgc) {"); + + const int stack_parameters = 2; + const int nbytes_save = (MacroAssembler::num_volatile_regs + stack_parameters) * BytesPerWord; + + __ save_volatile_gprs(R1_SP, -nbytes_save); + __ save_LR_CR(R0); + + // Load arguments back again from the stack. + __ ld(R3_ARG1, (intptr_t) -1 * BytesPerWord, R1_SP); // ref + __ ld(R4_ARG2, (intptr_t) -2 * BytesPerWord, R1_SP); // ref_addr + + __ push_frame_reg_args(nbytes_save, R0); + + __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators)); + + __ verify_oop(R3_RET, "Bad pointer after barrier invocation"); + __ mr(R0, R3_RET); + + __ pop_frame(); + __ restore_LR_CR(R3_RET); + __ restore_volatile_gprs(R1_SP, -nbytes_save); + + __ blr(); + + __ block_comment("} c1_load_barrier_runtime_stub (zgc)"); +} + +#undef __ +#endif // COMPILER1 + +#ifdef COMPILER2 + +OptoReg::Name ZBarrierSetAssembler::refine_register(const Node* node, OptoReg::Name opto_reg) const { + if (!OptoReg::is_reg(opto_reg)) { + return OptoReg::Bad; + } + + VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if ((vm_reg->is_Register() || vm_reg ->is_FloatRegister()) && (opto_reg & 1) != 0) { + return OptoReg::Bad; + } + + return opto_reg; +} + +#define __ _masm-> + +class ZSaveLiveRegisters { + + private: + MacroAssembler* _masm; + RegMask _reg_mask; + Register _result_reg; + + public: + ZSaveLiveRegisters(MacroAssembler *masm, ZLoadBarrierStubC2 *stub) + : _masm(masm), _reg_mask(stub->live()), _result_reg(stub->ref()) { + + const int total_regs_amount = iterate_over_register_mask(ACTION_SAVE); + + __ save_LR_CR(R0); + __ push_frame_reg_args(total_regs_amount * BytesPerWord, R0); + } + + ~ZSaveLiveRegisters() { + __ pop_frame(); + __ restore_LR_CR(R0); + + iterate_over_register_mask(ACTION_RESTORE); + } + + private: + enum IterationAction : int { + ACTION_SAVE = 0, + ACTION_RESTORE = 1 + }; + + int iterate_over_register_mask(IterationAction action) { + int reg_save_index = 0; + RegMaskIterator live_regs_iterator(_reg_mask); + + while(live_regs_iterator.has_next()) { + const OptoReg::Name opto_reg = live_regs_iterator.next(); + + // Filter out stack slots (spilled registers, i.e., stack-allocated registers). + if (!OptoReg::is_reg(opto_reg)) { + continue; + } + + const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if (vm_reg->is_Register()) { + Register std_reg = vm_reg->as_Register(); + + // '_result_reg' will hold the end result of the operation. Its content must thus not be preserved. + if (std_reg == _result_reg) { + continue; + } + + if (std_reg->encoding() >= R2->encoding() && std_reg->encoding() <= R12->encoding()) { + reg_save_index++; + + if (action == ACTION_SAVE) { + _masm->std(std_reg, (intptr_t) -reg_save_index * BytesPerWord, R1_SP); + } else if (action == ACTION_RESTORE) { + _masm->ld(std_reg, (intptr_t) -reg_save_index * BytesPerWord, R1_SP); + } else { + fatal("Sanity"); + } + } + } else if (vm_reg->is_FloatRegister()) { + FloatRegister fp_reg = vm_reg->as_FloatRegister(); + if (fp_reg->encoding() >= F0->encoding() && fp_reg->encoding() <= F13->encoding()) { + reg_save_index++; + + if (action == ACTION_SAVE) { + _masm->stfd(fp_reg, (intptr_t) -reg_save_index * BytesPerWord, R1_SP); + } else if (action == ACTION_RESTORE) { + _masm->lfd(fp_reg, (intptr_t) -reg_save_index * BytesPerWord, R1_SP); + } else { + fatal("Sanity"); + } + } + } else if (vm_reg->is_ConditionRegister()) { + // NOP. Conditions registers are covered by save_LR_CR + } else { + if (vm_reg->is_VectorRegister()) { + fatal("Vector registers are unsupported. Found register %s", vm_reg->name()); + } else if (vm_reg->is_SpecialRegister()) { + fatal("Special registers are unsupported. Found register %s", vm_reg->name()); + } else { + fatal("Register type is not known"); + } + } + } + + return reg_save_index; + } +}; + +#undef __ +#define __ _masm-> + +class ZSetupArguments { + private: + MacroAssembler* const _masm; + const Register _ref; + const Address _ref_addr; + + public: + ZSetupArguments(MacroAssembler* masm, ZLoadBarrierStubC2* stub) : + _masm(masm), + _ref(stub->ref()), + _ref_addr(stub->ref_addr()) { + + // Desired register/argument configuration: + // _ref: R3_ARG1 + // _ref_addr: R4_ARG2 + + // '_ref_addr' can be unspecified. In that case, the barrier will not heal the reference. + if (_ref_addr.base() == noreg) { + assert_different_registers(_ref, R0, noreg); + + __ mr_if_needed(R3_ARG1, _ref); + __ li(R4_ARG2, 0); + } else { + assert_different_registers(_ref, _ref_addr.base(), R0, noreg); + assert(!_ref_addr.index()->is_valid(), "reference addresses must not contain an index component"); + + if (_ref != R4_ARG2) { + // Calculate address first as the address' base register might clash with R4_ARG2 + __ add(R4_ARG2, (intptr_t) _ref_addr.disp(), _ref_addr.base()); + __ mr_if_needed(R3_ARG1, _ref); + } else if (_ref_addr.base() != R3_ARG1) { + __ mr(R3_ARG1, _ref); + __ add(R4_ARG2, (intptr_t) _ref_addr.disp(), _ref_addr.base()); // Cloberring _ref + } else { + // Arguments are provided in inverse order (i.e. _ref == R4_ARG2, _ref_addr == R3_ARG1) + __ mr(R0, _ref); + __ add(R4_ARG2, (intptr_t) _ref_addr.disp(), _ref_addr.base()); + __ mr(R3_ARG1, R0); + } + } + } +}; + +#undef __ +#define __ masm-> + +void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const { + __ block_comment("generate_c2_load_barrier_stub (zgc) {"); + + __ bind(*stub->entry()); + + Register ref = stub->ref(); + Address ref_addr = stub->ref_addr(); + + assert_different_registers(ref, ref_addr.base()); + + { + ZSaveLiveRegisters save_live_registers(masm, stub); + ZSetupArguments setup_arguments(masm, stub); + + __ call_VM_leaf(stub->slow_path()); + __ mr_if_needed(ref, R3_RET); + } + + __ b(*stub->continuation()); + + __ block_comment("} generate_c2_load_barrier_stub (zgc)"); +} + +#undef __ +#endif // COMPILER2 diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e2ff1bf53ae8052310663a8b18b44c7313f161e5 --- /dev/null +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef CPU_PPC_GC_Z_ZBARRIERSETASSEMBLER_PPC_HPP +#define CPU_PPC_GC_Z_ZBARRIERSETASSEMBLER_PPC_HPP + +#include "code/vmreg.hpp" +#include "oops/accessDecorators.hpp" +#ifdef COMPILER2 +#include "opto/optoreg.hpp" +#endif // COMPILER2 + +#ifdef COMPILER1 +class LIR_Assembler; +class LIR_OprDesc; +typedef LIR_OprDesc* LIR_Opr; +class StubAssembler; +class ZLoadBarrierStubC1; +#endif // COMPILER1 + +#ifdef COMPILER2 +class Node; +class ZLoadBarrierStubC2; +#endif // COMPILER2 + +class ZBarrierSetAssembler : public ZBarrierSetAssemblerBase { +public: + virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register base, RegisterOrConstant ind_or_offs, Register dst, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level, Label *L_handle_null = NULL); + +#ifdef ASSERT + virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register base, RegisterOrConstant ind_or_offs, Register val, + Register tmp1, Register tmp2, Register tmp3, + MacroAssembler::PreservationLevel preservation_level); +#endif // ASSERT + + virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register src, Register dst, Register count, + Register preserve1, Register preserve2); + + virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env, + Register obj, Register tmp, Label& slowpath); + +#ifdef COMPILER1 + void generate_c1_load_barrier_test(LIR_Assembler* ce, + LIR_Opr ref) const; + + void generate_c1_load_barrier_stub(LIR_Assembler* ce, + ZLoadBarrierStubC1* stub) const; + + void generate_c1_load_barrier_runtime_stub(StubAssembler* sasm, + DecoratorSet decorators) const; +#endif // COMPILER1 + +#ifdef COMPILER2 + OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg) const; + + void generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const; +#endif // COMPILER2 +}; + +#endif // CPU_AARCH64_GC_Z_ZBARRIERSETASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/ppc/gc/z/zGlobals_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zGlobals_ppc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..93c2f9b4dc44e5b1b41c2a60ad3b51879f44302d --- /dev/null +++ b/src/hotspot/cpu/ppc/gc/z/zGlobals_ppc.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "precompiled.hpp" +#include "gc/shared/gcLogPrecious.hpp" +#include "gc/shared/gc_globals.hpp" +#include "gc/z/zGlobals.hpp" +#include "runtime/globals.hpp" +#include "runtime/os.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/powerOfTwo.hpp" +#include + +#ifdef LINUX +#include +#endif // LINUX + +// +// The overall memory layouts across different power platforms are similar and only differ with regards to +// the position of the highest addressable bit; the position of the metadata bits and the size of the actual +// addressable heap address space are adjusted accordingly. +// +// The following memory schema shows an exemplary layout in which bit '45' is the highest addressable bit. +// It is assumed that this virtual memroy address space layout is predominant on the power platform. +// +// Standard Address Space & Pointer Layout +// --------------------------------------- +// +// +--------------------------------+ 0x00007FFFFFFFFFFF (127 TiB - 1) +// . . +// . . +// . . +// +--------------------------------+ 0x0000140000000000 (20 TiB) +// | Remapped View | +// +--------------------------------+ 0x0000100000000000 (16 TiB) +// . . +// +--------------------------------+ 0x00000c0000000000 (12 TiB) +// | Marked1 View | +// +--------------------------------+ 0x0000080000000000 (8 TiB) +// | Marked0 View | +// +--------------------------------+ 0x0000040000000000 (4 TiB) +// . . +// +--------------------------------+ 0x0000000000000000 +// +// 6 4 4 4 4 +// 3 6 5 2 1 0 +// +--------------------+----+-----------------------------------------------+ +// |00000000 00000000 00|1111|11 11111111 11111111 11111111 11111111 11111111| +// +--------------------+----+-----------------------------------------------+ +// | | | +// | | * 41-0 Object Offset (42-bits, 4TB address space) +// | | +// | * 45-42 Metadata Bits (4-bits) 0001 = Marked0 (Address view 4-8TB) +// | 0010 = Marked1 (Address view 8-12TB) +// | 0100 = Remapped (Address view 16-20TB) +// | 1000 = Finalizable (Address view N/A) +// | +// * 63-46 Fixed (18-bits, always zero) +// + +// Maximum value as per spec (Power ISA v2.07): 2 ^ 60 bytes, i.e. 1 EiB (exbibyte) +static const unsigned int MAXIMUM_MAX_ADDRESS_BIT = 60; + +// Most modern power processors provide an address space with not more than 45 bit addressable bit, +// that is an address space of 32 TiB in size. +static const unsigned int DEFAULT_MAX_ADDRESS_BIT = 45; + +// Minimum value returned, if probing fails: 64 GiB +static const unsigned int MINIMUM_MAX_ADDRESS_BIT = 36; + +// Determines the highest addressable bit of the virtual address space (depends on platform) +// by trying to interact with memory in that address range, +// i.e. by syncing existing mappings (msync) or by temporarily mapping the memory area (mmap). +// If one of those operations succeeds, it is proven that the targeted memory area is within the virtual address space. +// +// To reduce the number of required system calls to a bare minimum, the DEFAULT_MAX_ADDRESS_BIT is intentionally set +// lower than what the ABI would theoretically permit. +// Such an avoidance strategy, however, might impose unnecessary limits on processors that exceed this limit. +// If DEFAULT_MAX_ADDRESS_BIT is addressable, the next higher bit will be tested as well to ensure that +// the made assumption does not artificially restrict the memory availability. +static unsigned int probe_valid_max_address_bit(size_t init_bit, size_t min_bit) { + assert(init_bit >= min_bit, "Sanity"); + assert(init_bit <= MAXIMUM_MAX_ADDRESS_BIT, "Test bit is outside the assumed address space range"); + +#ifdef LINUX + unsigned int max_valid_address_bit = 0; + void* last_allocatable_address = nullptr; + + const unsigned int page_size = os::vm_page_size(); + + for (size_t i = init_bit; i >= min_bit; --i) { + void* base_addr = (void*) (((unsigned long) 1U) << i); + + /* ==== Try msync-ing already mapped memory page ==== */ + if (msync(base_addr, page_size, MS_ASYNC) == 0) { + // The page of the given address was synced by the linux kernel and must thus be both, mapped and valid. + max_valid_address_bit = i; + break; + } + if (errno != ENOMEM) { + // An unexpected error occurred, i.e. an error not indicating that the targeted memory page is unmapped, + // but pointing out another type of issue. + // Even though this should never happen, those issues may come up due to undefined behavior. +#ifdef ASSERT + fatal("Received '%s' while probing the address space for the highest valid bit", os::errno_name(errno)); +#else // ASSERT + log_warning_p(gc)("Received '%s' while probing the address space for the highest valid bit", os::errno_name(errno)); +#endif // ASSERT + continue; + } + + /* ==== Try mapping memory page on our own ==== */ + last_allocatable_address = mmap(base_addr, page_size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); + if (last_allocatable_address != MAP_FAILED) { + munmap(last_allocatable_address, page_size); + } + + if (last_allocatable_address == base_addr) { + // As the linux kernel mapped exactly the page we have requested, the address must be valid. + max_valid_address_bit = i; + break; + } + + log_info_p(gc, init)("Probe failed for bit '%zu'", i); + } + + if (max_valid_address_bit == 0) { + // Probing did not bring up any usable address bit. + // As an alternative, the VM evaluates the address returned by mmap as it is expected that the reserved page + // will be close to the probed address that was out-of-range. + // As per mmap(2), "the kernel [will take] [the address] as a hint about where to + // place the mapping; on Linux, the mapping will be created at a nearby page boundary". + // It should thus be a "close enough" approximation to the real virtual memory address space limit. + // + // This recovery strategy is only applied in production builds. + // In debug builds, an assertion in 'ZPlatformAddressOffsetBits' will bail out the VM to indicate that + // the assumed address space is no longer up-to-date. + if (last_allocatable_address != MAP_FAILED) { + const unsigned int bitpos = BitsPerSize_t - count_leading_zeros((size_t) last_allocatable_address) - 1; + log_info_p(gc, init)("Did not find any valid addresses within the range, using address '%u' instead", bitpos); + return bitpos; + } + +#ifdef ASSERT + fatal("Available address space can not be determined"); +#else // ASSERT + log_warning_p(gc)("Cannot determine available address space. Falling back to default value."); + return DEFAULT_MAX_ADDRESS_BIT; +#endif // ASSERT + } else { + if (max_valid_address_bit == init_bit) { + // An usable address bit has been found immediately. + // To ensure that the entire virtual address space is exploited, the next highest bit will be tested as well. + log_info_p(gc, init)("Hit valid address '%u' on first try, retrying with next higher bit", max_valid_address_bit); + return MAX2(max_valid_address_bit, probe_valid_max_address_bit(init_bit + 1, init_bit + 1)); + } + } + + log_info_p(gc, init)("Found valid address '%u'", max_valid_address_bit); + return max_valid_address_bit; +#else // LINUX + return DEFAULT_MAX_ADDRESS_BIT; +#endif // LINUX +} + +size_t ZPlatformAddressOffsetBits() { + const static unsigned int valid_max_address_offset_bits = + probe_valid_max_address_bit(DEFAULT_MAX_ADDRESS_BIT, MINIMUM_MAX_ADDRESS_BIT) + 1; + assert(valid_max_address_offset_bits >= MINIMUM_MAX_ADDRESS_BIT, + "Highest addressable bit is outside the assumed address space range"); + + const size_t max_address_offset_bits = valid_max_address_offset_bits - 3; + const size_t min_address_offset_bits = max_address_offset_bits - 2; + const size_t address_offset = round_up_power_of_2(MaxHeapSize * ZVirtualToPhysicalRatio); + const size_t address_offset_bits = log2i_exact(address_offset); + + return clamp(address_offset_bits, min_address_offset_bits, max_address_offset_bits); +} + +size_t ZPlatformAddressMetadataShift() { + return ZPlatformAddressOffsetBits(); +} diff --git a/src/hotspot/cpu/ppc/gc/z/zGlobals_ppc.hpp b/src/hotspot/cpu/ppc/gc/z/zGlobals_ppc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3657b16fc1aa6b39410ae5193bf4fd742978ade4 --- /dev/null +++ b/src/hotspot/cpu/ppc/gc/z/zGlobals_ppc.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef CPU_PPC_GC_Z_ZGLOBALS_PPC_HPP +#define CPU_PPC_GC_Z_ZGLOBALS_PPC_HPP + +#include "globalDefinitions_ppc.hpp" +const size_t ZPlatformGranuleSizeShift = 21; // 2MB +const size_t ZPlatformHeapViews = 3; +const size_t ZPlatformCacheLineSize = DEFAULT_CACHE_LINE_SIZE; + +size_t ZPlatformAddressOffsetBits(); +size_t ZPlatformAddressMetadataShift(); + +#endif // CPU_PPC_GC_Z_ZGLOBALS_PPC_HPP diff --git a/src/hotspot/cpu/ppc/gc/z/z_ppc.ad b/src/hotspot/cpu/ppc/gc/z/z_ppc.ad new file mode 100644 index 0000000000000000000000000000000000000000..a8ce64ed1d9c1913c360ebb60e311c5b583933e1 --- /dev/null +++ b/src/hotspot/cpu/ppc/gc/z/z_ppc.ad @@ -0,0 +1,298 @@ +// +// Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2021 SAP SE. All rights reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +// or visit www.oracle.com if you need additional information or have any +// questions. +// + +source_hpp %{ + +#include "gc/shared/gc_globals.hpp" +#include "gc/z/c2/zBarrierSetC2.hpp" +#include "gc/z/zThreadLocalData.hpp" + +%} + +source %{ + +static void z_load_barrier(MacroAssembler& _masm, const MachNode* node, Address ref_addr, Register ref, + Register tmp, uint8_t barrier_data) { + if (barrier_data == ZLoadBarrierElided) { + return; + } + + ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref, tmp, barrier_data); + __ ld(tmp, in_bytes(ZThreadLocalData::address_bad_mask_offset()), R16_thread); + __ and_(tmp, tmp, ref); + __ bne_far(CCR0, *stub->entry(), MacroAssembler::bc_far_optimize_on_relocate); + __ bind(*stub->continuation()); +} + +static void z_load_barrier_slow_path(MacroAssembler& _masm, const MachNode* node, Address ref_addr, Register ref, + Register tmp) { + ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref, tmp, ZLoadBarrierStrong); + __ b(*stub->entry()); + __ bind(*stub->continuation()); +} + +static void z_compare_and_swap(MacroAssembler& _masm, const MachNode* node, + Register res, Register mem, Register oldval, Register newval, + Register tmp_xchg, Register tmp_mask, + bool weak, bool acquire) { + // z-specific load barrier requires strong CAS operations. + // Weak CAS operations are thus only emitted if the barrier is elided. + __ cmpxchgd(CCR0, tmp_xchg, oldval, newval, mem, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), res, NULL, true, + weak && node->barrier_data() == ZLoadBarrierElided); + + if (node->barrier_data() != ZLoadBarrierElided) { + Label skip_barrier; + + __ ld(tmp_mask, in_bytes(ZThreadLocalData::address_bad_mask_offset()), R16_thread); + __ and_(tmp_mask, tmp_mask, tmp_xchg); + __ beq(CCR0, skip_barrier); + + // CAS must have failed because pointer in memory is bad. + z_load_barrier_slow_path(_masm, node, Address(mem), tmp_xchg, res /* used as tmp */); + + __ cmpxchgd(CCR0, tmp_xchg, oldval, newval, mem, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), res, NULL, true, weak); + + __ bind(skip_barrier); + } + + if (acquire) { + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + // Uses the isync instruction as an acquire barrier. + // This exploits the compare and the branch in the z load barrier (load, compare and branch, isync). + __ isync(); + } else { + __ sync(); + } + } +} + +static void z_compare_and_exchange(MacroAssembler& _masm, const MachNode* node, + Register res, Register mem, Register oldval, Register newval, Register tmp, + bool weak, bool acquire) { + // z-specific load barrier requires strong CAS operations. + // Weak CAS operations are thus only emitted if the barrier is elided. + __ cmpxchgd(CCR0, res, oldval, newval, mem, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), noreg, NULL, true, + weak && node->barrier_data() == ZLoadBarrierElided); + + if (node->barrier_data() != ZLoadBarrierElided) { + Label skip_barrier; + __ ld(tmp, in_bytes(ZThreadLocalData::address_bad_mask_offset()), R16_thread); + __ and_(tmp, tmp, res); + __ beq(CCR0, skip_barrier); + + z_load_barrier_slow_path(_masm, node, Address(mem), res, tmp); + + __ cmpxchgd(CCR0, res, oldval, newval, mem, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), noreg, NULL, true, weak); + + __ bind(skip_barrier); + } + + if (acquire) { + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + // Uses the isync instruction as an acquire barrier. + // This exploits the compare and the branch in the z load barrier (load, compare and branch, isync). + __ isync(); + } else { + __ sync(); + } + } +} + +%} + +instruct zLoadP(iRegPdst dst, memoryAlg4 mem, iRegPdst tmp, flagsRegCR0 cr0) +%{ + match(Set dst (LoadP mem)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr0); + ins_cost(MEMORY_REF_COST); + + predicate((UseZGC && n->as_Load()->barrier_data() != 0) + && (n->as_Load()->is_unordered() || followed_by_acquire(n))); + + format %{ "LD $dst, $mem" %} + ins_encode %{ + assert($mem$$index == 0, "sanity"); + __ ld($dst$$Register, $mem$$disp, $mem$$base$$Register); + z_load_barrier(_masm, this, Address($mem$$base$$Register, $mem$$disp), $dst$$Register, $tmp$$Register, barrier_data()); + %} + ins_pipe(pipe_class_default); +%} + +// Load Pointer Volatile +instruct zLoadP_acq(iRegPdst dst, memoryAlg4 mem, iRegPdst tmp, flagsRegCR0 cr0) +%{ + match(Set dst (LoadP mem)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr0); + ins_cost(3 * MEMORY_REF_COST); + + // Predicate on instruction order is implicitly present due to the predicate of the cheaper zLoadP operation + predicate(UseZGC && n->as_Load()->barrier_data() != 0); + + format %{ "LD acq $dst, $mem" %} + ins_encode %{ + __ ld($dst$$Register, $mem$$disp, $mem$$base$$Register); + z_load_barrier(_masm, this, Address($mem$$base$$Register, $mem$$disp), $dst$$Register, $tmp$$Register, barrier_data()); + + // Uses the isync instruction as an acquire barrier. + // This exploits the compare and the branch in the z load barrier (load, compare and branch, isync). + __ isync(); + %} + ins_pipe(pipe_class_default); +%} + +instruct zCompareAndSwapP(iRegIdst res, iRegPdst mem, iRegPsrc oldval, iRegPsrc newval, + iRegPdst tmp_xchg, iRegPdst tmp_mask, flagsRegCR0 cr0) %{ + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp_xchg, TEMP tmp_mask, KILL cr0); + + predicate((UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong) + && (((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*) n)->order() != MemNode::seqcst)); + + format %{ "CMPXCHG $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + z_compare_and_swap(_masm, this, + $res$$Register, $mem$$Register, $oldval$$Register, $newval$$Register, + $tmp_xchg$$Register, $tmp_mask$$Register, + false /* weak */, false /* acquire */); + %} + ins_pipe(pipe_class_default); +%} + +instruct zCompareAndSwapP_acq(iRegIdst res, iRegPdst mem, iRegPsrc oldval, iRegPsrc newval, + iRegPdst tmp_xchg, iRegPdst tmp_mask, flagsRegCR0 cr0) %{ + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp_xchg, TEMP tmp_mask, KILL cr0); + + predicate((UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong) + && (((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*) n)->order() == MemNode::seqcst)); + + format %{ "CMPXCHG acq $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + z_compare_and_swap(_masm, this, + $res$$Register, $mem$$Register, $oldval$$Register, $newval$$Register, + $tmp_xchg$$Register, $tmp_mask$$Register, + false /* weak */, true /* acquire */); + %} + ins_pipe(pipe_class_default); +%} + +instruct zCompareAndSwapPWeak(iRegIdst res, iRegPdst mem, iRegPsrc oldval, iRegPsrc newval, + iRegPdst tmp_xchg, iRegPdst tmp_mask, flagsRegCR0 cr0) %{ + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp_xchg, TEMP tmp_mask, KILL cr0); + + predicate((UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong) + && ((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*) n)->order() != MemNode::seqcst); + + format %{ "weak CMPXCHG $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + z_compare_and_swap(_masm, this, + $res$$Register, $mem$$Register, $oldval$$Register, $newval$$Register, + $tmp_xchg$$Register, $tmp_mask$$Register, + true /* weak */, false /* acquire */); + %} + ins_pipe(pipe_class_default); +%} + +instruct zCompareAndSwapPWeak_acq(iRegIdst res, iRegPdst mem, iRegPsrc oldval, iRegPsrc newval, + iRegPdst tmp_xchg, iRegPdst tmp_mask, flagsRegCR0 cr0) %{ + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp_xchg, TEMP tmp_mask, KILL cr0); + + predicate((UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong) + && (((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*) n)->order() == MemNode::seqcst)); + + format %{ "weak CMPXCHG acq $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + z_compare_and_swap(_masm, this, + $res$$Register, $mem$$Register, $oldval$$Register, $newval$$Register, + $tmp_xchg$$Register, $tmp_mask$$Register, + true /* weak */, true /* acquire */); + %} + ins_pipe(pipe_class_default); +%} + +instruct zCompareAndExchangeP(iRegPdst res, iRegPdst mem, iRegPsrc oldval, iRegPsrc newval, + iRegPdst tmp, flagsRegCR0 cr0) %{ + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + + predicate((UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong) + && ( + ((CompareAndSwapNode*)n)->order() != MemNode::acquire + && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst + )); + + format %{ "CMPXCHG $res, $mem, $oldval, $newval; as ptr; ptr" %} + ins_encode %{ + z_compare_and_exchange(_masm, this, + $res$$Register, $mem$$Register, $oldval$$Register, $newval$$Register, $tmp$$Register, + false /* weak */, false /* acquire */); + %} + ins_pipe(pipe_class_default); +%} + +instruct zCompareAndExchangeP_acq(iRegPdst res, iRegPdst mem, iRegPsrc oldval, iRegPsrc newval, + iRegPdst tmp, flagsRegCR0 cr0) %{ + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + + predicate((UseZGC && n->as_LoadStore()->barrier_data() == ZLoadBarrierStrong) + && ( + ((CompareAndSwapNode*)n)->order() == MemNode::acquire + || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst + )); + + format %{ "CMPXCHG acq $res, $mem, $oldval, $newval; as ptr; ptr" %} + ins_encode %{ + z_compare_and_exchange(_masm, this, + $res$$Register, $mem$$Register, $oldval$$Register, $newval$$Register, $tmp$$Register, + false /* weak */, true /* acquire */); + %} + ins_pipe(pipe_class_default); +%} + +instruct zGetAndSetP(iRegPdst res, iRegPdst mem, iRegPsrc newval, iRegPdst tmp, flagsRegCR0 cr0) %{ + match(Set res (GetAndSetP mem newval)); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + + predicate(UseZGC && n->as_LoadStore()->barrier_data() != 0); + + format %{ "GetAndSetP $res, $mem, $newval" %} + ins_encode %{ + __ getandsetd($res$$Register, $newval$$Register, $mem$$Register, MacroAssembler::cmpxchgx_hint_atomic_update()); + z_load_barrier(_masm, this, Address(noreg, (intptr_t) 0), $res$$Register, $tmp$$Register, barrier_data()); + + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + __ sync(); + } + %} + ins_pipe(pipe_class_default); +%} diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp index df1672b3048ec1c2987b67235f918c6e13b416a9..877f0be33c4413b644f76aef049c5707d6c0ec04 100644 --- a/src/hotspot/cpu/ppc/matcher_ppc.hpp +++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp @@ -164,5 +164,7 @@ return VM_Version::has_fcfids(); } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = false; #endif // CPU_PPC_MATCHER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 9a883c7231082fd0d8af2e3b059a5f83caaed53a..4a43aa2f12a7e17221efe232298657d061e669d7 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -5531,7 +5531,7 @@ instruct loadN2P_klass_unscaled(iRegPdst dst, memory mem) %{ // Load Pointer instruct loadP(iRegPdst dst, memoryAlg4 mem) %{ match(Set dst (LoadP mem)); - predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + predicate((n->as_Load()->is_unordered() || followed_by_acquire(n)) && n->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); format %{ "LD $dst, $mem \t// ptr" %} @@ -5545,6 +5545,8 @@ instruct loadP_ac(iRegPdst dst, memoryAlg4 mem) %{ match(Set dst (LoadP mem)); ins_cost(3*MEMORY_REF_COST); + predicate(n->as_Load()->barrier_data() == 0); + format %{ "LD $dst, $mem \t// ptr acquire\n\t" "TWI $dst\n\t" "ISYNC" %} @@ -5556,7 +5558,7 @@ instruct loadP_ac(iRegPdst dst, memoryAlg4 mem) %{ // LoadP + CastP2L instruct loadP2X(iRegLdst dst, memoryAlg4 mem) %{ match(Set dst (CastP2X (LoadP mem))); - predicate(_kids[0]->_leaf->as_Load()->is_unordered()); + predicate(_kids[0]->_leaf->as_Load()->is_unordered() && _kids[0]->_leaf->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); format %{ "LD $dst, $mem \t// ptr + p2x" %} @@ -7478,6 +7480,7 @@ instruct storeLConditional_regP_regL_regL(flagsReg crx, indirect mem_ptr, iRegLs instruct storePConditional_regP_regP_regP(flagsRegCR0 cr0, indirect mem_ptr, iRegPsrc oldVal, iRegPsrc newVal) %{ match(Set cr0 (StorePConditional mem_ptr (Binary oldVal newVal))); ins_cost(2*MEMORY_REF_COST); + predicate(n->as_LoadStore()->barrier_data() == 0); format %{ "STDCX_ if ($cr0 = ($oldVal == *$mem_ptr)) *mem_ptr = $newVal; as bool" %} ins_encode %{ @@ -7642,6 +7645,7 @@ instruct compareAndSwapL_regP_regL_regL(iRegIdst res, iRegPdst mem_ptr, iRegLsrc instruct compareAndSwapP_regP_regP_regP(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndSwapP mem_ptr (Binary src1 src2))); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump + predicate(n->as_LoadStore()->barrier_data() == 0); format %{ "CMPXCHGD $res, $mem_ptr, $src1, $src2; as bool; ptr" %} ins_encode %{ // CmpxchgX sets CCR0 to cmpX(src1, src2) and Rres to 'true'/'false'. @@ -7864,7 +7868,7 @@ instruct weakCompareAndSwapL_acq_regP_regL_regL(iRegIdst res, iRegPdst mem_ptr, instruct weakCompareAndSwapP_regP_regP_regP(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapP mem_ptr (Binary src1 src2))); - predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); + predicate((((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst) && n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump format %{ "weak CMPXCHGD $res, $mem_ptr, $src1, $src2; as bool; ptr" %} ins_encode %{ @@ -7878,7 +7882,7 @@ instruct weakCompareAndSwapP_regP_regP_regP(iRegIdst res, iRegPdst mem_ptr, iReg instruct weakCompareAndSwapP_acq_regP_regP_regP(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapP mem_ptr (Binary src1 src2))); - predicate(((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst); + predicate((((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst) && n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump format %{ "weak CMPXCHGD acq $res, $mem_ptr, $src1, $src2; as bool; ptr" %} ins_encode %{ @@ -8134,7 +8138,8 @@ instruct compareAndExchangeL_acq_regP_regL_regL(iRegLdst res, iRegPdst mem_ptr, instruct compareAndExchangeP_regP_regP_regP(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeP mem_ptr (Binary src1 src2))); - predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); + predicate((((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst) + && n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); format %{ "CMPXCHGD $res, $mem_ptr, $src1, $src2; as ptr; ptr" %} ins_encode %{ @@ -8148,7 +8153,8 @@ instruct compareAndExchangeP_regP_regP_regP(iRegPdst res, iRegPdst mem_ptr, iReg instruct compareAndExchangeP_acq_regP_regP_regP(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeP mem_ptr (Binary src1 src2))); - predicate(((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst); + predicate((((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst) + && n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); format %{ "CMPXCHGD acq $res, $mem_ptr, $src1, $src2; as ptr; ptr" %} ins_encode %{ @@ -8370,6 +8376,7 @@ instruct getAndSetL(iRegLdst res, iRegPdst mem_ptr, iRegLsrc src, flagsRegCR0 cr instruct getAndSetP(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src, flagsRegCR0 cr0) %{ match(Set res (GetAndSetP mem_ptr src)); + predicate(n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); format %{ "GetAndSetP $res, $mem_ptr, $src" %} ins_encode %{ @@ -12789,6 +12796,7 @@ instruct has_negatives(rarg1RegP ary1, iRegIsrc len, iRegIdst result, iRegLdst t // encode char[] to byte[] in ISO_8859_1 instruct encode_iso_array(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, iRegLdst tmp5, regCTR ctr, flagsRegCR0 cr0) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, USE_KILL src, USE_KILL dst, KILL ctr, KILL cr0); diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.hpp b/src/hotspot/cpu/ppc/vmreg_ppc.hpp index 090fe1d72a2a07a328a9e880437c710d6fc13ba6..16f6799d04643622d5e063c4779399c87edc73b1 100644 --- a/src/hotspot/cpu/ppc/vmreg_ppc.hpp +++ b/src/hotspot/cpu/ppc/vmreg_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2013 SAP SE. All rights reserved. + * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,21 @@ inline bool is_FloatRegister() { value() < ConcreteRegisterImpl::max_fpr; } +inline bool is_VectorRegister() { + return value() >= ConcreteRegisterImpl::max_fpr && + value() < ConcreteRegisterImpl::max_vsr; +} + +inline bool is_ConditionRegister() { + return value() >= ConcreteRegisterImpl::max_vsr && + value() < ConcreteRegisterImpl::max_cnd; +} + +inline bool is_SpecialRegister() { + return value() >= ConcreteRegisterImpl::max_cnd && + value() < ConcreteRegisterImpl::max_spr; +} + inline Register as_Register() { assert(is_Register() && is_even(value()), "even-aligned GPR name"); return ::as_Register(value()>>1); diff --git a/src/hotspot/cpu/s390/matcher_s390.hpp b/src/hotspot/cpu/s390/matcher_s390.hpp index 7577a7b2666037446819ab95db7764bc4a6568c0..09cb819a6414ab29b5f1a8c540767858ed27603a 100644 --- a/src/hotspot/cpu/s390/matcher_s390.hpp +++ b/src/hotspot/cpu/s390/matcher_s390.hpp @@ -152,4 +152,7 @@ return true; } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = false; + #endif // CPU_S390_MATCHER_S390_HPP diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index cd22b795886d74f3464a96e30366d07dc7e9e3d7..63004f8e2634349b1d801cfb002637b7e1502704 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -10282,6 +10282,7 @@ instruct has_negatives(rarg5RegP ary1, iRegI len, iRegI result, roddRegI oddReg, // encode char[] to byte[] in ISO_8859_1 instruct encode_iso_array(iRegP src, iRegP dst, iRegI result, iRegI len, iRegI tmp, flagsReg cr) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP_DEF result, TEMP tmp, KILL cr); // R0, R1 are killed, too. ins_cost(300); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index b573adc3acdd077ef209fe428a3fab9fcd2f8519..30056be9a658a1c427e71d91a2e7e12b770ed958 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -1170,7 +1170,20 @@ void MacroAssembler::addpd(XMMRegister dst, AddressLiteral src) { } } +// See 8273459. Function for ensuring 64-byte alignment, intended for stubs only. +// Stub code is generated once and never copied. +// NMethods can't use this because they get copied and we can't force alignment > 32 bytes. +void MacroAssembler::align64() { + align(64, (unsigned long long) pc()); +} + +void MacroAssembler::align32() { + align(32, (unsigned long long) pc()); +} + void MacroAssembler::align(int modulus) { + // 8273459: Ensure alignment is possible with current segment alignment + assert(modulus <= CodeEntryAlignment, "Alignment must be <= CodeEntryAlignment"); align(modulus, offset()); } @@ -5013,7 +5026,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X BIND(L_loop); if (MaxVectorSize >= 32) { - fill64_avx(base, 0, xtmp, use64byteVector); + fill64(base, 0, xtmp, use64byteVector); } else { movdqu(Address(base, 0), xtmp); movdqu(Address(base, 16), xtmp); @@ -5030,7 +5043,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X if (use64byteVector) { addptr(cnt, 8); jccb(Assembler::equal, L_end); - fill64_masked_avx(3, base, 0, xtmp, mask, cnt, rtmp, true); + fill64_masked(3, base, 0, xtmp, mask, cnt, rtmp, true); jmp(L_end); } else { addptr(cnt, 4); @@ -5049,7 +5062,7 @@ void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, X addptr(cnt, 4); jccb(Assembler::lessEqual, L_end); if (UseAVX > 2 && MaxVectorSize >= 32 && VM_Version::supports_avx512vl()) { - fill32_masked_avx(3, base, 0, xtmp, mask, cnt, rtmp); + fill32_masked(3, base, 0, xtmp, mask, cnt, rtmp); } else { decrement(cnt); @@ -5073,7 +5086,7 @@ void MacroAssembler::clear_mem(Register base, int cnt, Register rtmp, XMMRegiste // 64 byte initialization loop. vpxor(xtmp, xtmp, xtmp, use64byteVector ? AVX_512bit : AVX_256bit); for (int i = 0; i < vector64_count; i++) { - fill64_avx(base, i * 64, xtmp, use64byteVector); + fill64(base, i * 64, xtmp, use64byteVector); } // Clear remaining 64 byte tail. @@ -5194,6 +5207,15 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, Label L_exit; Label L_fill_2_bytes, L_fill_4_bytes; +#if defined(COMPILER2) && defined(_LP64) + if(MaxVectorSize >=32 && + VM_Version::supports_avx512vlbw() && + VM_Version::supports_bmi2()) { + generate_fill_avx3(t, to, value, count, rtmp, xtmp); + return; + } +#endif + int shift = -1; switch (t) { case T_BYTE: @@ -5414,7 +5436,31 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, BIND(L_exit); } -// encode char[] to byte[] in ISO_8859_1 +void MacroAssembler::evpbroadcast(BasicType type, XMMRegister dst, Register src, int vector_len) { + switch(type) { + case T_BYTE: + case T_BOOLEAN: + evpbroadcastb(dst, src, vector_len); + break; + case T_SHORT: + case T_CHAR: + evpbroadcastw(dst, src, vector_len); + break; + case T_INT: + case T_FLOAT: + evpbroadcastd(dst, src, vector_len); + break; + case T_LONG: + case T_DOUBLE: + evpbroadcastq(dst, src, vector_len); + break; + default: + fatal("Unhandled type : %s", type2name(type)); + break; + } +} + +// encode char[] to byte[] in ISO_8859_1 or ASCII //@IntrinsicCandidate //private static int implEncodeISOArray(byte[] sa, int sp, //byte[] da, int dp, int len) { @@ -5427,10 +5473,23 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, // } // return i; //} + // + //@IntrinsicCandidate + //private static int implEncodeAsciiArray(char[] sa, int sp, + // byte[] da, int dp, int len) { + // int i = 0; + // for (; i < len; i++) { + // char c = sa[sp++]; + // if (c >= '\u0080') + // break; + // da[dp++] = (byte)c; + // } + // return i; + //} void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1Reg, XMMRegister tmp2Reg, XMMRegister tmp3Reg, XMMRegister tmp4Reg, - Register tmp5, Register result) { + Register tmp5, Register result, bool ascii) { // rsi: src // rdi: dst @@ -5441,6 +5500,9 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, assert_different_registers(src, dst, len, tmp5, result); Label L_done, L_copy_1_char, L_copy_1_char_exit; + int mask = ascii ? 0xff80ff80 : 0xff00ff00; + int short_mask = ascii ? 0xff80 : 0xff00; + // set result xorl(result, result); // check for zero length @@ -5460,7 +5522,7 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, if (UseAVX >= 2) { Label L_chars_32_check, L_copy_32_chars, L_copy_32_chars_exit; - movl(tmp5, 0xff00ff00); // create mask to test for Unicode chars in vector + movl(tmp5, mask); // create mask to test for Unicode or non-ASCII chars in vector movdl(tmp1Reg, tmp5); vpbroadcastd(tmp1Reg, tmp1Reg, Assembler::AVX_256bit); jmp(L_chars_32_check); @@ -5469,7 +5531,7 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, vmovdqu(tmp3Reg, Address(src, len, Address::times_2, -64)); vmovdqu(tmp4Reg, Address(src, len, Address::times_2, -32)); vpor(tmp2Reg, tmp3Reg, tmp4Reg, /* vector_len */ 1); - vptest(tmp2Reg, tmp1Reg); // check for Unicode chars in vector + vptest(tmp2Reg, tmp1Reg); // check for Unicode or non-ASCII chars in vector jccb(Assembler::notZero, L_copy_32_chars_exit); vpackuswb(tmp3Reg, tmp3Reg, tmp4Reg, /* vector_len */ 1); vpermq(tmp4Reg, tmp3Reg, 0xD8, /* vector_len */ 1); @@ -5484,7 +5546,7 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, jccb(Assembler::greater, L_copy_16_chars_exit); } else if (UseSSE42Intrinsics) { - movl(tmp5, 0xff00ff00); // create mask to test for Unicode chars in vector + movl(tmp5, mask); // create mask to test for Unicode or non-ASCII chars in vector movdl(tmp1Reg, tmp5); pshufd(tmp1Reg, tmp1Reg, 0); jmpb(L_chars_16_check); @@ -5508,7 +5570,7 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, movdqu(tmp4Reg, Address(src, len, Address::times_2, -16)); por(tmp2Reg, tmp4Reg); } - ptest(tmp2Reg, tmp1Reg); // check for Unicode chars in vector + ptest(tmp2Reg, tmp1Reg); // check for Unicode or non-ASCII chars in vector jccb(Assembler::notZero, L_copy_16_chars_exit); packuswb(tmp3Reg, tmp4Reg); } @@ -5546,7 +5608,7 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, bind(L_copy_1_char); load_unsigned_short(tmp5, Address(src, len, Address::times_2, 0)); - testl(tmp5, 0xff00); // check if Unicode char + testl(tmp5, short_mask); // check if Unicode or non-ASCII char jccb(Assembler::notZero, L_copy_1_char_exit); movb(Address(dst, len, Address::times_1, 0), tmp5); addptr(len, 1); @@ -6880,7 +6942,7 @@ void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len, Regi // 128 bits per each of 4 parallel streams. movdqu(xmm0, ExternalAddress(StubRoutines::x86::crc_by128_masks_addr() + 32)); - align(32); + align32(); BIND(L_fold_512b_loop); fold_128bit_crc32(xmm1, xmm0, xmm5, buf, 0); fold_128bit_crc32(xmm2, xmm0, xmm5, buf, 16); @@ -8207,59 +8269,234 @@ void MacroAssembler::evmovdqu(BasicType type, KRegister kmask, Address dst, XMMR #if COMPILER2_OR_JVMCI +void MacroAssembler::fill_masked(BasicType bt, Address dst, XMMRegister xmm, KRegister mask, + Register length, Register temp, int vec_enc) { + // Computing mask for predicated vector store. + movptr(temp, -1); + bzhiq(temp, temp, length); + kmov(mask, temp); + evmovdqu(bt, mask, dst, xmm, vec_enc); +} // Set memory operation for length "less than" 64 bytes. -void MacroAssembler::fill64_masked_avx(uint shift, Register dst, int disp, +void MacroAssembler::fill64_masked(uint shift, Register dst, int disp, XMMRegister xmm, KRegister mask, Register length, Register temp, bool use64byteVector) { assert(MaxVectorSize >= 32, "vector length should be >= 32"); - assert(shift != 0, "shift value should be 1 (short),2(int) or 3(long)"); - BasicType type[] = { T_BYTE, T_SHORT, T_INT, T_LONG}; + BasicType type[] = { T_BYTE, T_SHORT, T_INT, T_LONG}; if (!use64byteVector) { - fill32_avx(dst, disp, xmm); + fill32(dst, disp, xmm); subptr(length, 32 >> shift); - fill32_masked_avx(shift, dst, disp + 32, xmm, mask, length, temp); + fill32_masked(shift, dst, disp + 32, xmm, mask, length, temp); } else { assert(MaxVectorSize == 64, "vector length != 64"); - movl(temp, 1); - shlxl(temp, temp, length); - subptr(temp, 1); - kmovwl(mask, temp); - evmovdqu(type[shift], mask, Address(dst, disp), xmm, Assembler::AVX_512bit); + fill_masked(type[shift], Address(dst, disp), xmm, mask, length, temp, Assembler::AVX_512bit); } } -void MacroAssembler::fill32_masked_avx(uint shift, Register dst, int disp, +void MacroAssembler::fill32_masked(uint shift, Register dst, int disp, XMMRegister xmm, KRegister mask, Register length, Register temp) { assert(MaxVectorSize >= 32, "vector length should be >= 32"); - assert(shift != 0, "shift value should be 1 (short), 2(int) or 3(long)"); - BasicType type[] = { T_BYTE, T_SHORT, T_INT, T_LONG}; - movl(temp, 1); - shlxl(temp, temp, length); - subptr(temp, 1); - kmovwl(mask, temp); - evmovdqu(type[shift], mask, Address(dst, disp), xmm, Assembler::AVX_256bit); + BasicType type[] = { T_BYTE, T_SHORT, T_INT, T_LONG}; + fill_masked(type[shift], Address(dst, disp), xmm, mask, length, temp, Assembler::AVX_256bit); } -void MacroAssembler::fill32_avx(Register dst, int disp, XMMRegister xmm) { +void MacroAssembler::fill32(Register dst, int disp, XMMRegister xmm) { assert(MaxVectorSize >= 32, "vector length should be >= 32"); vmovdqu(Address(dst, disp), xmm); } -void MacroAssembler::fill64_avx(Register dst, int disp, XMMRegister xmm, bool use64byteVector) { +void MacroAssembler::fill64(Register dst, int disp, XMMRegister xmm, bool use64byteVector) { assert(MaxVectorSize >= 32, "vector length should be >= 32"); BasicType type[] = {T_BYTE, T_SHORT, T_INT, T_LONG}; if (!use64byteVector) { - fill32_avx(dst, disp, xmm); - fill32_avx(dst, disp + 32, xmm); + fill32(dst, disp, xmm); + fill32(dst, disp + 32, xmm); } else { evmovdquq(Address(dst, disp), xmm, Assembler::AVX_512bit); } } +#ifdef _LP64 +void MacroAssembler::generate_fill_avx3(BasicType type, Register to, Register value, + Register count, Register rtmp, XMMRegister xtmp) { + Label L_exit; + Label L_fill_start; + Label L_fill_64_bytes; + Label L_fill_96_bytes; + Label L_fill_128_bytes; + Label L_fill_128_bytes_loop; + Label L_fill_128_loop_header; + Label L_fill_128_bytes_loop_header; + Label L_fill_128_bytes_loop_pre_header; + Label L_fill_zmm_sequence; + + int shift = -1; + switch(type) { + case T_BYTE: shift = 0; + break; + case T_SHORT: shift = 1; + break; + case T_INT: shift = 2; + break; + /* Uncomment when LONG fill stubs are supported. + case T_LONG: shift = 3; + break; + */ + default: + fatal("Unhandled type: %s\n", type2name(type)); + } + + if (AVX3Threshold != 0 || MaxVectorSize == 32) { + + if (MaxVectorSize == 64) { + cmpq(count, AVX3Threshold >> shift); + jcc(Assembler::greater, L_fill_zmm_sequence); + } + + evpbroadcast(type, xtmp, value, Assembler::AVX_256bit); + + bind(L_fill_start); + + cmpq(count, 32 >> shift); + jccb(Assembler::greater, L_fill_64_bytes); + fill32_masked(shift, to, 0, xtmp, k2, count, rtmp); + jmp(L_exit); + + bind(L_fill_64_bytes); + cmpq(count, 64 >> shift); + jccb(Assembler::greater, L_fill_96_bytes); + fill64_masked(shift, to, 0, xtmp, k2, count, rtmp); + jmp(L_exit); + + bind(L_fill_96_bytes); + cmpq(count, 96 >> shift); + jccb(Assembler::greater, L_fill_128_bytes); + fill64(to, 0, xtmp); + subq(count, 64 >> shift); + fill32_masked(shift, to, 64, xtmp, k2, count, rtmp); + jmp(L_exit); + + bind(L_fill_128_bytes); + cmpq(count, 128 >> shift); + jccb(Assembler::greater, L_fill_128_bytes_loop_pre_header); + fill64(to, 0, xtmp); + fill32(to, 64, xtmp); + subq(count, 96 >> shift); + fill32_masked(shift, to, 96, xtmp, k2, count, rtmp); + jmp(L_exit); + + bind(L_fill_128_bytes_loop_pre_header); + { + mov(rtmp, to); + andq(rtmp, 31); + jccb(Assembler::zero, L_fill_128_bytes_loop_header); + negq(rtmp); + addq(rtmp, 32); + mov64(r8, -1L); + bzhiq(r8, r8, rtmp); + kmovql(k2, r8); + evmovdqu(T_BYTE, k2, Address(to, 0), xtmp, Assembler::AVX_256bit); + addq(to, rtmp); + shrq(rtmp, shift); + subq(count, rtmp); + } + + cmpq(count, 128 >> shift); + jcc(Assembler::less, L_fill_start); + + bind(L_fill_128_bytes_loop_header); + subq(count, 128 >> shift); + + align32(); + bind(L_fill_128_bytes_loop); + fill64(to, 0, xtmp); + fill64(to, 64, xtmp); + addq(to, 128); + subq(count, 128 >> shift); + jccb(Assembler::greaterEqual, L_fill_128_bytes_loop); + + addq(count, 128 >> shift); + jcc(Assembler::zero, L_exit); + jmp(L_fill_start); + } + + if (MaxVectorSize == 64) { + // Sequence using 64 byte ZMM register. + Label L_fill_128_bytes_zmm; + Label L_fill_192_bytes_zmm; + Label L_fill_192_bytes_loop_zmm; + Label L_fill_192_bytes_loop_header_zmm; + Label L_fill_192_bytes_loop_pre_header_zmm; + Label L_fill_start_zmm_sequence; + + bind(L_fill_zmm_sequence); + evpbroadcast(type, xtmp, value, Assembler::AVX_512bit); + + bind(L_fill_start_zmm_sequence); + cmpq(count, 64 >> shift); + jccb(Assembler::greater, L_fill_128_bytes_zmm); + fill64_masked(shift, to, 0, xtmp, k2, count, rtmp, true); + jmp(L_exit); + + bind(L_fill_128_bytes_zmm); + cmpq(count, 128 >> shift); + jccb(Assembler::greater, L_fill_192_bytes_zmm); + fill64(to, 0, xtmp, true); + subq(count, 64 >> shift); + fill64_masked(shift, to, 64, xtmp, k2, count, rtmp, true); + jmp(L_exit); + + bind(L_fill_192_bytes_zmm); + cmpq(count, 192 >> shift); + jccb(Assembler::greater, L_fill_192_bytes_loop_pre_header_zmm); + fill64(to, 0, xtmp, true); + fill64(to, 64, xtmp, true); + subq(count, 128 >> shift); + fill64_masked(shift, to, 128, xtmp, k2, count, rtmp, true); + jmp(L_exit); + + bind(L_fill_192_bytes_loop_pre_header_zmm); + { + movq(rtmp, to); + andq(rtmp, 63); + jccb(Assembler::zero, L_fill_192_bytes_loop_header_zmm); + negq(rtmp); + addq(rtmp, 64); + mov64(r8, -1L); + bzhiq(r8, r8, rtmp); + kmovql(k2, r8); + evmovdqu(T_BYTE, k2, Address(to, 0), xtmp, Assembler::AVX_512bit); + addq(to, rtmp); + shrq(rtmp, shift); + subq(count, rtmp); + } + + cmpq(count, 192 >> shift); + jcc(Assembler::less, L_fill_start_zmm_sequence); + + bind(L_fill_192_bytes_loop_header_zmm); + subq(count, 192 >> shift); + + align32(); + bind(L_fill_192_bytes_loop_zmm); + fill64(to, 0, xtmp, true); + fill64(to, 64, xtmp, true); + fill64(to, 128, xtmp, true); + addq(to, 192); + subq(count, 192 >> shift); + jccb(Assembler::greaterEqual, L_fill_192_bytes_loop_zmm); + + addq(count, 192 >> shift); + jcc(Assembler::zero, L_exit); + jmp(L_fill_start_zmm_sequence); + } + bind(L_exit); +} +#endif #endif //COMPILER2_OR_JVMCI diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index b44f59a9ce3b9524bbd81f1096d881279b3b634d..415223253c7b49c8d3f13d47aaae71d7d9a119fc 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -194,6 +194,8 @@ class MacroAssembler: public Assembler { void incrementq(AddressLiteral dst); // Alignment + void align32(); + void align64(); void align(int modulus); void align(int modulus, int target); @@ -1303,6 +1305,7 @@ public: void evpcmpw(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, int comparison, bool is_signed, int vector_len, Register scratch_reg); + void evpbroadcast(BasicType type, XMMRegister dst, Register src, int vector_len); // Emit comparison instruction for the specified comparison predicate. void vpcmpCCW(XMMRegister dst, XMMRegister nds, XMMRegister src, ComparisonPredicate cond, Width width, int vector_len, Register scratch_reg); @@ -1724,7 +1727,7 @@ public: void encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3, - XMMRegister tmp4, Register tmp5, Register result); + XMMRegister tmp4, Register tmp5, Register result, bool ascii); #ifdef _LP64 void add2_with_carry(Register dest_hi, Register dest_lo, Register src1, Register src2); @@ -1836,17 +1839,20 @@ public: void byte_array_inflate(Register src, Register dst, Register len, XMMRegister tmp1, Register tmp2, KRegister mask = knoreg); - void fill64_masked_avx(uint shift, Register dst, int disp, + void fill_masked(BasicType bt, Address dst, XMMRegister xmm, KRegister mask, + Register length, Register temp, int vec_enc); + + void fill64_masked(uint shift, Register dst, int disp, XMMRegister xmm, KRegister mask, Register length, Register temp, bool use64byteVector = false); - void fill32_masked_avx(uint shift, Register dst, int disp, + void fill32_masked(uint shift, Register dst, int disp, XMMRegister xmm, KRegister mask, Register length, Register temp); - void fill32_avx(Register dst, int disp, XMMRegister xmm); + void fill32(Register dst, int disp, XMMRegister xmm); - void fill64_avx(Register dst, int dis, XMMRegister xmm, bool use64byteVector = false); + void fill64(Register dst, int dis, XMMRegister xmm, bool use64byteVector = false); #ifdef _LP64 void convert_f2i(Register dst, XMMRegister src); @@ -1883,6 +1889,10 @@ public: void copy64_avx(Register dst, Register src, Register index, XMMRegister xmm, bool conjoint, int shift = Address::times_1, int offset = 0, bool use64byteVector = false); + + void generate_fill_avx3(BasicType type, Register to, Register value, + Register count, Register rtmp, XMMRegister xtmp); + #endif // COMPILER2_OR_JVMCI #endif // _LP64 diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_adler.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_adler.cpp index 7e77dc899c2008315bf79e4b4f899bb7698b3e75..12e0e5ee7d3b2db8682610df6bd41bb9d4ddfa5b 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_adler.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_adler.cpp @@ -80,7 +80,7 @@ void MacroAssembler::updateBytesAdler32(Register init_d, Register data, Register cmpptr(data, end); jcc(Assembler::aboveEqual, SKIP_LOOP_1A); - align(32); + align32(); bind(SLOOP1A); vbroadcastf128(ydata, Address(data, 0), Assembler::AVX_256bit); addptr(data, CHUNKSIZE); @@ -178,7 +178,7 @@ void MacroAssembler::updateBytesAdler32(Register init_d, Register data, Register movdl(rax, xb); addl(b_d, rax); - align(32); + align32(); bind(FINAL_LOOP); movzbl(rax, Address(data, 0)); //movzx eax, byte[data] addl(a_d, rax); diff --git a/src/hotspot/cpu/x86/matcher_x86.hpp b/src/hotspot/cpu/x86/matcher_x86.hpp index 510395f121fa584e4b48164548530eecae0a0db8..2dcd1e6e7a94adacf4494dfa848dd884ef2e10a4 100644 --- a/src/hotspot/cpu/x86/matcher_x86.hpp +++ b/src/hotspot/cpu/x86/matcher_x86.hpp @@ -195,4 +195,7 @@ return true; } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = true; + #endif // CPU_X86_MATCHER_X86_HPP diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp index f289ad0a7a7c3dea1986dcff60189c00f1edeab1..654066ac87262194a4e87e9ee68b7ad699a41f07 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp @@ -2998,7 +2998,7 @@ class StubGenerator: public StubCodeGenerator { } address generate_upper_word_mask() { - __ align(64); + __ align64(); StubCodeMark mark(this, "StubRoutines", "upper_word_mask"); address start = __ pc(); __ emit_data(0x00000000, relocInfo::none, 0); @@ -3009,7 +3009,7 @@ class StubGenerator: public StubCodeGenerator { } address generate_shuffle_byte_flip_mask() { - __ align(64); + __ align64(); StubCodeMark mark(this, "StubRoutines", "shuffle_byte_flip_mask"); address start = __ pc(); __ emit_data(0x0c0d0e0f, relocInfo::none, 0); @@ -3068,7 +3068,7 @@ class StubGenerator: public StubCodeGenerator { } address generate_pshuffle_byte_flip_mask() { - __ align(64); + __ align64(); StubCodeMark mark(this, "StubRoutines", "pshuffle_byte_flip_mask"); address start = __ pc(); __ emit_data(0x00010203, relocInfo::none, 0); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 6ce1ea564790b94a0469a64407b6c556596614b6..c23b5edc3fb97d8f2d7d8ffcfa1790702a2f59e2 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -1484,7 +1484,7 @@ class StubGenerator: public StubCodeGenerator { __ subq(temp1, loop_size[shift]); // Main loop with aligned copy block size of 192 bytes at 32 byte granularity. - __ align(32); + __ align32(); __ BIND(L_main_loop); __ copy64_avx(to, from, temp4, xmm1, false, shift, 0); __ copy64_avx(to, from, temp4, xmm1, false, shift, 64); @@ -1551,7 +1551,7 @@ class StubGenerator: public StubCodeGenerator { // Main loop with aligned copy block size of 192 bytes at // 64 byte copy granularity. - __ align(32); + __ align32(); __ BIND(L_main_loop_64bytes); __ copy64_avx(to, from, temp4, xmm1, false, shift, 0 , true); __ copy64_avx(to, from, temp4, xmm1, false, shift, 64, true); @@ -1691,7 +1691,7 @@ class StubGenerator: public StubCodeGenerator { __ BIND(L_main_pre_loop); // Main loop with aligned copy block size of 192 bytes at 32 byte granularity. - __ align(32); + __ align32(); __ BIND(L_main_loop); __ copy64_avx(to, from, temp1, xmm1, true, shift, -64); __ copy64_avx(to, from, temp1, xmm1, true, shift, -128); @@ -1724,7 +1724,7 @@ class StubGenerator: public StubCodeGenerator { // Main loop with aligned copy block size of 192 bytes at // 64 byte copy granularity. - __ align(32); + __ align32(); __ BIND(L_main_loop_64bytes); __ copy64_avx(to, from, temp1, xmm1, true, shift, -64 , true); __ copy64_avx(to, from, temp1, xmm1, true, shift, -128, true); @@ -2113,13 +2113,14 @@ class StubGenerator: public StubCodeGenerator { BLOCK_COMMENT("Entry:"); - const Register to = c_rarg0; // source array address + const Register to = c_rarg0; // destination array address const Register value = c_rarg1; // value const Register count = c_rarg2; // elements count + __ mov(r11, count); __ enter(); // required for proper stackwalking of RuntimeStub frame - __ generate_fill(t, aligned, to, value, count, rax, xmm0); + __ generate_fill(t, aligned, to, value, r11, rax, xmm0); __ vzeroupper(); __ leave(); // required for proper stackwalking of RuntimeStub frame @@ -4194,7 +4195,7 @@ class StubGenerator: public StubCodeGenerator { } address generate_upper_word_mask() { - __ align(64); + __ align64(); StubCodeMark mark(this, "StubRoutines", "upper_word_mask"); address start = __ pc(); __ emit_data64(0x0000000000000000, relocInfo::none); @@ -4203,7 +4204,7 @@ class StubGenerator: public StubCodeGenerator { } address generate_shuffle_byte_flip_mask() { - __ align(64); + __ align64(); StubCodeMark mark(this, "StubRoutines", "shuffle_byte_flip_mask"); address start = __ pc(); __ emit_data64(0x08090a0b0c0d0e0f, relocInfo::none); @@ -4248,7 +4249,7 @@ class StubGenerator: public StubCodeGenerator { } address generate_pshuffle_byte_flip_mask() { - __ align(64); + __ align64(); StubCodeMark mark(this, "StubRoutines", "pshuffle_byte_flip_mask"); address start = __ pc(); __ emit_data64(0x0405060700010203, relocInfo::none); @@ -4274,7 +4275,7 @@ class StubGenerator: public StubCodeGenerator { //Mask for byte-swapping a couple of qwords in an XMM register using (v)pshufb. address generate_pshuffle_byte_flip_mask_sha512() { - __ align(32); + __ align32(); StubCodeMark mark(this, "StubRoutines", "pshuffle_byte_flip_mask_sha512"); address start = __ pc(); if (VM_Version::supports_avx2()) { @@ -4464,7 +4465,7 @@ class StubGenerator: public StubCodeGenerator { // This mask is used for incrementing counter value(linc0, linc4, etc.) address counter_mask_addr() { - __ align(64); + __ align64(); StubCodeMark mark(this, "StubRoutines", "counter_mask_addr"); address start = __ pc(); __ emit_data64(0x08090a0b0c0d0e0f, relocInfo::none);//lbswapmask @@ -5383,7 +5384,7 @@ address generate_avx_ghash_processBlocks() { address base64_shuffle_addr() { - __ align(64, (unsigned long long)__ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "shuffle_base64"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -5401,7 +5402,7 @@ address generate_avx_ghash_processBlocks() { address base64_avx2_shuffle_addr() { - __ align(32); + __ align32(); StubCodeMark mark(this, "StubRoutines", "avx2_shuffle_base64"); address start = __ pc(); __ emit_data64(0x0809070805060405, relocInfo::none); @@ -5413,7 +5414,7 @@ address generate_avx_ghash_processBlocks() { address base64_avx2_input_mask_addr() { - __ align(32); + __ align32(); StubCodeMark mark(this, "StubRoutines", "avx2_input_mask_base64"); address start = __ pc(); __ emit_data64(0x8000000000000000, relocInfo::none); @@ -5425,7 +5426,7 @@ address generate_avx_ghash_processBlocks() { address base64_avx2_lut_addr() { - __ align(32); + __ align32(); StubCodeMark mark(this, "StubRoutines", "avx2_lut_base64"); address start = __ pc(); __ emit_data64(0xfcfcfcfcfcfc4741, relocInfo::none); @@ -5443,7 +5444,7 @@ address generate_avx_ghash_processBlocks() { address base64_encoding_table_addr() { - __ align(64, (unsigned long long)__ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "encoding_table_base64"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -5530,7 +5531,7 @@ address generate_avx_ghash_processBlocks() { __ evmovdquq(xmm2, Address(encode_table, 0), Assembler::AVX_512bit); __ evpbroadcastq(xmm1, rax, Assembler::AVX_512bit); - __ align(32); + __ align32(); __ BIND(L_vbmiLoop); __ vpermb(xmm0, xmm3, Address(source, start_offset), Assembler::AVX_512bit); @@ -5730,7 +5731,7 @@ address generate_avx_ghash_processBlocks() { __ cmpl(length, 31); __ jcc(Assembler::belowEqual, L_process3); - __ align(32); + __ align32(); __ BIND(L_32byteLoop); // Get next 32 bytes @@ -5850,7 +5851,7 @@ address generate_avx_ghash_processBlocks() { // base64 AVX512vbmi tables address base64_vbmi_lookup_lo_addr() { - __ align(64, (unsigned long long) __ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "lookup_lo_base64"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -5867,7 +5868,7 @@ address generate_avx_ghash_processBlocks() { } address base64_vbmi_lookup_hi_addr() { - __ align(64, (unsigned long long) __ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "lookup_hi_base64"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -5883,7 +5884,7 @@ address generate_avx_ghash_processBlocks() { return start; } address base64_vbmi_lookup_lo_url_addr() { - __ align(64, (unsigned long long) __ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "lookup_lo_base64url"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -5900,7 +5901,7 @@ address generate_avx_ghash_processBlocks() { } address base64_vbmi_lookup_hi_url_addr() { - __ align(64, (unsigned long long) __ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "lookup_hi_base64url"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -5917,7 +5918,7 @@ address generate_avx_ghash_processBlocks() { } address base64_vbmi_pack_vec_addr() { - __ align(64, (unsigned long long) __ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "pack_vec_base64"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -5934,7 +5935,7 @@ address generate_avx_ghash_processBlocks() { } address base64_vbmi_join_0_1_addr() { - __ align(64, (unsigned long long) __ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "join_0_1_base64"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -5951,7 +5952,7 @@ address generate_avx_ghash_processBlocks() { } address base64_vbmi_join_1_2_addr() { - __ align(64, (unsigned long long) __ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "join_1_2_base64"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -5968,7 +5969,7 @@ address generate_avx_ghash_processBlocks() { } address base64_vbmi_join_2_3_addr() { - __ align(64, (unsigned long long) __ pc()); + __ align64(); StubCodeMark mark(this, "StubRoutines", "join_2_3_base64"); address start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, @@ -6177,7 +6178,7 @@ address generate_avx_ghash_processBlocks() { __ evmovdquq(join12, ExternalAddress(StubRoutines::x86::base64_vbmi_join_1_2_addr()), Assembler::AVX_512bit, r13); __ evmovdquq(join23, ExternalAddress(StubRoutines::x86::base64_vbmi_join_2_3_addr()), Assembler::AVX_512bit, r13); - __ align(32); + __ align32(); __ BIND(L_process256); // Grab input data __ evmovdquq(input0, Address(source, start_offset, Address::times_1, 0x00), Assembler::AVX_512bit); @@ -6259,7 +6260,7 @@ address generate_avx_ghash_processBlocks() { __ cmpl(length, 63); __ jcc(Assembler::lessEqual, L_finalBit); - __ align(32); + __ align32(); __ BIND(L_process64Loop); // Handle first 64-byte block @@ -6395,7 +6396,7 @@ address generate_avx_ghash_processBlocks() { __ shrq(rax, 1); __ jmp(L_donePadding); - __ align(32); + __ align32(); __ BIND(L_bruteForce); } // End of if(avx512_vbmi) @@ -6439,7 +6440,7 @@ address generate_avx_ghash_processBlocks() { __ jmp(L_bottomLoop); - __ align(32); + __ align32(); __ BIND(L_forceLoop); __ shll(byte1, 18); __ shll(byte2, 12); diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index b93e50b6d51468f4d78b0da7b330777b9713fb2b..4134990dcbb8c5686ad21a4ffadfdaa691703e98 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -33,7 +33,7 @@ static bool returns_to_call_stub(address return_pc) { return return_pc == _call_ enum platform_dependent_constants { code_size1 = 20000 LP64_ONLY(+10000), // simply increase if too small (assembler will crash if too small) - code_size2 = 35300 LP64_ONLY(+32000) // simply increase if too small (assembler will crash if too small) + code_size2 = 35300 LP64_ONLY(+35000) // simply increase if too small (assembler will crash if too small) }; class x86 { diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index eb60216d59598e8845b68b9950e13a1150a0a97c..605faf89f196ab96362c3395ef84a695f5b2405d 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1469,6 +1469,14 @@ void VM_Version::get_processor_features() { #endif } +#ifdef COMPILER2 + if (FLAG_IS_DEFAULT(OptimizeFill)) { + if (MaxVectorSize < 32 || !VM_Version::supports_avx512vlbw()) { + OptimizeFill = false; + } + } +#endif + #ifdef _LP64 if (UseSSE42Intrinsics) { if (FLAG_IS_DEFAULT(UseVectorizedMismatchIntrinsic)) { @@ -1585,12 +1593,6 @@ void VM_Version::get_processor_features() { // Modern processors allow misaligned memory operations for vectors. AlignVector = !UseUnalignedLoadStores; } - if (FLAG_IS_DEFAULT(OptimizeFill)) { - // 8247307: On x86, the auto-vectorized loop array fill code shows - // better performance than the array fill stubs. We should reenable - // this after the x86 stubs get improved. - OptimizeFill = false; - } #endif // COMPILER2 if (FLAG_IS_DEFAULT(AllocatePrefetchInstr)) { diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 7fb9e4e66a024beca6a235662a455675a5fc9b9d..18d213cc31a049dd3c52a078c5377f9a3ca9b196 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -12199,18 +12199,35 @@ instruct string_inflate_evex(Universe dummy, eSIRegP src, eDIRegP dst, eDXRegI l instruct encode_iso_array(eSIRegP src, eDIRegP dst, eDXRegI len, regD tmp1, regD tmp2, regD tmp3, regD tmp4, eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); - format %{ "Encode array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} + format %{ "Encode iso array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, - $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register, false); %} ins_pipe( pipe_slow ); %} +// encode char[] to byte[] in ASCII +instruct encode_ascii_array(eSIRegP src, eDIRegP dst, eDXRegI len, + regD tmp1, regD tmp2, regD tmp3, regD tmp4, + eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ + predicate(((EncodeISOArrayNode*)n)->is_ascii()); + match(Set result (EncodeISOArray src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); + + format %{ "Encode ascii array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} + ins_encode %{ + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register, true); + %} + ins_pipe( pipe_slow ); +%} //----------Control Flow Instructions------------------------------------------ // Signed compare Instructions diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index ad6c36c51d75a6229a1ce6666f3d53b11990f15e..e637f143659a537ee574d5e23918dcc629662821 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -8527,6 +8527,19 @@ instruct mulHiL_rReg(rdx_RegL dst, no_rax_RegL src, rax_RegL rax, rFlagsReg cr) ins_pipe(ialu_reg_reg_alu0); %} +instruct umulHiL_rReg(rdx_RegL dst, no_rax_RegL src, rax_RegL rax, rFlagsReg cr) +%{ + match(Set dst (UMulHiL src rax)); + effect(USE_KILL rax, KILL cr); + + ins_cost(300); + format %{ "mulq RDX:RAX, RAX, $src\t# umulhi" %} + ins_encode %{ + __ mulq($src$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + instruct divI_rReg(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div, rFlagsReg cr) %{ @@ -11770,14 +11783,32 @@ instruct string_inflate_evex(Universe dummy, rsi_RegP src, rdi_RegP dst, rdx_Reg instruct encode_iso_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, legRegD tmp1, legRegD tmp2, legRegD tmp3, legRegD tmp4, rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); + match(Set result (EncodeISOArray src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); + + format %{ "Encode iso array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} + ins_encode %{ + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register, false); + %} + ins_pipe( pipe_slow ); +%} + +// encode char[] to byte[] in ASCII +instruct encode_ascii_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, + legRegD tmp1, legRegD tmp2, legRegD tmp3, legRegD tmp4, + rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{ + predicate(((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); - format %{ "Encode array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} + format %{ "Encode ascii array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, - $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register, true); %} ins_pipe( pipe_slow ); %} diff --git a/src/hotspot/os/aix/osThread_aix.cpp b/src/hotspot/os/aix/osThread_aix.cpp index bf0d53e2b4962660435e15ab6b8bbddcdefb76eb..b6aa44de0aee33966d5a3eab7620405776a4543e 100644 --- a/src/hotspot/os/aix/osThread_aix.cpp +++ b/src/hotspot/os/aix/osThread_aix.cpp @@ -46,8 +46,7 @@ void OSThread::pd_initialize() { sigemptyset(&_caller_sigmask); - _startThread_lock = new Monitor(Mutex::event, "startThread_lock", - Monitor::_safepoint_check_never); + _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); assert(_startThread_lock != NULL, "check"); } diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 508f92f2e5a7bf25b4973d033a9363eb04fa92de..df8563067e43e7d103d52a64e581b23e9e8bbec0 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -1118,15 +1118,6 @@ void *os::dll_load(const char *filename, char *ebuf, int ebuflen) { return NULL; } -void* os::dll_lookup(void* handle, const char* name) { - void* res = dlsym(handle, name); - return res; -} - -void* os::get_default_process_handle() { - return (void*)::dlopen(NULL, RTLD_LAZY); -} - void os::print_dll_info(outputStream *st) { st->print_cr("Dynamic libraries:"); LoadedLibraries::print(st); diff --git a/src/hotspot/os/bsd/osThread_bsd.cpp b/src/hotspot/os/bsd/osThread_bsd.cpp index d41c9a992a82ad66e6b1bac777e445d8b912b6af..100a5ce5447d4c260665f5ddb62b3f199c17d3e6 100644 --- a/src/hotspot/os/bsd/osThread_bsd.cpp +++ b/src/hotspot/os/bsd/osThread_bsd.cpp @@ -45,8 +45,7 @@ void OSThread::pd_initialize() { sigemptyset(&_caller_sigmask); - _startThread_lock = new Monitor(Mutex::event, "startThread_lock", - Monitor::_safepoint_check_never); + _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); assert(_startThread_lock !=NULL, "check"); } diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 5a3d0f32dc18148c2288913f708d12ed739ff210..d1adfeb1ddd1cf4f469449072a69c98608f3edc6 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -1163,22 +1163,6 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { } #endif // !__APPLE__ -void* os::get_default_process_handle() { -#ifdef __APPLE__ - // MacOS X needs to use RTLD_FIRST instead of RTLD_LAZY - // to avoid finding unexpected symbols on second (or later) - // loads of a library. - return (void*)::dlopen(NULL, RTLD_FIRST); -#else - return (void*)::dlopen(NULL, RTLD_LAZY); -#endif -} - -// XXX: Do we need a lock around this as per Linux? -void* os::dll_lookup(void* handle, const char* name) { - return dlsym(handle, name); -} - int _print_dll_info_cb(const char * name, address base_address, address top_address, void * param) { outputStream * out = (outputStream *) param; out->print_cr(INTPTR_FORMAT " \t%s", (intptr_t)base_address, name); @@ -1327,13 +1311,17 @@ void os::get_summary_cpu_info(char* buf, size_t buflen) { strncpy(machine, "", sizeof(machine)); } - const char* emulated = ""; #if defined(__APPLE__) && !defined(ZERO) if (VM_Version::is_cpu_emulated()) { - emulated = " (EMULATED)"; + snprintf(buf, buflen, "\"%s\" %s (EMULATED) %d MHz", model, machine, mhz); + } else { + NOT_AARCH64(snprintf(buf, buflen, "\"%s\" %s %d MHz", model, machine, mhz)); + // aarch64 CPU doesn't report its speed + AARCH64_ONLY(snprintf(buf, buflen, "\"%s\" %s", model, machine)); } +#else + snprintf(buf, buflen, "\"%s\" %s %d MHz", model, machine, mhz); #endif - snprintf(buf, buflen, "\"%s\" %s%s %d MHz", model, machine, emulated, mhz); } void os::print_memory_info(outputStream* st) { diff --git a/src/hotspot/os/linux/osThread_linux.cpp b/src/hotspot/os/linux/osThread_linux.cpp index 3846c571454ecf3c39e4eb523b1ffaa3bec1790c..b6365558da18b01767892375184543112c9d2629 100644 --- a/src/hotspot/os/linux/osThread_linux.cpp +++ b/src/hotspot/os/linux/osThread_linux.cpp @@ -40,8 +40,7 @@ void OSThread::pd_initialize() { sigemptyset(&_caller_sigmask); - _startThread_lock = new Monitor(Mutex::event, "startThread_lock", - Monitor::_safepoint_check_never); + _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); assert(_startThread_lock !=NULL, "check"); } diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 27abc8d9030a973bb1bbe49e6b73a4009af40841..e75e2e51b0e467b99b16e4904e551185b933a22b 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1781,15 +1781,6 @@ void * os::Linux::dll_load_in_vmthread(const char *filename, char *ebuf, return result; } -void* os::dll_lookup(void* handle, const char* name) { - void* res = dlsym(handle, name); - return res; -} - -void* os::get_default_process_handle() { - return (void*)::dlopen(NULL, RTLD_LAZY); -} - static bool _print_ascii_file(const char* filename, outputStream* st, const char* hdr = NULL) { int fd = ::open(filename, O_RDONLY); if (fd == -1) { diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index cf4d7f3d139ce025335ee613d6f92729bf249d95..60820b31f925cd9ac838c1bed906564a3752e526 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -639,6 +639,21 @@ bool os::has_allocatable_memory_limit(size_t* limit) { #endif } +void* os::get_default_process_handle() { +#ifdef __APPLE__ + // MacOS X needs to use RTLD_FIRST instead of RTLD_LAZY + // to avoid finding unexpected symbols on second (or later) + // loads of a library. + return (void*)::dlopen(NULL, RTLD_FIRST); +#else + return (void*)::dlopen(NULL, RTLD_LAZY); +#endif +} + +void* os::dll_lookup(void* handle, const char* name) { + return dlsym(handle, name); +} + void os::dll_unload(void *lib) { ::dlclose(lib); } @@ -732,6 +747,10 @@ void os::exit(int num) { ::exit(num); } +void os::_exit(int num) { + ::_exit(num); +} + // Builds a platform dependent Agent_OnLoad_ function name // which is used to find statically linked in agents. // Parameters: diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index e6881fa846580ecb33cf38ff17b1f73cf783dd2e..6d0610ccd7591983ef79815596b9855c46a7b012 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -1858,7 +1858,11 @@ void os::win32::print_windows_version(outputStream* st) { case 10000: if (is_workstation) { - st->print("10"); + if (build_number >= 22000) { + st->print("11"); + } else { + st->print("10"); + } } else { // distinguish Windows Server by build number // - 2016 GA 10/2016 build: 14393 @@ -4159,8 +4163,8 @@ int os::win32::exit_process_or_thread(Ept what, int exit_code) { _endthreadex((unsigned)exit_code); } else if (what == EPT_PROCESS) { ::exit(exit_code); - } else { - _exit(exit_code); + } else { // EPT_PROCESS_DIE + ::_exit(exit_code); } // Should not reach here @@ -4763,6 +4767,10 @@ void os::exit(int num) { win32::exit_process_or_thread(win32::EPT_PROCESS, num); } +void os::_exit(int num) { + win32::exit_process_or_thread(win32::EPT_PROCESS_DIE, num); +} + // Is a (classpath) directory empty? bool os::dir_is_empty(const char* path) { errno_t err; diff --git a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp index 73799524140640c31bb7b0b8ca272360479d1087..1f9917acae71dec118e7a2d346ba725aada1eefe 100644 --- a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp +++ b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2013 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,7 +29,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read(void *loc, intx interval) { +inline void Prefetch::read(const void *loc, intx interval) { #if !defined(USE_XLC_BUILTINS) __asm__ __volatile__ ( " dcbt 0, %0 \n" diff --git a/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp b/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp index e24d0328e6db5e16123e19e122ce7244d93a8055..1bcbdcaf90d246c36559e0c26dd75405a47abd39 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp @@ -30,7 +30,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read (void *loc, intx interval) { +inline void Prefetch::read (const void *loc, intx interval) { if (interval >= 0) asm("prfm PLDL1KEEP, [%0, %1]" : : "r"(loc), "r"(interval)); } diff --git a/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp b/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp index 35bf6bbc4fd411148198512dff3a8ec52b90831f..cb0db2f360c763237cf91dd63d3c5f191d5cc119 100644 --- a/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp +++ b/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read (void *loc, intx interval) { +inline void Prefetch::read (const void *loc, intx interval) { #ifdef AMD64 __asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval)); #endif // AMD64 diff --git a/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp b/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp index 794f26e8a84794dec1cafa297ad0cbb8dafb4943..220d6a08f68e4686eebb01225fbfa98c68963d5d 100644 --- a/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp +++ b/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,7 +28,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read(void* loc, intx interval) { +inline void Prefetch::read(const void* loc, intx interval) { } inline void Prefetch::write(void* loc, intx interval) { diff --git a/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.S b/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.S index 3007587d9c22ca958f076f7209b7240cfbfbecb2..9c91942cb335a163537fc99e3a00fddcd6780529 100644 --- a/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.S +++ b/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.S @@ -47,6 +47,28 @@ aarch64_atomic_fetch_add_4_default_impl: mov w0, w2 ret + .global aarch64_atomic_fetch_add_8_relaxed_default_impl + .align 5 +aarch64_atomic_fetch_add_8_relaxed_default_impl: + prfm pstl1strm, [x0] +0: ldxr x2, [x0] + add x8, x2, x1 + stxr w9, x8, [x0] + cbnz w9, 0b + mov x0, x2 + ret + + .global aarch64_atomic_fetch_add_4_relaxed_default_impl + .align 5 +aarch64_atomic_fetch_add_4_relaxed_default_impl: + prfm pstl1strm, [x0] +0: ldxr w2, [x0] + add w8, w2, w1 + stxr w9, w8, [x0] + cbnz w9, 0b + mov w0, w2 + ret + .globl aarch64_atomic_xchg_4_default_impl .align 5 aarch64_atomic_xchg_4_default_impl: diff --git a/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.hpp index 316e877ec1f6499af9e6bc0043720dc03c04ab26..3208db5b4a6b7b04ad5df0e8bc82e87e50afa8e7 100644 --- a/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -87,9 +87,14 @@ inline D Atomic::PlatformAdd<4>::fetch_and_add(D volatile* dest, I add_value, atomic_memory_order order) const { STATIC_ASSERT(4 == sizeof(I)); STATIC_ASSERT(4 == sizeof(D)); - D old_value - = atomic_fastcall(aarch64_atomic_fetch_add_4_impl, dest, add_value); - return old_value; + aarch64_atomic_stub_t stub; + switch (order) { + case memory_order_relaxed: + stub = aarch64_atomic_fetch_add_4_relaxed_impl; break; + default: + stub = aarch64_atomic_fetch_add_4_impl; break; + } + return atomic_fastcall(stub, dest, add_value); } template<> @@ -98,9 +103,14 @@ inline D Atomic::PlatformAdd<8>::fetch_and_add(D volatile* dest, I add_value, atomic_memory_order order) const { STATIC_ASSERT(8 == sizeof(I)); STATIC_ASSERT(8 == sizeof(D)); - D old_value - = atomic_fastcall(aarch64_atomic_fetch_add_8_impl, dest, add_value); - return old_value; + aarch64_atomic_stub_t stub; + switch (order) { + case memory_order_relaxed: + stub = aarch64_atomic_fetch_add_8_relaxed_impl; break; + default: + stub = aarch64_atomic_fetch_add_8_impl; break; + } + return atomic_fastcall(stub, dest, add_value); } template<> diff --git a/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp b/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp index e11579de29065ab87bcdcdea6b4b63134f4caf6a..168a680a404ce7f082691a056098111e6caa910a 100644 --- a/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,7 +29,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read (void *loc, intx interval) { +inline void Prefetch::read (const void *loc, intx interval) { if (interval >= 0) asm("prfm PLDL1KEEP, [%0, %1]" : : "r"(loc), "r"(interval)); } diff --git a/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp b/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp index e75a8c5c90d34b00971d2513344fa3a6ae636d3a..dfbab55d9b9e0ebc6063be73a130dbdb0836bc19 100644 --- a/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp +++ b/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read (void *loc, intx interval) { +inline void Prefetch::read (const void *loc, intx interval) { #if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_5TE__) __asm__ volatile ("pld [%0]" : : "r" (loc)); #endif diff --git a/src/hotspot/os_cpu/linux_ppc/gc/z/zSyscall_linux_ppc.hpp b/src/hotspot/os_cpu/linux_ppc/gc/z/zSyscall_linux_ppc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5950b52136db83d711e8d8a43e3af3a353f7e1ce --- /dev/null +++ b/src/hotspot/os_cpu/linux_ppc/gc/z/zSyscall_linux_ppc.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef OS_CPU_LINUX_PPC_GC_Z_ZSYSCALL_LINUX_PPC_HPP +#define OS_CPU_LINUX_PPC_GC_Z_ZSYSCALL_LINUX_PPC_HPP + +#include + +// +// Support for building on older Linux systems +// + + +#ifndef SYS_memfd_create +#define SYS_memfd_create 360 +#endif +#ifndef SYS_fallocate +#define SYS_fallocate 309 +#endif + +#endif // OS_CPU_LINUX_PPC_GC_Z_ZSYSCALL_LINUX_PPC_HPP diff --git a/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp b/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp index d795d0c91bb68f706c80d9e79163478b9f8c6823..12c65e6bf0051583c858006a94983c44c6f0b858 100644 --- a/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp +++ b/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2013 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,7 +29,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read(void *loc, intx interval) { +inline void Prefetch::read(const void *loc, intx interval) { __asm__ __volatile__ ( " dcbt 0, %0 \n" : diff --git a/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp b/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp index e922b6dc8534af796aa86b489f61669b8c08db08..ee55d01886f2e3de00b1669e40c2c95e7a194b19 100644 --- a/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp +++ b/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,7 +28,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read(void* loc, intx interval) { +inline void Prefetch::read(const void* loc, intx interval) { // No prefetch instructions on z/Architecture -> implement trivially. } diff --git a/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp b/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp index c15cc75b31e634096c3e2ce3e3aaad07ecafb21a..cf60c2cbd6b178bb05c00680ca3c153294cd1fef 100644 --- a/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp +++ b/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read (void *loc, intx interval) { +inline void Prefetch::read (const void *loc, intx interval) { #ifdef AMD64 __asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval)); #endif // AMD64 diff --git a/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp b/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp index 85cf90b42d3fa5c76ab67913dbd386b97b9d348f..a510fa878348cc4d17e84d90fc6ef8035d1eccc5 100644 --- a/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp +++ b/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,7 +28,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read(void* loc, intx interval) { +inline void Prefetch::read(const void* loc, intx interval) { } inline void Prefetch::write(void* loc, intx interval) { diff --git a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp index d2bd8f1411967f529123db80e4b83600e1ced606..df301ade92dacb19bea7605408aa6f06d754c79a 100644 --- a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp @@ -28,7 +28,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read (void *loc, intx interval) { +inline void Prefetch::read (const void *loc, intx interval) { } inline void Prefetch::write(void *loc, intx interval) { diff --git a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp index d4a5fd32eab7692d3bc2438b3221cefb6486bc6f..996625cb0ad9b908831d31ab27cac8e68641af2c 100644 --- a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp +++ b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ #include "runtime/prefetch.hpp" -inline void Prefetch::read (void *loc, intx interval) {} +inline void Prefetch::read (const void *loc, intx interval) {} inline void Prefetch::write(void *loc, intx interval) {} #endif // OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP diff --git a/src/hotspot/share/adlc/output_h.cpp b/src/hotspot/share/adlc/output_h.cpp index 4a31813139049dfaedbadf4dff66a4fa48b8f99a..0ca2048a765eb53d876d95070b6d3039a79c34f6 100644 --- a/src/hotspot/share/adlc/output_h.cpp +++ b/src/hotspot/share/adlc/output_h.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1940,31 +1940,20 @@ void ArchDesc::declareClasses(FILE *fp) { // it doesn't understand what that might alias. fprintf(fp," const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } // Box?\n"); } - else if( instr->_matrule && instr->_matrule->_rChild && !strcmp(instr->_matrule->_rChild->_opType,"CMoveP") ) { + else if (instr->_matrule && instr->_matrule->_rChild && + (!strcmp(instr->_matrule->_rChild->_opType,"CMoveP") || !strcmp(instr->_matrule->_rChild->_opType,"CMoveN")) ) { int offset = 1; // Special special hack to see if the Cmp? has been incorporated in the conditional move MatchNode *rl = instr->_matrule->_rChild->_lChild; - if( rl && !strcmp(rl->_opType, "Binary") ) { - MatchNode *rlr = rl->_rChild; - if (rlr && strncmp(rlr->_opType, "Cmp", 3) == 0) - offset = 2; - } - // Special hack for ideal CMoveP; ideal type depends on inputs - fprintf(fp," const Type *bottom_type() const { const Type *t = in(oper_input_base()+%d)->bottom_type(); return (req() <= oper_input_base()+%d) ? t : t->meet(in(oper_input_base()+%d)->bottom_type()); } // CMoveP\n", - offset, offset+1, offset+1); - } - else if( instr->_matrule && instr->_matrule->_rChild && !strcmp(instr->_matrule->_rChild->_opType,"CMoveN") ) { - int offset = 1; - // Special special hack to see if the Cmp? has been incorporated in the conditional move - MatchNode *rl = instr->_matrule->_rChild->_lChild; - if( rl && !strcmp(rl->_opType, "Binary") ) { - MatchNode *rlr = rl->_rChild; - if (rlr && strncmp(rlr->_opType, "Cmp", 3) == 0) - offset = 2; + if (rl && !strcmp(rl->_opType, "Binary") && rl->_rChild && strncmp(rl->_rChild->_opType, "Cmp", 3) == 0) { + offset = 2; + fprintf(fp," const Type *bottom_type() const { if (req() == 3) return in(2)->bottom_type();\n\tconst Type *t = in(oper_input_base()+%d)->bottom_type(); return (req() <= oper_input_base()+%d) ? t : t->meet(in(oper_input_base()+%d)->bottom_type()); } // %s\n", + offset, offset+1, offset+1, instr->_matrule->_rChild->_opType); + } else { + // Special hack for ideal CMove; ideal type depends on inputs + fprintf(fp," const Type *bottom_type() const { const Type *t = in(oper_input_base()+%d)->bottom_type(); return (req() <= oper_input_base()+%d) ? t : t->meet(in(oper_input_base()+%d)->bottom_type()); } // %s\n", + offset, offset+1, offset+1, instr->_matrule->_rChild->_opType); } - // Special hack for ideal CMoveN; ideal type depends on inputs - fprintf(fp," const Type *bottom_type() const { const Type *t = in(oper_input_base()+%d)->bottom_type(); return (req() <= oper_input_base()+%d) ? t : t->meet(in(oper_input_base()+%d)->bottom_type()); } // CMoveN\n", - offset, offset+1, offset+1); } else if (instr->is_tls_instruction()) { // Special hack for tlsLoadP diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 45b2db20146b59fa062fc4f95ea948f8c0e036e3..cb5c0aeb8c78008f044e0d009bddbd102fa38bbb 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -1128,10 +1128,6 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, print_region_stats(mapinfo, closed_heap_regions, open_heap_regions); mapinfo->set_requested_base((char*)MetaspaceShared::requested_base_address()); - if (mapinfo->header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC) { - mapinfo->set_header_base_archive_name_size(strlen(Arguments::GetSharedArchivePath()) + 1); - mapinfo->set_header_base_archive_is_default(FLAG_IS_DEFAULT(SharedArchiveFile)); - } mapinfo->set_header_crc(mapinfo->compute_header_crc()); // After this point, we should not write any data into mapinfo->header() since this // would corrupt its checksum we have calculated before. diff --git a/src/hotspot/share/cds/cdsConstants.cpp b/src/hotspot/share/cds/cdsConstants.cpp index bb62306b5bd2430c0c1a0862026d7f836f77e186..5d8948b0ee0612cb21ac17547dd2b6f0b92a0969 100644 --- a/src/hotspot/share/cds/cdsConstants.cpp +++ b/src/hotspot/share/cds/cdsConstants.cpp @@ -31,15 +31,17 @@ #include "utilities/globalDefinitions.hpp" CDSConst CDSConstants::offsets[] = { - { "CDSFileMapHeaderBase::_magic", offset_of(CDSFileMapHeaderBase, _magic) }, - { "CDSFileMapHeaderBase::_crc", offset_of(CDSFileMapHeaderBase, _crc) }, - { "CDSFileMapHeaderBase::_version", offset_of(CDSFileMapHeaderBase, _version) }, - { "CDSFileMapHeaderBase::_space[0]", offset_of(CDSFileMapHeaderBase, _space) }, - { "FileMapHeader::_jvm_ident", offset_of(FileMapHeader, _jvm_ident) }, - { "FileMapHeader::_base_archive_name_size", offset_of(FileMapHeader, _base_archive_name_size) }, - { "CDSFileMapRegion::_crc", offset_of(CDSFileMapRegion, _crc) }, - { "CDSFileMapRegion::_used", offset_of(CDSFileMapRegion, _used) }, - { "DynamicArchiveHeader::_base_region_crc", offset_of(DynamicArchiveHeader, _base_region_crc) } + { "GenericCDSFileMapHeader::_magic", offset_of(GenericCDSFileMapHeader, _magic) }, + { "GenericCDSFileMapHeader::_crc", offset_of(GenericCDSFileMapHeader, _crc) }, + { "GenericCDSFileMapHeader::_version", offset_of(GenericCDSFileMapHeader, _version) }, + { "GenericCDSFileMapHeader::_header_size", offset_of(GenericCDSFileMapHeader, _header_size) }, + { "GenericCDSFileMapHeader::_base_archive_path_offset", offset_of(GenericCDSFileMapHeader, _base_archive_path_offset) }, + { "GenericCDSFileMapHeader::_base_archive_name_size", offset_of(GenericCDSFileMapHeader, _base_archive_name_size) }, + { "CDSFileMapHeaderBase::_space[0]", offset_of(CDSFileMapHeaderBase, _space) }, + { "FileMapHeader::_jvm_ident", offset_of(FileMapHeader, _jvm_ident) }, + { "CDSFileMapRegion::_crc", offset_of(CDSFileMapRegion, _crc) }, + { "CDSFileMapRegion::_used", offset_of(CDSFileMapRegion, _used) }, + { "DynamicArchiveHeader::_base_region_crc", offset_of(DynamicArchiveHeader, _base_region_crc) } }; CDSConst CDSConstants::constants[] = { diff --git a/src/hotspot/share/cds/cdsConstants.hpp b/src/hotspot/share/cds/cdsConstants.hpp index 72590dce657a008cb77dce794b144a463a2511ad..d116fbf47aec7ca2646a5b993258a0af0d947326 100644 --- a/src/hotspot/share/cds/cdsConstants.hpp +++ b/src/hotspot/share/cds/cdsConstants.hpp @@ -26,6 +26,7 @@ #define SHARE_CDS_CDSCONSTANTS_HPP #include "memory/allStatic.hpp" +#include "utilities/globalDefinitions.hpp" typedef struct { const char* _name; diff --git a/src/hotspot/share/cds/classListWriter.cpp b/src/hotspot/share/cds/classListWriter.cpp index 45421457a633a99755d46ec9dfd0ed42e9302993..6750a5d4cac09c113ae466ff631929b29149afbc 100644 --- a/src/hotspot/share/cds/classListWriter.cpp +++ b/src/hotspot/share/cds/classListWriter.cpp @@ -59,6 +59,11 @@ void ClassListWriter::write(const InstanceKlass* k, const ClassFileStream* cfs) return; } + // filter out java/lang/invoke/BoundMethodHandle$Species.... + if (cfs != NULL && strcmp(cfs->source(), "_ClassSpecializer_generateConcreteSpeciesCode") == 0) { + return; + } + ClassListWriter w; write_to_stream(k, w.stream(), cfs); } diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index d868eb535b853a43a60539499e6c45f1b980ef83..33da23e10fec65ba096c4465640914b467d52179 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -112,6 +112,7 @@ public: // Block concurrent class unloading from changing the _dumptime_table MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); SystemDictionaryShared::check_excluded_classes(); + SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary(); // save dumptime tables SystemDictionaryShared::clone_dumptime_tables(); @@ -180,14 +181,15 @@ public: void DynamicArchiveBuilder::init_header() { FileMapInfo* mapinfo = new FileMapInfo(false); assert(FileMapInfo::dynamic_info() == mapinfo, "must be"); + FileMapInfo* base_info = FileMapInfo::current_info(); + // header only be available after populate_header + mapinfo->populate_header(base_info->core_region_alignment()); _header = mapinfo->dynamic_header(); - FileMapInfo* base_info = FileMapInfo::current_info(); _header->set_base_header_crc(base_info->crc()); for (int i = 0; i < MetaspaceShared::n_regions; i++) { _header->set_base_region_crc(i, base_info->space_crc(i)); } - _header->populate(base_info, base_info->core_region_alignment()); } void DynamicArchiveBuilder::release_header() { @@ -325,7 +327,7 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data) { size_t file_size = pointer_delta(top, base, sizeof(char)); log_info(cds, dynamic)("Written dynamic archive " PTR_FORMAT " - " PTR_FORMAT - " [" SIZE_FORMAT " bytes header, " SIZE_FORMAT " bytes total]", + " [" UINT32_FORMAT " bytes header, " SIZE_FORMAT " bytes total]", p2i(base), p2i(top), _header->header_size(), file_size); log_info(cds, dynamic)("%d klasses; %d symbols", klasses()->length(), symbols()->length()); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index acd6ad2552f58aec090decd38e3c2e06988f80ba..1651aef99b08295f27b545a3f3a96acce258296e 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -54,6 +54,7 @@ #include "oops/oop.inline.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/arguments.hpp" +#include "runtime/globals_extension.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" @@ -169,21 +170,13 @@ template static void get_header_version(char (&header_version) [N]) { FileMapInfo::FileMapInfo(bool is_static) { memset((void*)this, 0, sizeof(FileMapInfo)); _is_static = is_static; - size_t header_size; - if (is_static) { + if (_is_static) { assert(_current_info == NULL, "must be singleton"); // not thread safe _current_info = this; - header_size = sizeof(FileMapHeader); } else { assert(_dynamic_archive_info == NULL, "must be singleton"); // not thread safe _dynamic_archive_info = this; - header_size = sizeof(DynamicArchiveHeader); } - _header = (FileMapHeader*)os::malloc(header_size, mtInternal); - memset((void*)_header, 0, header_size); - _header->set_header_size(header_size); - _header->set_version(INVALID_CDS_ARCHIVE_VERSION); - _header->set_has_platform_or_app_classes(true); _file_offset = 0; _file_open = false; } @@ -199,16 +192,49 @@ FileMapInfo::~FileMapInfo() { } void FileMapInfo::populate_header(size_t core_region_alignment) { - header()->populate(this, core_region_alignment); -} - -void FileMapHeader::populate(FileMapInfo* mapinfo, size_t core_region_alignment) { - if (DynamicDumpSharedSpaces) { - _magic = CDS_DYNAMIC_ARCHIVE_MAGIC; + assert(_header == NULL, "Sanity check"); + size_t c_header_size; + size_t header_size; + size_t base_archive_name_size = 0; + size_t base_archive_path_offset = 0; + if (is_static()) { + c_header_size = sizeof(FileMapHeader); + header_size = c_header_size; } else { - _magic = CDS_ARCHIVE_MAGIC; + // dynamic header including base archive name for non-default base archive + c_header_size = sizeof(DynamicArchiveHeader); + header_size = c_header_size; + if (!FLAG_IS_DEFAULT(SharedArchiveFile)) { + base_archive_name_size = strlen(Arguments::GetSharedArchivePath()) + 1; + header_size += base_archive_name_size; + base_archive_path_offset = c_header_size; + } + } + _header = (FileMapHeader*)os::malloc(header_size, mtInternal); + memset((void*)_header, 0, header_size); + _header->populate(this, + core_region_alignment, + header_size, + base_archive_name_size, + base_archive_path_offset); +} + +void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, + size_t header_size, size_t base_archive_name_size, + size_t base_archive_path_offset) { + // 1. We require _generic_header._magic to be at the beginning of the file + // 2. FileMapHeader also assumes that _generic_header is at the beginning of the file + assert(offset_of(FileMapHeader, _generic_header) == 0, "must be"); + set_header_size((unsigned int)header_size); + set_base_archive_path_offset((unsigned int)base_archive_path_offset); + set_base_archive_name_size((unsigned int)base_archive_name_size); + set_magic(DynamicDumpSharedSpaces ? CDS_DYNAMIC_ARCHIVE_MAGIC : CDS_ARCHIVE_MAGIC); + set_version(CURRENT_CDS_ARCHIVE_VERSION); + + if (!info->is_static() && base_archive_name_size != 0) { + // copy base archive name + copy_base_archive_name(Arguments::GetSharedArchivePath()); } - _version = CURRENT_CDS_ARCHIVE_VERSION; _core_region_alignment = core_region_alignment; _obj_alignment = ObjectAlignmentInBytes; _compact_strings = CompactStrings; @@ -245,22 +271,29 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t core_region_alignment) _requested_base_address = (char*)SharedBaseAddress; _mapped_base_address = (char*)SharedBaseAddress; _allow_archiving_with_java_agent = AllowArchivingWithJavaAgent; - // the following 2 fields will be set in write_header for dynamic archive header - _base_archive_name_size = 0; - _base_archive_is_default = false; if (!DynamicDumpSharedSpaces) { - set_shared_path_table(mapinfo->_shared_path_table); + set_shared_path_table(info->_shared_path_table); CDS_JAVA_HEAP_ONLY(_heap_obj_roots = CompressedOops::encode(HeapShared::roots());) } } +void FileMapHeader::copy_base_archive_name(const char* archive) { + assert(base_archive_name_size() != 0, "_base_archive_name_size not set"); + assert(base_archive_path_offset() != 0, "_base_archive_path_offset not set"); + assert(header_size() > sizeof(*this), "_base_archive_name_size not included in header size?"); + memcpy((char*)this + base_archive_path_offset(), archive, base_archive_name_size()); +} + void FileMapHeader::print(outputStream* st) { ResourceMark rm; - st->print_cr("- magic: 0x%08x", _magic); - st->print_cr("- crc: 0x%08x", _crc); - st->print_cr("- version: %d", _version); + st->print_cr("- magic: 0x%08x", magic()); + st->print_cr("- crc: 0x%08x", crc()); + st->print_cr("- version: %d", version()); + st->print_cr("- header_size: " UINT32_FORMAT, header_size()); + st->print_cr("- base_archive_path_offset: " UINT32_FORMAT, base_archive_path_offset()); + st->print_cr("- base_archive_name_size: " UINT32_FORMAT, base_archive_name_size()); for (int i = 0; i < NUM_CDS_REGIONS; i++) { FileMapRegion* si = space_at(i); @@ -268,7 +301,6 @@ void FileMapHeader::print(outputStream* st) { } st->print_cr("============ end regions ======== "); - st->print_cr("- header_size: " SIZE_FORMAT, _header_size); st->print_cr("- core_region_alignment: " SIZE_FORMAT, _core_region_alignment); st->print_cr("- obj_alignment: %d", _obj_alignment); st->print_cr("- narrow_oop_base: " INTPTR_FORMAT, p2i(_narrow_oop_base)); @@ -283,9 +315,7 @@ void FileMapHeader::print(outputStream* st) { st->print_cr("- cloned_vtables_offset: " SIZE_FORMAT_HEX, _cloned_vtables_offset); st->print_cr("- serialized_data_offset: " SIZE_FORMAT_HEX, _serialized_data_offset); st->print_cr("- heap_end: " INTPTR_FORMAT, p2i(_heap_end)); - st->print_cr("- base_archive_is_default: %d", _base_archive_is_default); st->print_cr("- jvm_ident: %s", _jvm_ident); - st->print_cr("- base_archive_name_size: " SIZE_FORMAT, _base_archive_name_size); st->print_cr("- shared_path_table_offset: " SIZE_FORMAT_HEX, _shared_path_table_offset); st->print_cr("- shared_path_table_size: %d", _shared_path_table_size); st->print_cr("- app_class_paths_start_index: %d", _app_class_paths_start_index); @@ -1012,122 +1042,169 @@ void FileMapInfo::validate_non_existent_class_paths() { } } +// a utility class for checking file header +class FileHeaderHelper { + int _fd; + GenericCDSFileMapHeader _header; + +public: + FileHeaderHelper() { + _fd = -1; + } + + ~FileHeaderHelper() { + if (_fd != -1) { + os::close(_fd); + } + } + + bool initialize(const char* archive_name) { + _fd = os::open(archive_name, O_RDONLY | O_BINARY, 0); + if (_fd < 0) { + return false; + } + return initialize(_fd); + } + + // for an already opened file, do not set _fd + bool initialize(int fd) { + assert(fd != -1, "Archive should be opened"); + size_t size = sizeof(GenericCDSFileMapHeader); + lseek(fd, 0, SEEK_SET); + size_t n = os::read(fd, (void*)&_header, (unsigned int)size); + if (n != size) { + vm_exit_during_initialization("Unable to read generic CDS file map header from shared archive"); + return false; + } + return true; + } + + GenericCDSFileMapHeader* get_generic_file_header() { + return &_header; + } + + bool read_base_archive_name(char** target) { + assert(_fd != -1, "Archive should be open"); + size_t name_size = (size_t)_header._base_archive_name_size; + assert(name_size != 0, "For non-default base archive, name size should be non-zero!"); + *target = NEW_C_HEAP_ARRAY(char, name_size, mtInternal); + lseek(_fd, _header._base_archive_path_offset, SEEK_SET); // position to correct offset. + size_t n = os::read(_fd, *target, (unsigned int)name_size); + if (n != name_size) { + log_info(cds)("Unable to read base archive name from archive"); + FREE_C_HEAP_ARRAY(char, *target); + return false; + } + if (!os::file_exists(*target)) { + log_info(cds)("Base archive %s does not exist", *target); + FREE_C_HEAP_ARRAY(char, *target); + return false; + } + return true; + } +}; + bool FileMapInfo::check_archive(const char* archive_name, bool is_static) { - int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0); - if (fd < 0) { + FileHeaderHelper file_helper; + if (!file_helper.initialize(archive_name)) { // do not vm_exit_during_initialization here because Arguments::init_shared_archive_paths() // requires a shared archive name. The open_for_read() function will log a message regarding // failure in opening a shared archive. return false; } - size_t sz = is_static ? sizeof(FileMapHeader) : sizeof(DynamicArchiveHeader); - void* header = os::malloc(sz, mtInternal); - memset(header, 0, sz); - size_t n = os::read(fd, header, (unsigned int)sz); - if (n != sz) { - os::free(header); - os::close(fd); - vm_exit_during_initialization("Unable to read header from shared archive", archive_name); - return false; - } + GenericCDSFileMapHeader* header = file_helper.get_generic_file_header(); if (is_static) { - FileMapHeader* static_header = (FileMapHeader*)header; - if (static_header->magic() != CDS_ARCHIVE_MAGIC) { - os::free(header); - os::close(fd); + if (header->_magic != CDS_ARCHIVE_MAGIC) { vm_exit_during_initialization("Not a base shared archive", archive_name); return false; } + if (header->_base_archive_path_offset != 0) { + log_info(cds)("_base_archive_path_offset should be 0"); + log_info(cds)("_base_archive_path_offset = " UINT32_FORMAT, header->_base_archive_path_offset); + return false; + } } else { - DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)header; - if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) { - os::free(header); - os::close(fd); + if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) { vm_exit_during_initialization("Not a top shared archive", archive_name); return false; } + unsigned int name_size = header->_base_archive_name_size; + unsigned int path_offset = header->_base_archive_path_offset; + unsigned int header_size = header->_header_size; + if (path_offset + name_size != header_size) { + log_info(cds)("_header_size should be equal to _base_archive_path_offset plus _base_archive_name_size"); + log_info(cds)(" _base_archive_name_size = " UINT32_FORMAT, name_size); + log_info(cds)(" _base_archive_path_offset = " UINT32_FORMAT, path_offset); + log_info(cds)(" _header_size = " UINT32_FORMAT, header_size); + return false; + } + char* base_name = NULL; + if (!file_helper.read_base_archive_name(&base_name)) { + return false; + } + FREE_C_HEAP_ARRAY(char, base_name); } - os::free(header); - os::close(fd); return true; } bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name, - int* size, char** base_archive_name) { - int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0); - if (fd < 0) { - *size = 0; + char** base_archive_name) { + FileHeaderHelper file_helper; + if (!file_helper.initialize(archive_name)) { return false; } - - // read the header as a dynamic archive header - size_t sz = sizeof(DynamicArchiveHeader); - DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)os::malloc(sz, mtInternal); - size_t n = os::read(fd, dynamic_header, (unsigned int)sz); - if (n != sz) { - fail_continue("Unable to read the file header."); - os::free(dynamic_header); - os::close(fd); + GenericCDSFileMapHeader* header = file_helper.get_generic_file_header(); + if (header->_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) { + // Not a dynamic header, no need to proceed further. return false; } - if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) { - // Not a dynamic header, no need to proceed further. - *size = 0; - os::free(dynamic_header); - os::close(fd); + + if ((header->_base_archive_name_size == 0 && header->_base_archive_path_offset != 0) || + (header->_base_archive_name_size != 0 && header->_base_archive_path_offset == 0)) { + fail_continue("Default base archive not set correct"); return false; } - if (dynamic_header->base_archive_is_default()) { + if (header->_base_archive_name_size == 0 && + header->_base_archive_path_offset == 0) { *base_archive_name = Arguments::get_default_shared_archive_path(); } else { // read the base archive name - size_t name_size = dynamic_header->base_archive_name_size(); - if (name_size == 0) { - os::free(dynamic_header); - os::close(fd); - return false; - } - *base_archive_name = NEW_C_HEAP_ARRAY(char, name_size, mtInternal); - n = os::read(fd, *base_archive_name, (unsigned int)name_size); - if (n != name_size) { - fail_continue("Unable to read the base archive name from the header."); - FREE_C_HEAP_ARRAY(char, *base_archive_name); + if (!file_helper.read_base_archive_name(base_archive_name)) { *base_archive_name = NULL; - os::free(dynamic_header); - os::close(fd); return false; } } - - os::free(dynamic_header); - os::close(fd); return true; } // Read the FileMapInfo information from the file. bool FileMapInfo::init_from_file(int fd) { - size_t sz = is_static() ? sizeof(FileMapHeader) : sizeof(DynamicArchiveHeader); - size_t n = os::read(fd, header(), (unsigned int)sz); - if (n != sz) { + FileHeaderHelper file_helper; + if (!file_helper.initialize(fd)) { fail_continue("Unable to read the file header."); return false; } - - if (!Arguments::has_jimage()) { - FileMapInfo::fail_continue("The shared archive file cannot be used with an exploded module build."); - return false; - } + GenericCDSFileMapHeader* gen_header = file_helper.get_generic_file_header(); unsigned int expected_magic = is_static() ? CDS_ARCHIVE_MAGIC : CDS_DYNAMIC_ARCHIVE_MAGIC; - if (header()->magic() != expected_magic) { + if (gen_header->_magic != expected_magic) { log_info(cds)("_magic expected: 0x%08x", expected_magic); - log_info(cds)(" actual: 0x%08x", header()->magic()); + log_info(cds)(" actual: 0x%08x", gen_header->_magic); FileMapInfo::fail_continue("The shared archive file has a bad magic number."); return false; } + _header = (FileMapHeader*)os::malloc(gen_header->_header_size, mtInternal); + lseek(fd, 0, SEEK_SET); // reset to begin of the archive + size_t size = gen_header->_header_size; + size_t n = os::read(fd, (void*)_header, (unsigned int)size); + if (n != size) { + fail_continue("Failed to read file header from the top archive file\n"); + return false; + } + if (header()->version() != CURRENT_CDS_ARCHIVE_VERSION) { log_info(cds)("_version expected: %d", CURRENT_CDS_ARCHIVE_VERSION); log_info(cds)(" actual: %d", header()->version()); @@ -1135,11 +1212,17 @@ bool FileMapInfo::init_from_file(int fd) { return false; } - if (header()->header_size() != sz) { - log_info(cds)("_header_size expected: " SIZE_FORMAT, sz); - log_info(cds)(" actual: " SIZE_FORMAT, header()->header_size()); - FileMapInfo::fail_continue("The shared archive file has an incorrect header size."); - return false; + unsigned int base_offset = header()->base_archive_path_offset(); + unsigned int name_size = header()->base_archive_name_size(); + unsigned int header_size = header()->header_size(); + if (base_offset != 0 && name_size != 0) { + if (header_size != base_offset + name_size) { + log_info(cds)("_header_size: " UINT32_FORMAT, header_size); + log_info(cds)("base_archive_name_size: " UINT32_FORMAT, name_size); + log_info(cds)("base_archive_path_offset: " UINT32_FORMAT, base_offset); + FileMapInfo::fail_continue("The shared archive file has an incorrect header size."); + return false; + } } const char* actual_ident = header()->jvm_ident(); @@ -1169,7 +1252,7 @@ bool FileMapInfo::init_from_file(int fd) { } } - _file_offset = n + header()->base_archive_name_size(); // accounts for the size of _base_archive_name + _file_offset = header()->header_size(); // accounts for the size of _base_archive_name if (is_static()) { // just checking the last region is sufficient since the archive is written @@ -1253,16 +1336,12 @@ void FileMapInfo::open_for_write(const char* path) { // Seek past the header. We will write the header after all regions are written // and their CRCs computed. size_t header_bytes = header()->header_size(); - if (header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC) { - header_bytes += strlen(Arguments::GetSharedArchivePath()) + 1; - } header_bytes = align_up(header_bytes, MetaspaceShared::core_region_alignment()); _file_offset = header_bytes; seek_to_position(_file_offset); } - // Write the header to the file, seek to the next allocation boundary. void FileMapInfo::write_header() { @@ -1270,13 +1349,6 @@ void FileMapInfo::write_header() { seek_to_position(_file_offset); assert(is_file_position_aligned(), "must be"); write_bytes(header(), header()->header_size()); - - if (header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC) { - char* base_archive_name = (char*)Arguments::GetSharedArchivePath(); - if (base_archive_name != NULL) { - write_bytes(base_archive_name, header()->base_archive_name_size()); - } - } } size_t FileMapRegion::used_aligned() const { @@ -1597,15 +1669,17 @@ MapArchiveResult FileMapInfo::map_regions(int regions[], int num_regions, char* return MAP_ARCHIVE_SUCCESS; } -bool FileMapInfo::read_region(int i, char* base, size_t size) { +bool FileMapInfo::read_region(int i, char* base, size_t size, bool do_commit) { FileMapRegion* si = space_at(i); - log_info(cds)("Commit %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)%s", - is_static() ? "static " : "dynamic", i, p2i(base), p2i(base + size), - shared_region_name[i], si->allow_exec() ? " exec" : ""); - if (!os::commit_memory(base, size, si->allow_exec())) { - log_error(cds)("Failed to commit %s region #%d (%s)", is_static() ? "static " : "dynamic", - i, shared_region_name[i]); - return false; + if (do_commit) { + log_info(cds)("Commit %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)%s", + is_static() ? "static " : "dynamic", i, p2i(base), p2i(base + size), + shared_region_name[i], si->allow_exec() ? " exec" : ""); + if (!os::commit_memory(base, size, si->allow_exec())) { + log_error(cds)("Failed to commit %s region #%d (%s)", is_static() ? "static " : "dynamic", + i, shared_region_name[i]); + return false; + } } if (lseek(_fd, (long)si->file_offset(), SEEK_SET) != (int)si->file_offset() || read_bytes(base, size) != size) { @@ -1646,7 +1720,7 @@ MapArchiveResult FileMapInfo::map_region(int i, intx addr_delta, char* mapped_ba // that covers all the FileMapRegions to ensure all regions can be mapped. However, Windows // can't mmap into a ReservedSpace, so we just os::read() the data. We're going to patch all the // regions anyway, so there's no benefit for mmap anyway. - if (!read_region(i, requested_addr, size)) { + if (!read_region(i, requested_addr, size, /* do_commit = */ true)) { log_info(cds)("Failed to read %s shared space into reserved space at " INTPTR_FORMAT, shared_region_name[i], p2i(requested_addr)); return MAP_ARCHIVE_OTHER_FAILURE; // oom or I/O error. @@ -1817,7 +1891,7 @@ void FileMapInfo::map_or_load_heap_regions() { } else if (HeapShared::can_load()) { success = HeapShared::load_heap_regions(this); } else { - log_info(cds)("Cannot use CDS heap data. UseG1GC or UseEpsilonGC are required."); + log_info(cds)("Cannot use CDS heap data. UseEpsilonGC, UseG1GC or UseSerialGC are required."); } } @@ -2218,6 +2292,11 @@ bool FileMapInfo::initialize() { return false; } + if (!Arguments::has_jimage()) { + FileMapInfo::fail_continue("The shared archive file cannot be used with an exploded module build."); + return false; + } + if (!open_for_read()) { return false; } @@ -2256,9 +2335,9 @@ void FileMapHeader::set_as_offset(char* p, size_t *offset) { int FileMapHeader::compute_crc() { char* start = (char*)this; - // start computing from the field after _crc - char* buf = (char*)&_crc + sizeof(_crc); - size_t sz = _header_size - (buf - start); + // start computing from the field after _crc to end of base archive name. + char* buf = (char*)&(_generic_header._crc) + sizeof(_generic_header._crc); + size_t sz = header_size() - (buf - start); int crc = ClassLoader::crc32(0, buf, (jint)sz); return crc; } diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index bf6a724f2fbd2bcdd0440d648ef510c5104cf2e2..7708bbe7fa585a12d90c40e9071d702b155ceb33 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -184,8 +184,6 @@ class FileMapHeader: private CDSFileMapHeaderBase { friend class VMStructs; private: - size_t _header_size; - // The following fields record the states of the VM during dump time. // They are compared with the runtime states to see if the archive // can be used. @@ -203,15 +201,12 @@ private: size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize() address _heap_begin; // heap begin at dump time. address _heap_end; // heap end at dump time. - bool _base_archive_is_default; // indicates if the base archive is the system default one bool _has_non_jar_in_classpath; // non-jar file entry exists in classpath // The following fields are all sanity checks for whether this archive // will function correctly with this JVM and the bootclasspath it's // invoked with. char _jvm_ident[JVM_IDENT_MAX]; // identifier string of the jvm that created this dump - // size of the base archive name including NULL terminator - size_t _base_archive_name_size; // The following is a table of all the boot/app/module path entries that were used // during dumping. At run time, we validate these entries according to their @@ -243,17 +238,21 @@ private: } void set_as_offset(char* p, size_t *offset); public: - // Accessors -- fields declared in CDSFileMapHeaderBase - unsigned int magic() const { return _magic; } - int crc() const { return _crc; } - int version() const { return _version; } - - void set_crc(int crc_value) { _crc = crc_value; } - void set_version(int v) { _version = v; } - - // Accessors -- fields declared in FileMapHeader + // Accessors -- fields declared in GenericCDSFileMapHeader + unsigned int magic() const { return _generic_header._magic; } + int crc() const { return _generic_header._crc; } + int version() const { return _generic_header._version; } + unsigned int header_size() const { return _generic_header._header_size; } + unsigned int base_archive_path_offset() const { return _generic_header._base_archive_path_offset; } + unsigned int base_archive_name_size() const { return _generic_header._base_archive_name_size; } + + void set_magic(unsigned int m) { _generic_header._magic = m; } + void set_crc(int crc_value) { _generic_header._crc = crc_value; } + void set_version(int v) { _generic_header._version = v; } + void set_header_size(unsigned int s) { _generic_header._header_size = s; } + void set_base_archive_path_offset(unsigned int s) { _generic_header._base_archive_path_offset = s; } + void set_base_archive_name_size(unsigned int s) { _generic_header._base_archive_name_size = s; } - size_t header_size() const { return _header_size; } size_t core_region_alignment() const { return _core_region_alignment; } int obj_alignment() const { return _obj_alignment; } address narrow_oop_base() const { return _narrow_oop_base; } @@ -267,9 +266,7 @@ public: char* serialized_data() const { return from_mapped_offset(_serialized_data_offset); } address heap_begin() const { return _heap_begin; } address heap_end() const { return _heap_end; } - bool base_archive_is_default() const { return _base_archive_is_default; } const char* jvm_ident() const { return _jvm_ident; } - size_t base_archive_name_size() const { return _base_archive_name_size; } char* requested_base_address() const { return _requested_base_address; } char* mapped_base_address() const { return _mapped_base_address; } bool has_platform_or_app_classes() const { return _has_platform_or_app_classes; } @@ -287,12 +284,10 @@ public: void set_has_platform_or_app_classes(bool v) { _has_platform_or_app_classes = v; } void set_cloned_vtables(char* p) { set_as_offset(p, &_cloned_vtables_offset); } void set_serialized_data(char* p) { set_as_offset(p, &_serialized_data_offset); } - void set_base_archive_name_size(size_t s) { _base_archive_name_size = s; } - void set_base_archive_is_default(bool b) { _base_archive_is_default = b; } - void set_header_size(size_t s) { _header_size = s; } void set_ptrmap_size_in_bits(size_t s) { _ptrmap_size_in_bits = s; } void set_mapped_base_address(char* p) { _mapped_base_address = p; } void set_heap_obj_roots(narrowOop r) { _heap_obj_roots = r; } + void copy_base_archive_name(const char* name); void set_shared_path_table(SharedPathTable table) { set_as_offset((char*)table.table(), &_shared_path_table_offset); @@ -317,8 +312,8 @@ public: return FileMapRegion::cast(&_space[i]); } - void populate(FileMapInfo* info, size_t core_region_alignment); - + void populate(FileMapInfo *info, size_t core_region_alignment, size_t header_size, + size_t base_archive_name_size, size_t base_archive_path_offset); static bool is_valid_region(int region) { return (0 <= region && region < NUM_CDS_REGIONS); } @@ -363,7 +358,7 @@ private: public: static bool get_base_archive_name_from_header(const char* archive_name, - int* size, char** base_archive_name); + char** base_archive_name); static bool check_archive(const char* archive_name, bool is_static); static SharedPathTable shared_path_table() { return _shared_path_table; @@ -398,9 +393,6 @@ public: int narrow_klass_shift() const { return header()->narrow_klass_shift(); } size_t core_region_alignment() const { return header()->core_region_alignment(); } - void set_header_base_archive_name_size(size_t size) { header()->set_base_archive_name_size(size); } - void set_header_base_archive_is_default(bool is_default) { header()->set_base_archive_is_default(is_default); } - CompressedOops::Mode narrow_oop_mode() const { return header()->narrow_oop_mode(); } jshort app_module_paths_start_index() const { return header()->app_module_paths_start_index(); } jshort app_class_paths_start_index() const { return header()->app_class_paths_start_index(); } @@ -477,7 +469,7 @@ public: int first_region_idx) NOT_CDS_JAVA_HEAP_RETURN; bool has_heap_regions() NOT_CDS_JAVA_HEAP_RETURN_(false); MemRegion get_heap_regions_range_with_current_oop_encoding_mode() NOT_CDS_JAVA_HEAP_RETURN_(MemRegion()); - bool read_region(int i, char* base, size_t size); + bool read_region(int i, char* base, size_t size, bool do_commit); char* map_bitmap_region(); void unmap_region(int i); bool verify_region_checksum(int i); diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 821e6594faa7b0148b34544baac98aeb7523404b..aa4694168c12e4ec25ce164670b5bf8d8be4d6a0 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -429,7 +429,7 @@ void HeapShared::copy_open_objects(GrowableArray* open_regions) { // Copy _pending_archive_roots into an objArray void HeapShared::copy_roots() { int length = _pending_roots != NULL ? _pending_roots->length() : 0; - int size = objArrayOopDesc::object_size(length); + size_t size = objArrayOopDesc::object_size(length); Klass* k = Universe::objectArrayKlassObj(); // already relocated to point to archived klass HeapWord* mem = G1CollectedHeap::heap()->archive_mem_allocate(size); @@ -448,7 +448,7 @@ void HeapShared::copy_roots() { for (int i = 0; i < length; i++) { roots()->obj_at_put(i, _pending_roots->at(i)); } - log_info(cds)("archived obj roots[%d] = %d words, klass = %p, obj = %p", length, size, k, mem); + log_info(cds)("archived obj roots[%d] = " SIZE_FORMAT " words, klass = %p, obj = %p", length, size, k, mem); } void HeapShared::init_narrow_oop_decoding(address base, int shift) { @@ -912,7 +912,7 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure { if (!_record_klasses_only && log_is_enabled(Debug, cds, heap)) { ResourceMark rm; - log_debug(cds, heap)("(%d) %s[" SIZE_FORMAT "] ==> " PTR_FORMAT " size %d %s", _level, + log_debug(cds, heap)("(%d) %s[" SIZE_FORMAT "] ==> " PTR_FORMAT " size " SIZE_FORMAT " %s", _level, _orig_referencing_obj->klass()->external_name(), field_delta, p2i(obj), obj->size() * HeapWordSize, obj->klass()->external_name()); LogTarget(Trace, cds, heap) log; @@ -1023,7 +1023,7 @@ oop HeapShared::archive_reachable_objects_from(int level, ResourceMark rm; log_error(cds, heap)( "Cannot archive the sub-graph referenced from %s object (" - PTR_FORMAT ") size %d, skipped.", + PTR_FORMAT ") size " SIZE_FORMAT ", skipped.", orig_obj->klass()->external_name(), p2i(orig_obj), orig_obj->size() * HeapWordSize); if (level == 1) { // Don't archive a subgraph root that's too big. For archives static fields, that's OK @@ -1586,7 +1586,7 @@ class PatchLoadedRegionPointers: public BitMapClosure { }; int HeapShared::init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions, - uintptr_t* buffer_ret) { + MemRegion& archive_space) { size_t total_bytes = 0; int num_loaded_regions = 0; for (int i = MetaspaceShared::first_archive_heap_region; @@ -1604,12 +1604,16 @@ int HeapShared::init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegio } assert(is_aligned(total_bytes, HeapWordSize), "must be"); - uintptr_t buffer = (uintptr_t) - Universe::heap()->allocate_loaded_archive_space(total_bytes / HeapWordSize); - _loaded_heap_bottom = buffer; - _loaded_heap_top = buffer + total_bytes; + size_t word_size = total_bytes / HeapWordSize; + HeapWord* buffer = Universe::heap()->allocate_loaded_archive_space(word_size); + if (buffer == nullptr) { + return 0; + } + + archive_space = MemRegion(buffer, word_size); + _loaded_heap_bottom = (uintptr_t)archive_space.start(); + _loaded_heap_top = _loaded_heap_bottom + total_bytes; - *buffer_ret = buffer; return num_loaded_regions; } @@ -1638,15 +1642,17 @@ bool HeapShared::load_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loa LoadedArchiveHeapRegion* ri = &loaded_regions[i]; FileMapRegion* r = mapinfo->space_at(ri->_region_index); - if (!mapinfo->read_region(ri->_region_index, (char*)load_address, r->used())) { + if (!mapinfo->read_region(ri->_region_index, (char*)load_address, r->used(), /* do_commit = */ false)) { // There's no easy way to free the buffer, so we will fill it with zero later // in fill_failed_loaded_region(), and it will eventually be GC'ed. log_warning(cds)("Loading of heap region %d has failed. Archived objects are disabled", i); _loading_failed = true; return false; } - log_info(cds)("Loaded heap region #%d at base " INTPTR_FORMAT " size = " SIZE_FORMAT_W(8) " bytes, delta = " INTX_FORMAT, - ri->_region_index, load_address, ri->_region_size, ri->_runtime_offset); + log_info(cds)("Loaded heap region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT + " size " SIZE_FORMAT_W(6) " delta " INTX_FORMAT, + ri->_region_index, load_address, load_address + ri->_region_size, + ri->_region_size, ri->_runtime_offset); uintptr_t oopmap = bitmap_base + r->oopmap_offset(); BitMapView bm((BitMap::bm_word_t*)oopmap, r->oopmap_size_in_bits()); @@ -1675,10 +1681,14 @@ bool HeapShared::load_heap_regions(FileMapInfo* mapinfo) { LoadedArchiveHeapRegion loaded_regions[MetaspaceShared::max_num_heap_regions]; memset(loaded_regions, 0, sizeof(loaded_regions)); - uintptr_t buffer; - int num_loaded_regions = init_loaded_regions(mapinfo, loaded_regions, &buffer); - sort_loaded_regions(loaded_regions, num_loaded_regions, buffer); - if (!load_regions(mapinfo, loaded_regions, num_loaded_regions, buffer)) { + MemRegion archive_space; + int num_loaded_regions = init_loaded_regions(mapinfo, loaded_regions, archive_space); + if (num_loaded_regions <= 0) { + return false; + } + sort_loaded_regions(loaded_regions, num_loaded_regions, (uintptr_t)archive_space.start()); + if (!load_regions(mapinfo, loaded_regions, num_loaded_regions, (uintptr_t)archive_space.start())) { + assert(_loading_failed, "must be"); return false; } @@ -1711,7 +1721,15 @@ class VerifyLoadedHeapEmbeddedPointers: public BasicOopIterateClosure { } }; -void HeapShared::verify_loaded_heap() { +void HeapShared::finish_initialization() { + if (is_loaded()) { + HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; + HeapWord* top = (HeapWord*)_loaded_heap_top; + + MemRegion archive_space = MemRegion(bottom, top); + Universe::heap()->complete_loaded_archive_space(archive_space); + } + if (VerifyArchivedFields <= 0 || !is_loaded()) { return; } @@ -1739,9 +1757,12 @@ void HeapShared::verify_loaded_heap() { void HeapShared::fill_failed_loaded_region() { assert(_loading_failed, "must be"); - HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; - HeapWord* top = (HeapWord*)_loaded_heap_top; - Universe::heap()->fill_with_objects(bottom, top - bottom); + if (_loaded_heap_bottom != 0) { + assert(_loaded_heap_top != 0, "must be"); + HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; + HeapWord* top = (HeapWord*)_loaded_heap_top; + Universe::heap()->fill_with_objects(bottom, top - bottom); + } } #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 2809825a204b3726b22be610f89ee91c6a98484c..0a673c51e864f7cd3d668f6e65d4f8d154848a17 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -170,7 +170,7 @@ public: // Can this VM load the objects from archived heap regions into the heap at start-up? static bool can_load() NOT_CDS_JAVA_HEAP_RETURN_(false); - static void verify_loaded_heap() NOT_CDS_JAVA_HEAP_RETURN; + static void finish_initialization() NOT_CDS_JAVA_HEAP_RETURN; static bool is_loaded() { CDS_JAVA_HEAP_ONLY(return _is_loaded;) NOT_CDS_JAVA_HEAP(return false;) @@ -346,7 +346,7 @@ private: static void init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record); static int init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions, - uintptr_t* buffer_ret); + MemRegion& archive_space); static void sort_loaded_regions(LoadedArchiveHeapRegion* loaded_regions, int num_loaded_regions, uintptr_t buffer); static bool load_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions, diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index a884cbd0c001f1822efb5b2f3db2233de9274ac4..5dd296410c783adfd41f99fba6b2421734f22a79 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -521,6 +521,7 @@ void VM_PopulateDumpSharedSpace::doit() { // Block concurrent class unloading from changing the _dumptime_table MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); SystemDictionaryShared::check_excluded_classes(); + SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary(); StaticArchiveBuilder builder; builder.gather_source_objs(); @@ -584,20 +585,19 @@ void VM_PopulateDumpSharedSpace::doit() { class CollectCLDClosure : public CLDClosure { GrowableArray _loaded_cld; - GrowableArray _loaded_cld_handles; // keep the CLDs alive + GrowableArray _loaded_cld_handles; // keep the CLDs alive Thread* _current_thread; public: CollectCLDClosure(Thread* thread) : _current_thread(thread) {} ~CollectCLDClosure() { - for (int i = 0; i < _loaded_cld.length(); i++) { - ClassLoaderData* cld = _loaded_cld.at(i); + for (int i = 0; i < _loaded_cld_handles.length(); i++) { + _loaded_cld_handles.at(i).release(Universe::vm_global()); } } void do_cld(ClassLoaderData* cld) { - if (!cld->is_unloading()) { - _loaded_cld.append(cld); - _loaded_cld_handles.append(Handle(_current_thread, cld->holder_phantom())); - } + assert(cld->is_alive(), "must be"); + _loaded_cld.append(cld); + _loaded_cld_handles.append(OopHandle(Universe::vm_global(), cld->holder_phantom())); } int nof_cld() const { return _loaded_cld.length(); } @@ -1434,7 +1434,7 @@ void MetaspaceShared::initialize_shared_spaces() { // Finish up archived heap initialization. These must be // done after ReadClosure. static_mapinfo->patch_heap_embedded_pointers(); - HeapShared::verify_loaded_heap(); + HeapShared::finish_initialization(); // Close the mapinfo file static_mapinfo->close(); diff --git a/src/hotspot/share/cds/unregisteredClasses.cpp b/src/hotspot/share/cds/unregisteredClasses.cpp index c17c35431096acc0ec1c09bccdb58e576eca346b..c0b20e9a9664b265e5274b7243823e4477b16002 100644 --- a/src/hotspot/share/cds/unregisteredClasses.cpp +++ b/src/hotspot/share/cds/unregisteredClasses.cpp @@ -34,7 +34,8 @@ #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" #include "oops/instanceKlass.hpp" -#include "runtime/handles.hpp" +#include "oops/oopHandle.inline.hpp" +#include "runtime/handles.inline.hpp" #include "runtime/javaCalls.hpp" #include "services/threadService.hpp" @@ -72,7 +73,7 @@ InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path, T } class URLClassLoaderTable : public ResourceHashtable< - Symbol*, Handle, + Symbol*, OopHandle, 137, // prime number ResourceObj::C_HEAP> {}; @@ -103,12 +104,12 @@ Handle UnregisteredClasses::get_url_classloader(Symbol* path, TRAPS) { if (_url_classloader_table == NULL) { _url_classloader_table = new (ResourceObj::C_HEAP, mtClass)URLClassLoaderTable(); } - Handle* url_classloader_ptr = _url_classloader_table->get(path); + OopHandle* url_classloader_ptr = _url_classloader_table->get(path); if (url_classloader_ptr != NULL) { - return *url_classloader_ptr; + return Handle(THREAD, (*url_classloader_ptr).resolve()); } else { Handle url_classloader = create_url_classloader(path, CHECK_NH); - _url_classloader_table->put(path, url_classloader); + _url_classloader_table->put(path, OopHandle(Universe::vm_global(), url_classloader())); path->increment_refcount(); return url_classloader; } diff --git a/src/hotspot/share/ci/ciArrayKlass.hpp b/src/hotspot/share/ci/ciArrayKlass.hpp index f801c4399e9102ccc48fa945227b02807c06258f..a64d80b20f3f46bea7b02300e78d13a49715efde 100644 --- a/src/hotspot/share/ci/ciArrayKlass.hpp +++ b/src/hotspot/share/ci/ciArrayKlass.hpp @@ -54,7 +54,6 @@ public: // What kind of vmObject is this? bool is_array_klass() const { return true; } - bool is_java_klass() const { return true; } static ciArrayKlass* make(ciType* element_type); }; diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp index 443f5a0d1b073f8fa4c07a3234abad1c3335e43c..1d58cdb95250dbf7808d6ee6abfda8ebfbc2561c 100644 --- a/src/hotspot/share/ci/ciEnv.cpp +++ b/src/hotspot/share/ci/ciEnv.cpp @@ -636,8 +636,15 @@ ciKlass* ciEnv::get_klass_by_index_impl(const constantPoolHandle& cpool, } // It is known to be accessible, since it was found in the constant pool. + ciKlass* ciKlass = get_klass(klass); is_accessible = true; - return get_klass(klass); +#ifndef PRODUCT + if (ReplayCompiles && ciKlass == _unloaded_ciinstance_klass) { + // Klass was unresolved at replay dump time and therefore not accessible. + is_accessible = false; + } +#endif + return ciKlass; } // ------------------------------------------------------------------ diff --git a/src/hotspot/share/ci/ciEnv.hpp b/src/hotspot/share/ci/ciEnv.hpp index c027f15e776fd59f22c3fb433f6263c6448a5cd5..32dc5f51eaf9caaaad4c842e6fe441c52f81a841 100644 --- a/src/hotspot/share/ci/ciEnv.hpp +++ b/src/hotspot/share/ci/ciEnv.hpp @@ -27,6 +27,7 @@ #include "ci/ciClassList.hpp" #include "ci/ciObjectFactory.hpp" +#include "ci/ciReplay.hpp" #include "classfile/vmClassMacros.hpp" #include "code/debugInfoRec.hpp" #include "code/dependencies.hpp" @@ -187,6 +188,15 @@ private: if (o == NULL) { return NULL; } else { +#ifndef PRODUCT + if (ReplayCompiles && o->is_klass()) { + Klass* k = (Klass*)o; + if (k->is_instance_klass() && ciReplay::is_klass_unresolved((InstanceKlass*)k)) { + // Klass was unresolved at replay dump time. Simulate this case. + return ciEnv::_unloaded_ciinstance_klass; + } + } +#endif return _factory->get_metadata(o); } } diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp index 8557410f956a6a14c9eb6def1db7df5eab800f3b..cc361744149b5a45bb71f0a7a3e17bda1a28f78c 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.cpp +++ b/src/hotspot/share/ci/ciInstanceKlass.cpp @@ -63,7 +63,6 @@ ciInstanceKlass::ciInstanceKlass(Klass* k) : _has_finalizer = access_flags.has_finalizer(); _has_subklass = flags().is_final() ? subklass_false : subklass_unknown; _init_state = ik->init_state(); - _nonstatic_field_size = ik->nonstatic_field_size(); _has_nonstatic_fields = ik->has_nonstatic_fields(); _has_nonstatic_concrete_methods = ik->has_nonstatic_concrete_methods(); _is_hidden = ik->is_hidden(); @@ -123,7 +122,6 @@ ciInstanceKlass::ciInstanceKlass(ciSymbol* name, { assert(name->char_at(0) != JVM_SIGNATURE_ARRAY, "not an instance klass"); _init_state = (InstanceKlass::ClassState)0; - _nonstatic_field_size = -1; _has_nonstatic_fields = false; _nonstatic_fields = NULL; _has_injected_fields = -1; @@ -496,9 +494,6 @@ int ciInstanceKlass::compute_nonstatic_fields() { } assert(!is_java_lang_Object(), "bootstrap OK"); - // Size in bytes of my fields, including inherited fields. - int fsize = nonstatic_field_size() * heapOopSize; - ciInstanceKlass* super = this->super(); GrowableArray* super_fields = NULL; if (super != NULL && super->has_nonstatic_fields()) { @@ -707,7 +702,7 @@ class StaticFinalFieldPrinter : public FieldClosure { assert(fd->field_type() == T_OBJECT, ""); if (value->is_a(vmClasses::String_klass())) { const char* ascii_value = java_lang_String::as_quoted_ascii(value); - _out->print("\"%s\"", (ascii_value != NULL) ? ascii_value : ""); + _out->print_cr("\"%s\"", (ascii_value != NULL) ? ascii_value : ""); } else { const char* klass_name = value->klass()->name()->as_quoted_ascii(); _out->print_cr("%s", klass_name); diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp index 6ea4b4f1a4862461bc7de357479357c357cefaa0..3e79198eddbe1a5a1e4136b36d1b14fb9661d862 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.hpp +++ b/src/hotspot/share/ci/ciInstanceKlass.hpp @@ -43,6 +43,7 @@ class ciInstanceKlass : public ciKlass { friend class ciExceptionHandler; friend class ciMethod; friend class ciField; + friend class ciReplay; private: enum SubklassValue { subklass_unknown, subklass_false, subklass_true }; @@ -60,8 +61,6 @@ private: bool _is_record; ciFlags _flags; - jint _nonstatic_field_size; - jint _nonstatic_oop_map_size; // Lazy fields get filled in only upon request. ciInstanceKlass* _super; @@ -172,15 +171,9 @@ public: return (Klass::layout_helper_size_in_bytes(layout_helper()) >> LogHeapWordSize); } - jint nonstatic_field_size() { - assert(is_loaded(), "must be loaded"); - return _nonstatic_field_size; } jint has_nonstatic_fields() { assert(is_loaded(), "must be loaded"); return _has_nonstatic_fields; } - jint nonstatic_oop_map_size() { - assert(is_loaded(), "must be loaded"); - return _nonstatic_oop_map_size; } ciInstanceKlass* super(); jint nof_implementors() { ciInstanceKlass* impl; @@ -282,7 +275,6 @@ public: // What kind of ciObject is this? bool is_instance_klass() const { return true; } - bool is_java_klass() const { return true; } virtual ciKlass* exact_klass() { if (is_loaded() && is_final() && !is_interface()) { diff --git a/src/hotspot/share/ci/ciObjectFactory.cpp b/src/hotspot/share/ci/ciObjectFactory.cpp index 979136316e957d2c2eec6032363a5fa8766f663e..0f53c9957ce66bcec084643479a05ca583450f5c 100644 --- a/src/hotspot/share/ci/ciObjectFactory.cpp +++ b/src/hotspot/share/ci/ciObjectFactory.cpp @@ -378,6 +378,7 @@ ciMetadata* ciObjectFactory::create_new_metadata(Metadata* o) { if (o->is_klass()) { Klass* k = (Klass*)o; if (k->is_instance_klass()) { + assert(!ReplayCompiles || ciReplay::no_replay_state() || !ciReplay::is_klass_unresolved((InstanceKlass*)k), "must be whitelisted for replay compilation"); return new (arena()) ciInstanceKlass(k); } else if (k->is_objArray_klass()) { return new (arena()) ciObjArrayKlass(k); diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp index 96475184b684332eb1a1a71136f9209573a07c1b..90db561dece84e0bc2aabc481f7cb2b1f502162b 100644 --- a/src/hotspot/share/ci/ciReplay.cpp +++ b/src/hotspot/share/ci/ciReplay.cpp @@ -49,6 +49,7 @@ #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/jniHandles.inline.hpp" #include "runtime/java.hpp" #include "utilities/copy.hpp" #include "utilities/macros.hpp" @@ -90,6 +91,11 @@ typedef struct _ciMethodRecord { int _backedge_counter; } ciMethodRecord; +typedef struct _ciInstanceKlassRecord { + const InstanceKlass* _klass; + jobject _java_mirror; // Global handle to java mirror to prevent unloading +} ciInstanceKlassRecord; + typedef struct _ciInlineRecord { const char* _klass_name; const char* _method_name; @@ -111,6 +117,7 @@ class CompileReplay : public StackObj { GrowableArray _ci_method_records; GrowableArray _ci_method_data_records; + GrowableArray _ci_instance_klass_records; // Use pointer because we may need to return inline records // without destroying them. @@ -121,7 +128,6 @@ class CompileReplay : public StackObj { char* _bufptr; char* _buffer; int _buffer_length; - int _buffer_pos; // "compile" data ciKlass* _iklass; @@ -146,7 +152,6 @@ class CompileReplay : public StackObj { _buffer_length = 32; _buffer = NEW_RESOURCE_ARRAY(char, _buffer_length); _bufptr = _buffer; - _buffer_pos = 0; _imethod = NULL; _iklass = NULL; @@ -182,10 +187,6 @@ class CompileReplay : public StackObj { void report_error(const char* msg) { _error_message = msg; - // Restore the _buffer contents for error reporting - for (int i = 0; i < _buffer_pos; i++) { - if (_buffer[i] == '\0') _buffer[i] = ' '; - } } int parse_int(const char* label) { @@ -225,6 +226,10 @@ class CompileReplay : public StackObj { } } + // Ignore the rest of the line + void skip_remaining() { + _bufptr = &_bufptr[strlen(_bufptr)]; // skip ahead to terminator + } char* scan_and_terminate(char delim) { char* str = _bufptr; @@ -574,8 +579,9 @@ class CompileReplay : public StackObj { } int get_line(int c) { + int buffer_pos = 0; while(c != EOF) { - if (_buffer_pos + 1 >= _buffer_length) { + if (buffer_pos + 1 >= _buffer_length) { int new_length = _buffer_length * 2; // Next call will throw error in case of OOM. _buffer = REALLOC_RESOURCE_ARRAY(char, _buffer, _buffer_length, new_length); @@ -587,13 +593,12 @@ class CompileReplay : public StackObj { } else if (c == '\r') { // skip LF } else { - _buffer[_buffer_pos++] = c; + _buffer[buffer_pos++] = c; } c = getc(_stream); } // null terminate it, reset the pointer - _buffer[_buffer_pos] = '\0'; // NL or EOF - _buffer_pos = 0; + _buffer[buffer_pos] = '\0'; // NL or EOF _bufptr = _buffer; return c; } @@ -607,7 +612,8 @@ class CompileReplay : public StackObj { c = get_line(c); process_command(THREAD); if (had_error()) { - tty->print_cr("Error while parsing line %d: %s\n", line_no, _error_message); + int pos = _bufptr - _buffer + 1; + tty->print_cr("Error while parsing line %d at position %d: %s\n", line_no, pos, _error_message); if (ReplayIgnoreInitErrors) { CLEAR_PENDING_EXCEPTION; _error_message = NULL; @@ -625,7 +631,11 @@ class CompileReplay : public StackObj { return; } if (strcmp("#", cmd) == 0) { - // ignore + // comment line, print or ignore + if (Verbose) { + tty->print_cr("# %s", _bufptr); + } + skip_remaining(); } else if (strcmp("compile", cmd) == 0) { process_compile(CHECK); } else if (strcmp("ciMethod", cmd) == 0) { @@ -645,6 +655,9 @@ class CompileReplay : public StackObj { } else { report_error("unknown command"); } + if (!had_error() && *_bufptr != '\0') { + report_error("line not properly terminated"); + } } // validation of comp_level @@ -870,9 +883,13 @@ class CompileReplay : public StackObj { report_error("hidden class with comment expected"); return; } - if (is_comment && Verbose) { - const char* hidden = parse_string(); - tty->print_cr("Found %s for %s", k->name()->as_quoted_ascii(), hidden); + // comment, print or ignore + if (is_comment) { + if (Verbose) { + const char* hidden = parse_string(); + tty->print_cr("Found %s for %s", k->name()->as_quoted_ascii(), hidden); + } + skip_remaining(); } } @@ -882,7 +899,7 @@ class CompileReplay : public StackObj { // constant pool is the same length as 'length' and make sure the // constant pool tags are in the same state. void process_ciInstanceKlass(TRAPS) { - InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK); + InstanceKlass* k = (InstanceKlass*)parse_klass(CHECK); if (k == NULL) { return; } @@ -905,6 +922,7 @@ class CompileReplay : public StackObj { } else if (is_linked) { k->link_class(CHECK); } + new_ciInstanceKlass(k); ConstantPool* cp = k->constants(); if (length != cp->length()) { report_error("constant pool length mismatch: wrong class files?"); @@ -951,10 +969,10 @@ class CompileReplay : public StackObj { break; case JVM_CONSTANT_Class: - if (tag == JVM_CONSTANT_Class) { - } else if (tag == JVM_CONSTANT_UnresolvedClass) { - tty->print_cr("Warning: entry was unresolved in the replay data"); - } else { + if (tag == JVM_CONSTANT_UnresolvedClass) { + Klass* k = cp->klass_at(i, CHECK); + tty->print_cr("Warning: entry was unresolved in the replay data: %s", k->name()->as_utf8()); + } else if (tag != JVM_CONSTANT_Class) { report_error("Unexpected tag"); return; } @@ -982,6 +1000,7 @@ class CompileReplay : public StackObj { if (k == NULL || ReplaySuppressInitializers == 0 || (ReplaySuppressInitializers == 2 && k->class_loader() == NULL)) { + skip_remaining(); return; } @@ -1132,6 +1151,28 @@ class CompileReplay : public StackObj { return NULL; } + // Create and initialize a record for a ciInstanceKlass which was present at replay dump time. + void new_ciInstanceKlass(const InstanceKlass* klass) { + ciInstanceKlassRecord* rec = NEW_RESOURCE_OBJ(ciInstanceKlassRecord); + rec->_klass = klass; + oop java_mirror = klass->java_mirror(); + Handle h_java_mirror(_thread, java_mirror); + rec->_java_mirror = JNIHandles::make_global(h_java_mirror); + _ci_instance_klass_records.append(rec); + } + + // Check if a ciInstanceKlass was present at replay dump time for a klass. + ciInstanceKlassRecord* find_ciInstanceKlass(const InstanceKlass* klass) { + for (int i = 0; i < _ci_instance_klass_records.length(); i++) { + ciInstanceKlassRecord* rec = _ci_instance_klass_records.at(i); + if (klass == rec->_klass) { + // ciInstanceKlass for this klass was resolved. + return rec; + } + } + return NULL; + } + // Create and initialize a record for a ciMethodData ciMethodDataRecord* new_ciMethodData(Method* method) { ciMethodDataRecord* rec = NEW_RESOURCE_OBJ(ciMethodDataRecord); @@ -1265,6 +1306,10 @@ void ciReplay::replay(TRAPS) { vm_exit(exit_code); } +bool ciReplay::no_replay_state() { + return replay_state == NULL; +} + void* ciReplay::load_inline_data(ciMethod* method, int entry_bci, int comp_level) { if (FLAG_IS_DEFAULT(InlineDataFile)) { tty->print_cr("ERROR: no inline replay data file specified (use -XX:InlineDataFile=inline_pid12345.txt)."); @@ -1336,7 +1381,7 @@ int ciReplay::replay_impl(TRAPS) { } void ciReplay::initialize(ciMethodData* m) { - if (replay_state == NULL) { + if (no_replay_state()) { return; } @@ -1390,7 +1435,7 @@ void ciReplay::initialize(ciMethodData* m) { bool ciReplay::should_not_inline(ciMethod* method) { - if (replay_state == NULL) { + if (no_replay_state()) { return false; } VM_ENTRY_MARK; @@ -1427,7 +1472,7 @@ bool ciReplay::should_not_inline(void* data, ciMethod* method, int bci, int inli } void ciReplay::initialize(ciMethod* m) { - if (replay_state == NULL) { + if (no_replay_state()) { return; } @@ -1456,8 +1501,17 @@ void ciReplay::initialize(ciMethod* m) { } } +void ciReplay::initialize(ciInstanceKlass* ci_ik, InstanceKlass* ik) { + assert(!no_replay_state(), "must have replay state"); + + ASSERT_IN_VM; + ciInstanceKlassRecord* rec = replay_state->find_ciInstanceKlass(ik); + assert(rec != NULL, "ciInstanceKlass must be whitelisted"); + ci_ik->_java_mirror = CURRENT_ENV->get_instance(JNIHandles::resolve(rec->_java_mirror)); +} + bool ciReplay::is_loaded(Method* method) { - if (replay_state == NULL) { + if (no_replay_state()) { return true; } @@ -1467,6 +1521,16 @@ bool ciReplay::is_loaded(Method* method) { ciMethodRecord* rec = replay_state->find_ciMethodRecord(method); return rec != NULL; } + +bool ciReplay::is_klass_unresolved(const InstanceKlass* klass) { + if (no_replay_state()) { + return false; + } + + // Check if klass is found on whitelist. + ciInstanceKlassRecord* rec = replay_state->find_ciInstanceKlass(klass); + return rec == NULL; +} #endif // PRODUCT oop ciReplay::obj_field(oop obj, Symbol* name) { diff --git a/src/hotspot/share/ci/ciReplay.hpp b/src/hotspot/share/ci/ciReplay.hpp index b63a62109f905451a8adb1fb541598e4b39c5c1a..e7bd626fea592097192ecdf636f7d7b5d79030fb 100644 --- a/src/hotspot/share/ci/ciReplay.hpp +++ b/src/hotspot/share/ci/ciReplay.hpp @@ -106,6 +106,7 @@ class ciReplay { public: // Replay specified compilation and exit VM. static void replay(TRAPS); + static bool no_replay_state(); // Load inlining decisions from file and use them // during compilation of specified method. static void* load_inline_data(ciMethod* method, int entry_bci, int comp_level); @@ -114,7 +115,9 @@ class ciReplay { // replay file when replaying compiles. static void initialize(ciMethodData* method); static void initialize(ciMethod* method); + static void initialize(ciInstanceKlass* ciKlass, InstanceKlass* ik); + static bool is_klass_unresolved(const InstanceKlass* klass); static bool is_loaded(Method* method); static bool should_not_inline(ciMethod* method); diff --git a/src/hotspot/share/ci/ciStreams.hpp b/src/hotspot/share/ci/ciStreams.hpp index 84f12952da5f896838c2a22ebafca5656555ea69..8f46510a0d288182e813e9961d4f83c315b495b8 100644 --- a/src/hotspot/share/ci/ciStreams.hpp +++ b/src/hotspot/share/ci/ciStreams.hpp @@ -203,11 +203,13 @@ public: } // For a lookup or switch table, return target destination - int get_int_table( int index ) const { - return Bytes::get_Java_u4((address)&_table_base[index]); } + jint get_int_table( int index ) const { + return (jint)Bytes::get_Java_u4((address)&_table_base[index]); + } int get_dest_table( int index ) const { - return cur_bci() + get_int_table(index); } + return cur_bci() + get_int_table(index); + } // --- Constant pool access --- int get_constant_raw_index() const; diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 6fad53908fa28fefeef5550610c36752f1142f97..8827f0542b5ebd96bb51604435ba7e0d6788ef79 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -3141,6 +3141,13 @@ u2 ClassFileParser::parse_classfile_inner_classes_attribute(const ClassFileStrea valid_klass_reference_at(outer_class_info_index), "outer_class_info_index %u has bad constant type in class file %s", outer_class_info_index, CHECK_0); + + if (outer_class_info_index != 0) { + const Symbol* const outer_class_name = cp->klass_name_at(outer_class_info_index); + char* bytes = (char*)outer_class_name->bytes(); + guarantee_property(bytes[0] != JVM_SIGNATURE_ARRAY, + "Outer class is an array class in class file %s", CHECK_0); + } // Inner class name const u2 inner_name_index = cfs->get_u2_fast(); check_property( diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index c5e7da83a76393798cd795b53827a3e7ab8d672f..cf5ede2415fbcb41bd25eb3b188fdd2c7b914606 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -1138,7 +1138,7 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR const char* const class_name = name->as_C_string(); - EventMark m("loading class %s", class_name); + EventMarkClassLoading m("Loading class %s", class_name); const char* const file_name = file_name_for_class_name(class_name, name->utf8_length()); diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 67fa8602df0f267eaa227f760ce8de9120efb9ef..0f1840cc9a6c0f9537f0bb07a41320e99fbf8c20 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -55,6 +55,7 @@ #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" @@ -133,8 +134,7 @@ void ClassLoaderData::initialize_name(Handle class_loader) { ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_holder) : _metaspace(NULL), - _metaspace_lock(new Mutex(Mutex::nosafepoint-2, "MetaspaceAllocation_lock", - Mutex::_safepoint_check_never)), + _metaspace_lock(new Mutex(Mutex::nosafepoint-2, "MetaspaceAllocation_lock")), _unloading(false), _has_class_mirror_holder(has_class_mirror_holder), _modified_oops(true), // A non-strong hidden class loader data doesn't have anything to keep @@ -353,6 +353,9 @@ void ClassLoaderData::methods_do(void f(Method*)) { } void ClassLoaderData::loaded_classes_do(KlassClosure* klass_closure) { + // To call this, one must have the MultiArray_lock held, but the _klasses list still has lock free reads. + assert_locked_or_safepoint(MultiArray_lock); + // Lock-free access requires load_acquire for (Klass* k = Atomic::load_acquire(&_klasses); k != NULL; k = k->next_link()) { // Do not filter ArrayKlass oops here... @@ -885,6 +888,8 @@ void ClassLoaderData::free_deallocate_list_C_heap_structures() { // Remove the class so unloading events aren't triggered for // this class (scratch or error class) in do_unloading(). remove_class(ik); + // But still have to remove it from the dumptime_table. + SystemDictionaryShared::handle_class_unloading(ik); } } } diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp index 72a1e78838f2e18f6ba4ce41c28f204e50252bc2..c1e672a0c4343939cc0651f23075db1f842f25ba 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -426,21 +426,6 @@ void ClassLoaderDataGraph::classes_unloading_do(void f(Klass* const)) { while (ClassLoaderData* X = iter.get_next()) \ if (X->dictionary() != NULL) -// Walk classes in the loaded class dictionaries in various forms. -// Only walks the classes defined in this class loader. -void ClassLoaderDataGraph::dictionary_classes_do(void f(InstanceKlass*)) { - FOR_ALL_DICTIONARY(cld) { - cld->dictionary()->classes_do(f); - } -} - -// Only walks the classes defined in this class loader. -void ClassLoaderDataGraph::dictionary_classes_do(void f(InstanceKlass*, TRAPS), TRAPS) { - FOR_ALL_DICTIONARY(cld) { - cld->dictionary()->classes_do(f, CHECK); - } -} - void ClassLoaderDataGraph::verify_dictionary() { FOR_ALL_DICTIONARY(cld) { cld->dictionary()->verify(); diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.hpp index ea6bf0129f88630e87e6fda7d58f4b655ede6e34..fd5fdca633e1ebd112dbe34dd7688c8f195845be 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.hpp @@ -96,13 +96,6 @@ class ClassLoaderDataGraph : public AllStatic { // Called from VMOperation static void walk_metadata_and_clean_metaspaces(); - // dictionary do - // Iterate over all klasses in dictionary, but - // just the classes from defining class loaders. - static void dictionary_classes_do(void f(InstanceKlass*)); - // Added for initialize_itable_for_klass to handle exceptions. - static void dictionary_classes_do(void f(InstanceKlass*, TRAPS), TRAPS); - // VM_CounterDecay iteration support static InstanceKlass* try_get_next_class(); static void adjust_saved_class(ClassLoaderData* cld); diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp index 9e93c4ef9263c177bdb21ca45048dac7059e1436..e5b51ac277cc3132470d481f5e182c19fb22ac19 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ #include "classfile/javaClasses.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" +#include "runtime/orderAccess.hpp" inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader) { guarantee(loader() != NULL && oopDesc::is_oop(loader()), "Loader must be oop"); @@ -43,29 +44,29 @@ inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader) { } size_t ClassLoaderDataGraph::num_instance_classes() { - return _num_instance_classes; + return Atomic::load(&_num_instance_classes); } size_t ClassLoaderDataGraph::num_array_classes() { - return _num_array_classes; + return Atomic::load(&_num_array_classes); } void ClassLoaderDataGraph::inc_instance_classes(size_t count) { - Atomic::add(&_num_instance_classes, count); + Atomic::add(&_num_instance_classes, count, memory_order_relaxed); } void ClassLoaderDataGraph::dec_instance_classes(size_t count) { - assert(count <= _num_instance_classes, "Sanity"); - Atomic::sub(&_num_instance_classes, count); + size_t old_count = Atomic::fetch_and_add(&_num_instance_classes, -count, memory_order_relaxed); + assert(old_count >= count, "Sanity"); } void ClassLoaderDataGraph::inc_array_classes(size_t count) { - Atomic::add(&_num_array_classes, count); + Atomic::add(&_num_array_classes, count, memory_order_relaxed); } void ClassLoaderDataGraph::dec_array_classes(size_t count) { - assert(count <= _num_array_classes, "Sanity"); - Atomic::sub(&_num_array_classes, count); + size_t old_count = Atomic::fetch_and_add(&_num_array_classes, -count, memory_order_relaxed); + assert(old_count >= count, "Sanity"); } bool ClassLoaderDataGraph::should_clean_metaspaces_and_reset() { diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 6ec79035dda3961f73d5a398fac232506f812495..63d1eeb7986e4ad2a2f0720db5a9553feb310637 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1377,10 +1377,11 @@ void java_lang_Class::fixup_module_field(Klass* k, Handle module) { java_lang_Class::set_module(k->java_mirror(), module()); } -void java_lang_Class::set_oop_size(HeapWord* java_class, int size) { +void java_lang_Class::set_oop_size(HeapWord* java_class, size_t size) { assert(_oop_size_offset != 0, "must be set"); - assert(size > 0, "Oop size must be greater than zero, not %d", size); - *(int*)(((char*)java_class) + _oop_size_offset) = size; + assert(size > 0, "Oop size must be greater than zero, not " SIZE_FORMAT, size); + assert(size <= INT_MAX, "Lossy conversion: " SIZE_FORMAT, size); + *(int*)(((char*)java_class) + _oop_size_offset) = (int)size; } int java_lang_Class::static_oop_field_count(oop java_class) { @@ -4220,8 +4221,8 @@ void java_lang_invoke_MethodHandleNatives_CallSiteContext::serialize_offsets(Ser DependencyContext java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(oop call_site) { assert(java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(call_site), ""); - nmethodBucket* volatile* vmdeps_addr = (nmethodBucket* volatile*)call_site->field_addr(_vmdependencies_offset); - volatile uint64_t* last_cleanup_addr = (volatile uint64_t*)call_site->field_addr(_last_cleanup_offset); + nmethodBucket* volatile* vmdeps_addr = call_site->field_addr(_vmdependencies_offset); + volatile uint64_t* last_cleanup_addr = call_site->field_addr(_last_cleanup_offset); DependencyContext dep_ctx(vmdeps_addr, last_cleanup_addr); return dep_ctx; } @@ -4279,19 +4280,19 @@ int java_lang_ClassLoader::_parent_offset; ClassLoaderData* java_lang_ClassLoader::loader_data_acquire(oop loader) { assert(loader != NULL, "loader must not be NULL"); assert(oopDesc::is_oop(loader), "loader must be oop"); - return HeapAccess::load_at(loader, _loader_data_offset); + return Atomic::load_acquire(loader->field_addr(_loader_data_offset)); } ClassLoaderData* java_lang_ClassLoader::loader_data(oop loader) { assert(loader != NULL, "loader must not be NULL"); assert(oopDesc::is_oop(loader), "loader must be oop"); - return HeapAccess<>::load_at(loader, _loader_data_offset); + return *loader->field_addr(_loader_data_offset); } void java_lang_ClassLoader::release_set_loader_data(oop loader, ClassLoaderData* new_data) { assert(loader != NULL, "loader must not be NULL"); assert(oopDesc::is_oop(loader), "loader must be oop"); - HeapAccess::store_at(loader, _loader_data_offset, new_data); + Atomic::release_store(loader->field_addr(_loader_data_offset), new_data); } #define CLASSLOADER_FIELDS_DO(macro) \ diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 62481fbcc1a1bdc096a8665a4347a62a1aca27c9..8a2d5fdf24c618d0fef1938e64f503e93bc854cf 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -365,8 +365,8 @@ class java_lang_Class : AllStatic { static oop source_file(oop java_class); static void set_source_file(oop java_class, oop source_file); - static int oop_size(oop java_class); - static void set_oop_size(HeapWord* java_class, int size); + static size_t oop_size(oop java_class); + static void set_oop_size(HeapWord* java_class, size_t size); static int static_oop_field_count(oop java_class); static void set_static_oop_field_count(oop java_class, int size); diff --git a/src/hotspot/share/classfile/javaClasses.inline.hpp b/src/hotspot/share/classfile/javaClasses.inline.hpp index e8ee4fce25e740347b3ec5419c55bd150a891702..f3f2b2a7e27d0e4027d181d6b013afb586a1eb14 100644 --- a/src/hotspot/share/classfile/javaClasses.inline.hpp +++ b/src/hotspot/share/classfile/javaClasses.inline.hpp @@ -77,7 +77,7 @@ bool java_lang_String::is_latin1(oop java_string) { uint8_t* java_lang_String::flags_addr(oop java_string) { assert(_initialized, "Must be initialized"); assert(is_instance(java_string), "Must be java string"); - return java_string->obj_field_addr(_flags_offset); + return java_string->field_addr(_flags_offset); } bool java_lang_String::is_flag_set(oop java_string, uint8_t flag_mask) { @@ -146,7 +146,7 @@ void java_lang_ref_Reference::clear_referent(oop ref) { } HeapWord* java_lang_ref_Reference::referent_addr_raw(oop ref) { - return ref->obj_field_addr(_referent_offset); + return ref->field_addr(_referent_offset); } oop java_lang_ref_Reference::next(oop ref) { @@ -162,7 +162,7 @@ void java_lang_ref_Reference::set_next_raw(oop ref, oop value) { } HeapWord* java_lang_ref_Reference::next_addr_raw(oop ref) { - return ref->obj_field_addr(_next_offset); + return ref->field_addr(_next_offset); } oop java_lang_ref_Reference::discovered(oop ref) { @@ -178,7 +178,7 @@ void java_lang_ref_Reference::set_discovered_raw(oop ref, oop value) { } HeapWord* java_lang_ref_Reference::discovered_addr_raw(oop ref) { - return ref->obj_field_addr(_discovered_offset); + return ref->field_addr(_discovered_offset); } bool java_lang_ref_Reference::is_final(oop ref) { @@ -261,7 +261,7 @@ inline bool java_lang_Class::is_primitive(oop java_class) { return is_primitive; } -inline int java_lang_Class::oop_size(oop java_class) { +inline size_t java_lang_Class::oop_size(oop java_class) { assert(_oop_size_offset != 0, "must be set"); int size = java_class->int_field(_oop_size_offset); assert(size > 0, "Oop size must be greater than zero, not %d", size); diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index 7151464703888957d3405ca1bcfad23e0350574c..c43f940b790a49b61f04df02a0fd930f16315cb6 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -538,18 +538,19 @@ void StringTable::rehash_table() { } // Statistics -static int literal_size(oop obj) { - // NOTE: this would over-count if (pre-JDK8) - // java_lang_Class::has_offset_field() is true and the String.value array is - // shared by several Strings. However, starting from JDK8, the String.value - // array is not shared anymore. +static size_t literal_size(oop obj) { if (obj == NULL) { return 0; - } else if (obj->klass() == vmClasses::String_klass()) { - return (obj->size() + java_lang_String::value(obj)->size()) * HeapWordSize; - } else { - return obj->size(); } + + size_t word_size = obj->size(); + + if (obj->klass() == vmClasses::String_klass()) { + // This may overcount if String.value arrays are shared. + word_size += java_lang_String::value(obj)->size(); + } + + return word_size * HeapWordSize; } struct SizeFunc : StackObj { diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index f7484e7d60d8951f2683d47cd0f844e1db4f8168..31814259452f359ad21205fc6fcbfb3d1b82fba5 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -76,6 +76,7 @@ #include "runtime/signature.hpp" #include "services/classLoadingService.hpp" #include "services/diagnosticCommand.hpp" +#include "services/finalizerService.hpp" #include "services/threadService.hpp" #include "utilities/macros.hpp" #include "utilities/utf8.hpp" @@ -1600,7 +1601,7 @@ bool SystemDictionary::do_unloading(GCTimer* gc_timer) { if (unloading_occurred) { MutexLocker ml2(is_concurrent ? Module_lock : NULL); JFR_ONLY(Jfr::on_unloading_classes();) - + MANAGEMENT_ONLY(FinalizerService::purge_unloaded();) MutexLocker ml1(is_concurrent ? SystemDictionary_lock : NULL); ClassLoaderDataGraph::clean_module_and_package_info(); constraints()->purge_loader_constraints(); @@ -2351,11 +2352,10 @@ void SystemDictionary::invoke_bootstrap_method(BootstrapInfo& bootstrap_specifie assert(appendix_box->obj_at(0) == NULL, ""); } - // call condy: java.lang.invoke.MethodHandleNatives::linkDynamicConstant(caller, condy_index, bsm, type, info) - // indy: java.lang.invoke.MethodHandleNatives::linkCallSite(caller, indy_index, bsm, name, mtype, info, &appendix) + // call condy: java.lang.invoke.MethodHandleNatives::linkDynamicConstant(caller, bsm, type, info) + // indy: java.lang.invoke.MethodHandleNatives::linkCallSite(caller, bsm, name, mtype, info, &appendix) JavaCallArguments args; args.push_oop(Handle(THREAD, bootstrap_specifier.caller_mirror())); - args.push_int(bootstrap_specifier.bss_index()); args.push_oop(bootstrap_specifier.bsm()); args.push_oop(bootstrap_specifier.name_arg()); args.push_oop(bootstrap_specifier.type_arg()); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 809deb8617f72b78ed1eb89924f1d1d76da1d2c4..48f8147b6ee2c7df63a461bcacbf3d1362273371 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -1598,6 +1598,28 @@ void SystemDictionaryShared::restore_dumptime_tables() { _cloned_dumptime_lambda_proxy_class_dictionary = NULL; } +class CleanupDumpTimeLambdaProxyClassTable: StackObj { + public: + bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { + assert_lock_strong(DumpTimeTable_lock); + for (int i = 0; i < info._proxy_klasses->length(); i++) { + InstanceKlass* ik = info._proxy_klasses->at(i); + if (!ik->can_be_verified_at_dumptime()) { + info._proxy_klasses->remove_at(i); + } + } + return info._proxy_klasses->length() == 0 ? true /* delete the node*/ : false; + } +}; + +void SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary() { + assert_lock_strong(DumpTimeTable_lock); + if (_dumptime_lambda_proxy_class_dictionary != NULL) { + CleanupDumpTimeLambdaProxyClassTable cleanup_proxy_classes; + _dumptime_lambda_proxy_class_dictionary->unlink(&cleanup_proxy_classes); + } +} + #if INCLUDE_CDS_JAVA_HEAP class ArchivedMirrorPatcher { diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index e876c00dda67da143ba81eac0cd860f4fe18b68c..039b9d01e9a3425cab23c662d5a627ed47c6f786 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -288,6 +288,7 @@ public: static size_t estimate_size_for_archive(); static void write_to_archive(bool is_static_archive = true); static void adjust_lambda_proxy_class_dictionary(); + static void cleanup_lambda_proxy_class_dictionary(); static void serialize_dictionary_headers(class SerializeClosure* soc, bool is_static_archive = true); static void serialize_vm_classes(class SerializeClosure* soc); diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 47468a16a2056589e82815b86dcf29701b9edb73..80825f0339f53f45493d19d5a92af8a7da1e623c 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -1805,7 +1805,7 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { no_control_flow = true; break; default: // We only need to check the valid bytecodes in class file. - // And jsr and ret are not in the new class file format in JDK1.5. + // And jsr and ret are not in the new class file format in JDK1.6. verify_error(ErrorContext::bad_code(bci), "Bad instruction: %02x", opcode); no_control_flow = false; @@ -2316,6 +2316,7 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, // Get field name and signature Symbol* field_name = cp->name_ref_at(index); Symbol* field_sig = cp->signature_ref_at(index); + bool is_getfield = false; // Field signature was checked in ClassFileParser. assert(SignatureVerifier::is_valid_type_signature(field_sig), @@ -2362,11 +2363,9 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, break; } case Bytecodes::_getfield: { + is_getfield = true; stack_object_type = current_frame->pop_stack( target_class_type, CHECK_VERIFY(this)); - for (int i = 0; i < n; i++) { - current_frame->push_stack(field_type[i], CHECK_VERIFY(this)); - } goto check_protected; } case Bytecodes::_putfield: { @@ -2396,7 +2395,15 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, check_protected: { if (_this_type == stack_object_type) break; // stack_object_type must be assignable to _current_class_type - if (was_recursively_verified()) return; + if (was_recursively_verified()) { + if (is_getfield) { + // Push field type for getfield. + for (int i = 0; i < n; i++) { + current_frame->push_stack(field_type[i], CHECK_VERIFY(this)); + } + } + return; + } Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(index)); if (!name_in_supers(ref_class_name, current_class())) @@ -2417,7 +2424,8 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, verify_error(ErrorContext::bad_type(bci, current_frame->stack_top_ctx(), TypeOrigin::implicit(current_type())), - "Bad access to protected data in getfield"); + "Bad access to protected data in %s", + is_getfield ? "getfield" : "putfield"); return; } } @@ -2425,6 +2433,12 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, } default: ShouldNotReachHere(); } + if (is_getfield) { + // Push field type for getfield after doing protection check. + for (int i = 0; i < n; i++) { + current_frame->push_stack(field_type[i], CHECK_VERIFY(this)); + } + } } // Look at the method's handlers. If the bci is in the handler's try block diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index 205ba8969cb0a755eb9496c370a690aa4d100cdc..c15e1154b13c08dd5a4671f74dee21d5e421b3ef 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -505,6 +505,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { if (!SpecialArraysEquals) return true; break; case vmIntrinsics::_encodeISOArray: + case vmIntrinsics::_encodeAsciiArray: case vmIntrinsics::_encodeByteISOArray: if (!SpecialEncodeISOArray) return true; break; diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 2a4f54880c9e74584911d2d429c20b85c732099c..fbe9c140b77530f32d41f98c972403b7d148c1c2 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -140,6 +140,7 @@ class methodHandle; do_name(incrementExact_name,"incrementExact") \ do_name(multiplyExact_name,"multiplyExact") \ do_name(multiplyHigh_name,"multiplyHigh") \ + do_name(unsignedMultiplyHigh_name,"unsignedMultiplyHigh") \ do_name(negateExact_name,"negateExact") \ do_name(subtractExact_name,"subtractExact") \ do_name(fma_name, "fma") \ @@ -173,6 +174,7 @@ class methodHandle; do_intrinsic(_multiplyExactI, java_lang_Math, multiplyExact_name, int2_int_signature, F_S) \ do_intrinsic(_multiplyExactL, java_lang_Math, multiplyExact_name, long2_long_signature, F_S) \ do_intrinsic(_multiplyHigh, java_lang_Math, multiplyHigh_name, long2_long_signature, F_S) \ + do_intrinsic(_unsignedMultiplyHigh, java_lang_Math, unsignedMultiplyHigh_name, long2_long_signature, F_S) \ do_intrinsic(_negateExactI, java_lang_Math, negateExact_name, int_int_signature, F_S) \ do_intrinsic(_negateExactL, java_lang_Math, negateExact_name, long_long_signature, F_S) \ do_intrinsic(_subtractExactI, java_lang_Math, subtractExact_name, int2_int_signature, F_S) \ @@ -353,6 +355,9 @@ class methodHandle; \ do_intrinsic(_encodeByteISOArray, java_lang_StringCoding, encodeISOArray_name, indexOfI_signature, F_S) \ \ + do_intrinsic(_encodeAsciiArray, java_lang_StringCoding, encodeAsciiArray_name, encodeISOArray_signature, F_S) \ + do_name( encodeAsciiArray_name, "implEncodeAsciiArray") \ + \ do_class(java_math_BigInteger, "java/math/BigInteger") \ do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_S) \ do_name( multiplyToLen_name, "implMultiplyToLen") \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index f8dbdc70f116aeb439ff9df735f79cc30d55e26f..6357019adb7757d3d309559d2985bd114d2a2c18 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -339,9 +339,9 @@ template(linkMethod_name, "linkMethod") \ template(linkMethod_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \ template(linkDynamicConstant_name, "linkDynamicConstant") \ - template(linkDynamicConstant_signature, "(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \ + template(linkDynamicConstant_signature, "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \ template(linkCallSite_name, "linkCallSite") \ - template(linkCallSite_signature, "(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \ + template(linkCallSite_signature, "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \ template(setTargetNormal_name, "setTargetNormal") \ template(setTargetVolatile_name, "setTargetVolatile") \ template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \ @@ -697,7 +697,7 @@ \ /* CDS */ \ template(dumpSharedArchive, "dumpSharedArchive") \ - template(dumpSharedArchive_signature, "(ZLjava/lang/String;)V") \ + template(dumpSharedArchive_signature, "(ZLjava/lang/String;)Ljava/lang/String;") \ template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \ template(generateLambdaFormHolderClasses_signature, "([Ljava/lang/String;)[Ljava/lang/Object;") \ template(java_lang_invoke_Invokers_Holder, "java/lang/invoke/Invokers$Holder") \ diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 05f56ff0e4adfc476ce25038d9743e2edec6b786..a1fcea77e4833d00112c93378668ed441b4a56cb 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -653,7 +653,12 @@ void CodeBlob::dump_for_addr(address addr, outputStream* st, bool verbose) const nm->method()->print_value_on(st); } st->cr(); - nm->print_nmethod(verbose); + if (verbose && st == tty) { + // verbose is only ever true when called from findpc in debug.cpp + nm->print_nmethod(true); + } else { + nm->print(st); + } return; } st->print_cr(INTPTR_FORMAT " is at code_begin+%d in ", p2i(addr), (int)(addr - code_begin())); diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp index 0eb6c58fb42f98ed232889c251bf11c7b0b75d13..8ca9c59fc8270105691f7772366c612adabf57a5 100644 --- a/src/hotspot/share/code/dependencies.cpp +++ b/src/hotspot/share/code/dependencies.cpp @@ -1842,6 +1842,26 @@ Klass* Dependencies::find_witness_AME(InstanceKlass* ctxk, Method* m, KlassDepCh return NULL; } +// This function is used by find_unique_concrete_method(non vtable based) +// to check whether subtype method overrides the base method. +static bool overrides(Method* sub_m, Method* base_m) { + assert(base_m != NULL, "base method should be non null"); + if (sub_m == NULL) { + return false; + } + /** + * If base_m is public or protected then sub_m always overrides. + * If base_m is !public, !protected and !private (i.e. base_m is package private) + * then sub_m should be in the same package as that of base_m. + * For package private base_m this is conservative approach as it allows only subset of all allowed cases in + * the jvm specification. + **/ + if (base_m->is_public() || base_m->is_protected() || + base_m->method_holder()->is_same_class_package(sub_m->method_holder())) { + return true; + } + return false; +} // Find the set of all non-abstract methods under ctxk that match m. // (The method m must be defined or inherited in ctxk.) @@ -1879,6 +1899,9 @@ Method* Dependencies::find_unique_concrete_method(InstanceKlass* ctxk, Method* m } else if (Dependencies::find_witness_AME(ctxk, fm) != NULL) { // Found a concrete subtype which does not override abstract root method. return NULL; + } else if (!overrides(fm, m)) { + // Found method doesn't override abstract root method. + return NULL; } assert(Dependencies::is_concrete_root_method(fm, ctxk) == Dependencies::is_concrete_method(m, ctxk), "mismatch"); #ifndef PRODUCT diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 68776290ad5ae7803ee56c2de24885f54c87f389..f695f242231209c1c07af21130863cad5ad8a591 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -67,6 +67,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/signature.hpp" #include "runtime/sweeper.hpp" +#include "runtime/threadWXSetters.inline.hpp" #include "runtime/vmThread.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" @@ -923,7 +924,7 @@ void nmethod::print_on(outputStream* st, const char* msg) const { CompileTask::print(st, this, msg, /*short_form:*/ true); st->print_cr(" (" INTPTR_FORMAT ")", p2i(this)); } else { - CompileTask::print(st, this, msg, /*short_form:*/ false); + CompileTask::print(st, this, msg, /*short_form:*/ false, /* cr */ true, /* timestamp */ false); } } } @@ -2528,7 +2529,7 @@ void nmethod::print(outputStream* st) const { st->print("(n/a) "); } - print_on(tty, NULL); + print_on(st, NULL); if (WizardMode) { st->print("((nmethod*) " INTPTR_FORMAT ") ", p2i(this)); @@ -2879,6 +2880,9 @@ void nmethod::decode2(outputStream* ost) const { AbstractDisassembler::show_block_comment()); #endif + // Decoding an nmethod can write to a PcDescCache (see PcDescCache::add_pc_desc) + MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current());) + st->cr(); this->print(st); st->cr(); @@ -2888,7 +2892,10 @@ void nmethod::decode2(outputStream* ost) const { //---< Print real disassembly >--- //---------------------------------- if (! use_compressed_format) { + st->print_cr("[Disassembly]"); Disassembler::decode(const_cast(this), st); + st->bol(); + st->print_cr("[/Disassembly]"); return; } #endif diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 1f9902895f1f254adc574cbc578f2922a485d1fb..592cb5d65f2f400feb94fd75bced2012a7da7ffd 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -887,6 +887,10 @@ bool CompilationPolicy::is_method_profiled(const methodHandle& method) { // Determine is a method is mature. bool CompilationPolicy::is_mature(Method* method) { + if (Arguments::is_compiler_only()) { + // Always report profiles as immature with -Xcomp + return false; + } methodHandle mh(Thread::current(), method); MethodData* mdo = method->method_data(); if (mdo != NULL) { diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp index 2ecb066ba8569485b182cf939cc536d43cb61ee4..721efa3688e14cf8a4b4c926c22ffbb3f24f0253 100644 --- a/src/hotspot/share/compiler/compilationPolicy.hpp +++ b/src/hotspot/share/compiler/compilationPolicy.hpp @@ -237,6 +237,7 @@ class CompilationPolicy : AllStatic { // m must be compiled before executing it static bool must_be_compiled(const methodHandle& m, int comp_level = CompLevel_any); public: + static int min_invocations() { return Tier4MinInvocationThreshold; } static int c1_count() { return _c1_count; } static int c2_count() { return _c2_count; } static int compiler_count(CompLevel comp_level); diff --git a/src/hotspot/share/compiler/compileTask.cpp b/src/hotspot/share/compiler/compileTask.cpp index d610d8bdcf814730579233cf65bd9649566b2d25..b78806346c5db44ff38163c72172ef9efd23add8 100644 --- a/src/hotspot/share/compiler/compileTask.cpp +++ b/src/hotspot/share/compiler/compileTask.cpp @@ -236,11 +236,13 @@ void CompileTask::print_tty() { // CompileTask::print_impl void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, int comp_level, bool is_osr_method, int osr_bci, bool is_blocking, - const char* msg, bool short_form, bool cr, + const char* msg, bool short_form, bool cr, bool timestamp, jlong time_queued, jlong time_started) { if (!short_form) { - // Print current time - st->print("%7d ", (int)tty->time_stamp().milliseconds()); + if (timestamp) { + // Print current time + st->print("%7d ", (int)tty->time_stamp().milliseconds()); + } if (Verbose && time_queued != 0) { // Print time in queue and time being processed by compiler thread jlong now = os::elapsed_counter(); diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp index 812c3ae9fb06aa22cce8c543f498cc15acdae7f0..a900bfd4f44b29812186dd5e3584817dce2e06b1 100644 --- a/src/hotspot/share/compiler/compileTask.hpp +++ b/src/hotspot/share/compiler/compileTask.hpp @@ -104,7 +104,7 @@ class CompileTask : public CHeapObj { public: CompileTask() : _failure_reason(NULL), _failure_reason_on_C_heap(false) { - _lock = new Monitor(Mutex::nonleaf+2, "CompileTaskLock", Mutex::_safepoint_check_always); + _lock = new Monitor(Mutex::safepoint, "CompileTask_lock"); } void initialize(int compile_id, const methodHandle& method, int osr_bci, int comp_level, @@ -187,16 +187,16 @@ class CompileTask : public CHeapObj { private: static void print_impl(outputStream* st, Method* method, int compile_id, int comp_level, bool is_osr_method = false, int osr_bci = -1, bool is_blocking = false, - const char* msg = NULL, bool short_form = false, bool cr = true, + const char* msg = NULL, bool short_form = false, bool cr = true, bool timestamp = true, jlong time_queued = 0, jlong time_started = 0); public: void print(outputStream* st = tty, const char* msg = NULL, bool short_form = false, bool cr = true); void print_ul(const char* msg = NULL); - static void print(outputStream* st, const nmethod* nm, const char* msg = NULL, bool short_form = false, bool cr = true) { + static void print(outputStream* st, const nmethod* nm, const char* msg = NULL, bool short_form = false, bool cr = true, bool timestamp = true) { print_impl(st, nm->method(), nm->compile_id(), nm->comp_level(), nm->is_osr_method(), nm->is_osr_method() ? nm->osr_entry_bci() : -1, /*is_blocking*/ false, - msg, short_form, cr); + msg, short_form, cr, timestamp); } static void print_ul(const nmethod* nm, const char* msg = NULL); diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index da11221a7add8773dcf9929f07a3f8322a875dbd..54cea9cc2811d56e1e1011c6768513b710764528 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -872,7 +872,7 @@ void Disassembler::decode(CodeBlob* cb, outputStream* st) { if (cb->is_nmethod()) { // If we have an nmethod at hand, // call the specialized decoder directly. - decode((nmethod*)cb, st); + ((nmethod*)cb)->decode2(st); return; } diff --git a/src/hotspot/share/compiler/methodMatcher.cpp b/src/hotspot/share/compiler/methodMatcher.cpp index 50cae0430d7508ba1ef89443dee09c3d154f1763..045e1365d34dd83fa05823dac574c1bf8791aa65 100644 --- a/src/hotspot/share/compiler/methodMatcher.cpp +++ b/src/hotspot/share/compiler/methodMatcher.cpp @@ -45,16 +45,20 @@ // 0x28 '(' and 0x29 ')' are used for the signature // 0x2e '.' is always replaced before the matching // 0x2f '/' is only used in the class name as package separator +// +// It seems hard to get Non-ASCII characters to work in all circumstances due +// to limitations in Windows. So only ASCII characters are supported on Windows. -#define RANGEBASE "\x1\x2\x3\x4\x5\x6\x7\x8\xa\xb\xc\xd\xe\xf" \ +#define RANGEBASE_ASCII "\x1\x2\x3\x4\x5\x6\x7\x8\xa\xb\xc\xd\xe\xf" \ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \ "\x21\x22\x23\x24\x25\x26\x27\x2a\x2b\x2c\x2d" \ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5c\x5e\x5f" \ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \ - "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" \ - "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" \ + "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + +#define RANGEBASE_NON_ASCII "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" \ "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" \ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" \ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" \ @@ -62,6 +66,8 @@ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" \ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" +#define RANGEBASE RANGEBASE_ASCII NOT_WINDOWS(RANGEBASE_NON_ASCII) + #define RANGE0 "[*" RANGEBASE "]" #define RANGESLASH "[*" RANGEBASE "/]" @@ -167,6 +173,15 @@ bool MethodMatcher::canonicalize(char * line, const char *& error_msg) { if (*lp == ':') *lp = ' '; } if (*lp == ',' || *lp == '.') *lp = ' '; + +#ifdef _WINDOWS + // It seems hard to get Non-ASCII characters to work in all circumstances due + // to limitations in Windows. So only ASCII characters are supported on Windows. + if (!isascii(*lp)) { + error_msg = "Non-ASCII characters are not supported on Windows."; + return false; + } +#endif } return true; } @@ -240,10 +255,6 @@ void skip_leading_spaces(char*& line, int* total_bytes_read ) { } } -PRAGMA_DIAG_PUSH -// warning C4189: The file contains a character that cannot be represented -// in the current code page -PRAGMA_DISABLE_MSVC_WARNING(4819) void MethodMatcher::parse_method_pattern(char*& line, const char*& error_msg, MethodMatcher* matcher) { MethodMatcher::Mode c_match; MethodMatcher::Mode m_match; @@ -334,7 +345,6 @@ void MethodMatcher::parse_method_pattern(char*& line, const char*& error_msg, Me error_msg = "Could not parse method pattern"; } } -PRAGMA_DIAG_POP bool MethodMatcher::matches(const methodHandle& method) const { Symbol* class_name = method->method_holder()->name(); diff --git a/src/hotspot/share/gc/epsilon/epsilonArguments.cpp b/src/hotspot/share/gc/epsilon/epsilonArguments.cpp index 17c4783d5c8f59e26010812fa36c0cd1cbe66a83..9196d49e18bedb1f56e53928ce50d5e4cd1e3124 100644 --- a/src/hotspot/share/gc/epsilon/epsilonArguments.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonArguments.cpp @@ -27,6 +27,7 @@ #include "gc/epsilon/epsilonHeap.hpp" #include "gc/shared/gcArguments.hpp" #include "gc/shared/tlab_globals.hpp" +#include "logging/log.hpp" #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp index d4b2d4e6bfadedb6f33ce2a926fae6563a551784..332b5f792ca06ccea3294519d833f59cc69886ae 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp @@ -29,6 +29,7 @@ #include "gc/epsilon/epsilonThreadLocalData.hpp" #include "gc/shared/gcArguments.hpp" #include "gc/shared/locationPrinter.inline.hpp" +#include "logging/log.hpp" #include "memory/allocation.hpp" #include "memory/allocation.inline.hpp" #include "memory/metaspaceUtils.hpp" diff --git a/src/hotspot/share/gc/g1/g1Allocator.cpp b/src/hotspot/share/gc/g1/g1Allocator.cpp index 8f67d28cfcbdb7798668c48654a260296bebdac6..bf38b786f03644fcf025c6148f846a30279def5b 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.cpp +++ b/src/hotspot/share/gc/g1/g1Allocator.cpp @@ -475,7 +475,6 @@ HeapWord* G1ArchiveAllocator::archive_mem_allocate(size_t word_size) { // Non-zero space; need to insert the filler size_t fill_size = free_words; CollectedHeap::fill_with_object(old_top, fill_size); - _summary_bytes_used += fill_size * HeapWordSize; } // Set the current chunk as "full" _allocation_region->set_top(_max); @@ -495,7 +494,6 @@ HeapWord* G1ArchiveAllocator::archive_mem_allocate(size_t word_size) { } assert(pointer_delta(_max, old_top) >= word_size, "enough space left"); _allocation_region->set_top(old_top + word_size); - _summary_bytes_used += word_size * HeapWordSize; return old_top; } diff --git a/src/hotspot/share/gc/g1/g1Allocator.hpp b/src/hotspot/share/gc/g1/g1Allocator.hpp index 2f84a6efb9887650e5677ad324776495f965a515..5014442845ab91469cf966e1a450b296660d3200 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.hpp +++ b/src/hotspot/share/gc/g1/g1Allocator.hpp @@ -223,9 +223,6 @@ protected: // Regions allocated for the current archive range. GrowableArray _allocated_regions; - // The number of bytes used in the current range. - size_t _summary_bytes_used; - // Current allocation window within the current region. HeapWord* _bottom; HeapWord* _top; @@ -243,7 +240,6 @@ public: _allocated_regions((ResourceObj::set_allocation_type((address) &_allocated_regions, ResourceObj::C_HEAP), 2), mtGC), - _summary_bytes_used(0), _bottom(NULL), _top(NULL), _max(NULL) { } @@ -261,19 +257,6 @@ public: // aligning to the requested alignment. void complete_archive(GrowableArray* ranges, size_t end_alignment_in_bytes); - - // The number of bytes allocated by this allocator. - size_t used() { - return _summary_bytes_used; - } - - // Clear the count of bytes allocated in prior G1 regions. This - // must be done when recalculate_use is used to reset the counter - // for the generic allocator, since it counts bytes in all G1 - // regions, including those still associated with this allocator. - void clear_used() { - _summary_bytes_used = 0; - } }; #endif // SHARE_GC_G1_G1ALLOCATOR_HPP diff --git a/src/hotspot/share/gc/g1/g1BatchedGangTask.cpp b/src/hotspot/share/gc/g1/g1BatchedTask.cpp similarity index 84% rename from src/hotspot/share/gc/g1/g1BatchedGangTask.cpp rename to src/hotspot/share/gc/g1/g1BatchedTask.cpp index 0c108d8d86f66df353472908be98adcd69c992a9..9ab47e9c4c9d0337f73ac84630d10488d2080e61 100644 --- a/src/hotspot/share/gc/g1/g1BatchedGangTask.cpp +++ b/src/hotspot/share/gc/g1/g1BatchedTask.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" -#include "gc/g1/g1BatchedGangTask.hpp" +#include "gc/g1/g1BatchedTask.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1GCParPhaseTimesTracker.hpp" #include "runtime/atomic.hpp" @@ -40,30 +40,30 @@ const char* G1AbstractSubTask::name() const { return g1h->phase_times()->phase_name(_tag); } -bool G1BatchedGangTask::try_claim_serial_task(int& task) { +bool G1BatchedTask::try_claim_serial_task(int& task) { task = Atomic::fetch_and_add(&_num_serial_tasks_done, 1); return task < _serial_tasks.length(); } -void G1BatchedGangTask::add_serial_task(G1AbstractSubTask* task) { +void G1BatchedTask::add_serial_task(G1AbstractSubTask* task) { assert(task != nullptr, "must be"); _serial_tasks.push(task); } -void G1BatchedGangTask::add_parallel_task(G1AbstractSubTask* task) { +void G1BatchedTask::add_parallel_task(G1AbstractSubTask* task) { assert(task != nullptr, "must be"); _parallel_tasks.push(task); } -G1BatchedGangTask::G1BatchedGangTask(const char* name, G1GCPhaseTimes* phase_times) : - AbstractGangTask(name), +G1BatchedTask::G1BatchedTask(const char* name, G1GCPhaseTimes* phase_times) : + WorkerTask(name), _num_serial_tasks_done(0), _phase_times(phase_times), _serial_tasks(), _parallel_tasks() { } -uint G1BatchedGangTask::num_workers_estimate() const { +uint G1BatchedTask::num_workers_estimate() const { double sum = 0.0; for (G1AbstractSubTask* task : _serial_tasks) { sum += task->worker_cost(); @@ -74,7 +74,7 @@ uint G1BatchedGangTask::num_workers_estimate() const { return ceil(sum); } -void G1BatchedGangTask::set_max_workers(uint max_workers) { +void G1BatchedTask::set_max_workers(uint max_workers) { for (G1AbstractSubTask* task : _serial_tasks) { task->set_max_workers(max_workers); } @@ -83,7 +83,7 @@ void G1BatchedGangTask::set_max_workers(uint max_workers) { } } -void G1BatchedGangTask::work(uint worker_id) { +void G1BatchedTask::work(uint worker_id) { int t = 0; while (try_claim_serial_task(t)) { G1AbstractSubTask* task = _serial_tasks.at(t); @@ -96,7 +96,7 @@ void G1BatchedGangTask::work(uint worker_id) { } } -G1BatchedGangTask::~G1BatchedGangTask() { +G1BatchedTask::~G1BatchedTask() { assert(Atomic::load(&_num_serial_tasks_done) >= _serial_tasks.length(), "Only %d tasks of %d claimed", Atomic::load(&_num_serial_tasks_done), _serial_tasks.length()); diff --git a/src/hotspot/share/gc/g1/g1BatchedGangTask.hpp b/src/hotspot/share/gc/g1/g1BatchedTask.hpp similarity index 85% rename from src/hotspot/share/gc/g1/g1BatchedGangTask.hpp rename to src/hotspot/share/gc/g1/g1BatchedTask.hpp index 30917402ae4a948c446c547584f1b107228e5788..0a627c3a4c986355f4e7f7e4110fc96f474652db 100644 --- a/src/hotspot/share/gc/g1/g1BatchedGangTask.hpp +++ b/src/hotspot/share/gc/g1/g1BatchedTask.hpp @@ -22,18 +22,18 @@ * */ -#ifndef SHARE_GC_G1_G1BATCHEDGANGTASK_HPP -#define SHARE_GC_G1_G1BATCHEDGANGTASK_HPP +#ifndef SHARE_GC_G1_G1BATCHEDTASK_HPP +#define SHARE_GC_G1_G1BATCHEDTASK_HPP #include "gc/g1/g1GCPhaseTimes.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" template class GrowableArrayCHeap; // G1AbstractSubTask represents a task to be performed either within a -// G1BatchedGangTask running on a single worker ("serially") or multiple workers +// G1BatchedTask running on a single worker ("serially") or multiple workers // ("in parallel"). A G1AbstractSubTask is always associated with a phase tag // that is used to automatically store timing information. // @@ -46,7 +46,7 @@ class GrowableArrayCHeap; // splits across the heap in some way. Current examples would be clearing the // card table. // -// See G1BatchedGangTask for information on execution. +// See G1BatchedTask for information on execution. class G1AbstractSubTask : public CHeapObj { G1GCPhaseTimes::GCParPhases _tag; @@ -65,10 +65,10 @@ public: // How many workers (threads) would this task be able to keep busy for at least // as long as to amortize worker startup costs. - // Called by G1BatchedGangTask to determine total number of workers. + // Called by G1BatchedTask to determine total number of workers. virtual double worker_cost() const = 0; - // Called by G1BatchedGangTask to provide information about the the maximum + // Called by G1BatchedTask to provide information about the the maximum // number of workers for all subtasks after it has been determined. virtual void set_max_workers(uint max_workers) { } @@ -81,7 +81,7 @@ public: const char* name() const; }; -// G1BatchedGangTask runs a set of G1AbstractSubTask using a work gang. +// G1BatchedTask runs a set of G1AbstractSubTask using workers. // // Subclasses of this class add their G1AbstractSubTasks into either the list // of "serial" or the list of "parallel" tasks. They are supposed to be the owners @@ -94,7 +94,7 @@ public: // add_parallel_task(new SomeOtherSubTask()); // [...] // -// During execution in the work gang, this class will make sure that the "serial" +// During execution in workers, this class will make sure that the "serial" // tasks are executed by a single worker exactly once, but different "serial" // tasks may be executed in parallel using different workers. "Parallel" tasks' // do_work() method may be called by different workers passing a different @@ -119,13 +119,13 @@ public: // 4) T::do_work() // potentially in parallel with any other registered G1AbstractSubTask // 5) ~T() // -class G1BatchedGangTask : public AbstractGangTask { +class G1BatchedTask : public WorkerTask { volatile int _num_serial_tasks_done; G1GCPhaseTimes* _phase_times; bool try_claim_serial_task(int& task); - NONCOPYABLE(G1BatchedGangTask); + NONCOPYABLE(G1BatchedTask); GrowableArrayCHeap _serial_tasks; GrowableArrayCHeap _parallel_tasks; @@ -134,19 +134,19 @@ protected: void add_serial_task(G1AbstractSubTask* task); void add_parallel_task(G1AbstractSubTask* task); - G1BatchedGangTask(const char* name, G1GCPhaseTimes* phase_times); + G1BatchedTask(const char* name, G1GCPhaseTimes* phase_times); public: void work(uint worker_id) override; - // How many workers can this gang task keep busy and should be started for + // How many workers can this task keep busy and should be started for // "optimal" performance. uint num_workers_estimate() const; // Informs the G1AbstractSubTasks about that we will start execution with the // given number of workers. void set_max_workers(uint max_workers); - ~G1BatchedGangTask(); + ~G1BatchedTask(); }; -#endif // SHARE_GC_G1_G1BATCHEDGANGTASK_HPP \ No newline at end of file +#endif // SHARE_GC_G1_G1BATCHEDTASK_HPP diff --git a/src/hotspot/share/gc/g1/g1CardSet.cpp b/src/hotspot/share/gc/g1/g1CardSet.cpp index caa9124bd6fd5aa10184326739c3826c60dac811..4b91dc5409283684bf7fc7edc14421acc310ac20 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.cpp +++ b/src/hotspot/share/gc/g1/g1CardSet.cpp @@ -45,53 +45,65 @@ G1CardSet::CardSetPtr G1CardSet::FullCardSet = (G1CardSet::CardSetPtr)-1; G1CardSetConfiguration::G1CardSetConfiguration() : - _inline_ptr_bits_per_card(HeapRegion::LogCardsPerRegion) { - - // Array of Cards card set container size calculation - _num_cards_in_array = G1RemSetArrayOfCardsEntries; - - // Full card set container size calculation - _max_cards_in_card_set = (uint)HeapRegion::CardsPerRegion; - assert(is_power_of_2(_max_cards_in_card_set), - "max_cards_in_card_set must be a power of 2: %u", _max_cards_in_card_set); - _cards_in_howl_threshold = _max_cards_in_card_set * (double)G1RemSetCoarsenHowlToFullPercent / 100; - - // Howl card set container size calculation. - _num_buckets_in_howl = G1RemSetHowlNumBuckets; - - // Howl Bitmap card set container size calculation. - _num_cards_in_howl_bitmap = G1CardSetHowl::bitmap_size(_max_cards_in_card_set, _num_buckets_in_howl); - _log2_num_cards_in_howl_bitmap = log2i_exact(_num_cards_in_howl_bitmap); - _cards_in_howl_bitmap_threshold = _num_cards_in_howl_bitmap * (double)G1RemSetCoarsenHowlBitmapToHowlFullPercent / 100; - _bitmap_hash_mask = ~(~(0) << _log2_num_cards_in_howl_bitmap); - - log_configuration(); -} + G1CardSetConfiguration(HeapRegion::LogCardsPerRegion, /* inline_ptr_bits_per_card */ + G1RemSetArrayOfCardsEntries, /* num_cards_in_array */ + (double)G1RemSetCoarsenHowlBitmapToHowlFullPercent / 100, /* cards_in_bitmap_threshold_percent */ + G1RemSetHowlNumBuckets, /* num_buckets_in_howl */ + (double)G1RemSetCoarsenHowlToFullPercent / 100, /* cards_in_howl_threshold_percent */ + (uint)HeapRegion::CardsPerRegion) /* max_cards_in_cardset */ + { } + +G1CardSetConfiguration::G1CardSetConfiguration(uint num_cards_in_array, + double cards_in_bitmap_threshold_percent, + uint max_buckets_in_howl, + double cards_in_howl_threshold_percent, + uint max_cards_in_card_set) : + G1CardSetConfiguration(log2i_exact(max_cards_in_card_set), /* inline_ptr_bits_per_card */ + num_cards_in_array, /* num_cards_in_array */ + cards_in_bitmap_threshold_percent, /* cards_in_bitmap_threshold_percent */ + G1CardSetHowl::num_buckets(max_cards_in_card_set, /* num_buckets_in_howl */ + num_cards_in_array, + max_buckets_in_howl), + cards_in_howl_threshold_percent, /* cards_in_howl_threshold_percent */ + max_cards_in_card_set) /* max_cards_in_cardset */ + { } G1CardSetConfiguration::G1CardSetConfiguration(uint inline_ptr_bits_per_card, uint num_cards_in_array, - double cards_in_bitmap_threshold, - uint max_buckets_in_howl, - double cards_in_howl_threshold, - uint max_cards_in_cardset) : + double cards_in_bitmap_threshold_percent, + uint num_buckets_in_howl, + double cards_in_howl_threshold_percent, + uint max_cards_in_card_set) : _inline_ptr_bits_per_card(inline_ptr_bits_per_card), _num_cards_in_array(num_cards_in_array), - _max_cards_in_card_set(max_cards_in_cardset), - _cards_in_howl_threshold(max_cards_in_cardset * cards_in_howl_threshold) { + _num_buckets_in_howl(num_buckets_in_howl), + _max_cards_in_card_set(max_cards_in_card_set), + _cards_in_howl_threshold(max_cards_in_card_set * cards_in_howl_threshold_percent), + _num_cards_in_howl_bitmap(G1CardSetHowl::bitmap_size(_max_cards_in_card_set, _num_buckets_in_howl)), + _cards_in_howl_bitmap_threshold(_num_cards_in_howl_bitmap * cards_in_bitmap_threshold_percent), + _log2_num_cards_in_howl_bitmap(log2i_exact(_num_cards_in_howl_bitmap)), + _bitmap_hash_mask(~(~(0) << _log2_num_cards_in_howl_bitmap)) { assert(is_power_of_2(_max_cards_in_card_set), - "max_cards_in_card_set must be a power of 2: %u", _max_cards_in_card_set); - - _num_buckets_in_howl = G1CardSetHowl::num_buckets(_max_cards_in_card_set, _num_cards_in_array, max_buckets_in_howl); + "max_cards_in_card_set must be a power of 2: %u", _max_cards_in_card_set); - _num_cards_in_howl_bitmap = G1CardSetHowl::bitmap_size(_max_cards_in_card_set, _num_buckets_in_howl); - _cards_in_howl_bitmap_threshold = _num_cards_in_howl_bitmap * cards_in_bitmap_threshold; - _log2_num_cards_in_howl_bitmap = log2i_exact(_num_cards_in_howl_bitmap); - _bitmap_hash_mask = ~(~(0) << _log2_num_cards_in_howl_bitmap); + init_card_set_alloc_options(); log_configuration(); } +G1CardSetConfiguration::~G1CardSetConfiguration() { + FREE_C_HEAP_ARRAY(size_t, _card_set_alloc_options); +} + +void G1CardSetConfiguration::init_card_set_alloc_options() { + _card_set_alloc_options = NEW_C_HEAP_ARRAY(G1CardSetAllocOptions, num_mem_object_types(), mtGC); + new (&_card_set_alloc_options[0]) G1CardSetAllocOptions((uint)CardSetHash::get_node_size()); + new (&_card_set_alloc_options[1]) G1CardSetAllocOptions((uint)G1CardSetArray::size_in_bytes(_num_cards_in_array), 2, 256); + new (&_card_set_alloc_options[2]) G1CardSetAllocOptions((uint)G1CardSetBitMap::size_in_bytes(_num_cards_in_howl_bitmap), 2, 256); + new (&_card_set_alloc_options[3]) G1CardSetAllocOptions((uint)G1CardSetHowl::size_in_bytes(_num_buckets_in_howl), 2, 256); +} + void G1CardSetConfiguration::log_configuration() { log_debug_p(gc, remset)("Card Set container configuration: " "InlinePtr #elems %u size %zu " @@ -112,15 +124,8 @@ uint G1CardSetConfiguration::num_cards_in_inline_ptr(uint bits_per_card) { return G1CardSetInlinePtr::max_cards_in_inline_ptr(bits_per_card); } -G1CardSetAllocOptions* G1CardSetConfiguration::mem_object_alloc_options() { - G1CardSetAllocOptions* result = NEW_C_HEAP_ARRAY(G1CardSetAllocOptions, num_mem_object_types(), mtGC); - - result[0] = { (uint)CardSetHash::get_node_size() }; - result[1] = { (uint)G1CardSetArray::size_in_bytes(num_cards_in_array()), 2, 256 }; - result[2] = { (uint)G1CardSetBitMap::size_in_bytes(num_cards_in_howl_bitmap()), 2, 256 }; - result[3] = { (uint)G1CardSetHowl::size_in_bytes(num_buckets_in_howl()), 2, 256 }; - - return result; +const G1CardSetAllocOptions* G1CardSetConfiguration::mem_object_alloc_options(uint idx) { + return &_card_set_alloc_options[idx]; } const char* G1CardSetConfiguration::mem_object_type_name_str(uint index) { diff --git a/src/hotspot/share/gc/g1/g1CardSet.hpp b/src/hotspot/share/gc/g1/g1CardSet.hpp index 5220c2ce2721896b96bb736a88a19eee892cec91..3589ef4988e00cc8b5fd6a843a9c311628c553ef 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.hpp +++ b/src/hotspot/share/gc/g1/g1CardSet.hpp @@ -32,7 +32,6 @@ #include "utilities/lockFreeStack.hpp" class G1CardSetAllocOptions; -class G1CardSetBufferList; class G1CardSetHashTable; class G1CardSetHashTableValue; class G1CardSetMemoryManager; @@ -52,26 +51,38 @@ class G1CardSetConfiguration { uint _inline_ptr_bits_per_card; uint _num_cards_in_array; - uint _num_cards_in_howl_bitmap; uint _num_buckets_in_howl; uint _max_cards_in_card_set; uint _cards_in_howl_threshold; + uint _num_cards_in_howl_bitmap; uint _cards_in_howl_bitmap_threshold; uint _log2_num_cards_in_howl_bitmap; size_t _bitmap_hash_mask; + G1CardSetAllocOptions* _card_set_alloc_options; + + G1CardSetConfiguration(uint inline_ptr_bits_per_card, + uint num_cards_in_array, + double cards_in_bitmap_threshold_percent, + uint num_buckets_in_howl, + double cards_in_howl_threshold_percent, + uint max_cards_in_card_set); + void init_card_set_alloc_options(); + void log_configuration(); public: // Initialize card set configuration from globals. G1CardSetConfiguration(); // Initialize card set configuration from parameters. - G1CardSetConfiguration(uint inline_ptr_bits_per_card, - uint num_cards_in_array, - double cards_in_bitmap_threshold, + // Only for test + G1CardSetConfiguration(uint num_cards_in_array, + double cards_in_bitmap_threshold_percent, uint max_buckets_in_howl, - double cards_in_howl_threshold, - uint max_cards_in_cardset); + double cards_in_howl_threshold_percent, + uint max_cards_in_card_set); + + ~G1CardSetConfiguration(); // Inline pointer configuration uint inline_ptr_bits_per_card() const { return _inline_ptr_bits_per_card; } @@ -108,9 +119,8 @@ public: // Number of distinctly sized memory objects on the card set heap. // Currently contains CHT-Nodes, ArrayOfCards, BitMaps, Howl static constexpr uint num_mem_object_types() { return 4; } - // Returns the memory allocation options for the memory objects on the card set heap. The returned - // array must be freed by the caller. - G1CardSetAllocOptions* mem_object_alloc_options(); + // Returns the memory allocation options for the memory objects on the card set heap. + const G1CardSetAllocOptions* mem_object_alloc_options(uint idx); // For a given memory object, get a descriptive name. static const char* mem_object_type_name_str(uint index); diff --git a/src/hotspot/share/gc/g1/g1CardSetContainers.cpp b/src/hotspot/share/gc/g1/g1CardSetContainers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..adee0a8e5a94706dc20a5cc431ccc11adc5f13f3 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1CardSetContainers.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" + +#include "gc/g1/g1CardSetContainers.hpp" +#include "utilities/globalDefinitions.hpp" + +// The only limitation is from the G1CardSetArray. +uint G1CardSetContainer::LogCardsPerRegionLimit = sizeof(G1CardSetArray::EntryDataType) * BitsPerByte; diff --git a/src/hotspot/share/gc/g1/g1CardSetContainers.hpp b/src/hotspot/share/gc/g1/g1CardSetContainers.hpp index 267af7d36950547b5ee8c5cc8226f3dd57d7ee20..cd6868c8c176abd75c6525f6f31eea23113dbc06 100644 --- a/src/hotspot/share/gc/g1/g1CardSetContainers.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetContainers.hpp @@ -177,6 +177,9 @@ public: void set_next(G1CardSetContainer* next) { _next = next; } + + // Log of largest card index that can be stored in any G1CardSetContainer + static uint LogCardsPerRegionLimit; }; class G1CardSetArray : public G1CardSetContainer { diff --git a/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp b/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp index 7562f5b18a035b5acf8b88ccb5d042ff8b2d9288..645f4a9ea3ea2be89030e76bdfc7412d22ddaad7 100644 --- a/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp @@ -27,6 +27,7 @@ #include "gc/g1/g1CardSetContainers.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" +#include "utilities/globalDefinitions.hpp" inline G1CardSetInlinePtr::CardSetPtr G1CardSetInlinePtr::merge(CardSetPtr orig_value, uint card_in_region, uint idx, uint bits_per_card) { assert((idx & (SizeFieldMask >> SizeFieldPos)) == idx, "Index %u too large to fit into size field", idx); diff --git a/src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.hpp b/src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.hpp index b0c0864f9f5e4ea24b8b6114499af9dfb4b9f3b1..3d617243aa4c3fbd5f0186ace6484e81157d4b4b 100644 --- a/src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.hpp @@ -31,8 +31,6 @@ #include "utilities/growableArray.hpp" #include "utilities/ticks.hpp" -class G1CardSetBuffer; - // Task handling deallocation of free card set memory. class G1CardSetFreeMemoryTask : public G1ServiceTask { diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp index ae14e555774e9ab357fbdd69da5dc15221eef4dc..fe5beceef144f3624cfb22e80051af626062df67 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp @@ -30,99 +30,20 @@ #include "utilities/formatBuffer.hpp" #include "utilities/ostream.hpp" -G1CardSetBuffer::G1CardSetBuffer(uint elem_size, uint num_instances, G1CardSetBuffer* next) : - _elem_size(elem_size), _num_elems(num_instances), _next(next), _next_allocate(0) { - - _buffer = NEW_C_HEAP_ARRAY(char, (size_t)_num_elems * elem_size, mtGCCardSet); -} - -G1CardSetBuffer::~G1CardSetBuffer() { - FREE_C_HEAP_ARRAY(mtGCCardSet, _buffer); -} - -void* G1CardSetBuffer::get_new_buffer_elem() { - if (_next_allocate >= _num_elems) { - return nullptr; - } - uint result = Atomic::fetch_and_add(&_next_allocate, 1u, memory_order_relaxed); - if (result >= _num_elems) { - return nullptr; - } - void* r = _buffer + (uint)result * _elem_size; - return r; -} - -void G1CardSetBufferList::bulk_add(G1CardSetBuffer& first, G1CardSetBuffer& last, size_t num, size_t mem_size) { - _list.prepend(first, last); - Atomic::add(&_num_buffers, num, memory_order_relaxed); - Atomic::add(&_mem_size, mem_size, memory_order_relaxed); -} - -void G1CardSetBufferList::print_on(outputStream* out, const char* prefix) { - out->print_cr("%s: buffers %zu size %zu", prefix, Atomic::load(&_num_buffers), Atomic::load(&_mem_size)); -} - -G1CardSetBuffer* G1CardSetBufferList::get() { - GlobalCounter::CriticalSection cs(Thread::current()); - - G1CardSetBuffer* result = _list.pop(); - if (result != nullptr) { - Atomic::dec(&_num_buffers, memory_order_relaxed); - Atomic::sub(&_mem_size, result->mem_size(), memory_order_relaxed); - } - return result; -} - -G1CardSetBuffer* G1CardSetBufferList::get_all(size_t& num_buffers, size_t& mem_size) { - GlobalCounter::CriticalSection cs(Thread::current()); - - G1CardSetBuffer* result = _list.pop_all(); - num_buffers = Atomic::load(&_num_buffers); - mem_size = Atomic::load(&_mem_size); - - if (result != nullptr) { - Atomic::sub(&_num_buffers, num_buffers, memory_order_relaxed); - Atomic::sub(&_mem_size, mem_size, memory_order_relaxed); - } - return result; -} - -void G1CardSetBufferList::free_all() { - size_t num_freed = 0; - size_t mem_size_freed = 0; - G1CardSetBuffer* cur; - - while ((cur = _list.pop()) != nullptr) { - mem_size_freed += cur->mem_size(); - num_freed++; - delete cur; - } - - Atomic::sub(&_num_buffers, num_freed, memory_order_relaxed); - Atomic::sub(&_mem_size, mem_size_freed, memory_order_relaxed); -} template G1CardSetAllocator::G1CardSetAllocator(const char* name, - const G1CardSetAllocOptions& buffer_options, + const G1CardSetAllocOptions* buffer_options, G1CardSetBufferList* free_buffer_list) : - _alloc_options(buffer_options), - _first(nullptr), - _last(nullptr), - _num_buffers(0), - _mem_size(0), - _free_buffer_list(free_buffer_list), + _segmented_array(name, buffer_options, free_buffer_list), _transfer_lock(false), _free_nodes_list(), _pending_nodes_list(), _num_pending_nodes(0), - _num_free_nodes(0), - _num_allocated_nodes(0), - _num_available_nodes(0) + _num_free_nodes(0) { - assert(elem_size() >= sizeof(G1CardSetContainer), "Element instance size %u for allocator %s too small", - elem_size(), name); - assert(_free_buffer_list != nullptr, "precondition!"); + uint elem_size = _segmented_array.elem_size(); + assert(elem_size >= sizeof(G1CardSetContainer), "Element instance size %u for allocator %s too small", elem_size, name); } template @@ -164,7 +85,6 @@ bool G1CardSetAllocator::try_transfer_pending() { template void G1CardSetAllocator::free(Elem* elem) { assert(elem != nullptr, "precondition"); - assert(elem_size() >= sizeof(G1CardSetContainer), "size mismatch"); // Desired minimum transfer batch size. There is relatively little // importance to the specific number. It shouldn't be too big, else // we're wasting space when the release rate is low. If the release @@ -192,47 +112,28 @@ template void G1CardSetAllocator::drop_all() { _free_nodes_list.pop_all(); _pending_nodes_list.pop_all(); - G1CardSetBuffer* cur = Atomic::load_acquire(&_first); - - if (cur != nullptr) { - assert(_last != nullptr, "If there is at least one element, there must be a last one."); - - G1CardSetBuffer* first = cur; -#ifdef ASSERT - // Check list consistency. - G1CardSetBuffer* last = cur; - uint num_buffers = 0; - size_t mem_size = 0; - while (cur != nullptr) { - mem_size += cur->mem_size(); - num_buffers++; - - G1CardSetBuffer* next = cur->next(); - last = cur; - cur = next; - } -#endif - assert(num_buffers == _num_buffers, "Buffer count inconsistent %u %u", num_buffers, _num_buffers); - assert(mem_size == _mem_size, "Memory size inconsistent"); - assert(last == _last, "Inconsistent last element"); - - _free_buffer_list->bulk_add(*first, *_last, _num_buffers, _mem_size); - } - - _first = nullptr; - _last = nullptr; - _num_available_nodes = 0; - _num_allocated_nodes = 0; _num_pending_nodes = 0; - _num_buffers = 0; - _mem_size = 0; _num_free_nodes = 0; + _segmented_array.drop_all(); } template void G1CardSetAllocator::print(outputStream* os) { + uint num_allocated_nodes = _segmented_array.num_allocated_nodes(); + uint num_available_nodes = _segmented_array.num_available_nodes(); + uint highest = _segmented_array.first_array_buffer() != nullptr + ? _segmented_array.first_array_buffer()->num_elems() + : 0; + uint num_buffers = _segmented_array.num_buffers(); os->print("MA " PTR_FORMAT ": %u elems pending (allocated %u available %u) used %.3f highest %u buffers %u size %zu ", - p2i(this), _num_pending_nodes, _num_allocated_nodes, _num_available_nodes, percent_of(_num_allocated_nodes - _num_pending_nodes, _num_available_nodes), _first != nullptr ? _first->num_elems() : 0, _num_buffers, mem_size()); + p2i(this), + _num_pending_nodes, + num_allocated_nodes, + num_available_nodes, + percent_of(num_allocated_nodes - _num_pending_nodes, num_available_nodes), + highest, + num_buffers, + mem_size()); } G1CardSetMemoryStats::G1CardSetMemoryStats() { @@ -411,13 +312,11 @@ G1CardSetMemoryManager::G1CardSetMemoryManager(G1CardSetConfiguration* config, _allocators = NEW_C_HEAP_ARRAY(G1CardSetAllocator, _config->num_mem_object_types(), mtGC); - G1CardSetAllocOptions* alloc_options = _config->mem_object_alloc_options(); for (uint i = 0; i < num_mem_object_types(); i++) { new (&_allocators[i]) G1CardSetAllocator(_config->mem_object_type_name_str(i), - alloc_options[i], + _config->mem_object_alloc_options(i), free_list_pool->free_list(i)); } - FREE_C_HEAP_ARRAY(size_t, alloc_options); } uint G1CardSetMemoryManager::num_mem_object_types() const { diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.hpp b/src/hotspot/share/gc/g1/g1CardSetMemory.hpp index 4cc03c913b9f9fbc6deeef55e3f4f3c638d3f722..df64c4af2b8afac7bde03f009bf3c17451954ee5 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.hpp @@ -27,6 +27,8 @@ #include "gc/g1/g1CardSet.hpp" #include "gc/g1/g1CardSetContainers.hpp" +#include "gc/g1/g1CardSetContainers.inline.hpp" +#include "gc/g1/g1SegmentedArray.hpp" #include "memory/allocation.hpp" #include "utilities/growableArray.hpp" #include "utilities/lockFreeStack.hpp" @@ -36,130 +38,32 @@ class outputStream; // Collects G1CardSetAllocator options/heuristics. Called by G1CardSetAllocator // to determine the next size of the allocated G1CardSetBuffer. -class G1CardSetAllocOptions { - uint _elem_size; - uint _initial_num_elems; - // Defines a limit to the number of elements in the buffer - uint _max_num_elems; +class G1CardSetAllocOptions : public G1SegmentedArrayAllocOptions { + static const uint MinimumBufferSize = 8; + static const uint MaximumBufferSize = UINT_MAX / 2; - uint exponential_expand(uint prev_num_elems) { + uint exponential_expand(uint prev_num_elems) const { return clamp(prev_num_elems * 2, _initial_num_elems, _max_num_elems); } public: static const uint BufferAlignment = 8; - static const uint MinimumBufferSize = 8; - static const uint MaximumBufferSize = UINT_MAX / 2; G1CardSetAllocOptions(uint elem_size, uint initial_num_elems = MinimumBufferSize, uint max_num_elems = MaximumBufferSize) : - _elem_size(align_up(elem_size, BufferAlignment)), - _initial_num_elems(initial_num_elems), - _max_num_elems(max_num_elems) { + G1SegmentedArrayAllocOptions(align_up(elem_size, BufferAlignment), initial_num_elems, max_num_elems, BufferAlignment) { } - uint next_num_elems(uint prev_num_elems) { + virtual uint next_num_elems(uint prev_num_elems) const override { return exponential_expand(prev_num_elems); } - - uint elem_size () const {return _elem_size;} -}; - -// A single buffer/arena containing _num_elems blocks of memory of _elem_size. -// G1CardSetBuffers can be linked together using a singly linked list. -class G1CardSetBuffer : public CHeapObj { - uint _elem_size; - uint _num_elems; - - G1CardSetBuffer* volatile _next; - - char* _buffer; // Actual data. - - // Index into the next free block to allocate into. Full if equal (or larger) - // to _num_elems (can be larger because we atomically increment this value and - // check only afterwards if the allocation has been successful). - uint volatile _next_allocate; - -public: - G1CardSetBuffer(uint elem_size, uint num_elems, G1CardSetBuffer* next); - ~G1CardSetBuffer(); - - G1CardSetBuffer* volatile* next_addr() { return &_next; } - - void* get_new_buffer_elem(); - - uint num_elems() const { return _num_elems; } - - G1CardSetBuffer* next() const { return _next; } - - void set_next(G1CardSetBuffer* next) { - assert(next != this, " loop condition"); - _next = next; - } - - void reset(G1CardSetBuffer* next) { - _next_allocate = 0; - assert(next != this, " loop condition"); - set_next(next); - memset((void*)_buffer, 0, (size_t)_num_elems * _elem_size); - } - - uint elem_size() const { return _elem_size; } - - size_t mem_size() const { return sizeof(*this) + (size_t)_num_elems * _elem_size; } - - bool is_full() const { return _next_allocate >= _num_elems; } }; -// Set of (free) G1CardSetBuffers. The assumed usage is that allocation -// to it and removal of elements is strictly separate, but every action may be -// performed by multiple threads at the same time. -// Counts and memory usage are current on a best-effort basis if accessed concurrently. -class G1CardSetBufferList { - static G1CardSetBuffer* volatile* next_ptr(G1CardSetBuffer& node) { - return node.next_addr(); - } - typedef LockFreeStack NodeStack; - - NodeStack _list; - - volatile size_t _num_buffers; - volatile size_t _mem_size; - -public: - G1CardSetBufferList() : _list(), _num_buffers(0), _mem_size(0) { } - ~G1CardSetBufferList() { free_all(); } - - void bulk_add(G1CardSetBuffer& first, G1CardSetBuffer& last, size_t num, size_t mem_size); - void add(G1CardSetBuffer& elem) { _list.prepend(elem); } +typedef G1SegmentedArrayBuffer G1CardSetBuffer; - G1CardSetBuffer* get(); - G1CardSetBuffer* get_all(size_t& num_buffers, size_t& mem_size); - - // Give back all memory to the OS. - void free_all(); - - void print_on(outputStream* out, const char* prefix = ""); - - size_t num_buffers() const { return Atomic::load(&_num_buffers); } - size_t mem_size() const { return Atomic::load(&_mem_size); } -}; +typedef G1SegmentedArrayBufferList G1CardSetBufferList; // Arena-like allocator for (card set) heap memory objects (Elem elements). // -// Actual allocation from the C heap occurs on G1CardSetBuffer basis, i.e. sets -// of elements. The assumed allocation pattern for these G1CardSetBuffer elements -// is assumed to be strictly two-phased: -// -// - in the first phase, G1CardSetBuffers are allocated from the C heap (or a free -// list given at initialization time). This allocation may occur in parallel. This -// typically corresponds to a single mutator phase, but may extend over multiple. -// -// - in the second phase, G1CardSetBuffers are given back in bulk to the free list. -// This is typically done during a GC pause. -// -// Some third party is responsible for giving back memory from the free list to -// the operating system. -// // Allocation and deallocation in the first phase on G1CardSetContainer basis // may occur by multiple threads at once. // @@ -168,7 +72,7 @@ public: // none, this class allocates a new G1CardSetBuffer (allocated from the C heap, // asking the G1CardSetAllocOptions instance about sizes etc) and uses that one. // -// The G1CardSetContainerOnHeaps free list is a linked list of G1CardSetContainers +// The NodeStack free list is a linked list of G1CardSetContainers // within all G1CardSetBuffer instances allocated so far. It uses a separate // pending list and global synchronization to avoid the ABA problem when the // user frees a memory object. @@ -184,24 +88,13 @@ template class G1CardSetAllocator { // G1CardSetBuffer management. - // G1CardSetAllocOptions provides parameters for allocation buffer - // sizing and expansion. - G1CardSetAllocOptions _alloc_options; - - G1CardSetBuffer* volatile _first; // The (start of the) list of all buffers. - G1CardSetBuffer* _last; // The last element of the list of all buffers. - volatile uint _num_buffers; // Number of assigned buffers to this allocator. - volatile size_t _mem_size; // Memory used by all buffers. - - G1CardSetBufferList* _free_buffer_list; // The global free buffer list to - // preferentially get new buffers from. - + typedef G1SegmentedArray SegmentedArray; // G1CardSetContainer node management within the G1CardSetBuffers allocated // by this allocator. - static G1CardSetContainer* volatile* next_ptr(G1CardSetContainer& node); typedef LockFreeStack NodeStack; + SegmentedArray _segmented_array; volatile bool _transfer_lock; NodeStack _free_nodes_list; NodeStack _pending_nodes_list; @@ -209,9 +102,6 @@ class G1CardSetAllocator { volatile uint _num_pending_nodes; // Number of nodes in the pending list. volatile uint _num_free_nodes; // Number of nodes in the free list. - volatile uint _num_allocated_nodes; // Number of total nodes allocated and in use. - volatile uint _num_available_nodes; // Number of nodes available in all buffers (allocated + free + pending + not yet used). - // Try to transfer nodes from _pending_nodes_list to _free_nodes_list, with a // synchronization delay for any in-progress pops from the _free_nodes_list // to solve ABA here. @@ -219,13 +109,9 @@ class G1CardSetAllocator { uint num_free_elems() const; - G1CardSetBuffer* create_new_buffer(G1CardSetBuffer* const prev); - - uint elem_size() const { return _alloc_options.elem_size(); } - public: G1CardSetAllocator(const char* name, - const G1CardSetAllocOptions& buffer_options, + const G1CardSetAllocOptions* buffer_options, G1CardSetBufferList* free_buffer_list); ~G1CardSetAllocator() { drop_all(); @@ -238,17 +124,17 @@ public: // be called in a globally synchronized area. void drop_all(); - uint num_buffers() const; - size_t mem_size() const { return sizeof(*this) + - num_buffers() * sizeof(G1CardSetBuffer) + (size_t)_num_available_nodes * elem_size(); + _segmented_array.num_buffers() * sizeof(G1CardSetBuffer) + _segmented_array.num_available_nodes() * _segmented_array.elem_size(); } size_t wasted_mem_size() const { - return ((size_t)_num_available_nodes - (_num_allocated_nodes - _num_pending_nodes)) * elem_size(); + return (_segmented_array.num_available_nodes() - (_segmented_array.num_allocated_nodes() - _num_pending_nodes)) * _segmented_array.elem_size(); } + inline uint num_buffers() { return _segmented_array.num_buffers(); } + void print(outputStream* os); }; diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp b/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp index 4fd68d5d55a66bde7fa8d9293695ab6e3a9ff01f..0eaac226b593e62c07f187abb27ab92d19f05df2 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp @@ -27,6 +27,7 @@ #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CardSetContainers.hpp" +#include "gc/g1/g1SegmentedArray.inline.hpp" #include "utilities/ostream.hpp" #include "gc/g1/g1CardSetContainers.inline.hpp" @@ -37,42 +38,9 @@ G1CardSetContainer* volatile* G1CardSetAllocator::next_ptr(G1CardSetContai return node.next_addr(); } -template -G1CardSetBuffer* G1CardSetAllocator::create_new_buffer(G1CardSetBuffer* const prev) { - - // Take an existing buffer if available. - G1CardSetBuffer* next = _free_buffer_list->get(); - if (next == nullptr) { - uint prev_num_elems = (prev != nullptr) ? prev->num_elems() : 0; - uint num_elems = _alloc_options.next_num_elems(prev_num_elems); - next = new G1CardSetBuffer(elem_size(), num_elems, prev); - } else { - assert(elem_size() == next->elem_size() , "Mismatch %d != %d Elem %zu", elem_size(), next->elem_size(), sizeof(Elem)); - next->reset(prev); - } - - // Install it as current allocation buffer. - G1CardSetBuffer* old = Atomic::cmpxchg(&_first, prev, next); - if (old != prev) { - // Somebody else installed the buffer, use that one. - delete next; - return old; - } else { - // Did we install the first element in the list? If so, this is also the last. - if (prev == nullptr) { - _last = next; - } - // Successfully installed the buffer into the list. - Atomic::inc(&_num_buffers, memory_order_relaxed); - Atomic::add(&_mem_size, next->mem_size(), memory_order_relaxed); - Atomic::add(&_num_available_nodes, next->num_elems(), memory_order_relaxed); - return next; - } -} - template Elem* G1CardSetAllocator::allocate() { - assert(elem_size() > 0, "instance size not set."); + assert(_segmented_array.elem_size() > 0, "instance size not set."); if (num_free_elems() > 0) { // Pop under critical section to deal with ABA problem @@ -88,22 +56,9 @@ Elem* G1CardSetAllocator::allocate() { } } - G1CardSetBuffer* cur = Atomic::load_acquire(&_first); - if (cur == nullptr) { - cur = create_new_buffer(cur); - } - - while (true) { - Elem* elem = (Elem*)cur->get_new_buffer_elem(); - if (elem != nullptr) { - Atomic::inc(&_num_allocated_nodes, memory_order_relaxed); - guarantee(is_aligned(elem, 8), "result " PTR_FORMAT " not aligned", p2i(elem)); - return elem; - } - // The buffer is full. Next round. - assert(cur->is_full(), "must be"); - cur = create_new_buffer(cur); - } + Elem* elem = _segmented_array.allocate(); + assert(elem != nullptr, "must be"); + return elem; } inline uint8_t* G1CardSetMemoryManager::allocate(uint type) { @@ -119,11 +74,6 @@ inline void G1CardSetMemoryManager::free_node(void* value) { free(0, value); } -template -inline uint G1CardSetAllocator::num_buffers() const { - return Atomic::load(&_num_buffers); -} - template inline uint G1CardSetAllocator::num_free_elems() const { return Atomic::load(&_num_free_nodes); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index f59485f3407de869ddabcefb11308f2667a6fb5f..ee47e55f9d2ec48e8e7c0b3dd66a6c5789a91e56 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -32,7 +32,7 @@ #include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1Arguments.hpp" #include "gc/g1/g1BarrierSet.hpp" -#include "gc/g1/g1BatchedGangTask.hpp" +#include "gc/g1/g1BatchedTask.hpp" #include "gc/g1/g1CardSetFreeMemoryTask.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" @@ -138,7 +138,7 @@ void G1RegionMappingChangedListener::on_commit(uint start_idx, size_t num_region reset_from_card_cache(start_idx, num_regions); } -void G1CollectedHeap::run_batch_task(G1BatchedGangTask* cl) { +void G1CollectedHeap::run_batch_task(G1BatchedTask* cl) { uint num_workers = MAX2(1u, MIN2(cl->num_workers_estimate(), workers()->active_workers())); cl->set_max_workers(num_workers); workers()->run_task(cl, num_workers); @@ -498,9 +498,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size) { void G1CollectedHeap::begin_archive_alloc_range(bool open) { assert_at_safepoint_on_vm_thread(); - if (_archive_allocator == NULL) { - _archive_allocator = G1ArchiveAllocator::create_allocator(this, open); - } + assert(_archive_allocator == nullptr, "should not be initialized"); + _archive_allocator = G1ArchiveAllocator::create_allocator(this, open); } bool G1CollectedHeap::is_archive_alloc_too_large(size_t word_size) { @@ -512,9 +511,9 @@ bool G1CollectedHeap::is_archive_alloc_too_large(size_t word_size) { HeapWord* G1CollectedHeap::archive_mem_allocate(size_t word_size) { assert_at_safepoint_on_vm_thread(); - assert(_archive_allocator != NULL, "_archive_allocator not initialized"); + assert(_archive_allocator != nullptr, "_archive_allocator not initialized"); if (is_archive_alloc_too_large(word_size)) { - return NULL; + return nullptr; } return _archive_allocator->archive_mem_allocate(word_size); } @@ -522,13 +521,13 @@ HeapWord* G1CollectedHeap::archive_mem_allocate(size_t word_size) { void G1CollectedHeap::end_archive_alloc_range(GrowableArray* ranges, size_t end_alignment_in_bytes) { assert_at_safepoint_on_vm_thread(); - assert(_archive_allocator != NULL, "_archive_allocator not initialized"); + assert(_archive_allocator != nullptr, "_archive_allocator not initialized"); // Call complete_archive to do the real work, filling in the MemRegion // array with the archive regions. _archive_allocator->complete_archive(ranges, end_alignment_in_bytes); delete _archive_allocator; - _archive_allocator = NULL; + _archive_allocator = nullptr; } bool G1CollectedHeap::check_archive_addresses(MemRegion* ranges, size_t count) { @@ -1274,7 +1273,7 @@ HeapWord* G1CollectedHeap::expand_and_allocate(size_t word_size) { return NULL; } -bool G1CollectedHeap::expand(size_t expand_bytes, WorkGang* pretouch_workers, double* expand_time_ms) { +bool G1CollectedHeap::expand(size_t expand_bytes, WorkerThreads* pretouch_workers, double* expand_time_ms) { size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes); aligned_expand_bytes = align_up(aligned_expand_bytes, HeapRegion::GrainBytes); @@ -1449,7 +1448,7 @@ G1CollectedHeap::G1CollectedHeap() : _verifier(NULL), _summary_bytes_used(0), _bytes_used_during_gc(0), - _archive_allocator(NULL), + _archive_allocator(nullptr), _survivor_evac_stats("Young", YoungPLABSize, PLABWeight), _old_evac_stats("Old", OldPLABSize, PLABWeight), _monitoring_support(nullptr), @@ -1672,6 +1671,8 @@ jint G1CollectedHeap::initialize() { guarantee(HeapRegion::CardsPerRegion < max_cards_per_region, "too many cards per region"); + HeapRegionRemSet::initialize(_reserved); + FreeRegionList::set_unrealistically_long_length(max_regions() + 1); _bot = new G1BlockOffsetTable(reserved(), bot_storage); @@ -1683,7 +1684,7 @@ jint G1CollectedHeap::initialize() { _humongous_reclaim_candidates.initialize(reserved(), granularity); } - _workers = new WorkGang("GC Thread", ParallelGCThreads); + _workers = new WorkerThreads("GC Thread", ParallelGCThreads); if (_workers == NULL) { return JNI_ENOMEM; } @@ -1861,9 +1862,7 @@ void G1CollectedHeap::iterate_hcc_closure(G1CardTableEntryClosure* cl, uint work // Computes the sum of the storage used by the various regions. size_t G1CollectedHeap::used() const { size_t result = _summary_bytes_used + _allocator->used_in_alloc_regions(); - if (_archive_allocator != NULL) { - result += _archive_allocator->used(); - } + assert(_archive_allocator == nullptr, "must be, should not contribute to used"); return result; } @@ -2694,15 +2693,15 @@ bool G1CollectedHeap::is_potential_eager_reclaim_candidate(HeapRegion* r) const } #ifndef PRODUCT -void G1CollectedHeap::verify_region_attr_remset_update() { +void G1CollectedHeap::verify_region_attr_remset_is_tracked() { class VerifyRegionAttrRemSet : public HeapRegionClosure { public: virtual bool do_heap_region(HeapRegion* r) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - bool const needs_remset_update = g1h->region_attr(r->bottom()).needs_remset_update(); - assert(r->rem_set()->is_tracked() == needs_remset_update, + bool const remset_is_tracked = g1h->region_attr(r->bottom()).remset_is_tracked(); + assert(r->rem_set()->is_tracked() == remset_is_tracked, "Region %u remset tracking status (%s) different to region attribute (%s)", - r->hrm_index(), BOOL_TO_STR(r->rem_set()->is_tracked()), BOOL_TO_STR(needs_remset_update)); + r->hrm_index(), BOOL_TO_STR(r->rem_set()->is_tracked()), BOOL_TO_STR(remset_is_tracked)); return false; } } cl; @@ -2752,6 +2751,7 @@ void G1CollectedHeap::verify_before_young_collection(G1HeapVerifier::G1VerifyTyp return; } Ticks start = Ticks::now(); + _verifier->prepare_for_verify(); _verifier->verify_region_sets_optional(); _verifier->verify_dirty_young_regions(); if (VerifyRememberedSets) { @@ -3196,9 +3196,7 @@ void G1CollectedHeap::rebuild_region_sets(bool free_list_only) { if (!free_list_only) { set_used(cl.total_used()); - if (_archive_allocator != NULL) { - _archive_allocator->clear_used(); - } + assert(_archive_allocator == nullptr, "must be, should not contribute to used"); } assert_used_and_recalculate_used_equal(this); } @@ -3393,9 +3391,7 @@ void G1CollectedHeap::update_used_after_gc(bool evacuation_failed) { set_used(recalculate_used()); - if (_archive_allocator != NULL) { - _archive_allocator->clear_used(); - } + assert(_archive_allocator == nullptr, "must be, should not contribute to used"); } else { // The "used" of the the collection set have already been subtracted // when they were freed. Add in the bytes used. diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 55c271f10c6c7fa71a0f9d47315fd988a8f666f9..80cc82c4af8241a3e0ba30c162375b6cde879739 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -64,7 +64,7 @@ // Forward declarations class G1Allocator; class G1ArchiveAllocator; -class G1BatchedGangTask; +class G1BatchedTask; class G1CardTableEntryClosure; class G1ConcurrentMark; class G1ConcurrentMarkThread; @@ -83,7 +83,7 @@ class MemoryPool; class nmethod; class ReferenceProcessor; class STWGCTimer; -class WorkGang; +class WorkerThreads; typedef OverflowTaskQueue G1ScannerTasksQueue; typedef GenericTaskQueueSet G1ScannerTasksQueueSet; @@ -145,7 +145,7 @@ private: G1ServiceTask* _periodic_gc_task; G1CardSetFreeMemoryTask* _free_card_set_memory_task; - WorkGang* _workers; + WorkerThreads* _workers; G1CardTable* _card_table; Ticks _collection_pause_end; @@ -538,10 +538,10 @@ public: G1ServiceThread* service_thread() const { return _service_thread; } - WorkGang* workers() const { return _workers; } + WorkerThreads* workers() const { return _workers; } - // Run the given batch task using the work gang. - void run_batch_task(G1BatchedGangTask* cl); + // Run the given batch task using the workers. + void run_batch_task(G1BatchedTask* cl); G1Allocator* allocator() { return _allocator; @@ -572,7 +572,7 @@ public: // Returns true if the heap was expanded by the requested amount; // false otherwise. // (Rounds up to a HeapRegion boundary.) - bool expand(size_t expand_bytes, WorkGang* pretouch_workers = NULL, double* expand_time_ms = NULL); + bool expand(size_t expand_bytes, WorkerThreads* pretouch_workers = NULL, double* expand_time_ms = NULL); bool expand_single_region(uint node_index); // Returns the PLAB statistics for a given destination. @@ -620,7 +620,7 @@ public: // Verify that the G1RegionAttr remset tracking corresponds to actual remset tracking // for all regions. - void verify_region_attr_remset_update() PRODUCT_RETURN; + void verify_region_attr_remset_is_tracked() PRODUCT_RETURN; bool is_user_requested_concurrent_full_gc(GCCause::Cause cause); @@ -1317,7 +1317,7 @@ public: // WhiteBox testing support. virtual bool supports_concurrent_gc_breakpoints() const; - virtual WorkGang* safepoint_workers() { return _workers; } + virtual WorkerThreads* safepoint_workers() { return _workers; } virtual bool is_archived_object(oop object) const; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 0fef0a944492f3ab49760ce431dab9a235436318..2e0f6028a5da781bc7cdf3532e7b110f45ddef1f 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -189,7 +189,7 @@ void G1CollectedHeap::register_new_survivor_region_with_region_attr(HeapRegion* } void G1CollectedHeap::register_region_with_region_attr(HeapRegion* r) { - _region_attr.set_has_remset(r->hrm_index(), r->rem_set()->is_tracked()); + _region_attr.set_remset_is_tracked(r->hrm_index(), r->rem_set()->is_tracked()); } void G1CollectedHeap::register_old_region_with_region_attr(HeapRegion* r) { diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 9c385a0de441cadb8d6f7e71215b75a28c3b5298..f44679f8aaed59dcfd7b48c58cbf080c59cffc40 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -513,7 +513,7 @@ bool G1CollectionSet::finalize_optional_for_evacuation(double remaining_pause_ti stop_incremental_building(); - _g1h->verify_region_attr_remset_update(); + _g1h->verify_region_attr_remset_is_tracked(); return num_selected_regions > 0; } @@ -530,7 +530,7 @@ void G1CollectionSet::abandon_optional_collection_set(G1ParScanThreadStateSet* p } free_optional_regions(); - _g1h->verify_region_attr_remset_update(); + _g1h->verify_region_attr_remset_is_tracked(); } #ifdef ASSERT diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp index 3086cff0903af2544f658a0ba8ddc5c049d35dad..e4acef5c2f3c35dbf7421d830f9d4cc9d8a083ac 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp @@ -26,7 +26,7 @@ #define SHARE_GC_G1_G1COLLECTIONSETCANDIDATES_HPP #include "gc/g1/g1CollectionSetCandidates.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" #include "runtime/globals.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp index 9136144f6cc13063d9576db398d9f95caf8ba50f..e7c78220943df6a3ce288f6737b124ec51c6ed33 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp @@ -69,7 +69,7 @@ static int order_regions(HeapRegion* hr1, HeapRegion* hr2) { // put them into some work area unsorted. At the end the array is sorted and // copied into the G1CollectionSetCandidates instance; the caller will be the new // owner of this object. -class G1BuildCandidateRegionsTask : public AbstractGangTask { +class G1BuildCandidateRegionsTask : public WorkerTask { // Work area for building the set of collection set candidates. Contains references // to heap regions with their GC efficiencies calculated. To reduce contention @@ -223,7 +223,7 @@ class G1BuildCandidateRegionsTask : public AbstractGangTask { public: G1BuildCandidateRegionsTask(uint max_num_regions, uint chunk_size, uint num_workers) : - AbstractGangTask("G1 Build Candidate Regions"), + WorkerTask("G1 Build Candidate Regions"), _g1h(G1CollectedHeap::heap()), _hrclaimer(num_workers), _num_regions_added(0), @@ -311,7 +311,7 @@ void G1CollectionSetChooser::prune(G1CollectionSetCandidates* candidates) { } } -G1CollectionSetCandidates* G1CollectionSetChooser::build(WorkGang* workers, uint max_num_regions) { +G1CollectionSetCandidates* G1CollectionSetChooser::build(WorkerThreads* workers, uint max_num_regions) { uint num_workers = workers->active_workers(); uint chunk_size = calculate_work_chunk_size(num_workers, max_num_regions); diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp index 2fadcd8945b89afcec9896e811e2aeb91cb2e084..5692a0c407e273dfa07ab877b03e7a88c0f752cd 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp @@ -30,7 +30,7 @@ #include "runtime/globals.hpp" class G1CollectionSetCandidates; -class WorkGang; +class WorkerThreads; // Helper class to calculate collection set candidates, and containing some related // methods. @@ -59,7 +59,7 @@ public: // Build and return set of collection set candidates sorted by decreasing gc // efficiency. - static G1CollectionSetCandidates* build(WorkGang* workers, uint max_num_regions); + static G1CollectionSetCandidates* build(WorkerThreads* workers, uint max_num_regions); }; #endif // SHARE_GC_G1_G1COLLECTIONSETCHOOSER_HPP diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index 975d0873d0822d53f2723e87b0ea28e4daaca0fd..b0248b63d9861c5c9e8e6702f8989782770ad40f 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -98,7 +98,7 @@ public: // Phase getters bool in_young_only_phase() const { return _in_young_only_phase && !_in_full_gc; } - bool in_mixed_phase() const { return !in_young_only_phase() && !_in_full_gc; } + bool in_mixed_phase() const { return !_in_young_only_phase && !_in_full_gc; } // Specific pauses bool in_young_gc_before_mixed() const { return _in_young_gc_before_mixed; } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index e9a52f02c8315be05b6d194338d72ddaf3f9ab21..9d533cb89cbd4c4d349012ee942285dc85bf7e3c 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -27,7 +27,7 @@ #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" #include "gc/g1/g1BarrierSet.hpp" -#include "gc/g1/g1BatchedGangTask.hpp" +#include "gc/g1/g1BatchedTask.hpp" #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorState.hpp" @@ -432,7 +432,7 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _num_concurrent_workers = ConcGCThreads; _max_concurrent_workers = _num_concurrent_workers; - _concurrent_workers = new WorkGang("G1 Conc", _max_concurrent_workers); + _concurrent_workers = new WorkerThreads("G1 Conc", _max_concurrent_workers); _concurrent_workers->initialize_workers(); if (!_global_mark_stack.initialize(MarkStackSize, MarkStackSizeMax)) { @@ -580,7 +580,7 @@ G1ConcurrentMark::~G1ConcurrentMark() { ShouldNotReachHere(); } -class G1ClearBitMapTask : public AbstractGangTask { +class G1ClearBitMapTask : public WorkerTask { public: static size_t chunk_size() { return M; } @@ -674,7 +674,7 @@ private: public: G1ClearBitMapTask(G1ConcurrentMark* cm, uint n_workers, bool suspendible) : - AbstractGangTask("G1 Clear Bitmap"), + WorkerTask("G1 Clear Bitmap"), _cl(cm, suspendible), _hr_claimer(n_workers), _suspendible(suspendible) @@ -690,7 +690,7 @@ public: } }; -void G1ConcurrentMark::clear_next_bitmap(WorkGang* workers, bool may_yield) { +void G1ConcurrentMark::clear_next_bitmap(WorkerThreads* workers, bool may_yield) { assert(may_yield || SafepointSynchronize::is_at_safepoint(), "Non-yielding bitmap clear only allowed at safepoint."); size_t const num_bytes_to_clear = (HeapRegion::GrainBytes * _g1h->num_regions()) / G1CMBitMap::heap_map_factor(); @@ -723,17 +723,17 @@ void G1ConcurrentMark::cleanup_for_next_mark() { guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); } -void G1ConcurrentMark::clear_next_bitmap(WorkGang* workers) { +void G1ConcurrentMark::clear_next_bitmap(WorkerThreads* workers) { assert_at_safepoint_on_vm_thread(); // To avoid fragmentation the full collection requesting to clear the bitmap // might use fewer workers than available. To ensure the bitmap is cleared // as efficiently as possible the number of active workers are temporarily // increased to include all currently created workers. - WithUpdatedActiveWorkers update(workers, workers->created_workers()); + WithActiveWorkers update(workers, workers->created_workers()); clear_next_bitmap(workers, false); } -class G1PreConcurrentStartTask : public G1BatchedGangTask { +class G1PreConcurrentStartTask : public G1BatchedTask { // Concurrent start needs claim bits to keep track of the marked-through CLDs. class CLDClearClaimedMarksTask; // Reset marking state. @@ -805,7 +805,7 @@ void G1PreConcurrentStartTask::NoteStartOfMarkTask::set_max_workers(uint max_wor } G1PreConcurrentStartTask::G1PreConcurrentStartTask(GCCause::Cause cause, G1ConcurrentMark* cm) : - G1BatchedGangTask("Pre Concurrent Start", G1CollectedHeap::heap()->phase_times()) { + G1BatchedTask("Pre Concurrent Start", G1CollectedHeap::heap()->phase_times()) { add_serial_task(new CLDClearClaimedMarksTask()); add_serial_task(new ResetMarkingStateTask(cm)); add_parallel_task(new NoteStartOfMarkTask()); @@ -889,7 +889,7 @@ void G1ConcurrentMark::enter_second_sync_barrier(uint worker_id) { // at this point everything should be re-initialized and ready to go } -class G1CMConcurrentMarkingTask : public AbstractGangTask { +class G1CMConcurrentMarkingTask : public WorkerTask { G1ConcurrentMark* _cm; public: @@ -923,7 +923,7 @@ public: } G1CMConcurrentMarkingTask(G1ConcurrentMark* cm) : - AbstractGangTask("Concurrent Mark"), _cm(cm) { } + WorkerTask("Concurrent Mark"), _cm(cm) { } ~G1CMConcurrentMarkingTask() { } }; @@ -965,17 +965,17 @@ void G1ConcurrentMark::scan_root_region(const MemRegion* region, uint worker_id) while (curr < end) { Prefetch::read(curr, interval); oop obj = cast_to_oop(curr); - int size = obj->oop_iterate_size(&cl); + size_t size = obj->oop_iterate_size(&cl); assert(size == obj->size(), "sanity"); curr += size; } } -class G1CMRootRegionScanTask : public AbstractGangTask { +class G1CMRootRegionScanTask : public WorkerTask { G1ConcurrentMark* _cm; public: G1CMRootRegionScanTask(G1ConcurrentMark* cm) : - AbstractGangTask("G1 Root Region Scan"), _cm(cm) { } + WorkerTask("G1 Root Region Scan"), _cm(cm) { } void work(uint worker_id) { G1CMRootMemRegions* root_regions = _cm->root_regions(); @@ -1046,8 +1046,8 @@ void G1ConcurrentMark::mark_from_roots() { // Setting active workers is not guaranteed since fewer // worker threads may currently exist and more may not be // available. - active_workers = _concurrent_workers->update_active_workers(active_workers); - log_info(gc, task)("Using %u workers of %u for marking", active_workers, _concurrent_workers->total_workers()); + active_workers = _concurrent_workers->set_active_workers(active_workers); + log_info(gc, task)("Using %u workers of %u for marking", active_workers, _concurrent_workers->max_workers()); // Parallel task terminator is set in "set_concurrency_and_phase()" set_concurrency_and_phase(active_workers, true /* concurrent */); @@ -1075,7 +1075,7 @@ void G1ConcurrentMark::verify_during_pause(G1HeapVerifier::G1VerifyType type, Ve verifier->check_bitmaps(caller); } -class G1UpdateRemSetTrackingBeforeRebuildTask : public AbstractGangTask { +class G1UpdateRemSetTrackingBeforeRebuildTask : public WorkerTask { G1CollectedHeap* _g1h; G1ConcurrentMark* _cm; HeapRegionClaimer _hrclaimer; @@ -1176,7 +1176,7 @@ class G1UpdateRemSetTrackingBeforeRebuildTask : public AbstractGangTask { public: G1UpdateRemSetTrackingBeforeRebuildTask(G1CollectedHeap* g1h, G1ConcurrentMark* cm, uint num_workers) : - AbstractGangTask("G1 Update RemSet Tracking Before Rebuild"), + WorkerTask("G1 Update RemSet Tracking Before Rebuild"), _g1h(g1h), _cm(cm), _hrclaimer(num_workers), _total_selected_for_rebuild(0), _cl("Post-Marking") { } virtual void work(uint worker_id) { @@ -1307,7 +1307,7 @@ void G1ConcurrentMark::remark() { policy->record_concurrent_mark_remark_end(); } -class G1ReclaimEmptyRegionsTask : public AbstractGangTask { +class G1ReclaimEmptyRegionsTask : public WorkerTask { // Per-region work during the Cleanup pause. class G1ReclaimEmptyRegionsClosure : public HeapRegionClosure { G1CollectedHeap* _g1h; @@ -1362,7 +1362,7 @@ class G1ReclaimEmptyRegionsTask : public AbstractGangTask { public: G1ReclaimEmptyRegionsTask(G1CollectedHeap* g1h, FreeRegionList* cleanup_list, uint n_workers) : - AbstractGangTask("G1 Cleanup"), + WorkerTask("G1 Cleanup"), _g1h(g1h), _cleanup_list(cleanup_list), _hrclaimer(n_workers) { @@ -1389,7 +1389,7 @@ public: }; void G1ConcurrentMark::reclaim_empty_regions() { - WorkGang* workers = _g1h->workers(); + WorkerThreads* workers = _g1h->workers(); FreeRegionList empty_regions_list("Empty Regions After Mark List"); G1ReclaimEmptyRegionsTask cl(_g1h, &empty_regions_list, workers->active_workers()); @@ -1613,7 +1613,7 @@ void G1ConcurrentMark::weak_refs_work() { // We need at least one active thread. If reference processing // is not multi-threaded we use the current (VMThread) thread, - // otherwise we use the work gang from the G1CollectedHeap and + // otherwise we use the workers from the G1CollectedHeap and // we utilize all the worker threads we can. uint active_workers = (ParallelRefProcEnabled ? _g1h->workers()->active_workers() : 1U); active_workers = clamp(active_workers, 1u, _max_num_tasks); @@ -1645,9 +1645,6 @@ void G1ConcurrentMark::weak_refs_work() { "Mark stack should be empty (unless it has overflown)"); assert(rp->num_queues() == active_workers, "why not"); - - rp->verify_no_references_recorded(); - assert(!rp->discovery_enabled(), "Post condition"); } if (has_overflown()) { @@ -1695,9 +1692,7 @@ void G1ConcurrentMark::preclean() { SuspendibleThreadSetJoiner joiner; - G1CMKeepAliveAndDrainClosure keep_alive(this, task(0), true /* is_serial */); BarrierEnqueueDiscoveredFieldClosure enqueue; - G1CMDrainMarkingStackClosure drain_mark_stack(this, task(0), true /* is_serial */); set_concurrency_and_phase(1, true); @@ -1707,9 +1702,7 @@ void G1ConcurrentMark::preclean() { // Precleaning is single threaded. Temporarily disable MT discovery. ReferenceProcessorMTDiscoveryMutator rp_mut_discovery(rp, false); rp->preclean_discovered_references(rp->is_alive_non_header(), - &keep_alive, &enqueue, - &drain_mark_stack, &yield_cl, _gc_timer_cm); } @@ -1802,7 +1795,7 @@ class G1RemarkThreadsClosure : public ThreadClosure { } }; -class G1CMRemarkTask : public AbstractGangTask { +class G1CMRemarkTask : public WorkerTask { G1ConcurrentMark* _cm; public: void work(uint worker_id) { @@ -1826,7 +1819,7 @@ public: } G1CMRemarkTask(G1ConcurrentMark* cm, uint active_workers) : - AbstractGangTask("Par Remark"), _cm(cm) { + WorkerTask("Par Remark"), _cm(cm) { _cm->terminator()->reset_for_reuse(active_workers); } }; @@ -1842,7 +1835,7 @@ void G1ConcurrentMark::finalize_marking() { // Leave _parallel_marking_threads at it's // value originally calculated in the G1ConcurrentMark // constructor and pass values of the active workers - // through the gang in the task. + // through the task. { StrongRootsScope srs(active_workers); @@ -2561,7 +2554,7 @@ bool G1ConcurrentMark::try_stealing(uint worker_id, G1TaskQueueEntry& task_entry processing closures. The value of is_serial must be false when do_marking_step is - being called by any of the worker threads in a work gang. + being called by any of the worker threads. Examples include the concurrent marking code (CMMarkingTask), the MT remark code, and the MT reference processing closures. diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 054fc39e6b942083614cc36499f9834963a8d910..28752225f133688726d2731fa676d5c6577076ba 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -34,7 +34,8 @@ #include "gc/shared/taskTerminator.hpp" #include "gc/shared/taskqueue.hpp" #include "gc/shared/verifyOption.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" +#include "gc/shared/workerUtils.hpp" #include "memory/allocation.hpp" #include "utilities/compilerWarnings.hpp" #include "utilities/numberSeq.hpp" @@ -325,8 +326,8 @@ class G1ConcurrentMark : public CHeapObj { // ensure, that no task starts doing work before all data // structures (local and global) have been re-initialized. When they // exit it, they are free to start working again. - WorkGangBarrierSync _first_overflow_barrier_sync; - WorkGangBarrierSync _second_overflow_barrier_sync; + WorkerThreadsBarrierSync _first_overflow_barrier_sync; + WorkerThreadsBarrierSync _second_overflow_barrier_sync; // This is set by any task, when an overflow on the global data // structures is detected @@ -354,7 +355,7 @@ class G1ConcurrentMark : public CHeapObj { double* _accum_task_vtime; // Accumulated task vtime - WorkGang* _concurrent_workers; + WorkerThreads* _concurrent_workers; uint _num_concurrent_workers; // The number of marking worker threads we're using uint _max_concurrent_workers; // Maximum number of marking worker threads @@ -440,9 +441,9 @@ class G1ConcurrentMark : public CHeapObj { void enter_first_sync_barrier(uint worker_id); void enter_second_sync_barrier(uint worker_id); - // Clear the next marking bitmap in parallel using the given WorkGang. If may_yield is + // Clear the next marking bitmap in parallel using the given WorkerThreads. If may_yield is // true, periodically insert checks to see if this method should exit prematurely. - void clear_next_bitmap(WorkGang* workers, bool may_yield); + void clear_next_bitmap(WorkerThreads* workers, bool may_yield); // Region statistics gathered during marking. G1RegionMarkStats* _region_mark_stats; @@ -534,7 +535,7 @@ public: void cleanup_for_next_mark(); // Clear the next marking bitmap during safepoint. - void clear_next_bitmap(WorkGang* workers); + void clear_next_bitmap(WorkerThreads* workers); // These two methods do the work that needs to be done at the start and end of the // concurrent start pause. diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkBitMap.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkBitMap.inline.hpp index ea542bec7a8d2d4cf01ac58577f347eb58ce5f9b..a6b9cdc7d7c0c344301c522060ace91fdf697ad0 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkBitMap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkBitMap.inline.hpp @@ -46,7 +46,7 @@ inline bool G1CMBitMap::iterate(G1CMBitMapClosure* cl, MemRegion mr) { if (!cl->do_addr(addr)) { return false; } - size_t const obj_size = (size_t)cast_to_oop(addr)->size(); + size_t const obj_size = cast_to_oop(addr)->size(); offset = _bm.get_next_one_offset(offset + (obj_size >> _shifter), end_offset); } return true; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp index 81f377d2b68508adb005771e949357f81d0e088b..2cac3c4b61b31d5975f159fa01c44c8eed802143 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp @@ -43,9 +43,9 @@ size_t G1CMObjArrayProcessor::process_array_slice(objArrayOop obj, HeapWord* sta } size_t G1CMObjArrayProcessor::process_obj(oop obj) { - assert(should_be_sliced(obj), "Must be an array object %d and large " SIZE_FORMAT, obj->is_objArray(), (size_t)obj->size()); + assert(should_be_sliced(obj), "Must be an array object %d and large " SIZE_FORMAT, obj->is_objArray(), obj->size()); - return process_array_slice(objArrayOop(obj), cast_from_oop(obj), (size_t)objArrayOop(obj)->size()); + return process_array_slice(objArrayOop(obj), cast_from_oop(obj), objArrayOop(obj)->size()); } size_t G1CMObjArrayProcessor::process_slice(HeapWord* slice) { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp index d72711c73f79f807ddf54e886e90e25d2bc2aa65..3b9944f581590397cfd0fa18f97688201907acce 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp @@ -32,7 +32,7 @@ #include "runtime/globals.hpp" inline bool G1CMObjArrayProcessor::should_be_sliced(oop obj) { - return obj->is_objArray() && ((size_t)((objArrayOop)obj)->size()) >= 2 * ObjArrayMarkingStride; + return obj->is_objArray() && ((objArrayOop)obj)->size() >= 2 * ObjArrayMarkingStride; } #endif // SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1EvacFailure.cpp b/src/hotspot/share/gc/g1/g1EvacFailure.cpp index d3b9b24bdd8bd9fd0e6c63e59bb48c74707aa64b..71a7714d120cd4edff25844407160299cf8c2a98 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailure.cpp +++ b/src/hotspot/share/gc/g1/g1EvacFailure.cpp @@ -201,7 +201,7 @@ public: }; G1ParRemoveSelfForwardPtrsTask::G1ParRemoveSelfForwardPtrsTask(G1EvacFailureRegions* evac_failure_regions) : - AbstractGangTask("G1 Remove Self-forwarding Pointers"), + WorkerTask("G1 Remove Self-forwarding Pointers"), _g1h(G1CollectedHeap::heap()), _hrclaimer(_g1h->workers()->active_workers()), _evac_failure_regions(evac_failure_regions), diff --git a/src/hotspot/share/gc/g1/g1EvacFailure.hpp b/src/hotspot/share/gc/g1/g1EvacFailure.hpp index b0387a3a24f8bdbf505a950849454087562e9320..facb1c7b2038c242c0a92cd7790148b219e04b44 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailure.hpp +++ b/src/hotspot/share/gc/g1/g1EvacFailure.hpp @@ -27,7 +27,7 @@ #include "gc/g1/g1OopClosures.hpp" #include "gc/g1/heapRegionManager.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "utilities/globalDefinitions.hpp" class G1CollectedHeap; @@ -35,7 +35,7 @@ class G1EvacFailureRegions; // Task to fixup self-forwarding pointers // installed as a result of an evacuation failure. -class G1ParRemoveSelfForwardPtrsTask: public AbstractGangTask { +class G1ParRemoveSelfForwardPtrsTask: public WorkerTask { protected: G1CollectedHeap* _g1h; HeapRegionClaimer _hrclaimer; diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 386158ae1725d1826c03d15669c0bc48bfdc19a0..603b381802903c1ff317cdd062b9f811b68b6e3a 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -75,7 +75,7 @@ ReferenceProcessor* G1FullCollector::reference_processor() { uint G1FullCollector::calc_active_workers() { G1CollectedHeap* heap = G1CollectedHeap::heap(); - uint max_worker_count = heap->workers()->total_workers(); + uint max_worker_count = heap->workers()->max_workers(); // Only calculate number of workers if UseDynamicNumberOfGCThreads // is enabled, otherwise use max. if (!UseDynamicNumberOfGCThreads) { @@ -102,7 +102,7 @@ uint G1FullCollector::calc_active_workers() { log_debug(gc, task)("Requesting %u active workers for full compaction (waste limited workers: %u, " "adaptive workers: %u, used limited workers: %u)", worker_count, heap_waste_worker_limit, active_worker_limit, used_worker_limit); - worker_count = heap->workers()->update_active_workers(worker_count); + worker_count = heap->workers()->set_active_workers(worker_count); log_info(gc, task)("Using %u workers of %u for full compaction", worker_count, max_worker_count); return worker_count; @@ -332,7 +332,7 @@ void G1FullCollector::restore_marks() { _preserved_marks_set.reclaim(); } -void G1FullCollector::run_task(AbstractGangTask* task) { +void G1FullCollector::run_task(WorkerTask* task) { _heap->workers()->run_task(task, _num_workers); } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index 1f9c12abd241ee7176c8e4dcd4eeb575a0899b4b..c4d019629dd89d8738ccd514439aa474f36f9cc6 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp @@ -39,7 +39,7 @@ #include "memory/allocation.hpp" #include "oops/oopsHierarchy.hpp" -class AbstractGangTask; +class WorkerTask; class G1CMBitMap; class G1FullGCMarker; class G1FullGCScope; @@ -134,7 +134,7 @@ private: void restore_marks(); void verify_after_marking(); - void run_task(AbstractGangTask* task); + void run_task(WorkerTask* task); }; diff --git a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp index 273db274a56d686b97ac2d25ed80bdefb8c072ae..b3a4ee11d5300016b510a1f94c5d9451caadf2b7 100644 --- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp @@ -81,7 +81,6 @@ class G1AdjustRegionClosure : public HeapRegionClosure { G1FullGCAdjustTask::G1FullGCAdjustTask(G1FullCollector* collector) : G1FullGCTask("G1 Adjust", collector), _root_processor(G1CollectedHeap::heap(), collector->workers()), - _references_done(false), _weak_proc_task(collector->workers()), _hrclaimer(collector->workers()), _adjust(collector) { @@ -97,14 +96,12 @@ void G1FullGCAdjustTask::work(uint worker_id) { G1FullGCMarker* marker = collector()->marker(worker_id); marker->preserved_stack()->adjust_during_full_gc(); - // Adjust the weak roots. - if (!Atomic::cmpxchg(&_references_done, false, true)) { - G1CollectedHeap::heap()->ref_processor_stw()->weak_oops_do(&_adjust); + { + // Adjust the weak roots. + AlwaysTrueClosure always_alive; + _weak_proc_task.work(worker_id, &always_alive, &_adjust); } - AlwaysTrueClosure always_alive; - _weak_proc_task.work(worker_id, &always_alive, &_adjust); - CLDToOopClosure adjust_cld(&_adjust, ClassLoaderData::_claim_strong); CodeBlobToOopClosure adjust_code(&_adjust, CodeBlobToOopClosure::FixRelocations); _root_processor.process_all_roots(&_adjust, &adjust_cld, &adjust_code); diff --git a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.hpp b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.hpp index 56c5957cd26abe6736b4e00d195a1ad24a919b17..c9b190acd065978ade161614717e39c6b8a83365 100644 --- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.hpp @@ -36,7 +36,6 @@ class G1CollectedHeap; class G1FullGCAdjustTask : public G1FullGCTask { G1RootProcessor _root_processor; - volatile bool _references_done; WeakProcessor::Task _weak_proc_task; HeapRegionClaimer _hrclaimer; G1AdjustClosure _adjust; diff --git a/src/hotspot/share/gc/g1/g1FullGCTask.hpp b/src/hotspot/share/gc/g1/g1FullGCTask.hpp index b1d85a2db8abf4f5f0440c4402d2d0d4701ac279..4bb62e5eb53fa3d3c1b5b2a4252ae496cb56f9df 100644 --- a/src/hotspot/share/gc/g1/g1FullGCTask.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCTask.hpp @@ -25,17 +25,17 @@ #ifndef SHARE_GC_G1_G1FULLGCTASK_HPP #define SHARE_GC_G1_G1FULLGCTASK_HPP -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "utilities/ticks.hpp" class G1FullCollector; -class G1FullGCTask : public AbstractGangTask { +class G1FullGCTask : public WorkerTask { G1FullCollector* _collector; protected: G1FullGCTask(const char* name, G1FullCollector* collector) : - AbstractGangTask(name), + WorkerTask(name), _collector(collector) { } G1FullCollector* collector() { return _collector; } diff --git a/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp b/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp index 3bb7c02b88de567e2e7e50352fd1befe5a5388c3..2b8fb3532ee0ea574a32d4e546ddb21648b8eaa4 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp @@ -39,12 +39,12 @@ public: #else typedef int8_t region_type_t; #endif - // _needs_remset_update_t is essentially bool, but we need precise control + // remset_is_tracked_t is essentially bool, but we need precise control // on the size, and sizeof(bool) is implementation specific. - typedef uint8_t needs_remset_update_t; + typedef uint8_t remset_is_tracked_t; private: - needs_remset_update_t _needs_remset_update; + remset_is_tracked_t _remset_is_tracked; region_type_t _type; public: @@ -65,8 +65,8 @@ public: static const region_type_t Old = 1; // The region is in the collection set and an old region. static const region_type_t Num = 2; - G1HeapRegionAttr(region_type_t type = NotInCSet, bool needs_remset_update = false) : - _needs_remset_update(needs_remset_update), _type(type) { + G1HeapRegionAttr(region_type_t type = NotInCSet, bool remset_is_tracked = false) : + _remset_is_tracked(remset_is_tracked), _type(type) { assert(is_valid(), "Invalid type %d", _type); } @@ -85,7 +85,7 @@ public: } } - bool needs_remset_update() const { return _needs_remset_update != 0; } + bool remset_is_tracked() const { return _remset_is_tracked != 0; } void set_new_survivor() { _type = NewSurvivor; } void set_old() { _type = Old; } @@ -93,7 +93,7 @@ public: assert(is_humongous() || !is_in_cset(), "must be"); _type = NotInCSet; } - void set_has_remset(bool value) { _needs_remset_update = value ? 1 : 0; } + void set_remset_is_tracked(bool value) { _remset_is_tracked = value ? 1 : 0; } bool is_in_cset_or_humongous() const { return is_in_cset() || is_humongous(); } bool is_in_cset() const { return type() >= Young; } @@ -126,10 +126,10 @@ class G1HeapRegionAttrBiasedMappedArray : public G1BiasedMappedArrayset_new_survivor(); } - void set_humongous(uintptr_t index, bool needs_remset_update) { + void set_humongous(uintptr_t index, bool remset_is_tracked) { assert(get_by_index(index).is_default(), "Region attributes at index " INTPTR_FORMAT " should be default but is %s", index, get_by_index(index).get_type_str()); - set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Humongous, needs_remset_update)); + set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Humongous, remset_is_tracked)); } void clear_humongous(uintptr_t index) { get_ref_by_index(index)->clear_humongous(); } - void set_has_remset(uintptr_t index, bool needs_remset_update) { - get_ref_by_index(index)->set_has_remset(needs_remset_update); + void set_remset_is_tracked(uintptr_t index, bool remset_is_tracked) { + get_ref_by_index(index)->set_remset_is_tracked(remset_is_tracked); } void set_in_young(uintptr_t index) { @@ -158,10 +158,10 @@ class G1HeapRegionAttrBiasedMappedArray : public G1BiasedMappedArray use "next" marking information, // _vo == UseFullMarking -> use "next" marking bitmap but no TAMS G1ParVerifyTask(G1CollectedHeap* g1h, VerifyOption vo) : - AbstractGangTask("Parallel verify task"), + WorkerTask("Parallel verify task"), _g1h(g1h), _vo(vo), _failures(false), diff --git a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp index 1afa9ebe5d0cc4569d44924aba2d26934e7ea592..7a164e6461f43dd3176f37b7cc7b3e4e4566033e 100644 --- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp +++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "gc/g1/g1PageBasedVirtualSpace.hpp" #include "gc/shared/pretouchTask.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "oops/markWord.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" @@ -233,10 +233,10 @@ void G1PageBasedVirtualSpace::uncommit(size_t start_page, size_t size_in_pages) _committed.par_clear_range(start_page, end_page, BitMap::unknown_range); } -void G1PageBasedVirtualSpace::pretouch(size_t start_page, size_t size_in_pages, WorkGang* pretouch_gang) { +void G1PageBasedVirtualSpace::pretouch(size_t start_page, size_t size_in_pages, WorkerThreads* pretouch_workers) { PretouchTask::pretouch("G1 PreTouch", page_start(start_page), bounded_end_addr(start_page + size_in_pages), - _page_size, pretouch_gang); + _page_size, pretouch_workers); } bool G1PageBasedVirtualSpace::contains(const void* p) const { diff --git a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp index 0bfb8c4dd775e1f176f151cb46c4840869e6676d..8b68ad60e560a3e702ea2f4e50fa332f13e1585d 100644 --- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp +++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp @@ -30,7 +30,7 @@ #include "utilities/align.hpp" #include "utilities/bitMap.hpp" -class WorkGang; +class WorkerThreads; // Virtual space management helper for a virtual space with an OS page allocation // granularity. @@ -117,7 +117,7 @@ class G1PageBasedVirtualSpace { // Uncommit the given area of pages starting at start being size_in_pages large. void uncommit(size_t start_page, size_t size_in_pages); - void pretouch(size_t start_page, size_t size_in_pages, WorkGang* pretouch_gang = NULL); + void pretouch(size_t start_page, size_t size_in_pages, WorkerThreads* pretouch_workers = NULL); // Initialize the given reserved space with the given base address and the size // actually used. diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 92772cc2db7f6c9d3d2b7ea12d8b39049ec15279..bba90e6a742676ea8759e3a90feb75a24a27c74d 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -85,7 +85,7 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _num_optional_regions(optional_cset_length), _numa(g1h->numa()), _obj_alloc_stat(NULL), - NOT_PRODUCT(_evac_failure_inject_counter(0) COMMA) + EVAC_FAILURE_INJECTOR_ONLY(_evac_failure_inject_counter(0) COMMA) _preserved_marks(preserved_marks), _evacuation_failed_info(), _evac_failure_regions(evac_failure_regions) @@ -420,7 +420,7 @@ HeapWord* G1ParScanThreadState::allocate_copy_slow(G1HeapRegionAttr* dest_attr, return obj_ptr; } -#ifndef PRODUCT +#if EVAC_FAILURE_INJECTOR bool G1ParScanThreadState::inject_evacuation_failure() { return _g1h->evac_failure_injector()->evacuation_should_fail(_evac_failure_inject_counter); } diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 0ecc11f50a516065c09dc8167fd4f319743c4cd2..478b8745731cadaf1562fe7091ddd2221468b6e2 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -28,6 +28,8 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1RedirtyCardsQueue.hpp" #include "gc/g1/g1OopClosures.hpp" +#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" +#include "gc/g1/g1_globals.hpp" #include "gc/shared/ageTable.hpp" #include "gc/shared/copyFailedInfo.hpp" #include "gc/shared/partialArrayTaskStepper.hpp" @@ -99,14 +101,13 @@ class G1ParScanThreadState : public CHeapObj { size_t* _obj_alloc_stat; // Per-thread evacuation failure data structures. -#ifndef PRODUCT - size_t _evac_failure_inject_counter; -#endif + EVAC_FAILURE_INJECTOR_ONLY(size_t _evac_failure_inject_counter;) + PreservedMarks* _preserved_marks; EvacuationFailedInfo _evacuation_failed_info; G1EvacFailureRegions* _evac_failure_regions; - bool inject_evacuation_failure() PRODUCT_RETURN_( return false; ); + bool inject_evacuation_failure() EVAC_FAILURE_INJECTOR_RETURN_( return false; ); public: G1ParScanThreadState(G1CollectedHeap* g1h, diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp index a36347371037660816713658e31f24d1469ef229..46651307bc1c6b160ebcf5db65939abab7f9202b 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp @@ -111,7 +111,7 @@ template void G1ParScanThreadState::write_ref_field_post(T* p, oop obj // References to the current collection set are references to objects that failed // evacuation. Currently these regions are always relabelled as old without // remembered sets, so skip them. - assert(dest_attr.is_in_cset() == (obj->forwardee() == obj), + assert(dest_attr.is_in_cset() == (obj->is_forwarded() && obj->forwardee() == obj), "Only evac-failed objects must be in the collection set here but " PTR_FORMAT " is not", p2i(obj)); if (dest_attr.is_in_cset()) { return; @@ -128,13 +128,13 @@ template void G1ParScanThreadState::enqueue_card_if_tracked(G1HeapRegi #ifdef ASSERT HeapRegion* const hr_obj = _g1h->heap_region_containing(o); - assert(region_attr.needs_remset_update() == hr_obj->rem_set()->is_tracked(), + assert(region_attr.remset_is_tracked() == hr_obj->rem_set()->is_tracked(), "State flag indicating remset tracking disagrees (%s) with actual remembered set (%s) for region %u", - BOOL_TO_STR(region_attr.needs_remset_update()), + BOOL_TO_STR(region_attr.remset_is_tracked()), BOOL_TO_STR(hr_obj->rem_set()->is_tracked()), hr_obj->hrm_index()); #endif - if (!region_attr.needs_remset_update()) { + if (!region_attr.remset_is_tracked()) { return; } size_t card_index = ct()->index_for(p); diff --git a/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp b/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp index abe06f71f7a349ec583ea3cc5e67afb28a5a8b5f..b528afffa02ee98e387654feddebb5544c890b2a 100644 --- a/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp +++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp @@ -54,7 +54,7 @@ void JVMCICleaningTask::work(bool unloading_occurred) { G1ParallelCleaningTask::G1ParallelCleaningTask(BoolObjectClosure* is_alive, uint num_workers, bool unloading_occurred) : - AbstractGangTask("G1 Parallel Cleaning"), + WorkerTask("G1 Parallel Cleaning"), _unloading_occurred(unloading_occurred), _code_cache_task(num_workers, is_alive, unloading_occurred), JVMCI_ONLY(_jvmci_cleaning_task() COMMA) diff --git a/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp b/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp index c87c7e22e43b0c496e7b46042e905104418cccfb..b8556b49398312b52ca1216782b9728fa70c133c 100644 --- a/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp +++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp @@ -43,7 +43,7 @@ private: // Do cleanup of some weakly held data in the same parallel task. // Assumes a non-moving context. -class G1ParallelCleaningTask : public AbstractGangTask { +class G1ParallelCleaningTask : public WorkerTask { private: bool _unloading_occurred; CodeCacheUnloadingTask _code_cache_task; diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index ef704c2240d477164d93fdde648edbaccad38594..dc47bebd7e49943bdb670fa690a36e126cbdd94a 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -1501,7 +1501,6 @@ void G1Policy::update_survival_estimates_for_next_collection() { void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { start_adding_survivor_regions(); - HeapRegion* last = NULL; for (GrowableArrayIterator it = survivors->regions()->begin(); it != survivors->regions()->end(); ++it) { @@ -1512,8 +1511,6 @@ void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { // the incremental collection set for the next evacuation // pause. _collection_set->add_survivor_regions(curr); - - last = curr; } stop_adding_survivor_regions(); diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp index db9f91641ba3e09e83809abf0684242f03113ff0..f8ce06237c1bcf9dd6915649dc5f99086090745e 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp @@ -47,7 +47,7 @@ G1RegionMarkStatsCache::~G1RegionMarkStatsCache() { void G1RegionMarkStatsCache::add_live_words(oop obj) { uint region_index = G1CollectedHeap::heap()->addr_to_region(cast_from_oop(obj)); - add_live_words(region_index, (size_t) obj->size()); + add_live_words(region_index, obj->size()); } // Evict all remaining statistics, returning cache hits and misses. diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp index b74b36e937cca266e8c1cc06ecc60ad2cbcd42da..b8f1f5de153837d4af6ad6e9cb06bb81cd86ca5c 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp @@ -89,7 +89,7 @@ class G1RegionsLargerThanCommitSizeMapper : public G1RegionToSpaceMapper { return _region_commit_map.get_next_one_offset(start_idx, end) == end; } - virtual void commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) { + virtual void commit_regions(uint start_idx, size_t num_regions, WorkerThreads* pretouch_workers) { guarantee(is_range_uncommitted(start_idx, num_regions), "Range not uncommitted, start: %u, num_regions: " SIZE_FORMAT, start_idx, num_regions); @@ -105,7 +105,7 @@ class G1RegionsLargerThanCommitSizeMapper : public G1RegionToSpaceMapper { } } if (AlwaysPreTouch) { - _storage.pretouch(start_page, size_in_pages, pretouch_gang); + _storage.pretouch(start_page, size_in_pages, pretouch_workers); } _region_commit_map.par_set_range(start_idx, start_idx + num_regions, BitMap::unknown_range); fire_on_commit(start_idx, num_regions, zero_filled); @@ -167,12 +167,12 @@ class G1RegionsSmallerThanCommitSizeMapper : public G1RegionToSpaceMapper { MEMFLAGS type) : G1RegionToSpaceMapper(rs, actual_size, page_size, alloc_granularity, commit_factor, type), _regions_per_page((page_size * commit_factor) / alloc_granularity), - _lock(Mutex::service-3, "G1Mapper_lock", Mutex::_safepoint_check_never) { + _lock(Mutex::service-3, "G1Mapper_lock") { guarantee((page_size * commit_factor) >= alloc_granularity, "allocation granularity smaller than commit granularity"); } - virtual void commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) { + virtual void commit_regions(uint start_idx, size_t num_regions, WorkerThreads* pretouch_workers) { uint region_limit = (uint)(start_idx + num_regions); assert(num_regions > 0, "Must commit at least one region"); assert(_region_commit_map.get_next_one_offset(start_idx, region_limit) == region_limit, @@ -219,7 +219,7 @@ class G1RegionsSmallerThanCommitSizeMapper : public G1RegionToSpaceMapper { } if (AlwaysPreTouch && num_committed > 0) { - _storage.pretouch(first_committed, num_committed, pretouch_gang); + _storage.pretouch(first_committed, num_committed, pretouch_workers); } fire_on_commit(start_idx, num_regions, all_zero_filled); diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp index 22dea6153c8af21d6c522df833636b1e936f467d..eeda48838df7eaedcb07278985564a9be64b4d70 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp @@ -29,7 +29,7 @@ #include "memory/allocation.hpp" #include "utilities/debug.hpp" -class WorkGang; +class WorkerThreads; class G1MappingChangedListener { public: @@ -70,7 +70,7 @@ class G1RegionToSpaceMapper : public CHeapObj { virtual ~G1RegionToSpaceMapper() {} - virtual void commit_regions(uint start_idx, size_t num_regions = 1, WorkGang* pretouch_workers = NULL) = 0; + virtual void commit_regions(uint start_idx, size_t num_regions = 1, WorkerThreads* pretouch_workers = NULL) = 0; virtual void uncommit_regions(uint start_idx, size_t num_regions = 1) = 0; // Creates an appropriate G1RegionToSpaceMapper for the given parameters. diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 467b9d864f8610b7b0d98e46e4c08af241e463ae..b64a6dcd254930ef5c16fa4821b58a7bdc3840b2 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "gc/g1/g1BarrierSet.hpp" -#include "gc/g1/g1BatchedGangTask.hpp" +#include "gc/g1/g1BatchedTask.hpp" #include "gc/g1/g1BlockOffsetTable.inline.hpp" #include "gc/g1/g1CardSet.inline.hpp" #include "gc/g1/g1CardTable.inline.hpp" @@ -406,7 +406,7 @@ public: G1CollectedHeap* g1h = G1CollectedHeap::heap(); - WorkGang* workers = g1h->workers(); + WorkerThreads* workers = g1h->workers(); uint const max_workers = workers->active_workers(); uint const start_pos = num_regions * worker_id / max_workers; @@ -1102,7 +1102,7 @@ public: } }; -class G1MergeHeapRootsTask : public AbstractGangTask { +class G1MergeHeapRootsTask : public WorkerTask { class G1MergeCardSetStats { size_t _merged[G1GCPhaseTimes::MergeRSContainersSentinel]; @@ -1289,7 +1289,7 @@ class G1MergeHeapRootsTask : public AbstractGangTask { r->rem_set()->set_state_complete(); #ifdef ASSERT G1HeapRegionAttr region_attr = g1h->region_attr(r->hrm_index()); - assert(region_attr.needs_remset_update(), "must be"); + assert(region_attr.remset_is_tracked(), "must be"); #endif assert(r->rem_set()->is_empty(), "At this point any humongous candidate remembered set must be empty."); @@ -1371,7 +1371,7 @@ class G1MergeHeapRootsTask : public AbstractGangTask { public: G1MergeHeapRootsTask(G1RemSetScanState* scan_state, uint num_workers, bool initial_evacuation) : - AbstractGangTask("G1 Merge Heap Roots"), + WorkerTask("G1 Merge Heap Roots"), _hr_claimer(num_workers), _scan_state(scan_state), _dirty_card_buffers(), @@ -1490,7 +1490,7 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) { } } - WorkGang* workers = g1h->workers(); + WorkerThreads* workers = g1h->workers(); size_t const increment_length = g1h->collection_set()->increment_length(); uint const num_workers = initial_evacuation ? workers->active_workers() : @@ -1738,7 +1738,7 @@ void G1RemSet::print_summary_info() { } } -class G1RebuildRemSetTask: public AbstractGangTask { +class G1RebuildRemSetTask: public WorkerTask { // Aggregate the counting data that was constructed concurrently // with marking. class G1RebuildRemSetHeapRegionClosure : public HeapRegionClosure { @@ -1974,7 +1974,7 @@ public: G1RebuildRemSetTask(G1ConcurrentMark* cm, uint n_workers, uint worker_id_offset) : - AbstractGangTask("G1 Rebuild Remembered Set"), + WorkerTask("G1 Rebuild Remembered Set"), _hr_claimer(n_workers), _cm(cm), _worker_id_offset(worker_id_offset) { @@ -1991,7 +1991,7 @@ public: }; void G1RemSet::rebuild_rem_set(G1ConcurrentMark* cm, - WorkGang* workers, + WorkerThreads* workers, uint worker_id_offset) { uint num_workers = workers->active_workers(); diff --git a/src/hotspot/share/gc/g1/g1RemSet.hpp b/src/hotspot/share/gc/g1/g1RemSet.hpp index 7bc463650f033ff79af9c809f019b5d2900be9a3..89ce4f6e5ac60005a954d88cefd34c392e5819ae 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.hpp +++ b/src/hotspot/share/gc/g1/g1RemSet.hpp @@ -113,7 +113,7 @@ public: // Print coarsening stats. void print_coarsen_stats(); - // Creates a gang task for cleaining up temporary data structures and the + // Creates a task for cleaining up temporary data structures and the // card table, removing temporary duplicate detection information. G1AbstractSubTask* create_cleanup_after_scan_heap_roots_task(); // Excludes the given region from heap root scanning. @@ -149,8 +149,8 @@ public: void print_periodic_summary_info(const char* header, uint period_count); // Rebuilds the remembered set by scanning from bottom to TARS for all regions - // using the given work gang. - void rebuild_rem_set(G1ConcurrentMark* cm, WorkGang* workers, uint worker_id_offset); + // using the given workers. + void rebuild_rem_set(G1ConcurrentMark* cm, WorkerThreads* workers, uint worker_id_offset); }; #endif // SHARE_GC_G1_G1REMSET_HPP diff --git a/src/hotspot/share/gc/g1/g1SegmentedArray.hpp b/src/hotspot/share/gc/g1/g1SegmentedArray.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fce3d9187b45dfb1101ff1df8e8b5a7b990d0018 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1SegmentedArray.hpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, Huawei Technologies Co. Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1SEGMENTEDARRAY_HPP +#define SHARE_GC_G1_G1SEGMENTEDARRAY_HPP + +#include "memory/allocation.hpp" +#include "utilities/lockFreeStack.hpp" + +// A single buffer/arena containing _num_elems blocks of memory of _elem_size. +// G1SegmentedArrayBuffers can be linked together using a singly linked list. +template +class G1SegmentedArrayBuffer : public CHeapObj { + const uint _elem_size; + const uint _num_elems; + + G1SegmentedArrayBuffer* volatile _next; + + char* _buffer; // Actual data. + + // Index into the next free block to allocate into. Full if equal (or larger) + // to _num_elems (can be larger because we atomically increment this value and + // check only afterwards if the allocation has been successful). + uint volatile _next_allocate; + +public: + G1SegmentedArrayBuffer(uint elem_size, uint num_elems, G1SegmentedArrayBuffer* next); + ~G1SegmentedArrayBuffer(); + + G1SegmentedArrayBuffer* volatile* next_addr() { return &_next; } + + void* get_new_buffer_elem(); + + uint num_elems() const { return _num_elems; } + + G1SegmentedArrayBuffer* next() const { return _next; } + + void set_next(G1SegmentedArrayBuffer* next) { + assert(next != this, " loop condition"); + _next = next; + } + + void reset(G1SegmentedArrayBuffer* next) { + _next_allocate = 0; + assert(next != this, " loop condition"); + set_next(next); + memset((void*)_buffer, 0, (size_t)_num_elems * _elem_size); + } + + uint elem_size() const { return _elem_size; } + + size_t mem_size() const { return sizeof(*this) + (size_t)_num_elems * _elem_size; } + + bool is_full() const { return _next_allocate >= _num_elems; } +}; + +// Set of (free) G1SegmentedArrayBuffers. The assumed usage is that allocation +// to it and removal of elements is strictly separate, but every action may be +// performed by multiple threads at the same time. +// Counts and memory usage are current on a best-effort basis if accessed concurrently. +template +class G1SegmentedArrayBufferList { + static G1SegmentedArrayBuffer* volatile* next_ptr(G1SegmentedArrayBuffer& node) { + return node.next_addr(); + } + typedef LockFreeStack, &G1SegmentedArrayBufferList::next_ptr> NodeStack; + + NodeStack _list; + + volatile size_t _num_buffers; + volatile size_t _mem_size; + +public: + G1SegmentedArrayBufferList() : _list(), _num_buffers(0), _mem_size(0) { } + ~G1SegmentedArrayBufferList() { free_all(); } + + void bulk_add(G1SegmentedArrayBuffer& first, G1SegmentedArrayBuffer& last, size_t num, size_t mem_size); + void add(G1SegmentedArrayBuffer& elem) { _list.prepend(elem); } + + G1SegmentedArrayBuffer* get(); + G1SegmentedArrayBuffer* get_all(size_t& num_buffers, size_t& mem_size); + + // Give back all memory to the OS. + void free_all(); + + void print_on(outputStream* out, const char* prefix = ""); + + size_t num_buffers() const { return Atomic::load(&_num_buffers); } + size_t mem_size() const { return Atomic::load(&_mem_size); } +}; + +// Configuration for G1SegmentedArray, e.g element size, element number of next G1SegmentedArrayBuffer. +class G1SegmentedArrayAllocOptions { + +protected: + const uint _elem_size; + const uint _initial_num_elems; + // Defines a limit to the number of elements in the buffer + const uint _max_num_elems; + const uint _alignment; + +public: + G1SegmentedArrayAllocOptions(uint elem_size, uint initial_num_elems, uint max_num_elems, uint alignment) : + _elem_size(elem_size), + _initial_num_elems(initial_num_elems), + _max_num_elems(max_num_elems), + _alignment(alignment) { + assert(_elem_size > 0, "Must be"); + assert(_initial_num_elems > 0, "Must be"); + assert(_max_num_elems > 0, "Must be"); + assert(_alignment > 0, "Must be"); + } + + virtual uint next_num_elems(uint prev_num_elems) const { + return _initial_num_elems; + } + + uint elem_size() const { return _elem_size; } + + uint alignment() const { return _alignment; } +}; + +// A segmented array where G1SegmentedArrayBuffer is the segment, and +// G1SegmentedArrayBufferList is the free list to cache G1SegmentedArrayBuffer, +// and G1SegmentedArrayAllocOptions is the configuration for G1SegmentedArray +// attributes. +// +// Implementation details as below: +// +// Arena-like allocator for (card set, or ...) heap memory objects (Elem elements). +// +// Actual allocation from the C heap occurs on G1SegmentedArrayBuffer basis, i.e. segments +// of elements. The assumed allocation pattern for these G1SegmentedArrayBuffer elements +// is assumed to be strictly two-phased: +// +// - in the first phase, G1SegmentedArrayBuffers are allocated from the C heap (or a free +// list given at initialization time). This allocation may occur in parallel. This +// typically corresponds to a single mutator phase, but may extend over multiple. +// +// - in the second phase, G1SegmentedArrayBuffers are given back in bulk to the free list. +// This is typically done during a GC pause. +// +// Some third party is responsible for giving back memory from the free list to +// the operating system. +// +// Allocation and deallocation in the first phase basis may occur by multiple threads at once. +// +// The class also manages a few counters for statistics using atomic operations. +// Their values are only consistent within each other with extra global +// synchronization. +template +class G1SegmentedArray { + // G1SegmentedArrayAllocOptions provides parameters for allocation buffer + // sizing and expansion. + const G1SegmentedArrayAllocOptions* _alloc_options; + + G1SegmentedArrayBuffer* volatile _first; // The (start of the) list of all buffers. + G1SegmentedArrayBuffer* _last; // The last element of the list of all buffers. + volatile uint _num_buffers; // Number of assigned buffers to this allocator. + volatile size_t _mem_size; // Memory used by all buffers. + + G1SegmentedArrayBufferList* _free_buffer_list; // The global free buffer list to + // preferentially get new buffers from. + + volatile uint _num_available_nodes; // Number of nodes available in all buffers (allocated + free + pending + not yet used). + volatile uint _num_allocated_nodes; // Number of total nodes allocated and in use. + +private: + inline G1SegmentedArrayBuffer* create_new_buffer(G1SegmentedArrayBuffer* const prev); + +public: + const G1SegmentedArrayBuffer* first_array_buffer() const { return Atomic::load(&_first); } + + uint num_available_nodes() const { return Atomic::load(&_num_available_nodes); } + uint num_allocated_nodes() const { return Atomic::load(&_num_allocated_nodes); } + + inline uint elem_size() const; + + G1SegmentedArray(const char* name, + const G1SegmentedArrayAllocOptions* buffer_options, + G1SegmentedArrayBufferList* free_buffer_list); + ~G1SegmentedArray() { + drop_all(); + } + + // Deallocate all buffers to the free buffer list and reset this allocator. Must + // be called in a globally synchronized area. + void drop_all(); + + inline Elem* allocate(); + + inline uint num_buffers() const; +}; + +#endif //SHARE_GC_G1_G1SEGMENTEDARRAY_HPP diff --git a/src/hotspot/share/gc/g1/g1SegmentedArray.inline.hpp b/src/hotspot/share/gc/g1/g1SegmentedArray.inline.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e5b96aeb1469fe94f220018c2e488817977d6e3a --- /dev/null +++ b/src/hotspot/share/gc/g1/g1SegmentedArray.inline.hpp @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, Huawei Technologies Co. Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1SEGMENTEDARRAY_INLINE_HPP +#define SHARE_GC_G1_G1SEGMENTEDARRAY_INLINE_HPP + +#include "gc/g1/g1SegmentedArray.hpp" +#include "runtime/atomic.hpp" +#include "utilities/globalCounter.inline.hpp" + +template +G1SegmentedArrayBuffer::G1SegmentedArrayBuffer(uint elem_size, uint num_instances, G1SegmentedArrayBuffer* next) : + _elem_size(elem_size), _num_elems(num_instances), _next(next), _next_allocate(0) { + + _buffer = NEW_C_HEAP_ARRAY(char, (size_t)_num_elems * elem_size, mtGCCardSet); +} + +template +G1SegmentedArrayBuffer::~G1SegmentedArrayBuffer() { + FREE_C_HEAP_ARRAY(mtGCCardSet, _buffer); +} + +template +void* G1SegmentedArrayBuffer::get_new_buffer_elem() { + if (_next_allocate >= _num_elems) { + return nullptr; + } + uint result = Atomic::fetch_and_add(&_next_allocate, 1u, memory_order_relaxed); + if (result >= _num_elems) { + return nullptr; + } + void* r = _buffer + (uint)result * _elem_size; + return r; +} + +template +void G1SegmentedArrayBufferList::bulk_add(G1SegmentedArrayBuffer& first, + G1SegmentedArrayBuffer& last, + size_t num, + size_t mem_size) { + _list.prepend(first, last); + Atomic::add(&_num_buffers, num, memory_order_relaxed); + Atomic::add(&_mem_size, mem_size, memory_order_relaxed); +} + +template +void G1SegmentedArrayBufferList::print_on(outputStream* out, const char* prefix) { + out->print_cr("%s: buffers %zu size %zu", + prefix, Atomic::load(&_num_buffers), Atomic::load(&_mem_size)); +} + +template +G1SegmentedArrayBuffer* G1SegmentedArrayBufferList::get() { + GlobalCounter::CriticalSection cs(Thread::current()); + + G1SegmentedArrayBuffer* result = _list.pop(); + if (result != nullptr) { + Atomic::dec(&_num_buffers, memory_order_relaxed); + Atomic::sub(&_mem_size, result->mem_size(), memory_order_relaxed); + } + return result; +} + +template +G1SegmentedArrayBuffer* G1SegmentedArrayBufferList::get_all(size_t& num_buffers, + size_t& mem_size) { + GlobalCounter::CriticalSection cs(Thread::current()); + + G1SegmentedArrayBuffer* result = _list.pop_all(); + num_buffers = Atomic::load(&_num_buffers); + mem_size = Atomic::load(&_mem_size); + + if (result != nullptr) { + Atomic::sub(&_num_buffers, num_buffers, memory_order_relaxed); + Atomic::sub(&_mem_size, mem_size, memory_order_relaxed); + } + return result; +} + +template +void G1SegmentedArrayBufferList::free_all() { + size_t num_freed = 0; + size_t mem_size_freed = 0; + G1SegmentedArrayBuffer* cur; + + while ((cur = _list.pop()) != nullptr) { + mem_size_freed += cur->mem_size(); + num_freed++; + delete cur; + } + + Atomic::sub(&_num_buffers, num_freed, memory_order_relaxed); + Atomic::sub(&_mem_size, mem_size_freed, memory_order_relaxed); +} + +template +G1SegmentedArrayBuffer* G1SegmentedArray::create_new_buffer(G1SegmentedArrayBuffer* const prev) { + // Take an existing buffer if available. + G1SegmentedArrayBuffer* next = _free_buffer_list->get(); + if (next == nullptr) { + uint prev_num_elems = (prev != nullptr) ? prev->num_elems() : 0; + uint num_elems = _alloc_options->next_num_elems(prev_num_elems); + next = new G1SegmentedArrayBuffer(elem_size(), num_elems, prev); + } else { + assert(elem_size() == next->elem_size() , + "Mismatch %d != %d Elem %zu", elem_size(), next->elem_size(), sizeof(Elem)); + next->reset(prev); + } + + // Install it as current allocation buffer. + G1SegmentedArrayBuffer* old = Atomic::cmpxchg(&_first, prev, next); + if (old != prev) { + // Somebody else installed the buffer, use that one. + delete next; + return old; + } else { + // Did we install the first element in the list? If so, this is also the last. + if (prev == nullptr) { + _last = next; + } + // Successfully installed the buffer into the list. + Atomic::inc(&_num_buffers, memory_order_relaxed); + Atomic::add(&_mem_size, next->mem_size(), memory_order_relaxed); + Atomic::add(&_num_available_nodes, next->num_elems(), memory_order_relaxed); + return next; + } +} + +template +uint G1SegmentedArray::elem_size() const { + return _alloc_options->elem_size(); +} + +template +G1SegmentedArray::G1SegmentedArray(const char* name, + const G1SegmentedArrayAllocOptions* buffer_options, + G1SegmentedArrayBufferList* free_buffer_list) : + _alloc_options(buffer_options), + _first(nullptr), + _last(nullptr), + _num_buffers(0), + _mem_size(0), + _free_buffer_list(free_buffer_list), + _num_available_nodes(0), + _num_allocated_nodes(0) { + assert(_free_buffer_list != nullptr, "precondition!"); +} + +template +void G1SegmentedArray::drop_all() { + G1SegmentedArrayBuffer* cur = Atomic::load_acquire(&_first); + + if (cur != nullptr) { + assert(_last != nullptr, "If there is at least one element, there must be a last one."); + + G1SegmentedArrayBuffer* first = cur; +#ifdef ASSERT + // Check list consistency. + G1SegmentedArrayBuffer* last = cur; + uint num_buffers = 0; + size_t mem_size = 0; + while (cur != nullptr) { + mem_size += cur->mem_size(); + num_buffers++; + + G1SegmentedArrayBuffer* next = cur->next(); + last = cur; + cur = next; + } +#endif + assert(num_buffers == _num_buffers, "Buffer count inconsistent %u %u", num_buffers, _num_buffers); + assert(mem_size == _mem_size, "Memory size inconsistent"); + assert(last == _last, "Inconsistent last element"); + + _free_buffer_list->bulk_add(*first, *_last, _num_buffers, _mem_size); + } + + _first = nullptr; + _last = nullptr; + _num_buffers = 0; + _mem_size = 0; + _num_available_nodes = 0; + _num_allocated_nodes = 0; +} + +template +Elem* G1SegmentedArray::allocate() { + assert(elem_size() > 0, "instance size not set."); + + G1SegmentedArrayBuffer* cur = Atomic::load_acquire(&_first); + if (cur == nullptr) { + cur = create_new_buffer(cur); + } + + while (true) { + Elem* elem = (Elem*)cur->get_new_buffer_elem(); + if (elem != nullptr) { + Atomic::inc(&_num_allocated_nodes, memory_order_relaxed); + guarantee(is_aligned(elem, _alloc_options->alignment()), + "result " PTR_FORMAT " not aligned at %u", p2i(elem), _alloc_options->alignment()); + return elem; + } + // The buffer is full. Next round. + assert(cur->is_full(), "must be"); + cur = create_new_buffer(cur); + } +} + +template +inline uint G1SegmentedArray::num_buffers() const { + return Atomic::load(&_num_buffers); +} + +#endif //SHARE_GC_G1_G1SEGMENTEDARRAY_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.cpp b/src/hotspot/share/gc/g1/g1ServiceThread.cpp index 9945c17513cad2ce564deab533ba1aab2f6b9537..7ae7f10bf58c5ee91769d2c68748277b6ff33c90 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.cpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.cpp @@ -40,9 +40,7 @@ void G1SentinelTask::execute() { G1ServiceThread::G1ServiceThread() : ConcurrentGCThread(), - _monitor(Mutex::nosafepoint, - "G1ServiceThread_lock", - Monitor::_safepoint_check_never), + _monitor(Mutex::nosafepoint, "G1ServiceThread_lock"), _task_queue() { set_name("G1 Service"); create_and_start(); diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index e1c0fcd16dff01f788bf3c6e65a6209f89639be8..c900c1ce2488ca7cad6878e108ab9c23a40eb6c5 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -54,7 +54,7 @@ #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/workerPolicy.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "jfr/jfrEvents.hpp" #include "memory/resourceArea.hpp" #include "utilities/ticks.hpp" @@ -270,7 +270,7 @@ ReferenceProcessor* G1YoungCollector::ref_processor_stw() const { return _g1h->ref_processor_stw(); } -WorkGang* G1YoungCollector::workers() const { +WorkerThreads* G1YoungCollector::workers() const { return _g1h->workers(); } @@ -323,7 +323,7 @@ void G1YoungCollector::calculate_collection_set(G1EvacInfo* evacuation_info, dou } } -class G1PrepareEvacuationTask : public AbstractGangTask { +class G1PrepareEvacuationTask : public WorkerTask { class G1PrepareRegionsClosure : public HeapRegionClosure { G1CollectedHeap* _g1h; G1PrepareEvacuationTask* _parent_task; @@ -433,7 +433,7 @@ class G1PrepareEvacuationTask : public AbstractGangTask { } log_debug(gc, humongous)("Humongous region %u (object size " SIZE_FORMAT " @ " PTR_FORMAT ") remset " SIZE_FORMAT " code roots " SIZE_FORMAT " marked %d reclaim candidate %d type array %d", index, - (size_t)cast_to_oop(hr->bottom())->size() * HeapWordSize, + cast_to_oop(hr->bottom())->size() * HeapWordSize, p2i(hr->bottom()), hr->rem_set()->occupied(), hr->rem_set()->strong_code_roots_list_length(), @@ -460,7 +460,7 @@ class G1PrepareEvacuationTask : public AbstractGangTask { public: G1PrepareEvacuationTask(G1CollectedHeap* g1h) : - AbstractGangTask("Prepare Evacuation"), + WorkerTask("Prepare Evacuation"), _g1h(g1h), _claimer(_g1h->workers()->active_workers()), _humongous_total(0), @@ -495,18 +495,18 @@ public: } }; -Tickspan G1YoungCollector::run_task_timed(AbstractGangTask* task) { +Tickspan G1YoungCollector::run_task_timed(WorkerTask* task) { Ticks start = Ticks::now(); workers()->run_task(task); return Ticks::now() - start; } void G1YoungCollector::set_young_collection_default_active_worker_threads(){ - uint active_workers = WorkerPolicy::calc_active_workers(workers()->total_workers(), + uint active_workers = WorkerPolicy::calc_active_workers(workers()->max_workers(), workers()->active_workers(), Threads::number_of_non_daemon_threads()); - active_workers = workers()->update_active_workers(active_workers); - log_info(gc,task)("Using %u workers of %u for evacuation", active_workers, workers()->total_workers()); + active_workers = workers()->set_active_workers(active_workers); + log_info(gc,task)("Using %u workers of %u for evacuation", active_workers, workers()->max_workers()); } void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info, G1ParScanThreadStateSet* per_thread_states) { @@ -625,7 +625,7 @@ public: size_t term_attempts() const { return _term_attempts; } }; -class G1EvacuateRegionsBaseTask : public AbstractGangTask { +class G1EvacuateRegionsBaseTask : public WorkerTask { protected: G1CollectedHeap* _g1h; G1ParScanThreadStateSet* _per_thread_states; @@ -673,7 +673,7 @@ public: G1ParScanThreadStateSet* per_thread_states, G1ScannerTasksQueueSet* task_queues, uint num_workers) : - AbstractGangTask(name), + WorkerTask(name), _g1h(G1CollectedHeap::heap()), _per_thread_states(per_thread_states), _task_queues(task_queues), @@ -757,7 +757,7 @@ void G1YoungCollector::evacuate_initial_collection_set(G1ParScanThreadStateSet* has_optional_evacuation_work); task_time = run_task_timed(&g1_par_task); // Closing the inner scope will execute the destructor for the - // G1RootProcessor object. By subtracting the WorkGang task from the total + // G1RootProcessor object. By subtracting the WorkerThreads task from the total // time of this scope, we get the "NMethod List Cleanup" time. This list is // constructed during "STW two-phase nmethod root processing", see more in // nmethod.hpp @@ -926,16 +926,14 @@ class G1STWRefProcProxyTask : public RefProcProxyTask { public: G1EnqueueDiscoveredFieldClosure(G1CollectedHeap* g1h, G1ParScanThreadState* pss) : _g1h(g1h), _pss(pss) { } - void enqueue(oop reference, oop value) override { - HeapWord* discovered_addr = java_lang_ref_Reference::discovered_addr_raw(reference); - + void enqueue(HeapWord* discovered_field_addr, oop value) override { + assert(_g1h->is_in(discovered_field_addr), PTR_FORMAT " is not in heap ", p2i(discovered_field_addr)); // Store the value first, whatever it is. - RawAccess<>::oop_store(discovered_addr, value); - + RawAccess<>::oop_store(discovered_field_addr, value); if (value == nullptr) { return; } - _pss->write_ref_field_post(discovered_addr, value); + _pss->write_ref_field_post(discovered_field_addr, value); } }; @@ -986,8 +984,6 @@ void G1YoungCollector::process_discovered_references(G1ParScanThreadStateSet* pe _g1h->make_pending_list_reachable(); - rp->verify_no_references_recorded(); - phase_times()->record_ref_proc_time((Ticks::now() - start).seconds() * MILLIUNITS); } @@ -1106,12 +1102,12 @@ void G1YoungCollector::collect() { // Young GC internal pause timing G1YoungGCNotifyPauseMark npm(this); - // Verification may use the gang workers, so they must be set up before. + // Verification may use the workers, so they must be set up before. // Individual parallel phases may override this. set_young_collection_default_active_worker_threads(); // Wait for root region scan here to make sure that it is done before any - // use of the STW work gang to maximize cpu use (i.e. all cores are available + // use of the STW workers to maximize cpu use (i.e. all cores are available // just to do that). wait_for_root_region_scanning(); diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.hpp b/src/hotspot/share/gc/g1/g1YoungCollector.hpp index cc6370f1957f911ed18d7154be0cdb5fada9deec..4bc1eaf19421c90612613ba96a167ada2f2a6134 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.hpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.hpp @@ -30,9 +30,9 @@ #include "gc/shared/gcCause.hpp" #include "gc/shared/taskqueue.hpp" -class AbstractGangTask; +class WorkerTask; class G1Allocator; -class G1BatchedGangTask; +class G1BatchedTask; class G1CardSetMemoryStats; class G1CollectedHeap; class G1CollectionSet; @@ -52,7 +52,7 @@ class G1RemSet; class G1SurvivorRegions; class G1YoungGCEvacFailureInjector; class STWGCTimer; -class WorkGang; +class WorkerThreads; class outputStream; @@ -78,7 +78,7 @@ class G1YoungCollector { G1ScannerTasksQueueSet* task_queues() const; G1SurvivorRegions* survivor_regions() const; ReferenceProcessor* ref_processor_stw() const; - WorkGang* workers() const; + WorkerThreads* workers() const; G1YoungGCEvacFailureInjector* evac_failure_injector() const; GCCause::Cause _gc_cause; @@ -89,9 +89,9 @@ class G1YoungCollector { // Evacuation failure tracking. G1EvacFailureRegions _evac_failure_regions; - // Runs the given AbstractGangTask with the current active workers, + // Runs the given WorkerTask with the current active workers, // returning the total time taken. - Tickspan run_task_timed(AbstractGangTask* task); + Tickspan run_task_timed(WorkerTask* task); void wait_for_root_region_scanning(); diff --git a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.cpp index cedaba765ea6db07396f29a69f7bc43a84210844..67b44263004d691654af6a62def2a390a4bfc8ac 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.cpp @@ -28,7 +28,7 @@ #include "gc/g1/g1YoungGCEvacFailureInjector.inline.hpp" #include "gc/g1/g1_globals.hpp" -#ifndef PRODUCT +#if EVAC_FAILURE_INJECTOR bool G1YoungGCEvacFailureInjector::arm_if_needed_for_gc_type(bool for_young_gc, bool during_concurrent_start, @@ -76,4 +76,4 @@ void G1YoungGCEvacFailureInjector::reset() { _inject_evacuation_failure_for_current_gc = false; } -#endif // #ifndef PRODUCT +#endif // #if EVAC_FAILURE_INJECTOR diff --git a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.hpp b/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.hpp index 64d3f28b1ff421ea1d56a37ca93c32b8aaa8f7dc..eb0d4598c14fe49e3b401abcd048eab44bb11de4 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.hpp @@ -25,9 +25,20 @@ #ifndef SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_HPP #define SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_HPP +#include "gc/g1/g1_globals.hpp" #include "memory/allStatic.hpp" #include "utilities/globalDefinitions.hpp" +#if EVAC_FAILURE_INJECTOR +#define EVAC_FAILURE_INJECTOR_RETURN +#define EVAC_FAILURE_INJECTOR_RETURN_(code) +#define EVAC_FAILURE_INJECTOR_ONLY(code) code +#else +#define EVAC_FAILURE_INJECTOR_RETURN { return; } +#define EVAC_FAILURE_INJECTOR_RETURN_(code) { code } +#define EVAC_FAILURE_INJECTOR_ONLY(code) +#endif // EVAC_FAILURE_INJECTOR + // Support for injecting evacuation failures based on the G1EvacuationFailureALot* // flags. Analogous to PromotionFailureALot for the other collectors. // @@ -35,9 +46,9 @@ // inbetween we "arm" the injector to induce evacuation failures after // G1EvacuationFailureALotCount successful evacuations. // -// Available only in non-product builds. +// Available only when EVAC_FAILURE_INJECTOR is defined. class G1YoungGCEvacFailureInjector { -#ifndef PRODUCT +#if EVAC_FAILURE_INJECTOR // Should we inject evacuation failures in the current GC. bool _inject_evacuation_failure_for_current_gc; @@ -49,20 +60,20 @@ class G1YoungGCEvacFailureInjector { bool arm_if_needed_for_gc_type(bool for_young_gc, bool during_concurrent_start, - bool mark_or_rebuild_in_progress) PRODUCT_RETURN_( return false; ); + bool mark_or_rebuild_in_progress) EVAC_FAILURE_INJECTOR_RETURN_( return false; ); public: // Arm the evacuation failure injector if needed for the current // GC (based upon the type of GC and which command line flags are set); - void arm_if_needed() PRODUCT_RETURN; + void arm_if_needed() EVAC_FAILURE_INJECTOR_RETURN; // Return true if it's time to cause an evacuation failure; the caller // provides the (preferably thread-local) counter to minimize performance impact. - bool evacuation_should_fail(size_t& counter) PRODUCT_RETURN_( return false; ); + bool evacuation_should_fail(size_t& counter) EVAC_FAILURE_INJECTOR_RETURN_( return false; ); // Reset the evacuation failure injection counters. Should be called at // the end of an evacuation pause in which an evacuation failure occurred. - void reset() PRODUCT_RETURN; + void reset() EVAC_FAILURE_INJECTOR_RETURN; }; #endif /* SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_HPP */ diff --git a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.inline.hpp b/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.inline.hpp index 19499c266c22bb423f746ec282a0e365671d2974..e259fc6137650408729634b8b373ebeca9a80a60 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.inline.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGCEvacFailureInjector.inline.hpp @@ -30,7 +30,7 @@ #include "gc/g1/g1_globals.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#ifndef PRODUCT +#if EVAC_FAILURE_INJECTOR inline bool G1YoungGCEvacFailureInjector::evacuation_should_fail(size_t& counter) { if (!_inject_evacuation_failure_for_current_gc) { @@ -43,7 +43,7 @@ inline bool G1YoungGCEvacFailureInjector::evacuation_should_fail(size_t& counter return true; } -#endif // #ifndef PRODUCT +#endif // #if EVAC_FAILURE_INJECTOR #endif /* SHARE_GC_G1_G1YOUNGGCEVACUATIONFAILUREINJECTOR_INLINE_HPP */ diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 88c9cba3c606bccb88c0b9887e53bc3733190cb5..2529d5322efad4672f8061cf43f1c1dce4b07ff3 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -125,7 +125,7 @@ public: G1PostEvacuateCollectionSetCleanupTask1::G1PostEvacuateCollectionSetCleanupTask1(G1ParScanThreadStateSet* per_thread_states, G1EvacFailureRegions* evac_failure_regions) : - G1BatchedGangTask("Post Evacuate Cleanup 1", G1CollectedHeap::heap()->phase_times()) + G1BatchedTask("Post Evacuate Cleanup 1", G1CollectedHeap::heap()->phase_times()) { bool evacuation_failed = evac_failure_regions->evacuation_failed(); @@ -204,7 +204,7 @@ public: log_debug(gc, humongous)("Reclaimed humongous region %u (object size " SIZE_FORMAT " @ " PTR_FORMAT ")", region_idx, - (size_t)obj->size() * HeapWordSize, + obj->size() * HeapWordSize, p2i(r->bottom()) ); @@ -306,7 +306,7 @@ public: class G1PostEvacuateCollectionSetCleanupTask2::RestorePreservedMarksTask : public G1AbstractSubTask { PreservedMarksSet* _preserved_marks; - AbstractGangTask* _task; + WorkerTask* _task; public: RestorePreservedMarksTask(PreservedMarksSet* preserved_marks) : @@ -672,7 +672,7 @@ public: G1PostEvacuateCollectionSetCleanupTask2::G1PostEvacuateCollectionSetCleanupTask2(G1ParScanThreadStateSet* per_thread_states, G1EvacInfo* evacuation_info, G1EvacFailureRegions* evac_failure_regions) : - G1BatchedGangTask("Post Evacuate Cleanup 2", G1CollectedHeap::heap()->phase_times()) + G1BatchedTask("Post Evacuate Cleanup 2", G1CollectedHeap::heap()->phase_times()) { add_serial_task(new ResetHotCardCacheTask()); add_serial_task(new PurgeCodeRootsTask()); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp index 6613438e19b3fc35b3d1dcfd12785f5bfa2b0365..2de660948532f20d426227342a01883534537d55 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_GC_G1_G1YOUNGGCPOSTEVACUATETASKS_HPP #define SHARE_GC_G1_G1YOUNGGCPOSTEVACUATETASKS_HPP -#include "gc/g1/g1BatchedGangTask.hpp" +#include "gc/g1/g1BatchedTask.hpp" #include "gc/g1/g1EvacFailure.hpp" class FreeCSetStats; @@ -41,7 +41,7 @@ class G1ParScanThreadStateSet; // - Sample Collection Set Candidates (s) // - Remove Self Forwards (on evacuation failure) // - Clear Card Table -class G1PostEvacuateCollectionSetCleanupTask1 : public G1BatchedGangTask { +class G1PostEvacuateCollectionSetCleanupTask1 : public G1BatchedTask { class MergePssTask; class RecalculateUsedTask; class SampleCollectionSetCandidatesTask; @@ -60,7 +60,7 @@ public: // - Redirty Logged Cards // - Restore Preserved Marks (on evacuation failure) // - Free Collection Set -class G1PostEvacuateCollectionSetCleanupTask2 : public G1BatchedGangTask { +class G1PostEvacuateCollectionSetCleanupTask2 : public G1BatchedTask { class EagerlyReclaimHumongousObjectsTask; class PurgeCodeRootsTask; class ResetHotCardCacheTask; diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index 8b87c373d5e56d8f1a1585813b31bc2f9cb53a3a..34dbabe4fecc5d6992d1b625af79d374df6c4baa 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -27,6 +27,62 @@ #include "runtime/globals_shared.hpp" +// Enable evacuation failure injector by default in non-product builds. + +#ifdef EVAC_FAILURE_INJECTOR +#error "EVAC_FAILURE_INJECTOR already defined" +#endif +#ifndef PRODUCT +#define EVAC_FAILURE_INJECTOR 1 +#else +#define EVAC_FAILURE_INJECTOR 0 +#endif + +#if EVAC_FAILURE_INJECTOR +#define GC_G1_EVACUATION_FAILURE_FLAGS(develop, \ + develop_pd, \ + product, \ + product_pd, \ + notproduct, \ + range, \ + constraint) \ + \ + product(bool, G1EvacuationFailureALot, false, \ + "Force use of evacuation failure handling during certain " \ + "evacuation pauses") \ + \ + product(uintx, G1EvacuationFailureALotCount, 1000, \ + "Number of successful evacuations between evacuation failures " \ + "occurring at object copying per thread") \ + \ + product(uintx, G1EvacuationFailureALotInterval, 5, \ + "Total collections between forced triggering of evacuation " \ + "failures") \ + \ + product(bool, G1EvacuationFailureALotDuringConcMark, true, \ + "Force use of evacuation failure handling during evacuation " \ + "pauses when marking is in progress") \ + \ + product(bool, G1EvacuationFailureALotDuringConcurrentStart, true, \ + "Force use of evacuation failure handling during concurrent " \ + "start evacuation pauses") \ + \ + product(bool, G1EvacuationFailureALotDuringYoungGC, true, \ + "Force use of evacuation failure handling during young " \ + "evacuation pauses") \ + \ + product(bool, G1EvacuationFailureALotDuringMixedGC, true, \ + "Force use of evacuation failure handling during mixed " \ + "evacuation pauses") +#else +#define GC_G1_EVACUATION_FAILURE_FLAGS(develop, \ + develop_pd, \ + product, \ + product_pd, \ + notproduct, \ + range, \ + constraint) +#endif // // Defines all globals flags used by the garbage-first compiler. // @@ -269,34 +325,6 @@ "as a percentage of the heap size.") \ range(0, 100) \ \ - notproduct(bool, G1EvacuationFailureALot, false, \ - "Force use of evacuation failure handling during certain " \ - "evacuation pauses") \ - \ - develop(uintx, G1EvacuationFailureALotCount, 1000, \ - "Number of successful evacuations between evacuation failures " \ - "occurring at object copying per thread") \ - \ - develop(uintx, G1EvacuationFailureALotInterval, 5, \ - "Total collections between forced triggering of evacuation " \ - "failures") \ - \ - develop(bool, G1EvacuationFailureALotDuringConcMark, true, \ - "Force use of evacuation failure handling during evacuation " \ - "pauses when marking is in progress") \ - \ - develop(bool, G1EvacuationFailureALotDuringConcurrentStart, true, \ - "Force use of evacuation failure handling during concurrent " \ - "start evacuation pauses") \ - \ - develop(bool, G1EvacuationFailureALotDuringYoungGC, true, \ - "Force use of evacuation failure handling during young " \ - "evacuation pauses") \ - \ - develop(bool, G1EvacuationFailureALotDuringMixedGC, true, \ - "Force use of evacuation failure handling during mixed " \ - "evacuation pauses") \ - \ product(bool, G1VerifyRSetsDuringFullGC, false, DIAGNOSTIC, \ "If true, perform verification of each heap region's " \ "remembered set when verifying the heap during a full GC.") \ @@ -343,7 +371,15 @@ product(bool, G1UsePreventiveGC, true, DIAGNOSTIC, \ "Allows collections to be triggered proactively based on the \ number of free regions and the expected survival rates in each \ - section of the heap.") + section of the heap.") \ + \ + GC_G1_EVACUATION_FAILURE_FLAGS(develop, \ + develop_pd, \ + product, \ + product_pd, \ + notproduct, \ + range, \ + constraint) // end of GC_G1_FLAGS diff --git a/src/hotspot/share/gc/g1/heapRegion.cpp b/src/hotspot/share/gc/g1/heapRegion.cpp index 1b4adfd4bc2380094805e97b571e1c4543db3354..3e10be4741a3b5b79a7a6fc92ab7d6e49f4b9fd0 100644 --- a/src/hotspot/share/gc/g1/heapRegion.cpp +++ b/src/hotspot/share/gc/g1/heapRegion.cpp @@ -84,8 +84,6 @@ void HeapRegion::setup_heap_region_size(size_t max_heap_size) { LogOfHRGrainBytes = region_size_log; guarantee(GrainBytes == 0, "we should only set it once"); - // The cast to int is safe, given that we've bounded region_size by - // MIN_REGION_SIZE and MAX_REGION_SIZE. GrainBytes = region_size; guarantee(GrainWords == 0, "we should only set it once"); @@ -233,7 +231,7 @@ HeapRegion::HeapRegion(uint hrm_index, _top(NULL), _compaction_top(NULL), _bot_part(bot, this), - _par_alloc_lock(Mutex::service-2, "HeapRegionParAlloc_lock", Mutex::_safepoint_check_never), + _par_alloc_lock(Mutex::service-2, "HeapRegionParAlloc_lock"), _pre_dummy_top(NULL), _rem_set(NULL), _hrm_index(hrm_index), @@ -611,12 +609,11 @@ public: if (!_failures) { log.error("----------"); } - LogStream ls(log.error()); - to->rem_set()->print_info(&ls, p); log.error("Missing rem set entry:"); log.error("Field " PTR_FORMAT " of obj " PTR_FORMAT " in region " HR_FORMAT, p2i(p), p2i(_containing_obj), HR_FORMAT_PARAMS(from)); ResourceMark rm; + LogStream ls(log.error()); _containing_obj->print_on(&ls); log.error("points to obj " PTR_FORMAT " in region " HR_FORMAT " remset %s", p2i(obj), HR_FORMAT_PARAMS(to), to->rem_set()->get_state_str()); diff --git a/src/hotspot/share/gc/g1/heapRegion.inline.hpp b/src/hotspot/share/gc/g1/heapRegion.inline.hpp index 88404901c3a8b6642cc99429f63cbe26f556b38d..91ade8a5029c554343736e81b15a2fc1884175d5 100644 --- a/src/hotspot/share/gc/g1/heapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.inline.hpp @@ -327,7 +327,7 @@ HeapWord* HeapRegion::do_oops_on_memregion_in_humongous(MemRegion mr, // If obj is not an objArray and mr contains the start of the // obj, then this could be an imprecise mark, and we need to // process the entire object. - int size = obj->oop_iterate_size(cl); + size_t size = obj->oop_iterate_size(cl); // We have scanned to the end of the object, but since there can be no objects // after this humongous object in the region, we can return the end of the // region if it is greater. diff --git a/src/hotspot/share/gc/g1/heapRegionManager.cpp b/src/hotspot/share/gc/g1/heapRegionManager.cpp index 28e5403de1cbde6a24887d35bb87ec4b21ec40de..1f5459e15df0a1d2d2f7c80bc69c9e43724b20a7 100644 --- a/src/hotspot/share/gc/g1/heapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/heapRegionManager.cpp @@ -166,8 +166,8 @@ HeapRegion* HeapRegionManager::new_heap_region(uint hrm_index) { return g1h->new_heap_region(hrm_index, mr); } -void HeapRegionManager::expand(uint start, uint num_regions, WorkGang* pretouch_gang) { - commit_regions(start, num_regions, pretouch_gang); +void HeapRegionManager::expand(uint start, uint num_regions, WorkerThreads* pretouch_workers) { + commit_regions(start, num_regions, pretouch_workers); for (uint i = start; i < start + num_regions; i++) { HeapRegion* hr = _regions.get_by_index(i); if (hr == NULL) { @@ -181,21 +181,21 @@ void HeapRegionManager::expand(uint start, uint num_regions, WorkGang* pretouch_ activate_regions(start, num_regions); } -void HeapRegionManager::commit_regions(uint index, size_t num_regions, WorkGang* pretouch_gang) { +void HeapRegionManager::commit_regions(uint index, size_t num_regions, WorkerThreads* pretouch_workers) { guarantee(num_regions > 0, "Must commit more than zero regions"); guarantee(num_regions <= available(), "Cannot commit more than the maximum amount of regions"); - _heap_mapper->commit_regions(index, num_regions, pretouch_gang); + _heap_mapper->commit_regions(index, num_regions, pretouch_workers); // Also commit auxiliary data - _prev_bitmap_mapper->commit_regions(index, num_regions, pretouch_gang); - _next_bitmap_mapper->commit_regions(index, num_regions, pretouch_gang); + _prev_bitmap_mapper->commit_regions(index, num_regions, pretouch_workers); + _next_bitmap_mapper->commit_regions(index, num_regions, pretouch_workers); - _bot_mapper->commit_regions(index, num_regions, pretouch_gang); - _cardtable_mapper->commit_regions(index, num_regions, pretouch_gang); + _bot_mapper->commit_regions(index, num_regions, pretouch_workers); + _cardtable_mapper->commit_regions(index, num_regions, pretouch_workers); - _card_counts_mapper->commit_regions(index, num_regions, pretouch_gang); + _card_counts_mapper->commit_regions(index, num_regions, pretouch_workers); } void HeapRegionManager::uncommit_regions(uint start, uint num_regions) { @@ -346,7 +346,7 @@ uint HeapRegionManager::expand_inactive(uint num_regions) { return expanded; } -uint HeapRegionManager::expand_any(uint num_regions, WorkGang* pretouch_workers) { +uint HeapRegionManager::expand_any(uint num_regions, WorkerThreads* pretouch_workers) { assert(num_regions > 0, "Must expand at least 1 region"); uint offset = 0; @@ -368,7 +368,7 @@ uint HeapRegionManager::expand_any(uint num_regions, WorkGang* pretouch_workers) return expanded; } -uint HeapRegionManager::expand_by(uint num_regions, WorkGang* pretouch_workers) { +uint HeapRegionManager::expand_by(uint num_regions, WorkerThreads* pretouch_workers) { assert(num_regions > 0, "Must expand at least 1 region"); // First "undo" any requests to uncommit memory concurrently by @@ -384,7 +384,7 @@ uint HeapRegionManager::expand_by(uint num_regions, WorkGang* pretouch_workers) return expanded; } -void HeapRegionManager::expand_exact(uint start, uint num_regions, WorkGang* pretouch_workers) { +void HeapRegionManager::expand_exact(uint start, uint num_regions, WorkerThreads* pretouch_workers) { assert(num_regions != 0, "Need to request at least one region"); uint end = start + num_regions; @@ -555,7 +555,7 @@ uint HeapRegionManager::find_highest_free(bool* expanded) { return G1_NO_HRM_INDEX; } -bool HeapRegionManager::allocate_containing_regions(MemRegion range, size_t* commit_count, WorkGang* pretouch_workers) { +bool HeapRegionManager::allocate_containing_regions(MemRegion range, size_t* commit_count, WorkerThreads* pretouch_workers) { size_t commits = 0; uint start_index = (uint)_regions.get_index_by_address(range.start()); uint last_index = (uint)_regions.get_index_by_address(range.last()); @@ -760,7 +760,7 @@ bool HeapRegionClaimer::claim_region(uint region_index) { return old_val == Unclaimed; } -class G1RebuildFreeListTask : public AbstractGangTask { +class G1RebuildFreeListTask : public WorkerTask { HeapRegionManager* _hrm; FreeRegionList* _worker_freelists; uint _worker_chunk_size; @@ -768,7 +768,7 @@ class G1RebuildFreeListTask : public AbstractGangTask { public: G1RebuildFreeListTask(HeapRegionManager* hrm, uint num_workers) : - AbstractGangTask("G1 Rebuild Free List Task"), + WorkerTask("G1 Rebuild Free List Task"), _hrm(hrm), _worker_freelists(NEW_C_HEAP_ARRAY(FreeRegionList, num_workers, mtGC)), _worker_chunk_size((_hrm->reserved_length() + num_workers - 1) / num_workers), @@ -818,7 +818,7 @@ public: } }; -void HeapRegionManager::rebuild_free_list(WorkGang* workers) { +void HeapRegionManager::rebuild_free_list(WorkerThreads* workers) { // Abandon current free list to allow a rebuild. _free_list.abandon(); diff --git a/src/hotspot/share/gc/g1/heapRegionManager.hpp b/src/hotspot/share/gc/g1/heapRegionManager.hpp index 04f799899a7b6d7b6c172ff2e83aecfdb4122e6a..5846171ce9196dd9e631f473a38f52eff39da8f2 100644 --- a/src/hotspot/share/gc/g1/heapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/heapRegionManager.hpp @@ -36,7 +36,7 @@ class HeapRegion; class HeapRegionClosure; class HeapRegionClaimer; class FreeRegionList; -class WorkGang; +class WorkerThreads; class G1HeapRegionTable : public G1BiasedMappedArray { protected: @@ -89,7 +89,7 @@ class HeapRegionManager: public CHeapObj { HeapWord* heap_end() const {return _regions.end_address_mapped(); } // Pass down commit calls to the VirtualSpace. - void commit_regions(uint index, size_t num_regions = 1, WorkGang* pretouch_gang = NULL); + void commit_regions(uint index, size_t num_regions = 1, WorkerThreads* pretouch_workers = NULL); // Initialize the HeapRegions in the range and put them on the free list. void initialize_regions(uint start, uint num_regions); @@ -127,7 +127,7 @@ class HeapRegionManager: public CHeapObj { G1RegionToSpaceMapper* _next_bitmap_mapper; FreeRegionList _free_list; - void expand(uint index, uint num_regions, WorkGang* pretouch_gang = NULL); + void expand(uint index, uint num_regions, WorkerThreads* pretouch_workers = NULL); // G1RegionCommittedMap helpers. These functions do the work that comes with // the state changes tracked by G1CommittedRegionMap. To make sure this is @@ -147,11 +147,11 @@ class HeapRegionManager: public CHeapObj { HeapRegion* allocate_humongous_allow_expand(uint num_regions); // Expand helper for cases when the regions to expand are well defined. - void expand_exact(uint start, uint num_regions, WorkGang* pretouch_workers); + void expand_exact(uint start, uint num_regions, WorkerThreads* pretouch_workers); // Expand helper activating inactive regions rather than committing new ones. uint expand_inactive(uint num_regions); // Expand helper finding new regions to commit. - uint expand_any(uint num_regions, WorkGang* pretouch_workers); + uint expand_any(uint num_regions, WorkerThreads* pretouch_workers); #ifdef ASSERT public: @@ -197,7 +197,7 @@ public: inline void insert_into_free_list(HeapRegion* hr); // Rebuild the free region list from scratch. - void rebuild_free_list(WorkGang* workers); + void rebuild_free_list(WorkerThreads* workers); // Insert the given region list into the global free region list. void insert_list_into_free_list(FreeRegionList* list) { @@ -253,7 +253,7 @@ public: // HeapRegions, or re-use existing ones. Returns the number of regions the // sequence was expanded by. If a HeapRegion allocation fails, the resulting // number of regions might be smaller than what's desired. - uint expand_by(uint num_regions, WorkGang* pretouch_workers); + uint expand_by(uint num_regions, WorkerThreads* pretouch_workers); // Try to expand on the given node index, returning the index of the new region. uint expand_on_preferred_node(uint node_index); @@ -268,7 +268,7 @@ public: // Allocate the regions that contain the address range specified, committing the // regions if necessary. Return false if any of the regions is already committed // and not free, and return the number of regions newly committed in commit_count. - bool allocate_containing_regions(MemRegion range, size_t* commit_count, WorkGang* pretouch_workers); + bool allocate_containing_regions(MemRegion range, size_t* commit_count, WorkerThreads* pretouch_workers); // Apply blk->do_heap_region() on all committed regions in address order, // terminating the iteration early if do_heap_region() returns true. diff --git a/src/hotspot/share/gc/g1/heapRegionRemSet.cpp b/src/hotspot/share/gc/g1/heapRegionRemSet.cpp index 7182fae29309ea9d3b856a2ed438fb56e2c48043..c7633e772351c392423ec149123083e816487957 100644 --- a/src/hotspot/share/gc/g1/heapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/heapRegionRemSet.cpp @@ -35,6 +35,7 @@ #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/globals_extension.hpp" +#include "runtime/java.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/debug.hpp" #include "utilities/formatBuffer.hpp" @@ -42,12 +43,42 @@ #include "utilities/growableArray.hpp" #include "utilities/powerOfTwo.hpp" +uint HeapRegionRemSet::_split_card_shift = 0; +size_t HeapRegionRemSet::_split_card_mask = 0; +HeapWord* HeapRegionRemSet::_heap_base_address = nullptr; + const char* HeapRegionRemSet::_state_strings[] = {"Untracked", "Updating", "Complete"}; const char* HeapRegionRemSet::_short_state_strings[] = {"UNTRA", "UPDAT", "CMPLT"}; +void HeapRegionRemSet::initialize(MemRegion reserved) { + const uint BitsInUint = sizeof(uint) * BitsPerByte; + const uint CardBitsWithinCardRegion = MIN2((uint)HeapRegion::LogCardsPerRegion, G1CardSetContainer::LogCardsPerRegionLimit); + + // Check if the number of cards within a region fits an uint. + if (CardBitsWithinCardRegion > BitsInUint) { + vm_exit_during_initialization("Can not represent all cards in a card region within uint."); + } + + _split_card_shift = CardBitsWithinCardRegion + CardTable::card_shift; + _split_card_mask = ((size_t)1 << _split_card_shift) - 1; + + // Check if the card region/region within cards combination can cover the heap. + const uint HeapSizeBits = log2i_exact(round_up_power_of_2(reserved.byte_size())); + if (HeapSizeBits > (BitsInUint + _split_card_shift)) { + FormatBuffer<> fmt("Can not represent all cards in the heap with card region/card within region. " + "Heap %zuB (%u bits) Remembered set covers %u bits.", + reserved.byte_size(), + HeapSizeBits, + BitsInUint + _split_card_shift); + vm_exit_during_initialization(fmt, "Decrease heap size."); + } + + _heap_base_address = reserved.start(); +} + HeapRegionRemSet::HeapRegionRemSet(HeapRegion* hr, G1CardSetConfiguration* config) : - _m(Mutex::service - 1, FormatBuffer<128>("HeapRegionRemSet#%u_lock", hr->hrm_index()), Monitor::_safepoint_check_never), + _m(Mutex::service - 1, FormatBuffer<128>("HeapRegionRemSet#%u_lock", hr->hrm_index())), _code_roots(), _card_set_mm(config, G1CardSetFreePool::free_list_pool()), _card_set(config, &_card_set_mm), diff --git a/src/hotspot/share/gc/g1/heapRegionRemSet.hpp b/src/hotspot/share/gc/g1/heapRegionRemSet.hpp index 6bfe10df90820416b03734198a0f85501038f85d..958d6fceaf18920700779d30429f1c62df79b4d5 100644 --- a/src/hotspot/share/gc/g1/heapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/heapRegionRemSet.hpp @@ -50,6 +50,17 @@ class HeapRegionRemSet : public CHeapObj { HeapRegion* _hr; + // When splitting addresses into region and card within that region, the logical + // shift value to get the region. + static uint _split_card_shift; + // When splitting addresses into region and card within that region, the mask + // to get the offset within the region. + static size_t _split_card_mask; + // Cached value of heap base address. + static HeapWord* _heap_base_address; + + // Split the given address into region of that card and the card within that + // region. inline void split_card(OopOrNarrowOopStar from, uint& card_region, uint& card_within_region) const; void clear_fcc(); @@ -78,6 +89,8 @@ public: return _card_set.occupied(); } + static void initialize(MemRegion reserved); + // Coarsening statistics since VM start. static G1CardSetCoarsenStats coarsen_stats() { return G1CardSet::coarsen_stats(); } diff --git a/src/hotspot/share/gc/g1/heapRegionRemSet.inline.hpp b/src/hotspot/share/gc/g1/heapRegionRemSet.inline.hpp index 51a01e782535846afabc50189920e1d51602ec42..b6c4ee9c1bbd11ba694625fee29adcf704fa907c 100644 --- a/src/hotspot/share/gc/g1/heapRegionRemSet.inline.hpp +++ b/src/hotspot/share/gc/g1/heapRegionRemSet.inline.hpp @@ -62,9 +62,10 @@ inline void HeapRegionRemSet::iterate_for_merge(CardOrRangeVisitor& cl) { } void HeapRegionRemSet::split_card(OopOrNarrowOopStar from, uint& card_region, uint& card_within_region) const { - HeapRegion* hr = G1CollectedHeap::heap()->heap_region_containing(from); - card_region = hr->hrm_index(); - card_within_region = (uint)(pointer_delta((HeapWord*)from, hr->bottom()) >> (CardTable::card_shift - LogHeapWordSize)); + size_t offset = pointer_delta(from, _heap_base_address, 1); + card_region = (uint)(offset >> _split_card_shift); + card_within_region = (uint)((offset & _split_card_mask) >> CardTable::card_shift); + assert(card_within_region < ((uint)1 << G1CardSetContainer::LogCardsPerRegionLimit), "must be"); } void HeapRegionRemSet::add_reference(OopOrNarrowOopStar from, uint tid) { diff --git a/src/hotspot/share/gc/g1/heapRegionType.hpp b/src/hotspot/share/gc/g1/heapRegionType.hpp index 9f0f58f340bd3020db769a3a9de1e7410d55229e..74807c37746598eda34e1c32c53a8a87c1528333 100644 --- a/src/hotspot/share/gc/g1/heapRegionType.hpp +++ b/src/hotspot/share/gc/g1/heapRegionType.hpp @@ -58,8 +58,8 @@ private: // 01000 0 [16] Old Mask // // 10000 0 [32] Archive Mask - // 11100 0 [56] Open Archive - // 11100 1 [57] Closed Archive + // 10100 0 [40] Open Archive + // 10100 1 [41] Closed Archive // typedef enum { FreeTag = 0, diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index c00890d38e17ec0346c9964b3b221a64d0514a29..e359d0f9a39a025ebe86020ca735a6f0f0cb6171 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -27,7 +27,7 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" #include "gc/shared/spaceDecorator.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "memory/allocation.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.hpp" @@ -577,7 +577,7 @@ void MutableNUMASpace::initialize(MemRegion mr, bool clear_space, bool mangle_space, bool setup_pages, - WorkGang* pretouch_gang) { + WorkerThreads* pretouch_workers) { assert(clear_space, "Reallocation will destroy data!"); assert(lgrp_spaces()->length() > 0, "There should be at least one space"); diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp index a27601c66fe3352950c41e941d5fd96dd642a358..881f280c4b42ca202636b9c60453f7dff0e320a9 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp @@ -201,7 +201,7 @@ class MutableNUMASpace : public MutableSpace { bool clear_space, bool mangle_space, bool setup_pages = SetupPages, - WorkGang* pretouch_gang = NULL); + WorkerThreads* pretouch_workers = NULL); // Update space layout if necessary. Do all adaptive resizing job. virtual void update(); // Update allocation rate averages. diff --git a/src/hotspot/share/gc/parallel/mutableSpace.cpp b/src/hotspot/share/gc/parallel/mutableSpace.cpp index 363e115d10d8af47bdce35958a46a09eca75845f..e28a71e5691255ea275e8128de254043b4bac0f7 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.cpp @@ -72,7 +72,7 @@ void MutableSpace::initialize(MemRegion mr, bool clear_space, bool mangle_space, bool setup_pages, - WorkGang* pretouch_gang) { + WorkerThreads* pretouch_workers) { assert(Universe::on_page_boundary(mr.start()) && Universe::on_page_boundary(mr.end()), "invalid space boundaries"); @@ -122,10 +122,10 @@ void MutableSpace::initialize(MemRegion mr, size_t page_size = UseLargePages ? os::large_page_size() : os::vm_page_size(); PretouchTask::pretouch("ParallelGC PreTouch head", (char*)head.start(), (char*)head.end(), - page_size, pretouch_gang); + page_size, pretouch_workers); PretouchTask::pretouch("ParallelGC PreTouch tail", (char*)tail.start(), (char*)tail.end(), - page_size, pretouch_gang); + page_size, pretouch_workers); } // Remember where we stopped so that we can continue later. diff --git a/src/hotspot/share/gc/parallel/mutableSpace.hpp b/src/hotspot/share/gc/parallel/mutableSpace.hpp index b6bb131828f22b0da41bb9ad0247bde8da816b00..548b6a4949ee7cb0d5189e36e18b1ff7d45216a5 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.hpp @@ -32,7 +32,7 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" -class WorkGang; +class WorkerThreads; // A MutableSpace supports the concept of allocation. This includes the // concepts that a space may be only partially full, and the query methods @@ -102,7 +102,7 @@ class MutableSpace: public CHeapObj { bool clear_space, bool mangle_space, bool setup_pages = SetupPages, - WorkGang* pretouch_gang = NULL); + WorkerThreads* pretouch_workers = NULL); virtual void clear(bool mangle_space); virtual void update() { } diff --git a/src/hotspot/share/gc/parallel/parMarkBitMap.hpp b/src/hotspot/share/gc/parallel/parMarkBitMap.hpp index ed08fed4eb7518dd127a940d6a383c911e7752c4..7713af6e7a602b462b0f95d3279b195685116921 100644 --- a/src/hotspot/share/gc/parallel/parMarkBitMap.hpp +++ b/src/hotspot/share/gc/parallel/parMarkBitMap.hpp @@ -46,7 +46,7 @@ public: // Atomically mark an object as live. bool mark_obj(HeapWord* addr, size_t size); - inline bool mark_obj(oop obj, int size); + inline bool mark_obj(oop obj, size_t size); // Return whether the specified begin or end bit is set. inline bool is_obj_beg(idx_t bit) const; diff --git a/src/hotspot/share/gc/parallel/parMarkBitMap.inline.hpp b/src/hotspot/share/gc/parallel/parMarkBitMap.inline.hpp index 6b939abd594a77d1d670c5e69c7e99199e9cba3f..600209a961e415984405ac1b33b479b5dc9ace6a 100644 --- a/src/hotspot/share/gc/parallel/parMarkBitMap.inline.hpp +++ b/src/hotspot/share/gc/parallel/parMarkBitMap.inline.hpp @@ -144,8 +144,8 @@ inline ParMarkBitMap::IterationStatus ParMarkBitMap::iterate(ParMarkBitMapClosur addr_to_bit(dead_range_end)); } -inline bool ParMarkBitMap::mark_obj(oop obj, int size) { - return mark_obj(cast_from_oop(obj), (size_t)size); +inline bool ParMarkBitMap::mark_obj(oop obj, size_t size) { + return mark_obj(cast_from_oop(obj), size); } inline ParMarkBitMap::idx_t ParMarkBitMap::addr_to_bit(HeapWord* addr) const { diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 567ae94b83973ad1898622ff8bab3eb85f6f6f4a..0013007a923c4f1fddc026d1388a29c7bf93a247 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -85,7 +85,7 @@ jint ParallelScavengeHeap::initialize() { ReservedSpace young_rs = heap_rs.last_part(MaxOldSize); assert(young_rs.size() == MaxNewSize, "Didn't reserve all of the heap"); - // Set up WorkGang + // Set up WorkerThreads _workers.initialize_workers(); // Create and initialize the generations. diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index b8df770a2764a02e9d751cfe16c25554f909f1d6..9d8368fc3a4b86f4005a1a40efc4fc050aecb1dc 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -37,7 +37,7 @@ #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/softRefPolicy.hpp" #include "gc/shared/strongRootsScope.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "logging/log.hpp" #include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" @@ -91,7 +91,7 @@ class ParallelScavengeHeap : public CollectedHeap { MemoryPool* _survivor_pool; MemoryPool* _old_pool; - WorkGang _workers; + WorkerThreads _workers; virtual void initialize_serviceability(); @@ -243,7 +243,7 @@ class ParallelScavengeHeap : public CollectedHeap { virtual void gc_threads_do(ThreadClosure* tc) const; virtual void print_tracing_info() const; - virtual WorkGang* safepoint_workers() { return &_workers; } + virtual WorkerThreads* safepoint_workers() { return &_workers; } PreGenGCValues get_pre_gc_values() const; void print_heap_change(const PreGenGCValues& pre_gc_values) const; @@ -270,7 +270,7 @@ class ParallelScavengeHeap : public CollectedHeap { GCMemoryManager* old_gc_manager() const { return _old_manager; } GCMemoryManager* young_gc_manager() const { return _young_manager; } - WorkGang& workers() { + WorkerThreads& workers() { return _workers; } }; diff --git a/src/hotspot/share/gc/parallel/psClosure.inline.hpp b/src/hotspot/share/gc/parallel/psClosure.inline.hpp index 1495f8edac5953feda0045d963ec61ffa4f3263a..cd78583bb80c74e0347423976bd73013d7f18d21 100644 --- a/src/hotspot/share/gc/parallel/psClosure.inline.hpp +++ b/src/hotspot/share/gc/parallel/psClosure.inline.hpp @@ -45,7 +45,7 @@ public: oop new_obj = o->forwardee(); if (log_develop_is_enabled(Trace, gc, scavenge)) { ResourceMark rm; // required by internal_name() - log_develop_trace(gc, scavenge)("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}", + log_develop_trace(gc, scavenge)("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (" SIZE_FORMAT ")}", "forwarding", new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); } diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp index 883a0053610f8f61cd5031fc7b932fb182c19f91..0a71ec80e3bd5fc5ed035649b179597f1ef67a40 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp @@ -67,7 +67,7 @@ void ParCompactionManager::initialize(ParMarkBitMap* mbm) { _mark_bitmap = mbm; - uint parallel_gc_threads = ParallelScavengeHeap::heap()->workers().total_workers(); + uint parallel_gc_threads = ParallelScavengeHeap::heap()->workers().max_workers(); assert(_manager_array == NULL, "Attempt to initialize twice"); _manager_array = NEW_C_HEAP_ARRAY(ParCompactionManager*, parallel_gc_threads+1, mtGC); @@ -87,24 +87,23 @@ void ParCompactionManager::initialize(ParMarkBitMap* mbm) { // The VMThread gets its own ParCompactionManager, which is not available // for work stealing. _manager_array[parallel_gc_threads] = new ParCompactionManager(); - assert(ParallelScavengeHeap::heap()->workers().total_workers() != 0, + assert(ParallelScavengeHeap::heap()->workers().max_workers() != 0, "Not initialized?"); _shadow_region_array = new (ResourceObj::C_HEAP, mtGC) GrowableArray(10, mtGC); - _shadow_region_monitor = new Monitor(Mutex::nosafepoint, "CompactionManager_lock", - Monitor::_safepoint_check_never); + _shadow_region_monitor = new Monitor(Mutex::nosafepoint, "CompactionManager_lock"); } void ParCompactionManager::reset_all_bitmap_query_caches() { - uint parallel_gc_threads = ParallelScavengeHeap::heap()->workers().total_workers(); + uint parallel_gc_threads = ParallelScavengeHeap::heap()->workers().max_workers(); for (uint i=0; i<=parallel_gc_threads; i++) { _manager_array[i]->reset_bitmap_query_cache(); } } void ParCompactionManager::flush_all_string_dedup_requests() { - uint parallel_gc_threads = ParallelScavengeHeap::heap()->workers().total_workers(); + uint parallel_gc_threads = ParallelScavengeHeap::heap()->workers().max_workers(); for (uint i=0; i<=parallel_gc_threads; i++) { _manager_array[i]->flush_string_dedup_requests(); } diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp index a8b871f20113a92a79a0fb33cb10085cd4d26444..4f689ec72518bc156cbb9e8f3051cb6b80cda26f 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.cpp +++ b/src/hotspot/share/gc/parallel/psOldGen.cpp @@ -359,7 +359,7 @@ void PSOldGen::post_resize() { start_array()->set_covered_region(new_memregion); ParallelScavengeHeap::heap()->card_table()->resize_covered_region(new_memregion); - WorkGang* workers = Thread::current()->is_VM_thread() ? + WorkerThreads* workers = Thread::current()->is_VM_thread() ? &ParallelScavengeHeap::heap()->workers() : NULL; // The update of the space's end is done by this call. As that diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index f24d849c964752c955e3f294b2fc3660c630c7b4..212bd424e63ce0e07347d3dbf1895fb771e39ac1 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -60,7 +60,8 @@ #include "gc/shared/taskTerminator.hpp" #include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/workerPolicy.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" +#include "gc/shared/workerUtils.hpp" #include "logging/log.hpp" #include "memory/iterator.inline.hpp" #include "memory/metaspaceUtils.hpp" @@ -1765,10 +1766,10 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) { { const uint active_workers = - WorkerPolicy::calc_active_workers(ParallelScavengeHeap::heap()->workers().total_workers(), + WorkerPolicy::calc_active_workers(ParallelScavengeHeap::heap()->workers().max_workers(), ParallelScavengeHeap::heap()->workers().active_workers(), Threads::number_of_non_daemon_threads()); - ParallelScavengeHeap::heap()->workers().update_active_workers(active_workers); + ParallelScavengeHeap::heap()->workers().set_active_workers(active_workers); GCTraceCPUTime tcpu; GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause, true); @@ -1922,8 +1923,6 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) { old_gen->object_space()->check_mangled_unused_area_complete(); } - NOT_PRODUCT(ref_processor()->verify_no_references_recorded()); - collection_exit.update(); heap->print_heap_after_gc(); @@ -2016,7 +2015,7 @@ void steal_marking_work(TaskTerminator& terminator, uint worker_id) { } while (!terminator.offer_termination()); } -class MarkFromRootsTask : public AbstractGangTask { +class MarkFromRootsTask : public WorkerTask { StrongRootsScope _strong_roots_scope; // needed for Threads::possibly_parallel_threads_do OopStorageSetStrongParState _oop_storage_set_par_state; SequentialSubTasksDone _subtasks; @@ -2025,7 +2024,7 @@ class MarkFromRootsTask : public AbstractGangTask { public: MarkFromRootsTask(uint active_workers) : - AbstractGangTask("MarkFromRootsTask"), + WorkerTask("MarkFromRootsTask"), _strong_roots_scope(active_workers), _subtasks(ParallelRootType::sentinel), _terminator(active_workers, ParCompactionManager::oop_task_queues()), @@ -2152,7 +2151,7 @@ void PCAdjustPointerClosure::verify_cm(ParCompactionManager* cm) { } #endif -class PSAdjustTask final : public AbstractGangTask { +class PSAdjustTask final : public WorkerTask { SubTasksDone _sub_tasks; WeakProcessor::Task _weak_proc_task; OopStorageSetStrongParState _oop_storage_iter; @@ -2160,15 +2159,13 @@ class PSAdjustTask final : public AbstractGangTask { enum PSAdjustSubTask { PSAdjustSubTask_code_cache, - PSAdjustSubTask_old_ref_process, - PSAdjustSubTask_young_ref_process, PSAdjustSubTask_num_elements }; public: PSAdjustTask(uint nworkers) : - AbstractGangTask("PSAdjust task"), + WorkerTask("PSAdjust task"), _sub_tasks(PSAdjustSubTask_num_elements), _weak_proc_task(nworkers), _nworkers(nworkers) { @@ -2203,16 +2200,6 @@ public: CodeBlobToOopClosure adjust_code(&adjust, CodeBlobToOopClosure::FixRelocations); CodeCache::blobs_do(&adjust_code); } - if (_sub_tasks.try_claim_task(PSAdjustSubTask_old_ref_process)) { - PSParallelCompact::ref_processor()->weak_oops_do(&adjust); - } - if (_sub_tasks.try_claim_task(PSAdjustSubTask_young_ref_process)) { - // Roots were visited so references into the young gen in roots - // may have been scanned. Process them also. - // Should the reference processor have a span that excludes - // young gen objects? - PSScavenge::reference_processor()->weak_oops_do(&adjust); - } _sub_tasks.all_tasks_claimed(); } }; @@ -2491,14 +2478,14 @@ static void compaction_with_stealing_work(TaskTerminator* terminator, uint worke } } -class UpdateDensePrefixAndCompactionTask: public AbstractGangTask { +class UpdateDensePrefixAndCompactionTask: public WorkerTask { TaskQueue& _tq; TaskTerminator _terminator; uint _active_workers; public: UpdateDensePrefixAndCompactionTask(TaskQueue& tq, uint active_workers) : - AbstractGangTask("UpdateDensePrefixAndCompactionTask"), + WorkerTask("UpdateDensePrefixAndCompactionTask"), _tq(tq), _terminator(active_workers, ParCompactionManager::region_task_queues()), _active_workers(active_workers) { diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.inline.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.inline.hpp index f353b44d6276a8b954dfbca92d18e9ce6cf6a498..001f55c076a9f72ee5fc0109f5c4ed9f2a176600 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.inline.hpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.inline.hpp @@ -97,7 +97,7 @@ inline void PSParallelCompact::check_new_location(HeapWord* old_addr, HeapWord* #endif // ASSERT inline bool PSParallelCompact::mark_obj(oop obj) { - const int obj_size = obj->size(); + const size_t obj_size = obj->size(); if (mark_bitmap()->mark_obj(obj, obj_size)) { _summary_data.add_obj(obj, obj_size); return true; diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index 3a63ccde5db5fa5d2d702f0f242cf4f5bce84c6a..f4a2fce76ec8b6cd950fdac02672c49bca3089a2 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -54,7 +54,8 @@ #include "gc/shared/taskTerminator.hpp" #include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/workerPolicy.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" +#include "gc/shared/workerUtils.hpp" #include "memory/iterator.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -277,7 +278,7 @@ public: } }; -class ScavengeRootsTask : public AbstractGangTask { +class ScavengeRootsTask : public WorkerTask { StrongRootsScope _strong_roots_scope; // needed for Threads::possibly_parallel_threads_do OopStorageSetStrongParState _oop_storage_strong_par_state; SequentialSubTasksDone _subtasks; @@ -292,7 +293,7 @@ public: HeapWord* gen_top, uint active_workers, bool is_empty) : - AbstractGangTask("ScavengeRootsTask"), + WorkerTask("ScavengeRootsTask"), _strong_roots_scope(active_workers), _subtasks(ParallelRootType::sentinel), _old_gen(old_gen), @@ -463,10 +464,10 @@ bool PSScavenge::invoke_no_policy() { HeapWord* old_top = old_gen->object_space()->top(); const uint active_workers = - WorkerPolicy::calc_active_workers(ParallelScavengeHeap::heap()->workers().total_workers(), + WorkerPolicy::calc_active_workers(ParallelScavengeHeap::heap()->workers().max_workers(), ParallelScavengeHeap::heap()->workers().active_workers(), Threads::number_of_non_daemon_threads()); - ParallelScavengeHeap::heap()->workers().update_active_workers(active_workers); + ParallelScavengeHeap::heap()->workers().set_active_workers(active_workers); PSPromotionManager::pre_scavenge(); @@ -653,8 +654,6 @@ bool PSScavenge::invoke_no_policy() { DerivedPointerTable::update_pointers(); #endif - NOT_PRODUCT(reference_processor()->verify_no_references_recorded()); - // Re-verify object start arrays if (VerifyObjectStartArray && VerifyAfterGC) { diff --git a/src/hotspot/share/gc/parallel/psYoungGen.cpp b/src/hotspot/share/gc/parallel/psYoungGen.cpp index a4ebea468de93f996008e39e77db8003665be83a..9a09bfbd9176b6e08cd56642cc4e307fd14e61b4 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.cpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.cpp @@ -189,7 +189,7 @@ void PSYoungGen::set_space_boundaries(size_t eden_size, size_t survivor_size) { MemRegion to_mr ((HeapWord*)to_start, (HeapWord*)from_start); MemRegion from_mr((HeapWord*)from_start, (HeapWord*)from_end); - WorkGang& pretouch_workers = ParallelScavengeHeap::heap()->workers(); + WorkerThreads& pretouch_workers = ParallelScavengeHeap::heap()->workers(); eden_space()->initialize(eden_mr, true, ZapUnusedHeapArea, MutableSpace::SetupPages, &pretouch_workers); to_space()->initialize(to_mr , true, ZapUnusedHeapArea, MutableSpace::SetupPages, &pretouch_workers); from_space()->initialize(from_mr, true, ZapUnusedHeapArea, MutableSpace::SetupPages, &pretouch_workers); @@ -638,7 +638,7 @@ void PSYoungGen::resize_spaces(size_t requested_eden_size, to_space()->check_mangled_unused_area(limit); } - WorkGang* workers = &ParallelScavengeHeap::heap()->workers(); + WorkerThreads* workers = &ParallelScavengeHeap::heap()->workers(); // When an existing space is being initialized, it is not // mangled because the space has been previously mangled. diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index 63d33a14ac5418b2429da783e94f34c6d43e8d63..cd5e679cd074b6a3927f3502d524b52209df91a5 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -680,7 +680,7 @@ void DefNewGeneration::restore_preserved_marks() { } void DefNewGeneration::handle_promotion_failure(oop old) { - log_debug(gc, promotion)("Promotion failure size = %d) ", old->size()); + log_debug(gc, promotion)("Promotion failure size = " SIZE_FORMAT ") ", old->size()); _promotion_failed = true; _promotion_failed_info.register_copy_failure(old->size()); diff --git a/src/hotspot/share/gc/serial/markSweep.hpp b/src/hotspot/share/gc/serial/markSweep.hpp index f3e818f3e4cd8b425c096d5e1086ce6413ea4d59..03d9add10452d3f786dc01d752a0d1508e772a45 100644 --- a/src/hotspot/share/gc/serial/markSweep.hpp +++ b/src/hotspot/share/gc/serial/markSweep.hpp @@ -144,7 +144,7 @@ class MarkSweep : AllStatic { static void adjust_marks(); // Adjust the pointers in the preserved marks table static void restore_marks(); // Restore the marks that we saved in preserve_mark - static int adjust_pointers(oop obj); + static size_t adjust_pointers(oop obj); static void follow_stack(); // Empty marking stack. diff --git a/src/hotspot/share/gc/serial/markSweep.inline.hpp b/src/hotspot/share/gc/serial/markSweep.inline.hpp index 6af4ffe3e3cd57a3dec62fdf97ffe1c820150da9..ee0ef1e0b1bb1284d4e1e4dcd8be561d908c1dce 100644 --- a/src/hotspot/share/gc/serial/markSweep.inline.hpp +++ b/src/hotspot/share/gc/serial/markSweep.inline.hpp @@ -107,7 +107,7 @@ inline void AdjustPointerClosure::do_oop(oop* p) { do_oop_work(p); } inline void AdjustPointerClosure::do_oop(narrowOop* p) { do_oop_work(p); } -inline int MarkSweep::adjust_pointers(oop obj) { +inline size_t MarkSweep::adjust_pointers(oop obj) { return obj->oop_iterate_size(&MarkSweep::adjust_pointer_closure); } diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 19ca138d8d1f417e7696aa19fa1e6f08b43ddae8..0c506cd8d594a87b366014aa7c0ce4011447d9b0 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -30,6 +30,7 @@ #include "gc/shared/strongRootsScope.hpp" #include "gc/shared/suspendibleThreadSet.hpp" #include "memory/universe.hpp" +#include "runtime/mutexLocker.hpp" #include "services/memoryManager.hpp" SerialHeap* SerialHeap::heap() { @@ -112,3 +113,13 @@ void SerialHeap::safepoint_synchronize_end() { SuspendibleThreadSet::desynchronize(); } } + +HeapWord* SerialHeap::allocate_loaded_archive_space(size_t word_size) { + MutexLocker ml(Heap_lock); + return old_gen()->allocate(word_size, false /* is_tlab */); +} + +void SerialHeap::complete_loaded_archive_space(MemRegion archive_space) { + assert(old_gen()->used_region().contains(archive_space), "Archive space not contained in old gen"); + old_gen()->complete_loaded_archive_space(archive_space); +} diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index 8e79f21971ccdfc2061241fa6f7e5ed7ebe15920..208a997ad5fc85ce91cfc695892754a4b11dd721 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -83,6 +83,11 @@ public: virtual void safepoint_synchronize_begin(); virtual void safepoint_synchronize_end(); + + // Support for loading objects from CDS archive into the heap + bool can_load_archived_objects() const { return true; } + HeapWord* allocate_loaded_archive_space(size_t size); + void complete_loaded_archive_space(MemRegion archive_space); }; #endif // SHARE_GC_SERIAL_SERIALHEAP_HPP diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index 6eeb548ce6d3fce579863059efaf95150ed4f39f..1aec978b3820724b488cb725932773a618c3cbb6 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp @@ -219,6 +219,18 @@ void TenuredGeneration::object_iterate(ObjectClosure* blk) { _the_space->object_iterate(blk); } +void TenuredGeneration::complete_loaded_archive_space(MemRegion archive_space) { + // Create the BOT for the archive space. + TenuredSpace* space = (TenuredSpace*)_the_space; + space->initialize_threshold(); + HeapWord* start = archive_space.start(); + while (start < archive_space.end()) { + size_t word_size = _the_space->block_size(start); + space->alloc_block(start, start + word_size); + start += word_size; + } +} + void TenuredGeneration::save_marks() { _the_space->set_saved_mark(); } diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.hpp index eb60c775df197beb988f2d4377d31fc77889bf0f..44153320f6c1fbd66251a0a6976e5a0ded107a6a 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.hpp @@ -74,6 +74,8 @@ class TenuredGeneration: public CardGeneration { // Iteration void object_iterate(ObjectClosure* blk); + void complete_loaded_archive_space(MemRegion archive_space); + virtual inline HeapWord* allocate(size_t word_size, bool is_tlab); virtual inline HeapWord* par_allocate(size_t word_size, bool is_tlab); diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp index b34d0ab5744601b9ed64f168a44f31b102eea9af..d32a414e0335f2150230e58bd3658fd5c22ba59b 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp @@ -168,7 +168,7 @@ void CardTableBarrierSet::flush_deferred_card_mark_barrier(JavaThread* thread) { assert(!_card_table->is_in_young(old_obj), "Else should have been filtered in on_slowpath_allocation_exit()"); assert(oopDesc::is_oop(old_obj, true), "Not an oop"); - assert(deferred.word_size() == (size_t)(old_obj->size()), + assert(deferred.word_size() == old_obj->size(), "Mismatch: multiple objects?"); } write_region(deferred); diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 12a58dfa98a879983be31cdfc65b2cb276b05b67..efac5e76c86ac617af01a2382d4fd04cbb3be249 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -44,7 +44,7 @@ // class defines the functions that a heap must implement, and contains // infrastructure common to all heaps. -class AbstractGangTask; +class WorkerTask; class AdaptiveSizePolicy; class BarrierSet; class GCHeapLog; @@ -59,7 +59,7 @@ class SoftRefPolicy; class Thread; class ThreadClosure; class VirtualSpaceSummary; -class WorkGang; +class WorkerThreads; class nmethod; class ParallelObjectIterator : public CHeapObj { @@ -249,9 +249,9 @@ class CollectedHeap : public CHeapObj { void set_gc_cause(GCCause::Cause v); GCCause::Cause gc_cause() { return _gc_cause; } - oop obj_allocate(Klass* klass, int size, TRAPS); - virtual oop array_allocate(Klass* klass, int size, int length, bool do_zero, TRAPS); - oop class_allocate(Klass* klass, int size, TRAPS); + oop obj_allocate(Klass* klass, size_t size, TRAPS); + virtual oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS); + oop class_allocate(Klass* klass, size_t size, TRAPS); // Utilities for turning raw memory into filler objects. // @@ -469,7 +469,7 @@ class CollectedHeap : public CHeapObj { // concurrent marking) for an intermittent non-GC safepoint. // If this method returns NULL, SafepointSynchronize will // perform cleanup tasks serially in the VMThread. - virtual WorkGang* safepoint_workers() { return NULL; } + virtual WorkerThreads* safepoint_workers() { return NULL; } // Support for object pinning. This is used by JNI Get*Critical() // and Release*Critical() family of functions. If supported, the GC @@ -485,6 +485,7 @@ class CollectedHeap : public CHeapObj { // (usually as a snapshot of the old generation). virtual bool can_load_archived_objects() const { return false; } virtual HeapWord* allocate_loaded_archive_space(size_t size) { return NULL; } + virtual void complete_loaded_archive_space(MemRegion archive_space) { } virtual bool is_oop(oop object) const; // Non product verification and debugging. diff --git a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp index 89aa64f132d3a8543685054b250367ff766a3436..cd8a2b89d3285b52586846d8b639c607bf7ef026 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp @@ -31,17 +31,17 @@ #include "oops/oop.inline.hpp" #include "utilities/align.hpp" -inline oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) { +inline oop CollectedHeap::obj_allocate(Klass* klass, size_t size, TRAPS) { ObjAllocator allocator(klass, size, THREAD); return allocator.allocate(); } -inline oop CollectedHeap::array_allocate(Klass* klass, int size, int length, bool do_zero, TRAPS) { +inline oop CollectedHeap::array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS) { ObjArrayAllocator allocator(klass, size, length, do_zero, THREAD); return allocator.allocate(); } -inline oop CollectedHeap::class_allocate(Klass* klass, int size, TRAPS) { +inline oop CollectedHeap::class_allocate(Klass* klass, size_t size, TRAPS) { ClassAllocator allocator(klass, size, THREAD); return allocator.allocate(); } diff --git a/src/hotspot/share/gc/shared/gcLogPrecious.cpp b/src/hotspot/share/gc/shared/gcLogPrecious.cpp index b1a730829e65747d0b8531df24a180ed722ce54e..5070c38ce117ef9cf7294813b622956c341594f3 100644 --- a/src/hotspot/share/gc/shared/gcLogPrecious.cpp +++ b/src/hotspot/share/gc/shared/gcLogPrecious.cpp @@ -34,8 +34,7 @@ void GCLogPrecious::initialize() { _lines = new (ResourceObj::C_HEAP, mtGC) stringStream(); _temp = new (ResourceObj::C_HEAP, mtGC) stringStream(); _lock = new Mutex(Mutex::event, /* The lowest lock rank I could find */ - "GCLogPrecious Lock", - Mutex::_safepoint_check_never); + "GCLogPrecious Lock"); } void GCLogPrecious::vwrite_inner(LogTargetHandle log, const char* format, va_list args) { diff --git a/src/hotspot/share/gc/shared/gcTimer.cpp b/src/hotspot/share/gc/shared/gcTimer.cpp index ca835e30ed3390ffd6d833d6d52b7a5cbe990041..d7669604d82944898b578ca24c5f85ca7d03de8a 100644 --- a/src/hotspot/share/gc/shared/gcTimer.cpp +++ b/src/hotspot/share/gc/shared/gcTimer.cpp @@ -130,7 +130,7 @@ void TimePartitions::clear() { } void TimePartitions::report_gc_phase_start(const char* name, const Ticks& time, GCPhase::PhaseType type) { - assert(_phases->length() <= 1000, "Too many recored phases?"); + assert(_phases->length() <= 1000, "Too many recorded phases? (count: %d)", _phases->length()); int level = _active_phases.count(); diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.cpp b/src/hotspot/share/gc/shared/genCollectedHeap.cpp index c392f14d4a356d99bea73e71c8c4674da22ab09e..473e5abc1dff61bf87f1c837ad80e5b8a741da6d 100644 --- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp +++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp @@ -55,7 +55,7 @@ #include "gc/shared/space.hpp" #include "gc/shared/strongRootsScope.hpp" #include "gc/shared/weakProcessor.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "memory/iterator.hpp" #include "memory/metaspaceCounters.hpp" #include "memory/metaspaceUtils.hpp" @@ -798,8 +798,6 @@ void GenCollectedHeap::full_process_roots(bool is_adjust_phase, void GenCollectedHeap::gen_process_weak_roots(OopClosure* root_closure) { WeakProcessor::oops_do(root_closure); - _young_gen->ref_processor()->weak_oops_do(root_closure); - _old_gen->ref_processor()->weak_oops_do(root_closure); } bool GenCollectedHeap::no_allocs_since_save_marks() { @@ -1239,7 +1237,7 @@ oop GenCollectedHeap::handle_failed_promotion(Generation* old_gen, oop obj, size_t obj_size) { guarantee(old_gen == _old_gen, "We only get here with an old generation"); - assert(obj_size == (size_t)obj->size(), "bad obj_size passed in"); + assert(obj_size == obj->size(), "bad obj_size passed in"); HeapWord* result = NULL; result = old_gen->expand_and_allocate(obj_size, false); diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.hpp b/src/hotspot/share/gc/shared/genCollectedHeap.hpp index 2dfe006cc5461531b6b1e00efb0823e51a6bfdf8..28ed627b80f205349ccc9efe855bbd8ba27aa19d 100644 --- a/src/hotspot/share/gc/shared/genCollectedHeap.hpp +++ b/src/hotspot/share/gc/shared/genCollectedHeap.hpp @@ -37,7 +37,7 @@ class GCPolicyCounters; class GenerationSpec; class StrongRootsScope; class SubTasksDone; -class WorkGang; +class WorkerThreads; // A "GenCollectedHeap" is a CollectedHeap that uses generational // collection. It has two generations, young and old. diff --git a/src/hotspot/share/gc/shared/generation.cpp b/src/hotspot/share/gc/shared/generation.cpp index 4c4e7a24e6e5b4cbaffc9007adfaf98f4f435f3d..a0a57dea115e028a151af01f4b9fa87d959aae86 100644 --- a/src/hotspot/share/gc/shared/generation.cpp +++ b/src/hotspot/share/gc/shared/generation.cpp @@ -156,7 +156,7 @@ bool Generation::promotion_attempt_is_safe(size_t max_promotion_in_bytes) const // Ignores "ref" and calls allocate(). oop Generation::promote(oop obj, size_t obj_size) { - assert(obj_size == (size_t)obj->size(), "bad obj_size passed in"); + assert(obj_size == obj->size(), "bad obj_size passed in"); #ifndef PRODUCT if (GenCollectedHeap::heap()->promotion_should_fail()) { diff --git a/src/hotspot/share/gc/shared/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp index 501707148195b08b9f2789f4efb4053900cb8cda..7a314807af635f152594a254cf8415af66802233 100644 --- a/src/hotspot/share/gc/shared/memAllocator.cpp +++ b/src/hotspot/share/gc/shared/memAllocator.cpp @@ -423,6 +423,6 @@ oop ClassAllocator::initialize(HeapWord* mem) const { // concurrent GC. assert(_word_size > 0, "oop_size must be positive."); mem_clear(mem); - java_lang_Class::set_oop_size(mem, (int)_word_size); + java_lang_Class::set_oop_size(mem, _word_size); return finish(mem); } diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index b9ece642f91866cc5ea8d9ca8d22ddf80d55b059..cbc07e5153cdcdd629106b9f5029487650f37151 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -811,10 +811,10 @@ const size_t initial_active_array_size = 8; static Mutex* make_oopstorage_mutex(const char* storage_name, const char* kind, - int rank) { + Mutex::Rank rank) { char name[256]; os::snprintf(name, sizeof(name), "%s %s lock", storage_name, kind); - return new PaddedMutex(rank, name, Mutex::_safepoint_check_never); + return new PaddedMutex(rank, name); } void* OopStorage::operator new(size_t size, MEMFLAGS memflags) { @@ -844,10 +844,6 @@ OopStorage::OopStorage(const char* name, MEMFLAGS memflags) : "%s: active_mutex must have lower rank than allocation_mutex", _name); assert(Service_lock->rank() < _active_mutex->rank(), "%s: active_mutex must have higher rank than Service_lock", _name); - assert(_active_mutex->_safepoint_check_required == Mutex::_safepoint_check_never, - "%s: active mutex requires never safepoint check", _name); - assert(_allocation_mutex->_safepoint_check_required == Mutex::_safepoint_check_never, - "%s: allocation mutex requires never safepoint check", _name); } void OopStorage::delete_empty_block(const Block& block) { diff --git a/src/hotspot/share/gc/shared/parallelCleaning.hpp b/src/hotspot/share/gc/shared/parallelCleaning.hpp index 12c0382d2a31f928d06e1d792579e2a6f8d08e98..c600dbb563e14ddfc035e99d30674c1d7fef8fe0 100644 --- a/src/hotspot/share/gc/shared/parallelCleaning.hpp +++ b/src/hotspot/share/gc/shared/parallelCleaning.hpp @@ -28,7 +28,7 @@ #include "classfile/classLoaderDataGraph.hpp" #include "code/codeCache.hpp" #include "gc/shared/oopStorageParState.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" class CodeCacheUnloadingTask { diff --git a/src/hotspot/share/gc/shared/preservedMarks.cpp b/src/hotspot/share/gc/shared/preservedMarks.cpp index 8dfd7cf0ff4268d43fc225ca646dfd2ec9023047..718f97085f6789748cc4bb11dc37325499d31b89 100644 --- a/src/hotspot/share/gc/shared/preservedMarks.cpp +++ b/src/hotspot/share/gc/shared/preservedMarks.cpp @@ -24,7 +24,8 @@ #include "precompiled.hpp" #include "gc/shared/preservedMarks.inline.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" +#include "gc/shared/workerUtils.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" @@ -92,7 +93,7 @@ void PreservedMarksSet::init(uint num) { assert_empty(); } -class RestorePreservedMarksTask : public AbstractGangTask { +class RestorePreservedMarksTask : public WorkerTask { PreservedMarksSet* const _preserved_marks_set; SequentialSubTasksDone _sub_tasks; volatile size_t _total_size; @@ -109,7 +110,7 @@ public: } RestorePreservedMarksTask(PreservedMarksSet* preserved_marks_set) - : AbstractGangTask("Restore Preserved Marks"), + : WorkerTask("Restore Preserved Marks"), _preserved_marks_set(preserved_marks_set), _sub_tasks(preserved_marks_set->num()), _total_size(0) @@ -129,7 +130,7 @@ public: } }; -void PreservedMarksSet::restore(WorkGang* workers) { +void PreservedMarksSet::restore(WorkerThreads* workers) { { RestorePreservedMarksTask cl(this); if (workers == nullptr) { @@ -142,7 +143,7 @@ void PreservedMarksSet::restore(WorkGang* workers) { assert_empty(); } -AbstractGangTask* PreservedMarksSet::create_task() { +WorkerTask* PreservedMarksSet::create_task() { return new RestorePreservedMarksTask(this); } diff --git a/src/hotspot/share/gc/shared/preservedMarks.hpp b/src/hotspot/share/gc/shared/preservedMarks.hpp index d04d22ef3c91f7367ac2ed60d94538d0bbd0d7c2..21b40903e7fb2bc747b2e34455c1294dbb31f42b 100644 --- a/src/hotspot/share/gc/shared/preservedMarks.hpp +++ b/src/hotspot/share/gc/shared/preservedMarks.hpp @@ -30,9 +30,9 @@ #include "oops/oop.hpp" #include "utilities/stack.hpp" -class AbstractGangTask; +class WorkerTask; class PreservedMarksSet; -class WorkGang; +class WorkerThreads; class PreservedMarks { private: @@ -110,11 +110,11 @@ public: void init(uint num); // Iterate over all stacks, restore all preserved marks, and reclaim - // the memory taken up by the stack segments using the given WorkGang. If the WorkGang + // the memory taken up by the stack segments using the given WorkerThreads. If the WorkerThreads // is NULL, perform the work serially in the current thread. - void restore(WorkGang* workers); + void restore(WorkerThreads* workers); - AbstractGangTask* create_task(); + WorkerTask* create_task(); // Reclaim stack array. void reclaim(); diff --git a/src/hotspot/share/gc/shared/pretouchTask.cpp b/src/hotspot/share/gc/shared/pretouchTask.cpp index 4f900439a3f98baa32b43d6467b3bd3bfaaaaf6f..c345a12806b201e4ec87053a515b56f97cd9f674 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.cpp +++ b/src/hotspot/share/gc/shared/pretouchTask.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "gc/shared/gc_globals.hpp" #include "gc/shared/pretouchTask.hpp" +#include "logging/log.hpp" #include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" @@ -34,9 +35,8 @@ PretouchTask::PretouchTask(const char* task_name, char* end_address, size_t page_size, size_t chunk_size) : - AbstractGangTask(task_name), + WorkerTask(task_name), _cur_addr(start_address), - _start_addr(start_address), _end_addr(end_address), _page_size(page_size), _chunk_size(chunk_size) { @@ -52,19 +52,18 @@ size_t PretouchTask::chunk_size() { void PretouchTask::work(uint worker_id) { while (true) { - char* touch_addr = Atomic::fetch_and_add(&_cur_addr, _chunk_size); - if (touch_addr < _start_addr || touch_addr >= _end_addr) { + char* cur_start = Atomic::load(&_cur_addr); + char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1)); + if (cur_start >= cur_end) { break; - } - - char* end_addr = touch_addr + MIN2(_chunk_size, pointer_delta(_end_addr, touch_addr, sizeof(char))); - - os::pretouch_memory(touch_addr, end_addr, _page_size); + } else if (cur_start == Atomic::cmpxchg(&_cur_addr, cur_start, cur_end)) { + os::pretouch_memory(cur_start, cur_end, _page_size); + } // Else attempt to claim chunk failed, so try again. } } void PretouchTask::pretouch(const char* task_name, char* start_address, char* end_address, - size_t page_size, WorkGang* pretouch_gang) { + size_t page_size, WorkerThreads* pretouch_workers) { // Chunk size should be at least (unmodified) page size as using multiple threads // pretouch on a single page can decrease performance. size_t chunk_size = MAX2(PretouchTask::chunk_size(), page_size); @@ -81,14 +80,14 @@ void PretouchTask::pretouch(const char* task_name, char* start_address, char* en return; } - if (pretouch_gang != NULL) { - size_t num_chunks = (total_bytes + chunk_size - 1) / chunk_size; + if (pretouch_workers != NULL) { + size_t num_chunks = ((total_bytes - 1) / chunk_size) + 1; - uint num_workers = (uint)MIN2(num_chunks, (size_t)pretouch_gang->total_workers()); + uint num_workers = (uint)MIN2(num_chunks, (size_t)pretouch_workers->max_workers()); log_debug(gc, heap)("Running %s with %u workers for " SIZE_FORMAT " work units pre-touching " SIZE_FORMAT "B.", task.name(), num_workers, num_chunks, total_bytes); - pretouch_gang->run_task(&task, num_workers); + pretouch_workers->run_task(&task, num_workers); } else { log_debug(gc, heap)("Running %s pre-touching " SIZE_FORMAT "B.", task.name(), total_bytes); diff --git a/src/hotspot/share/gc/shared/pretouchTask.hpp b/src/hotspot/share/gc/shared/pretouchTask.hpp index 49cd48dc9cf4380f2981101361d58e58e5f2b653..7c66c8b717cebb7a5c4c73a0faeb0f59ca56c63b 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.hpp +++ b/src/hotspot/share/gc/shared/pretouchTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,10 @@ #ifndef SHARE_GC_SHARED_PRETOUCH_HPP #define SHARE_GC_SHARED_PRETOUCH_HPP -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" -class PretouchTask : public AbstractGangTask { +class PretouchTask : public WorkerTask { char* volatile _cur_addr; - char* const _start_addr; char* const _end_addr; size_t _page_size; size_t _chunk_size; @@ -42,7 +41,7 @@ public: static size_t chunk_size(); static void pretouch(const char* task_name, char* start_address, char* end_address, - size_t page_size, WorkGang* pretouch_gang); + size_t page_size, WorkerThreads* pretouch_workers); }; diff --git a/src/hotspot/share/gc/shared/referenceProcessor.cpp b/src/hotspot/share/gc/shared/referenceProcessor.cpp index e3886e8de14a90c3465fccbda8bd524eae913b09..b1b411e411999b8dd57d9199cec17722ceb398bf 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.cpp @@ -40,6 +40,7 @@ #include "oops/oop.inline.hpp" #include "runtime/java.hpp" #include "runtime/nonJavaThread.hpp" +#include "utilities/globalDefinitions.hpp" ReferencePolicy* ReferenceProcessor::_always_clear_soft_ref_policy = NULL; ReferencePolicy* ReferenceProcessor::_default_soft_ref_policy = NULL; @@ -215,13 +216,16 @@ ReferenceProcessorStats ReferenceProcessor::process_discovered_references(RefPro phase_times.set_total_time_ms((os::elapsedTime() - start_time) * 1000); + // Elements on discovered lists were pushed to the pending list. + verify_no_references_recorded(); + return stats; } -void BarrierEnqueueDiscoveredFieldClosure::enqueue(oop reference, oop value) { - HeapAccess::oop_store_at(reference, - java_lang_ref_Reference::discovered_offset(), - value); +void BarrierEnqueueDiscoveredFieldClosure::enqueue(HeapWord* discovered_field_addr, oop value) { + assert(Universe::heap()->is_in(discovered_field_addr), PTR_FORMAT " not in heap", p2i(discovered_field_addr)); + HeapAccess::oop_store(discovered_field_addr, + value); } void DiscoveredListIterator::load_ptrs(DEBUG_ONLY(bool allow_null_referent)) { @@ -255,9 +259,8 @@ void DiscoveredListIterator::remove() { } else { new_next = _next_discovered; } - // Remove Reference object from discovered list. Note that G1 does not need a - // pre-barrier here because we know the Reference has already been found/marked, - // that's how it ended up in the discovered list in the first place. + // Remove Reference object from discovered list. We do not need barriers here, + // as we only remove. We will do the barrier when we actually advance the cursor. RawAccess<>::oop_store(_prev_discovered_addr, new_next); _removed++; _refs_list.dec_length(1); @@ -277,7 +280,11 @@ void DiscoveredListIterator::clear_referent() { } void DiscoveredListIterator::enqueue() { - _enqueue->enqueue(_current_discovered, _next_discovered); + if (_prev_discovered_addr != _refs_list.adr_head()) { + _enqueue->enqueue(_prev_discovered_addr, _current_discovered); + } else { + RawAccess<>::oop_store(_prev_discovered_addr, _current_discovered); + } } void DiscoveredListIterator::complete_enqueue() { @@ -286,7 +293,16 @@ void DiscoveredListIterator::complete_enqueue() { // Swap refs_list into pending list and set obj's // discovered to what we read from the pending list. oop old = Universe::swap_reference_pending_list(_refs_list.head()); - _enqueue->enqueue(_prev_discovered, old); + _enqueue->enqueue(java_lang_ref_Reference::discovered_addr_raw(_prev_discovered), old); + } +} + +inline void log_preclean_ref(const DiscoveredListIterator& iter, const char* reason) { + if (log_develop_is_enabled(Trace, gc, ref)) { + ResourceMark rm; + log_develop_trace(gc, ref)("Precleaning %s reference " PTR_FORMAT ": %s", + reason, p2i(iter.obj()), + iter.obj()->klass()->internal_name()); } } @@ -692,12 +708,12 @@ void ReferenceProcessor::run_task(RefProcTask& task, RefProcProxyTask& proxy_tas proxy_task.prepare_run_task(task, num_queues(), processing_is_mt() ? RefProcThreadModel::Multi : RefProcThreadModel::Single, marks_oops_alive); if (processing_is_mt()) { - WorkGang* gang = Universe::heap()->safepoint_workers(); - assert(gang != NULL, "can not dispatch multi threaded without a work gang"); - assert(gang->active_workers() >= num_queues(), + WorkerThreads* workers = Universe::heap()->safepoint_workers(); + assert(workers != NULL, "can not dispatch multi threaded without workers"); + assert(workers->active_workers() >= num_queues(), "Ergonomically chosen workers(%u) should be less than or equal to active workers(%u)", - num_queues(), gang->active_workers()); - gang->run_task(&proxy_task, num_queues()); + num_queues(), workers->active_workers()); + workers->run_task(&proxy_task, num_queues()); } else { for (unsigned i = 0; i < _max_num_queues; ++i) { proxy_task.work(i); @@ -1049,9 +1065,7 @@ bool ReferenceProcessor::has_discovered_references() { } void ReferenceProcessor::preclean_discovered_references(BoolObjectClosure* is_alive, - OopClosure* keep_alive, EnqueueDiscoveredFieldClosure* enqueue, - VoidClosure* complete_gc, YieldClosure* yield, GCTimer* gc_timer) { // These lists can be handled here in any order and, indeed, concurrently. @@ -1065,7 +1079,7 @@ void ReferenceProcessor::preclean_discovered_references(BoolObjectClosure* is_al return; } if (preclean_discovered_reflist(_discoveredSoftRefs[i], is_alive, - keep_alive, enqueue, complete_gc, yield)) { + enqueue, yield)) { log_reflist("SoftRef abort: ", _discoveredSoftRefs, _max_num_queues); return; } @@ -1082,7 +1096,7 @@ void ReferenceProcessor::preclean_discovered_references(BoolObjectClosure* is_al return; } if (preclean_discovered_reflist(_discoveredWeakRefs[i], is_alive, - keep_alive, enqueue, complete_gc, yield)) { + enqueue, yield)) { log_reflist("WeakRef abort: ", _discoveredWeakRefs, _max_num_queues); return; } @@ -1099,7 +1113,7 @@ void ReferenceProcessor::preclean_discovered_references(BoolObjectClosure* is_al return; } if (preclean_discovered_reflist(_discoveredFinalRefs[i], is_alive, - keep_alive, enqueue, complete_gc, yield)) { + enqueue, yield)) { log_reflist("FinalRef abort: ", _discoveredFinalRefs, _max_num_queues); return; } @@ -1116,7 +1130,7 @@ void ReferenceProcessor::preclean_discovered_references(BoolObjectClosure* is_al return; } if (preclean_discovered_reflist(_discoveredPhantomRefs[i], is_alive, - keep_alive, enqueue, complete_gc, yield)) { + enqueue, yield)) { log_reflist("PhantomRef abort: ", _discoveredPhantomRefs, _max_num_queues); return; } @@ -1125,42 +1139,28 @@ void ReferenceProcessor::preclean_discovered_references(BoolObjectClosure* is_al } } -// Walk the given discovered ref list, and remove all reference objects -// whose referents are still alive, whose referents are NULL or which -// are not active (have a non-NULL next field). NOTE: When we are -// thus precleaning the ref lists (which happens single-threaded today), -// we do not disable refs discovery to honor the correct semantics of -// java.lang.Reference. As a result, we need to be careful below -// that ref removal steps interleave safely with ref discovery steps -// (in this thread). bool ReferenceProcessor::preclean_discovered_reflist(DiscoveredList& refs_list, BoolObjectClosure* is_alive, - OopClosure* keep_alive, EnqueueDiscoveredFieldClosure* enqueue, - VoidClosure* complete_gc, YieldClosure* yield) { - DiscoveredListIterator iter(refs_list, keep_alive, is_alive, enqueue); + DiscoveredListIterator iter(refs_list, nullptr /* keep_alive */, is_alive, enqueue); while (iter.has_next()) { if (yield->should_return_fine_grain()) { return true; } iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */)); - if (iter.referent() == NULL || iter.is_referent_alive()) { - // The referent has been cleared, or is alive; we need to trace - // and mark its cohort. - log_develop_trace(gc, ref)("Precleaning Reference (" INTPTR_FORMAT ": %s)", - p2i(iter.obj()), iter.obj()->klass()->internal_name()); - // Remove Reference object from list + if (iter.referent() == nullptr) { + log_preclean_ref(iter, "cleared"); + iter.remove(); + iter.move_to_next(); + } else if (iter.is_referent_alive()) { + log_preclean_ref(iter, "reachable"); iter.remove(); - // Keep alive its cohort. - iter.make_referent_alive(); iter.move_to_next(); } else { iter.next(); } } - // Close the reachable set - complete_gc->do_void(); if (iter.processed() > 0) { log_develop_trace(gc, ref)(" Dropped " SIZE_FORMAT " Refs out of " SIZE_FORMAT " Refs in discovered list " INTPTR_FORMAT, diff --git a/src/hotspot/share/gc/shared/referenceProcessor.hpp b/src/hotspot/share/gc/shared/referenceProcessor.hpp index 77e7ca23494341ef4b89ad8c9a3c7453e480e493..2ab68ab29fec4ffce8a9a779fbfc11e54f5fae1a 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.hpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.hpp @@ -28,7 +28,7 @@ #include "gc/shared/referenceDiscoverer.hpp" #include "gc/shared/referencePolicy.hpp" #include "gc/shared/referenceProcessorStats.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "memory/referenceType.hpp" #include "oops/instanceRefKlass.hpp" @@ -47,15 +47,16 @@ class RefProcProxyTask; // at the point of invocation. class EnqueueDiscoveredFieldClosure { public: - // For the given j.l.ref.Reference reference, set the discovered field to value. - virtual void enqueue(oop reference, oop value) = 0; + // For the given j.l.ref.Reference discovered field address, set the discovered + // field to value and apply any barriers to it. + virtual void enqueue(HeapWord* discovered_field_addr, oop value) = 0; }; // EnqueueDiscoveredFieldClosure that executes the default barrier on the discovered -// field of the j.l.ref.Reference reference with the given value. +// field of the j.l.ref.Reference with the given value. class BarrierEnqueueDiscoveredFieldClosure : public EnqueueDiscoveredFieldClosure { public: - void enqueue(oop reference, oop value) override; + void enqueue(HeapWord* discovered_field_addr, oop value) override; }; // List of discovered references. @@ -303,19 +304,19 @@ public: setup_policy(always_clear); } - // "Preclean" all the discovered reference lists by removing references that - // are active (e.g. due to the mutator calling enqueue()) or with NULL or - // strongly reachable referents. - // The first argument is a predicate on an oop that indicates - // its (strong) reachability and the fourth is a closure that - // may be used to incrementalize or abort the precleaning process. - // The caller is responsible for taking care of potential - // interference with concurrent operations on these lists - // (or predicates involved) by other threads. + // "Preclean" all the discovered reference lists by removing references whose + // referents are NULL or strongly reachable (`is_alive` returns true). + // Note: when a referent is strongly reachable, we assume it's already marked + // through, so this method doesn't perform (and doesn't need to) any marking + // work at all. Currently, this assumption holds because G1 uses SATB and the + // marking status of an object is *not* updated when `Reference.get()` is + // called. + // `yield` is a closure that may be used to incrementalize or abort the + // precleaning process. The caller is responsible for taking care of + // potential interference with concurrent operations on these lists (or + // predicates involved) by other threads. void preclean_discovered_references(BoolObjectClosure* is_alive, - OopClosure* keep_alive, EnqueueDiscoveredFieldClosure* enqueue, - VoidClosure* complete_gc, YieldClosure* yield, GCTimer* gc_timer); @@ -330,9 +331,7 @@ private: // Returns whether the operation should be aborted. bool preclean_discovered_reflist(DiscoveredList& refs_list, BoolObjectClosure* is_alive, - OopClosure* keep_alive, EnqueueDiscoveredFieldClosure* enqueue, - VoidClosure* complete_gc, YieldClosure* yield); // round-robin mod _num_queues (not: _not_ mod _max_num_queues) @@ -419,8 +418,6 @@ public: // iterate over oops void weak_oops_do(OopClosure* f); // weak roots - void verify_list(DiscoveredList& ref_list); - // Discover a Reference object, using appropriate discovery criteria virtual bool discover_reference(oop obj, ReferenceType rt); @@ -588,7 +585,7 @@ public: * of RefProcTask that will handle reference processing in a generic way for Serial, * Parallel and G1. This proxy will add the relevant closures, task terminators etc. */ -class RefProcProxyTask : public AbstractGangTask { +class RefProcProxyTask : public WorkerTask { protected: const uint _max_workers; RefProcTask* _rp_task; @@ -597,7 +594,7 @@ protected: bool _marks_oops_alive; public: - RefProcProxyTask(const char* name, uint max_workers) : AbstractGangTask(name), _max_workers(max_workers), _rp_task(nullptr),_tm(RefProcThreadModel::Single), _queue_count(0), _marks_oops_alive(false) {} + RefProcProxyTask(const char* name, uint max_workers) : WorkerTask(name), _max_workers(max_workers), _rp_task(nullptr),_tm(RefProcThreadModel::Single), _queue_count(0), _marks_oops_alive(false) {} void prepare_run_task(RefProcTask& rp_task, uint queue_count, RefProcThreadModel tm, bool marks_oops_alive) { _rp_task = &rp_task; diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index 29ee0bc0b48927b2ffdd9358b7f55b0be3f7f425..1638d994e407e4b5a02c2774c30368df3cd4815c 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -178,7 +178,7 @@ HeapWord* ContiguousSpaceDCTOC::get_actual_top(HeapWord* top, // Otherwise, it is possible that the object starting on the dirty // card spans the entire card, and that the store happened on a // later card. Figure out where the object ends. - assert(_sp->block_size(top_obj) == (size_t) cast_to_oop(top_obj)->size(), + assert(_sp->block_size(top_obj) == cast_to_oop(top_obj)->size(), "Block size and object size mismatch"); top = top_obj + cast_to_oop(top_obj)->size(); } @@ -773,8 +773,7 @@ void OffsetTableContigSpace::alloc_block(HeapWord* start, HeapWord* end) { OffsetTableContigSpace::OffsetTableContigSpace(BlockOffsetSharedArray* sharedOffsetArray, MemRegion mr) : _offsets(sharedOffsetArray, mr), - _par_alloc_lock(Mutex::leaf, "OffsetTableContigSpace par alloc lock", - Mutex::_safepoint_check_always, true) + _par_alloc_lock(Mutex::safepoint, "OffsetTableContigSpaceParAlloc_lock", true) { _offsets.set_contig_space(this); initialize(mr, SpaceDecorator::Clear, SpaceDecorator::Mangle); diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index 48e44febcc1aba9b131575c6d328162f25fbacde..f2a9c24904ed9b6c2897652e6910a1a115b0596f 100644 --- a/src/hotspot/share/gc/shared/space.hpp +++ b/src/hotspot/share/gc/shared/space.hpp @@ -27,7 +27,7 @@ #include "gc/shared/blockOffsetTable.hpp" #include "gc/shared/cardTable.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" #include "memory/iterator.hpp" #include "memory/memRegion.hpp" diff --git a/src/hotspot/share/gc/shared/space.inline.hpp b/src/hotspot/share/gc/shared/space.inline.hpp index 953e8aa9a2fd4cc868a703def176fa5f448882d5..4a1226b95af7645ab3f1e4ac1c9887718d0a9af2 100644 --- a/src/hotspot/share/gc/shared/space.inline.hpp +++ b/src/hotspot/share/gc/shared/space.inline.hpp @@ -116,7 +116,7 @@ public: oop obj = cast_to_oop(dead_start); obj->set_mark(obj->mark().set_marked()); - assert(dead_length == (size_t)obj->size(), "bad filler object size"); + assert(dead_length == obj->size(), "bad filler object size"); log_develop_trace(gc, compaction)("Inserting object to dead space: " PTR_FORMAT ", " PTR_FORMAT ", " SIZE_FORMAT "b", p2i(dead_start), p2i(dead_end), dead_length * HeapWordSize); diff --git a/src/hotspot/share/gc/shared/taskTerminator.cpp b/src/hotspot/share/gc/shared/taskTerminator.cpp index a3f9c8f8f80f47b81291f7a5a8ac3372f025dc20..74d66b74e129c4618d0a8aa81c2b90431f834050 100644 --- a/src/hotspot/share/gc/shared/taskTerminator.cpp +++ b/src/hotspot/share/gc/shared/taskTerminator.cpp @@ -72,7 +72,7 @@ TaskTerminator::TaskTerminator(uint n_threads, TaskQueueSetSuper* queue_set) : _n_threads(n_threads), _queue_set(queue_set), _offered_termination(0), - _blocker(Mutex::nosafepoint, "TaskTerminator_lock", Monitor::_safepoint_check_never), + _blocker(Mutex::nosafepoint, "TaskTerminator_lock"), _spin_master(NULL) { } TaskTerminator::~TaskTerminator() { diff --git a/src/hotspot/share/gc/shared/weakProcessor.cpp b/src/hotspot/share/gc/shared/weakProcessor.cpp index 374661edf48d9c4e528798cfcf990cae40eb1e0b..aa7c8a719a1e3226a9047af34ed7f314e7716720 100644 --- a/src/hotspot/share/gc/shared/weakProcessor.cpp +++ b/src/hotspot/share/gc/shared/weakProcessor.cpp @@ -125,6 +125,6 @@ void WeakProcessor::Task::report_num_dead() { _storage_states.report_num_dead(); } -void WeakProcessor::GangTask::work(uint worker_id) { +void WeakProcessor::WeakOopsDoTask::work(uint worker_id) { _erased_do_work(this, worker_id); } diff --git a/src/hotspot/share/gc/shared/weakProcessor.hpp b/src/hotspot/share/gc/shared/weakProcessor.hpp index 600f2520691207ef009e00d28bb81a94a5707b22..5eb2c59f3f2da460e757eb2f15e5ac6d240ef25d 100644 --- a/src/hotspot/share/gc/shared/weakProcessor.hpp +++ b/src/hotspot/share/gc/shared/weakProcessor.hpp @@ -27,11 +27,11 @@ #include "gc/shared/oopStorageParState.hpp" #include "gc/shared/oopStorageSetParState.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" class WeakProcessorTimes; -class WorkGang; +class WorkerThreads; // Helper class to aid in root scanning and cleaning of weak oops in the VM. // @@ -53,7 +53,7 @@ public: // IsAlive must be derived from BoolObjectClosure. // KeepAlive must be derived from OopClosure. template - static void weak_oops_do(WorkGang* workers, + static void weak_oops_do(WorkerThreads* workers, IsAlive* is_alive, KeepAlive* keep_alive, WeakProcessorTimes* times); @@ -64,7 +64,7 @@ public: // IsAlive must be derived from BoolObjectClosure. // KeepAlive must be derived from OopClosure. template - static void weak_oops_do(WorkGang* workers, + static void weak_oops_do(WorkerThreads* workers, IsAlive* is_alive, KeepAlive* keep_alive, uint indent_log); @@ -79,7 +79,7 @@ private: template class CountingClosure; - class GangTask; + class WeakOopsDoTask; }; class WeakProcessor::Task { diff --git a/src/hotspot/share/gc/shared/weakProcessor.inline.hpp b/src/hotspot/share/gc/shared/weakProcessor.inline.hpp index f1b8e6f92b099689f11bafb412ed3d3d37a976ff..edb1bf4471de57b5faf4b4ac7ea67a2c063f77bc 100644 --- a/src/hotspot/share/gc/shared/weakProcessor.inline.hpp +++ b/src/hotspot/share/gc/shared/weakProcessor.inline.hpp @@ -32,7 +32,7 @@ #include "gc/shared/oopStorageParState.inline.hpp" #include "gc/shared/oopStorageSet.hpp" #include "gc/shared/weakProcessorTimes.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "prims/resolvedMethodTable.hpp" #include "utilities/debug.hpp" #include "utilities/enumIterator.hpp" @@ -96,14 +96,14 @@ void WeakProcessor::Task::work(uint worker_id, } } -class WeakProcessor::GangTask : public AbstractGangTask { +class WeakProcessor::WeakOopsDoTask : public WorkerTask { Task _task; BoolObjectClosure* _is_alive; OopClosure* _keep_alive; - void (*_erased_do_work)(GangTask* task, uint worker_id); + void (*_erased_do_work)(WeakOopsDoTask* task, uint worker_id); template - static void erased_do_work(GangTask* task, uint worker_id) { + static void erased_do_work(WeakOopsDoTask* task, uint worker_id) { task->_task.work(worker_id, static_cast(task->_is_alive), static_cast(task->_keep_alive)); @@ -111,12 +111,12 @@ class WeakProcessor::GangTask : public AbstractGangTask { public: template - GangTask(const char* name, - IsAlive* is_alive, - KeepAlive* keep_alive, - WeakProcessorTimes* times, - uint nworkers) : - AbstractGangTask(name), + WeakOopsDoTask(const char* name, + IsAlive* is_alive, + KeepAlive* keep_alive, + WeakProcessorTimes* times, + uint nworkers) : + WorkerTask(name), _task(times, nworkers), _is_alive(is_alive), _keep_alive(keep_alive), @@ -128,26 +128,26 @@ public: }; template -void WeakProcessor::weak_oops_do(WorkGang* workers, +void WeakProcessor::weak_oops_do(WorkerThreads* workers, IsAlive* is_alive, KeepAlive* keep_alive, WeakProcessorTimes* times) { WeakProcessorTimeTracker tt(times); - uint nworkers = ergo_workers(MIN2(workers->total_workers(), + uint nworkers = ergo_workers(MIN2(workers->max_workers(), times->max_threads())); - GangTask task("Weak Processor", is_alive, keep_alive, times, nworkers); + WeakOopsDoTask task("Weak Processor", is_alive, keep_alive, times, nworkers); workers->run_task(&task, nworkers); task.report_num_dead(); } template -void WeakProcessor::weak_oops_do(WorkGang* workers, +void WeakProcessor::weak_oops_do(WorkerThreads* workers, IsAlive* is_alive, KeepAlive* keep_alive, uint indent_log) { - uint nworkers = ergo_workers(workers->total_workers()); + uint nworkers = ergo_workers(workers->max_workers()); WeakProcessorTimes times(nworkers); weak_oops_do(workers, is_alive, keep_alive, ×); times.log_subtotals(indent_log); // Caller logs total if desired. diff --git a/src/hotspot/share/gc/shared/workerManager.hpp b/src/hotspot/share/gc/shared/workerManager.hpp deleted file mode 100644 index d9698b0d82350cc35d7e9a12be09580a622c6ca3..0000000000000000000000000000000000000000 --- a/src/hotspot/share/gc/shared/workerManager.hpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_SHARED_WORKERMANAGER_HPP -#define SHARE_GC_SHARED_WORKERMANAGER_HPP - -#include "gc/shared/gc_globals.hpp" -#include "logging/log.hpp" -#include "memory/allocation.hpp" -#include "runtime/os.hpp" -#include "runtime/thread.hpp" -#include "utilities/globalDefinitions.hpp" - -class WorkerManager : public AllStatic { - public: - // Create additional workers as needed. - // active_workers - number of workers being requested for an upcoming - // parallel task. - // total_workers - total number of workers. This is the maximum - // number possible. - // created_workers - number of workers already created. This maybe - // less than, equal to, or greater than active workers. If greater than - // or equal to active_workers, nothing is done. - // worker_type - type of thread. - // initializing - true if this is called to get the initial number of - // GC workers. - // If initializing is true, do a vm exit if the workers cannot be created. - // The initializing = true case is for JVM start up and failing to - // create all the worker at start should considered a problem so exit. - // If initializing = false, there are already some number of worker - // threads and a failure would not be optimal but should not be fatal. - static uint add_workers (WorkGang* workers, - uint active_workers, - uint total_workers, - uint created_workers, - os::ThreadType worker_type, - bool initializing); - - // Log (at trace level) a change in the number of created workers. - static void log_worker_creation(WorkGang* workers, - uint previous_created_workers, - uint active_workers, - uint created_workers, - bool initializing); -}; - -uint WorkerManager::add_workers(WorkGang* workers, - uint active_workers, - uint total_workers, - uint created_workers, - os::ThreadType worker_type, - bool initializing) { - uint start = created_workers; - uint end = MIN2(active_workers, total_workers); - for (uint worker_id = start; worker_id < end; worker_id += 1) { - WorkerThread* new_worker = NULL; - if (initializing || !InjectGCWorkerCreationFailure) { - new_worker = workers->install_worker(worker_id); - } - if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) { - log_trace(gc, task)("WorkerManager::add_workers() : " - "creation failed due to failed allocation of native %s", - new_worker == NULL ? "memory" : "thread"); - delete new_worker; - if (initializing) { - vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create worker GC thread. Out of system resources."); - } - break; - } - created_workers++; - os::start_thread(new_worker); - } - - log_trace(gc, task)("WorkerManager::add_workers() : " - "created_workers: %u", created_workers); - - return created_workers; -} - -void WorkerManager::log_worker_creation(WorkGang* workers, - uint previous_created_workers, - uint active_workers, - uint created_workers, - bool initializing) { - if (previous_created_workers < created_workers) { - const char* initializing_msg = initializing ? "Adding initial" : "Creating additional"; - log_trace(gc, task)("%s %s(s) previously created workers %u active workers %u total created workers %u", - initializing_msg, workers->group_name(), previous_created_workers, active_workers, created_workers); - } -} - -#endif // SHARE_GC_SHARED_WORKERMANAGER_HPP diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..183349ef245ef5d61e37a7eef1e6abc36cc10333 --- /dev/null +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc/shared/gc_globals.hpp" +#include "gc/shared/workerThread.hpp" +#include "logging/log.hpp" +#include "runtime/atomic.hpp" +#include "runtime/init.hpp" +#include "runtime/java.hpp" +#include "runtime/os.hpp" + +WorkerTaskDispatcher::WorkerTaskDispatcher() : + _task(NULL), + _started(0), + _not_finished(0), + _start_semaphore(), + _end_semaphore() {} + +void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint num_workers) { + // No workers are allowed to read the state variables until they have been signaled. + _task = task; + _not_finished = num_workers; + + // Dispatch 'num_workers' number of tasks. + _start_semaphore.signal(num_workers); + + // Wait for the last worker to signal the coordinator. + _end_semaphore.wait(); + + // No workers are allowed to read the state variables after the coordinator has been signaled. + assert(_not_finished == 0, "%d not finished workers?", _not_finished); + _task = NULL; + _started = 0; +} + +void WorkerTaskDispatcher::worker_run_task() { + // Wait for the coordinator to dispatch a task. + _start_semaphore.wait(); + + // Get worker id. + const uint worker_id = Atomic::fetch_and_add(&_started, 1u); + + // Run task. + GCIdMark gc_id_mark(_task->gc_id()); + _task->work(worker_id); + + // Mark that the worker is done with the task. + // The worker is not allowed to read the state variables after this line. + const uint not_finished = Atomic::sub(&_not_finished, 1u); + + // The last worker signals to the coordinator that all work is completed. + if (not_finished == 0) { + _end_semaphore.signal(); + } +} + +WorkerThreads::WorkerThreads(const char* name, uint max_workers) : + _name(name), + _workers(NEW_C_HEAP_ARRAY(WorkerThread*, max_workers, mtInternal)), + _max_workers(max_workers), + _created_workers(0), + _active_workers(0), + _dispatcher() {} + +void WorkerThreads::initialize_workers() { + const uint initial_active_workers = UseDynamicNumberOfGCThreads ? 1 : _max_workers; + if (set_active_workers(initial_active_workers) != initial_active_workers) { + vm_exit_during_initialization(); + } +} + +WorkerThread* WorkerThreads::create_worker(uint id) { + if (is_init_completed() && InjectGCWorkerCreationFailure) { + return NULL; + } + + WorkerThread* const worker = new WorkerThread(_name, id, &_dispatcher); + + if (!os::create_thread(worker, os::gc_thread)) { + delete worker; + return NULL; + } + + os::start_thread(worker); + + return worker; +} + +uint WorkerThreads::set_active_workers(uint num_workers) { + assert(num_workers > 0 && num_workers <= _max_workers, + "Invalid number of active workers %u (should be 1-%u)", + num_workers, _max_workers); + + while (_created_workers < num_workers) { + WorkerThread* const worker = create_worker(_created_workers); + if (worker == NULL) { + log_error(gc, task)("Failed to create worker thread"); + break; + } + + _workers[_created_workers] = worker; + _created_workers++; + } + + _active_workers = MIN2(_created_workers, num_workers); + + log_trace(gc, task)("%s: using %d out of %d workers", _name, _active_workers, _max_workers); + + return _active_workers; +} + +void WorkerThreads::threads_do(ThreadClosure* tc) const { + for (uint i = 0; i < _created_workers; i++) { + tc->do_thread(_workers[i]); + } +} + +void WorkerThreads::run_task(WorkerTask* task) { + _dispatcher.coordinator_distribute_task(task, _active_workers); +} + +void WorkerThreads::run_task(WorkerTask* task, uint num_workers) { + WithActiveWorkers with_active_workers(this, num_workers); + run_task(task); +} + +WorkerThread::WorkerThread(const char* name_prefix, uint id, WorkerTaskDispatcher* dispatcher) : + _dispatcher(dispatcher), + _id(id) { + set_name("%s#%d", name_prefix, id); +} + +void WorkerThread::run() { + os::set_priority(this, NearMaxPriority); + + while (true) { + _dispatcher->worker_run_task(); + } +} diff --git a/src/hotspot/share/gc/shared/workerThread.hpp b/src/hotspot/share/gc/shared/workerThread.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d4c16c246f06b52d88ae1ed6d23caace3c248e81 --- /dev/null +++ b/src/hotspot/share/gc/shared/workerThread.hpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_SHARED_WORKERTHREAD_HPP +#define SHARE_GC_SHARED_WORKERTHREAD_HPP + +#include "gc/shared/gcId.hpp" +#include "memory/allocation.hpp" +#include "runtime/nonJavaThread.hpp" +#include "runtime/semaphore.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +class ThreadClosure; +class WorkerTaskDispatcher; +class WorkerThread; + +// An task to be worked on by worker threads +class WorkerTask : public CHeapObj { +private: + const char* _name; + const uint _gc_id; + + public: + explicit WorkerTask(const char* name) : + _name(name), + _gc_id(GCId::current_or_undefined()) {} + + const char* name() const { return _name; } + const uint gc_id() const { return _gc_id; } + + virtual void work(uint worker_id) = 0; +}; + +// WorkerThreads dispatcher implemented with semaphores +class WorkerTaskDispatcher { + // The task currently being dispatched to the WorkerThreads. + WorkerTask* _task; + + volatile uint _started; + volatile uint _not_finished; + + // Semaphore used to start the WorkerThreads. + Semaphore _start_semaphore; + // Semaphore used to notify the coordinator that all workers are done. + Semaphore _end_semaphore; + +public: + WorkerTaskDispatcher(); + + // Coordinator API. + + // Distributes the task out to num_workers workers. + // Returns when the task has been completed by all workers. + void coordinator_distribute_task(WorkerTask* task, uint num_workers); + + // Worker API. + + // Waits for a task to become available to the worker and runs it. + void worker_run_task(); +}; + +// A set of worker threads to execute tasks +class WorkerThreads : public CHeapObj { +private: + const char* const _name; + WorkerThread** _workers; + const uint _max_workers; + uint _created_workers; + uint _active_workers; + WorkerTaskDispatcher _dispatcher; + +protected: + virtual WorkerThread* create_worker(uint id); + +public: + WorkerThreads(const char* name, uint max_workers); + + void initialize_workers(); + + uint max_workers() const { return _max_workers; } + uint created_workers() const { return _created_workers; } + uint active_workers() const { return _active_workers; } + + uint set_active_workers(uint num_workers); + + void threads_do(ThreadClosure* tc) const; + + const char* name() const { return _name; } + + // Run a task using the current active number of workers, returns when the task is done. + void run_task(WorkerTask* task); + + // Run a task with the given number of workers, returns when the task is done. + void run_task(WorkerTask* task, uint num_workers); +}; + +class WorkerThread : public NamedThread { +private: + WorkerTaskDispatcher* const _dispatcher; + const uint _id; + +public: + static WorkerThread* current() { + return WorkerThread::cast(Thread::current()); + } + + static WorkerThread* cast(Thread* t) { + assert(t->is_Worker_thread(), "incorrect cast to WorkerThread"); + return static_cast(t); + } + + WorkerThread(const char* name_prefix, uint id, WorkerTaskDispatcher* dispatcher); + + uint id() const { return _id; } + + bool is_Worker_thread() const override { return true; } + const char* type_name() const override { return "WorkerThread"; } + + void run() override; +}; + +// Temporarily try to set the number of active workers. +// It's not guaranteed that it succeeds, and users need to +// query the number of active workers. +class WithActiveWorkers : public StackObj { +private: + WorkerThreads* const _workers; + const uint _prev_active_workers; + +public: + WithActiveWorkers(WorkerThreads* workers, uint num_workers) : + _workers(workers), + _prev_active_workers(workers->active_workers()) { + _workers->set_active_workers(num_workers); + } + + ~WithActiveWorkers() { + _workers->set_active_workers(_prev_active_workers); + } +}; + +#endif // SHARE_GC_SHARED_WORKERTHREAD_HPP diff --git a/src/hotspot/share/gc/shared/workerUtils.cpp b/src/hotspot/share/gc/shared/workerUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9b771a299fad32c35e59325a801aaed8efeef1c --- /dev/null +++ b/src/hotspot/share/gc/shared/workerUtils.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc/shared/workerUtils.hpp" +#include "runtime/atomic.hpp" +#include "runtime/mutexLocker.hpp" + +// *** WorkerThreadsBarrierSync + +WorkerThreadsBarrierSync::WorkerThreadsBarrierSync() + : _monitor(Mutex::nosafepoint, "WorkerThreadsBarrierSync_lock"), + _n_workers(0), _n_completed(0), _should_reset(false), _aborted(false) { +} + +void WorkerThreadsBarrierSync::set_n_workers(uint n_workers) { + _n_workers = n_workers; + _n_completed = 0; + _should_reset = false; + _aborted = false; +} + +bool WorkerThreadsBarrierSync::enter() { + MonitorLocker ml(monitor(), Mutex::_no_safepoint_check_flag); + if (should_reset()) { + // The should_reset() was set and we are the first worker to enter + // the sync barrier. We will zero the n_completed() count which + // effectively resets the barrier. + zero_completed(); + set_should_reset(false); + } + inc_completed(); + if (n_completed() == n_workers()) { + // At this point we would like to reset the barrier to be ready in + // case it is used again. However, we cannot set n_completed() to + // 0, even after the notify_all(), given that some other workers + // might still be waiting for n_completed() to become == + // n_workers(). So, if we set n_completed() to 0, those workers + // will get stuck (as they will wake up, see that n_completed() != + // n_workers() and go back to sleep). Instead, we raise the + // should_reset() flag and the barrier will be reset the first + // time a worker enters it again. + set_should_reset(true); + ml.notify_all(); + } else { + while (n_completed() != n_workers() && !aborted()) { + ml.wait(); + } + } + return !aborted(); +} + +void WorkerThreadsBarrierSync::abort() { + MutexLocker x(monitor(), Mutex::_no_safepoint_check_flag); + set_aborted(); + monitor()->notify_all(); +} + +// SubTasksDone functions. + +SubTasksDone::SubTasksDone(uint n) : + _tasks(NULL), _n_tasks(n) { + _tasks = NEW_C_HEAP_ARRAY(bool, n, mtInternal); + for (uint i = 0; i < _n_tasks; i++) { + _tasks[i] = false; + } +} + +#ifdef ASSERT +void SubTasksDone::all_tasks_claimed_impl(uint skipped[], size_t skipped_size) { + if (Atomic::cmpxchg(&_verification_done, false, true)) { + // another thread has done the verification + return; + } + // all non-skipped tasks are claimed + for (uint i = 0; i < _n_tasks; ++i) { + if (!_tasks[i]) { + auto is_skipped = false; + for (size_t j = 0; j < skipped_size; ++j) { + if (i == skipped[j]) { + is_skipped = true; + break; + } + } + assert(is_skipped, "%d not claimed.", i); + } + } + // all skipped tasks are *not* claimed + for (size_t i = 0; i < skipped_size; ++i) { + auto task_index = skipped[i]; + assert(task_index < _n_tasks, "Array in range."); + assert(!_tasks[task_index], "%d is both claimed and skipped.", task_index); + } +} +#endif + +bool SubTasksDone::try_claim_task(uint t) { + assert(t < _n_tasks, "bad task id."); + return !_tasks[t] && !Atomic::cmpxchg(&_tasks[t], false, true); +} + +SubTasksDone::~SubTasksDone() { + assert(_verification_done, "all_tasks_claimed must have been called."); + FREE_C_HEAP_ARRAY(bool, _tasks); +} + +// *** SequentialSubTasksDone + +bool SequentialSubTasksDone::try_claim_task(uint& t) { + t = _num_claimed; + if (t < _num_tasks) { + t = Atomic::add(&_num_claimed, 1u) - 1; + } + return t < _num_tasks; +} diff --git a/src/hotspot/share/gc/shared/workerUtils.hpp b/src/hotspot/share/gc/shared/workerUtils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..223dfb34eb20ea83bb7781b47df94dff07330485 --- /dev/null +++ b/src/hotspot/share/gc/shared/workerUtils.hpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_SHARED_WORKERUTILS_HPP +#define SHARE_GC_SHARED_WORKERUTILS_HPP + +#include "memory/allocation.hpp" +#include "metaprogramming/enableIf.hpp" +#include "metaprogramming/logical.hpp" +#include "runtime/mutex.hpp" +#include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" + +// A class that acts as a synchronisation barrier. Workers enter +// the barrier and must wait until all other workers have entered +// before any of them may leave. + +class WorkerThreadsBarrierSync : public StackObj { +protected: + Monitor _monitor; + uint _n_workers; + uint _n_completed; + bool _should_reset; + bool _aborted; + + Monitor* monitor() { return &_monitor; } + uint n_workers() { return _n_workers; } + uint n_completed() { return _n_completed; } + bool should_reset() { return _should_reset; } + bool aborted() { return _aborted; } + + void zero_completed() { _n_completed = 0; } + void inc_completed() { _n_completed++; } + void set_aborted() { _aborted = true; } + void set_should_reset(bool v) { _should_reset = v; } + +public: + WorkerThreadsBarrierSync(); + + // Set the number of workers that will use the barrier. + // Must be called before any of the workers start running. + void set_n_workers(uint n_workers); + + // Enter the barrier. A worker that enters the barrier will + // not be allowed to leave until all other threads have + // also entered the barrier or the barrier is aborted. + // Returns false if the barrier was aborted. + bool enter(); + + // Aborts the barrier and wakes up any threads waiting for + // the barrier to complete. The barrier will remain in the + // aborted state until the next call to set_n_workers(). + void abort(); +}; + +// A class to manage claiming of subtasks within a group of tasks. The +// subtasks will be identified by integer indices, usually elements of an +// enumeration type. + +class SubTasksDone: public CHeapObj { + volatile bool* _tasks; + uint _n_tasks; + + // make sure verification logic is run exactly once to avoid duplicate assertion failures + DEBUG_ONLY(volatile bool _verification_done = false;) + void all_tasks_claimed_impl(uint skipped[], size_t skipped_size) NOT_DEBUG_RETURN; + + NONCOPYABLE(SubTasksDone); + +public: + // Initializes "this" to a state in which there are "n" tasks to be + // processed, none of the which are originally claimed. + SubTasksDone(uint n); + + // Attempt to claim the task "t", returning true if successful, + // false if it has already been claimed. The task "t" is required + // to be within the range of "this". + bool try_claim_task(uint t); + + // The calling thread asserts that it has attempted to claim all the tasks + // that it will try to claim. Tasks that are meant to be skipped must be + // explicitly passed as extra arguments. Every thread in the parallel task + // must execute this. + template...>::value)> + void all_tasks_claimed(T0 first_skipped, Ts... more_skipped) { + static_assert(std::is_convertible::value, "not convertible"); + uint skipped[] = { static_cast(first_skipped), static_cast(more_skipped)... }; + all_tasks_claimed_impl(skipped, ARRAY_SIZE(skipped)); + } + // if there are no skipped tasks. + void all_tasks_claimed() { + all_tasks_claimed_impl(nullptr, 0); + } + + // Destructor. + ~SubTasksDone(); +}; + +// As above, but for sequential tasks, i.e. instead of claiming +// sub-tasks from a set (possibly an enumeration), claim sub-tasks +// in sequential order. This is ideal for claiming dynamically +// partitioned tasks (like striding in the parallel remembered +// set scanning). + +class SequentialSubTasksDone : public CHeapObj { + + uint _num_tasks; // Total number of tasks available. + volatile uint _num_claimed; // Number of tasks claimed. + + NONCOPYABLE(SequentialSubTasksDone); + +public: + SequentialSubTasksDone(uint num_tasks) : _num_tasks(num_tasks), _num_claimed(0) { } + ~SequentialSubTasksDone() { + // Claiming may try to claim more tasks than there are. + assert(_num_claimed >= _num_tasks, "Claimed %u tasks of %u", _num_claimed, _num_tasks); + } + + // Attempt to claim the next unclaimed task in the sequence, + // returning true if successful, with t set to the index of the + // claimed task. Returns false if there are no more unclaimed tasks + // in the sequence. In this case t is undefined. + bool try_claim_task(uint& t); +}; + +#endif // SHARE_GC_SHARED_WORKERUTILS_HPP diff --git a/src/hotspot/share/gc/shared/workgroup.cpp b/src/hotspot/share/gc/shared/workgroup.cpp deleted file mode 100644 index c09108c719d7101cd1744074786a81e53d345ca0..0000000000000000000000000000000000000000 --- a/src/hotspot/share/gc/shared/workgroup.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "gc/shared/gcId.hpp" -#include "gc/shared/workgroup.hpp" -#include "gc/shared/workerManager.hpp" -#include "memory/allocation.hpp" -#include "memory/allocation.inline.hpp" -#include "memory/iterator.hpp" -#include "runtime/atomic.hpp" -#include "runtime/os.hpp" -#include "runtime/semaphore.hpp" -#include "runtime/thread.inline.hpp" - -// WorkGang dispatcher implemented with semaphores. -// -// Semaphores don't require the worker threads to re-claim the lock when they wake up. -// This helps lowering the latency when starting and stopping the worker threads. -class GangTaskDispatcher : public CHeapObj { - // The task currently being dispatched to the GangWorkers. - AbstractGangTask* _task; - - volatile uint _started; - volatile uint _not_finished; - - // Semaphore used to start the GangWorkers. - Semaphore* _start_semaphore; - // Semaphore used to notify the coordinator that all workers are done. - Semaphore* _end_semaphore; - -public: - GangTaskDispatcher() : - _task(NULL), - _started(0), - _not_finished(0), - _start_semaphore(new Semaphore()), - _end_semaphore(new Semaphore()) -{ } - - ~GangTaskDispatcher() { - delete _start_semaphore; - delete _end_semaphore; - } - - // Coordinator API. - - // Distributes the task out to num_workers workers. - // Returns when the task has been completed by all workers. - void coordinator_execute_on_workers(AbstractGangTask* task, uint num_workers) { - // No workers are allowed to read the state variables until they have been signaled. - _task = task; - _not_finished = num_workers; - - // Dispatch 'num_workers' number of tasks. - _start_semaphore->signal(num_workers); - - // Wait for the last worker to signal the coordinator. - _end_semaphore->wait(); - - // No workers are allowed to read the state variables after the coordinator has been signaled. - assert(_not_finished == 0, "%d not finished workers?", _not_finished); - _task = NULL; - _started = 0; - - } - - // Worker API. - - // Waits for a task to become available to the worker. - // Returns when the worker has been assigned a task. - WorkData worker_wait_for_task() { - // Wait for the coordinator to dispatch a task. - _start_semaphore->wait(); - - uint num_started = Atomic::add(&_started, 1u); - - // Subtract one to get a zero-indexed worker id. - uint worker_id = num_started - 1; - - return WorkData(_task, worker_id); - } - - // Signal to the coordinator that the worker is done with the assigned task. - void worker_done_with_task() { - // Mark that the worker is done with the task. - // The worker is not allowed to read the state variables after this line. - uint not_finished = Atomic::sub(&_not_finished, 1u); - - // The last worker signals to the coordinator that all work is completed. - if (not_finished == 0) { - _end_semaphore->signal(); - } - } -}; -// Definitions of WorkGang methods. - -WorkGang::WorkGang(const char* name, uint workers) : - _workers(NULL), - _total_workers(workers), - _active_workers(UseDynamicNumberOfGCThreads ? 1U : workers), - _created_workers(0), - _name(name), - _dispatcher(new GangTaskDispatcher()) - { } - -WorkGang::~WorkGang() { - delete _dispatcher; -} - -// The current implementation will exit if the allocation -// of any worker fails. -void WorkGang::initialize_workers() { - log_develop_trace(gc, workgang)("Constructing work gang %s with %u threads", name(), total_workers()); - _workers = NEW_C_HEAP_ARRAY(GangWorker*, total_workers(), mtInternal); - add_workers(true); -} - - -GangWorker* WorkGang::install_worker(uint worker_id) { - GangWorker* new_worker = allocate_worker(worker_id); - set_thread(worker_id, new_worker); - return new_worker; -} - -void WorkGang::add_workers(bool initializing) { - uint previous_created_workers = _created_workers; - - _created_workers = WorkerManager::add_workers(this, - _active_workers, - _total_workers, - _created_workers, - os::gc_thread, - initializing); - _active_workers = MIN2(_created_workers, _active_workers); - - WorkerManager::log_worker_creation(this, previous_created_workers, _active_workers, _created_workers, initializing); -} - -GangWorker* WorkGang::worker(uint i) const { - // Array index bounds checking. - GangWorker* result = NULL; - assert(_workers != NULL, "No workers for indexing"); - assert(i < total_workers(), "Worker index out of bounds"); - result = _workers[i]; - assert(result != NULL, "Indexing to null worker"); - return result; -} - -void WorkGang::threads_do(ThreadClosure* tc) const { - assert(tc != NULL, "Null ThreadClosure"); - uint workers = created_workers(); - for (uint i = 0; i < workers; i++) { - tc->do_thread(worker(i)); - } -} - -GangWorker* WorkGang::allocate_worker(uint worker_id) { - return new GangWorker(this, worker_id); -} - -void WorkGang::run_task(AbstractGangTask* task) { - run_task(task, active_workers()); -} - -void WorkGang::run_task(AbstractGangTask* task, uint num_workers) { - guarantee(num_workers <= total_workers(), - "Trying to execute task %s with %u workers which is more than the amount of total workers %u.", - task->name(), num_workers, total_workers()); - guarantee(num_workers > 0, "Trying to execute task %s with zero workers", task->name()); - uint old_num_workers = _active_workers; - update_active_workers(num_workers); - _dispatcher->coordinator_execute_on_workers(task, num_workers); - update_active_workers(old_num_workers); -} - -GangWorker::GangWorker(WorkGang* gang, uint id) { - _gang = gang; - set_id(id); - set_name("%s#%d", gang->name(), id); -} - -void GangWorker::run() { - initialize(); - loop(); -} - -void GangWorker::initialize() { - assert(_gang != NULL, "No gang to run in"); - os::set_priority(this, NearMaxPriority); - log_develop_trace(gc, workgang)("Running gang worker for gang %s id %u", gang()->name(), id()); - assert(!Thread::current()->is_VM_thread(), "VM thread should not be part" - " of a work gang"); -} - -WorkData GangWorker::wait_for_task() { - return gang()->dispatcher()->worker_wait_for_task(); -} - -void GangWorker::signal_task_done() { - gang()->dispatcher()->worker_done_with_task(); -} - -void GangWorker::run_task(WorkData data) { - GCIdMark gc_id_mark(data._task->gc_id()); - log_develop_trace(gc, workgang)("Running work gang: %s task: %s worker: %u", name(), data._task->name(), data._worker_id); - - data._task->work(data._worker_id); - - log_develop_trace(gc, workgang)("Finished work gang: %s task: %s worker: %u thread: " PTR_FORMAT, - name(), data._task->name(), data._worker_id, p2i(Thread::current())); -} - -void GangWorker::loop() { - while (true) { - WorkData data = wait_for_task(); - - run_task(data); - - signal_task_done(); - } -} - -// *** WorkGangBarrierSync - -WorkGangBarrierSync::WorkGangBarrierSync() - : _monitor(Mutex::nosafepoint, "WorkGangBarrierSync_lock", - Monitor::_safepoint_check_never), - _n_workers(0), _n_completed(0), _should_reset(false), _aborted(false) { -} - -void WorkGangBarrierSync::set_n_workers(uint n_workers) { - _n_workers = n_workers; - _n_completed = 0; - _should_reset = false; - _aborted = false; -} - -bool WorkGangBarrierSync::enter() { - MonitorLocker ml(monitor(), Mutex::_no_safepoint_check_flag); - if (should_reset()) { - // The should_reset() was set and we are the first worker to enter - // the sync barrier. We will zero the n_completed() count which - // effectively resets the barrier. - zero_completed(); - set_should_reset(false); - } - inc_completed(); - if (n_completed() == n_workers()) { - // At this point we would like to reset the barrier to be ready in - // case it is used again. However, we cannot set n_completed() to - // 0, even after the notify_all(), given that some other workers - // might still be waiting for n_completed() to become == - // n_workers(). So, if we set n_completed() to 0, those workers - // will get stuck (as they will wake up, see that n_completed() != - // n_workers() and go back to sleep). Instead, we raise the - // should_reset() flag and the barrier will be reset the first - // time a worker enters it again. - set_should_reset(true); - ml.notify_all(); - } else { - while (n_completed() != n_workers() && !aborted()) { - ml.wait(); - } - } - return !aborted(); -} - -void WorkGangBarrierSync::abort() { - MutexLocker x(monitor(), Mutex::_no_safepoint_check_flag); - set_aborted(); - monitor()->notify_all(); -} - -// SubTasksDone functions. - -SubTasksDone::SubTasksDone(uint n) : - _tasks(NULL), _n_tasks(n) { - _tasks = NEW_C_HEAP_ARRAY(bool, n, mtInternal); - for (uint i = 0; i < _n_tasks; i++) { - _tasks[i] = false; - } -} - -#ifdef ASSERT -void SubTasksDone::all_tasks_claimed_impl(uint skipped[], size_t skipped_size) { - if (Atomic::cmpxchg(&_verification_done, false, true)) { - // another thread has done the verification - return; - } - // all non-skipped tasks are claimed - for (uint i = 0; i < _n_tasks; ++i) { - if (!_tasks[i]) { - auto is_skipped = false; - for (size_t j = 0; j < skipped_size; ++j) { - if (i == skipped[j]) { - is_skipped = true; - break; - } - } - assert(is_skipped, "%d not claimed.", i); - } - } - // all skipped tasks are *not* claimed - for (size_t i = 0; i < skipped_size; ++i) { - auto task_index = skipped[i]; - assert(task_index < _n_tasks, "Array in range."); - assert(!_tasks[task_index], "%d is both claimed and skipped.", task_index); - } -} -#endif - -bool SubTasksDone::try_claim_task(uint t) { - assert(t < _n_tasks, "bad task id."); - return !_tasks[t] && !Atomic::cmpxchg(&_tasks[t], false, true); -} - -SubTasksDone::~SubTasksDone() { - assert(_verification_done, "all_tasks_claimed must have been called."); - FREE_C_HEAP_ARRAY(bool, _tasks); -} - -// *** SequentialSubTasksDone - -bool SequentialSubTasksDone::try_claim_task(uint& t) { - t = _num_claimed; - if (t < _num_tasks) { - t = Atomic::add(&_num_claimed, 1u) - 1; - } - return t < _num_tasks; -} diff --git a/src/hotspot/share/gc/shared/workgroup.hpp b/src/hotspot/share/gc/shared/workgroup.hpp deleted file mode 100644 index d6446bb1dd3f3d0feb5d7d8315fda7d417cbee59..0000000000000000000000000000000000000000 --- a/src/hotspot/share/gc/shared/workgroup.hpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_SHARED_WORKGROUP_HPP -#define SHARE_GC_SHARED_WORKGROUP_HPP - -#include "memory/allocation.hpp" -#include "metaprogramming/enableIf.hpp" -#include "metaprogramming/logical.hpp" -#include "runtime/globals.hpp" -#include "runtime/nonJavaThread.hpp" -#include "runtime/thread.hpp" -#include "gc/shared/gcId.hpp" -#include "logging/log.hpp" -#include "utilities/debug.hpp" -#include "utilities/globalDefinitions.hpp" - -// Task class hierarchy: -// AbstractGangTask -// -// Gang/Group class hierarchy: -// WorkGang -// -// Worker class hierarchy: -// GangWorker (subclass of WorkerThread) - -// Forward declarations of classes defined here - -class GangWorker; -class Semaphore; -class ThreadClosure; -class GangTaskDispatcher; - -// An abstract task to be worked on by a gang. -// You subclass this to supply your own work() method -class AbstractGangTask : public CHeapObj { - const char* _name; - const uint _gc_id; - - public: - explicit AbstractGangTask(const char* name) : - _name(name), - _gc_id(GCId::current_or_undefined()) - {} - - // The abstract work method. - // The argument tells you which member of the gang you are. - virtual void work(uint worker_id) = 0; - - // Debugging accessor for the name. - const char* name() const { return _name; } - const uint gc_id() const { return _gc_id; } -}; - -struct WorkData { - AbstractGangTask* _task; - uint _worker_id; - WorkData(AbstractGangTask* task, uint worker_id) : _task(task), _worker_id(worker_id) {} -}; - -// The work gang is the collection of workers to execute tasks. -// The number of workers run for a task is "_active_workers" -// while "_total_workers" is the number of available workers. -class WorkGang : public CHeapObj { - // The array of worker threads for this gang. - GangWorker** _workers; - // The count of the number of workers in the gang. - uint _total_workers; - // The currently active workers in this gang. - uint _active_workers; - // The count of created workers in the gang. - uint _created_workers; - // Printing support. - const char* _name; - - // To get access to the GangTaskDispatcher instance. - friend class GangWorker; - GangTaskDispatcher* const _dispatcher; - - GangTaskDispatcher* dispatcher() const { return _dispatcher; } - - void set_thread(uint worker_id, GangWorker* worker) { - _workers[worker_id] = worker; - } - - // Add GC workers when _created_workers < _active_workers; otherwise, no-op. - // If there's no memory/thread allocation failure, _created_worker is - // adjusted to match _active_workers (_created_worker == _active_workers). - void add_workers(bool initializing); - - GangWorker* allocate_worker(uint which); - - public: - WorkGang(const char* name, uint workers); - - ~WorkGang(); - - // Initialize workers in the gang. Return true if initialization succeeded. - void initialize_workers(); - - uint total_workers() const { return _total_workers; } - - uint created_workers() const { - return _created_workers; - } - - uint active_workers() const { - assert(_active_workers != 0, "zero active workers"); - assert(_active_workers <= _total_workers, - "_active_workers: %u > _total_workers: %u", _active_workers, _total_workers); - return _active_workers; - } - - uint update_active_workers(uint v) { - assert(v <= _total_workers, - "Trying to set more workers active than there are"); - assert(v != 0, "Trying to set active workers to 0"); - _active_workers = v; - add_workers(false /* initializing */); - log_trace(gc, task)("%s: using %d out of %d workers", name(), _active_workers, _total_workers); - return _active_workers; - } - - // Return the Ith worker. - GangWorker* worker(uint i) const; - - // Base name (without worker id #) of threads. - const char* group_name() { return name(); } - - void threads_do(ThreadClosure* tc) const; - - // Create a GC worker and install it into the work gang. - virtual GangWorker* install_worker(uint which); - - // Debugging. - const char* name() const { return _name; } - - // Run a task using the current active number of workers, returns when the task is done. - void run_task(AbstractGangTask* task); - - // Run a task with the given number of workers, returns - // when the task is done. The number of workers must be at most the number of - // active workers. Additional workers may be created if an insufficient - // number currently exists. - void run_task(AbstractGangTask* task, uint num_workers); -}; - -// Temporarily try to set the number of active workers. -// It's not guaranteed that it succeeds, and users need to -// query the number of active workers. -class WithUpdatedActiveWorkers : public StackObj { -private: - WorkGang* const _gang; - const uint _old_active_workers; - -public: - WithUpdatedActiveWorkers(WorkGang* gang, uint requested_num_workers) : - _gang(gang), - _old_active_workers(gang->active_workers()) { - uint capped_num_workers = MIN2(requested_num_workers, gang->total_workers()); - gang->update_active_workers(capped_num_workers); - } - - ~WithUpdatedActiveWorkers() { - _gang->update_active_workers(_old_active_workers); - } -}; - -// Several instances of this class run in parallel as workers for a gang. -class GangWorker: public WorkerThread { -private: - WorkGang* _gang; - - void initialize(); - void loop(); - - WorkGang* gang() const { return _gang; } - - WorkData wait_for_task(); - void run_task(WorkData work); - void signal_task_done(); - -protected: - // The only real method: run a task for the gang. - void run() override; - -public: - GangWorker(WorkGang* gang, uint id); - - // Printing - const char* type_name() const override { return "GCTaskThread"; } -}; - -// A class that acts as a synchronisation barrier. Workers enter -// the barrier and must wait until all other workers have entered -// before any of them may leave. - -class WorkGangBarrierSync : public StackObj { -protected: - Monitor _monitor; - uint _n_workers; - uint _n_completed; - bool _should_reset; - bool _aborted; - - Monitor* monitor() { return &_monitor; } - uint n_workers() { return _n_workers; } - uint n_completed() { return _n_completed; } - bool should_reset() { return _should_reset; } - bool aborted() { return _aborted; } - - void zero_completed() { _n_completed = 0; } - void inc_completed() { _n_completed++; } - void set_aborted() { _aborted = true; } - void set_should_reset(bool v) { _should_reset = v; } - -public: - WorkGangBarrierSync(); - - // Set the number of workers that will use the barrier. - // Must be called before any of the workers start running. - void set_n_workers(uint n_workers); - - // Enter the barrier. A worker that enters the barrier will - // not be allowed to leave until all other threads have - // also entered the barrier or the barrier is aborted. - // Returns false if the barrier was aborted. - bool enter(); - - // Aborts the barrier and wakes up any threads waiting for - // the barrier to complete. The barrier will remain in the - // aborted state until the next call to set_n_workers(). - void abort(); -}; - -// A class to manage claiming of subtasks within a group of tasks. The -// subtasks will be identified by integer indices, usually elements of an -// enumeration type. - -class SubTasksDone: public CHeapObj { - volatile bool* _tasks; - uint _n_tasks; - - // make sure verification logic is run exactly once to avoid duplicate assertion failures - DEBUG_ONLY(volatile bool _verification_done = false;) - void all_tasks_claimed_impl(uint skipped[], size_t skipped_size) NOT_DEBUG_RETURN; - - NONCOPYABLE(SubTasksDone); - -public: - // Initializes "this" to a state in which there are "n" tasks to be - // processed, none of the which are originally claimed. - SubTasksDone(uint n); - - // Attempt to claim the task "t", returning true if successful, - // false if it has already been claimed. The task "t" is required - // to be within the range of "this". - bool try_claim_task(uint t); - - // The calling thread asserts that it has attempted to claim all the tasks - // that it will try to claim. Tasks that are meant to be skipped must be - // explicitly passed as extra arguments. Every thread in the parallel task - // must execute this. - template...>::value)> - void all_tasks_claimed(T0 first_skipped, Ts... more_skipped) { - static_assert(std::is_convertible::value, "not convertible"); - uint skipped[] = { static_cast(first_skipped), static_cast(more_skipped)... }; - all_tasks_claimed_impl(skipped, ARRAY_SIZE(skipped)); - } - // if there are no skipped tasks. - void all_tasks_claimed() { - all_tasks_claimed_impl(nullptr, 0); - } - - // Destructor. - ~SubTasksDone(); -}; - -// As above, but for sequential tasks, i.e. instead of claiming -// sub-tasks from a set (possibly an enumeration), claim sub-tasks -// in sequential order. This is ideal for claiming dynamically -// partitioned tasks (like striding in the parallel remembered -// set scanning). - -class SequentialSubTasksDone : public CHeapObj { - - uint _num_tasks; // Total number of tasks available. - volatile uint _num_claimed; // Number of tasks claimed. - - NONCOPYABLE(SequentialSubTasksDone); - -public: - SequentialSubTasksDone(uint num_tasks) : _num_tasks(num_tasks), _num_claimed(0) { } - ~SequentialSubTasksDone() { - // Claiming may try to claim more tasks than there are. - assert(_num_claimed >= _num_tasks, "Claimed %u tasks of %u", _num_claimed, _num_tasks); - } - - // Attempt to claim the next unclaimed task in the sequence, - // returning true if successful, with t set to the index of the - // claimed task. Returns false if there are no more unclaimed tasks - // in the sequence. In this case t is undefined. - bool try_claim_task(uint& t); -}; - -#endif // SHARE_GC_SHARED_WORKGROUP_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index f829b16cebc25f51d986356c9100fb9a7b49870d..e5282dc9c53e7ef3973a373ed8b9bee5614631f0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -87,7 +87,7 @@ void ShenandoahParallelCodeHeapIterator::parallel_blobs_do(CodeBlobClosure* f) { int current = count++; if ((current & stride_mask) == 0) { process_block = (current >= _claimed_idx) && - (Atomic::cmpxchg(&_claimed_idx, current, current + stride) == current); + (Atomic::cmpxchg(&_claimed_idx, current, current + stride, memory_order_relaxed) == current); } if (process_block) { if (cb->is_alive()) { @@ -153,14 +153,14 @@ public: } }; -class ShenandoahDisarmNMethodsTask : public AbstractGangTask { +class ShenandoahDisarmNMethodsTask : public WorkerTask { private: ShenandoahDisarmNMethodClosure _cl; ShenandoahConcurrentNMethodIterator _iterator; public: ShenandoahDisarmNMethodsTask() : - AbstractGangTask("Shenandoah Disarm NMethods"), + WorkerTask("Shenandoah Disarm NMethods"), _iterator(ShenandoahCodeRoots::table()) { assert(SafepointSynchronize::is_at_safepoint(), "Only at a safepoint"); MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); @@ -258,7 +258,7 @@ public: } }; -class ShenandoahUnlinkTask : public AbstractGangTask { +class ShenandoahUnlinkTask : public WorkerTask { private: ShenandoahNMethodUnlinkClosure _cl; ICRefillVerifier* _verifier; @@ -266,7 +266,7 @@ private: public: ShenandoahUnlinkTask(bool unloading_occurred, ICRefillVerifier* verifier) : - AbstractGangTask("Shenandoah Unlink NMethods"), + WorkerTask("Shenandoah Unlink NMethods"), _cl(unloading_occurred), _verifier(verifier), _iterator(ShenandoahCodeRoots::table()) { @@ -289,7 +289,7 @@ public: } }; -void ShenandoahCodeRoots::unlink(WorkGang* workers, bool unloading_occurred) { +void ShenandoahCodeRoots::unlink(WorkerThreads* workers, bool unloading_occurred) { assert(ShenandoahHeap::heap()->unload_classes(), "Only when running concurrent class unloading"); for (;;) { @@ -320,14 +320,14 @@ public: } }; -class ShenandoahNMethodPurgeTask : public AbstractGangTask { +class ShenandoahNMethodPurgeTask : public WorkerTask { private: ShenandoahNMethodPurgeClosure _cl; ShenandoahConcurrentNMethodIterator _iterator; public: ShenandoahNMethodPurgeTask() : - AbstractGangTask("Shenandoah Purge NMethods"), + WorkerTask("Shenandoah Purge NMethods"), _cl(), _iterator(ShenandoahCodeRoots::table()) { MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); @@ -344,7 +344,7 @@ public: } }; -void ShenandoahCodeRoots::purge(WorkGang* workers) { +void ShenandoahCodeRoots::purge(WorkerThreads* workers) { assert(ShenandoahHeap::heap()->unload_classes(), "Only when running concurrent class unloading"); ShenandoahNMethodPurgeTask task; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp index 16c0485bf48305ae47fa9a94f9d267ed486cae8f..17bc5b78934425b0a9582578a5c9c9f6a1ef249e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp @@ -37,7 +37,7 @@ class ShenandoahHeap; class ShenandoahHeapRegion; class ShenandoahNMethodTable; class ShenandoahNMethodTableSnapshot; -class WorkGang; +class WorkerThreads; class ShenandoahParallelCodeHeapIterator { friend class CodeCache; @@ -95,8 +95,8 @@ public: } // Concurrent nmethod unloading support - static void unlink(WorkGang* workers, bool unloading_occurred); - static void purge(WorkGang* workers); + static void unlink(WorkerThreads* workers, bool unloading_occurred); + static void purge(WorkerThreads* workers); static void arm_nmethods(); static void disarm_nmethods(); static int disarmed_value() { return _disarmed_value; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 6f115eaa01c1b59c8bc103da5a8af944469bf847..fed6b3f0a7f1d119a11cb457a77c82fd246b330a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -50,24 +50,37 @@ // Breakpoint support class ShenandoahBreakpointGCScope : public StackObj { +private: + const GCCause::Cause _cause; public: - ShenandoahBreakpointGCScope() { - ShenandoahBreakpoint::at_before_gc(); + ShenandoahBreakpointGCScope(GCCause::Cause cause) : _cause(cause) { + if (cause == GCCause::_wb_breakpoint) { + ShenandoahBreakpoint::start_gc(); + ShenandoahBreakpoint::at_before_gc(); + } } ~ShenandoahBreakpointGCScope() { - ShenandoahBreakpoint::at_after_gc(); + if (_cause == GCCause::_wb_breakpoint) { + ShenandoahBreakpoint::at_after_gc(); + } } }; class ShenandoahBreakpointMarkScope : public StackObj { +private: + const GCCause::Cause _cause; public: - ShenandoahBreakpointMarkScope() { - ShenandoahBreakpoint::at_after_marking_started(); + ShenandoahBreakpointMarkScope(GCCause::Cause cause) : _cause(cause) { + if (_cause == GCCause::_wb_breakpoint) { + ShenandoahBreakpoint::at_after_marking_started(); + } } ~ShenandoahBreakpointMarkScope() { - ShenandoahBreakpoint::at_before_marking_completed(); + if (_cause == GCCause::_wb_breakpoint) { + ShenandoahBreakpoint::at_before_marking_completed(); + } } }; @@ -86,10 +99,7 @@ void ShenandoahConcurrentGC::cancel() { bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); - if (cause == GCCause::_wb_breakpoint) { - ShenandoahBreakpoint::start_gc(); - } - ShenandoahBreakpointGCScope breakpoint_gc_scope; + ShenandoahBreakpointGCScope breakpoint_gc_scope(cause); // Reset for upcoming marking entry_reset(); @@ -98,7 +108,7 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { vmop_entry_init_mark(); { - ShenandoahBreakpointMarkScope breakpoint_mark_scope; + ShenandoahBreakpointMarkScope breakpoint_mark_scope(cause); // Concurrent mark roots entry_mark_roots(); if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_outside_cycle)) return false; @@ -625,13 +635,13 @@ void ShenandoahConcurrentEvacThreadClosure::do_thread(Thread* thread) { StackWatermarkSet::finish_processing(jt, _oops, StackWatermarkKind::gc); } -class ShenandoahConcurrentEvacUpdateThreadTask : public AbstractGangTask { +class ShenandoahConcurrentEvacUpdateThreadTask : public WorkerTask { private: ShenandoahJavaThreadsIterator _java_threads; public: ShenandoahConcurrentEvacUpdateThreadTask(uint n_workers) : - AbstractGangTask("Shenandoah Evacuate/Update Concurrent Thread Roots"), + WorkerTask("Shenandoah Evacuate/Update Concurrent Thread Roots"), _java_threads(ShenandoahPhaseTimings::conc_thread_roots, n_workers) { } @@ -657,7 +667,9 @@ void ShenandoahConcurrentGC::op_weak_refs() { assert(heap->is_concurrent_weak_root_in_progress(), "Only during this phase"); // Concurrent weak refs processing ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_weak_refs); - ShenandoahBreakpoint::at_after_reference_processing_started(); + if (heap->gc_cause() == GCCause::_wb_breakpoint) { + ShenandoahBreakpoint::at_after_reference_processing_started(); + } heap->ref_processor()->process_references(ShenandoahPhaseTimings::conc_weak_refs, heap->workers(), true /* concurrent */); } @@ -720,7 +732,7 @@ public: // This task not only evacuates/updates marked weak roots, but also "NULL" // dead weak roots. -class ShenandoahConcurrentWeakRootsEvacUpdateTask : public AbstractGangTask { +class ShenandoahConcurrentWeakRootsEvacUpdateTask : public WorkerTask { private: ShenandoahVMWeakRoots _vm_roots; @@ -732,7 +744,7 @@ private: public: ShenandoahConcurrentWeakRootsEvacUpdateTask(ShenandoahPhaseTimings::Phase phase) : - AbstractGangTask("Shenandoah Evacuate/Update Concurrent Weak Roots"), + WorkerTask("Shenandoah Evacuate/Update Concurrent Weak Roots"), _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/), _nmethod_itr(ShenandoahCodeRoots::table()), @@ -834,7 +846,7 @@ public: } }; -class ShenandoahConcurrentRootsEvacUpdateTask : public AbstractGangTask { +class ShenandoahConcurrentRootsEvacUpdateTask : public WorkerTask { private: ShenandoahPhaseTimings::Phase _phase; ShenandoahVMRoots _vm_roots; @@ -844,7 +856,7 @@ private: public: ShenandoahConcurrentRootsEvacUpdateTask(ShenandoahPhaseTimings::Phase phase) : - AbstractGangTask("Shenandoah Evacuate/Update Concurrent Strong Roots"), + WorkerTask("Shenandoah Evacuate/Update Concurrent Strong Roots"), _phase(phase), _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 214ab73a2a2c57f1800e2f42561eed3b42d1c3ac..310c46f44f4df1d5970fe8b9fdce2415277cb677 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -42,41 +42,14 @@ #include "memory/iterator.inline.hpp" #include "memory/resourceArea.hpp" -class ShenandoahUpdateRootsTask : public AbstractGangTask { -private: - ShenandoahRootUpdater* _root_updater; - bool _check_alive; -public: - ShenandoahUpdateRootsTask(ShenandoahRootUpdater* root_updater, bool check_alive) : - AbstractGangTask("Shenandoah Update Roots"), - _root_updater(root_updater), - _check_alive(check_alive){ - } - - void work(uint worker_id) { - assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); - ShenandoahParallelWorkerSession worker_session(worker_id); - - ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahUpdateRefsClosure cl; - if (_check_alive) { - ShenandoahForwardedIsAliveClosure is_alive; - _root_updater->roots_do(worker_id, &is_alive, &cl); - } else { - AlwaysTrueClosure always_true;; - _root_updater->roots_do(worker_id, &always_true, &cl); - } - } -}; - -class ShenandoahConcurrentMarkingTask : public AbstractGangTask { +class ShenandoahConcurrentMarkingTask : public WorkerTask { private: ShenandoahConcurrentMark* const _cm; TaskTerminator* const _terminator; public: ShenandoahConcurrentMarkingTask(ShenandoahConcurrentMark* cm, TaskTerminator* terminator) : - AbstractGangTask("Shenandoah Concurrent Mark"), _cm(cm), _terminator(terminator) { + WorkerTask("Shenandoah Concurrent Mark"), _cm(cm), _terminator(terminator) { } void work(uint worker_id) { @@ -120,7 +93,7 @@ public: } }; -class ShenandoahFinalMarkingTask : public AbstractGangTask { +class ShenandoahFinalMarkingTask : public WorkerTask { private: ShenandoahConcurrentMark* _cm; TaskTerminator* _terminator; @@ -128,7 +101,7 @@ private: public: ShenandoahFinalMarkingTask(ShenandoahConcurrentMark* cm, TaskTerminator* terminator, bool dedup_string) : - AbstractGangTask("Shenandoah Final Mark"), _cm(cm), _terminator(terminator), _dedup_string(dedup_string) { + WorkerTask("Shenandoah Final Mark"), _cm(cm), _terminator(terminator), _dedup_string(dedup_string) { } void work(uint worker_id) { @@ -164,7 +137,7 @@ ShenandoahConcurrentMark::ShenandoahConcurrentMark() : ShenandoahMark() {} // Mark concurrent roots during concurrent phases -class ShenandoahMarkConcurrentRootsTask : public AbstractGangTask { +class ShenandoahMarkConcurrentRootsTask : public WorkerTask { private: SuspendibleThreadSetJoiner _sts_joiner; ShenandoahConcurrentRootScanner _root_scanner; @@ -183,7 +156,7 @@ ShenandoahMarkConcurrentRootsTask::ShenandoahMarkConcurrentRootsTask(ShenandoahO ShenandoahReferenceProcessor* rp, ShenandoahPhaseTimings::Phase phase, uint nworkers) : - AbstractGangTask("Shenandoah Concurrent Mark Roots"), + WorkerTask("Shenandoah Concurrent Mark Roots"), _root_scanner(nworkers, phase), _queue_set(qs), _rp(rp) { @@ -203,7 +176,7 @@ void ShenandoahConcurrentMark::mark_concurrent_roots() { TASKQUEUE_STATS_ONLY(task_queues()->reset_taskqueue_stats()); - WorkGang* workers = heap->workers(); + WorkerThreads* workers = heap->workers(); ShenandoahReferenceProcessor* rp = heap->ref_processor(); task_queues()->reserve(workers->active_workers()); ShenandoahMarkConcurrentRootsTask task(task_queues(), rp, ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); @@ -226,7 +199,7 @@ public: void ShenandoahConcurrentMark::concurrent_mark() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); - WorkGang* workers = heap->workers(); + WorkerThreads* workers = heap->workers(); uint nworkers = workers->active_workers(); task_queues()->reserve(nworkers); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 97f1bfde2498e777609a4c5db8d8a7a268d028ee..9f5273a9750746171fd9c65c9f451fd7067f47d1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -47,8 +47,8 @@ ShenandoahControlThread::ShenandoahControlThread() : ConcurrentGCThread(), - _alloc_failure_waiters_lock(Mutex::leaf, "ShenandoahAllocFailureGC_lock", Monitor::_safepoint_check_always, true), - _gc_waiters_lock(Mutex::leaf, "ShenandoahRequestedGC_lock", Monitor::_safepoint_check_always, true), + _alloc_failure_waiters_lock(Mutex::safepoint-1, "ShenandoahAllocFailureGC_lock", true), + _gc_waiters_lock(Mutex::safepoint-1, "ShenandoahRequestedGC_lock", true), _periodic_task(this), _requested_gc_cause(GCCause::_no_cause_specified), _degen_point(ShenandoahGC::_degenerated_outside_cycle), @@ -97,8 +97,10 @@ void ShenandoahControlThread::run_service() { while (!in_graceful_shutdown() && !should_terminate()) { // Figure out if we have pending requests. bool alloc_failure_pending = _alloc_failure_gc.is_set(); - bool explicit_gc_requested = _gc_requested.is_set() && is_explicit_gc(_requested_gc_cause); - bool implicit_gc_requested = _gc_requested.is_set() && !is_explicit_gc(_requested_gc_cause); + bool is_gc_requested = _gc_requested.is_set(); + GCCause::Cause requested_gc_cause = _requested_gc_cause; + bool explicit_gc_requested = is_gc_requested && is_explicit_gc(requested_gc_cause); + bool implicit_gc_requested = is_gc_requested && !is_explicit_gc(requested_gc_cause); // This control loop iteration have seen this much allocations. size_t allocs_seen = Atomic::xchg(&_allocs_seen, (size_t)0, memory_order_relaxed); @@ -132,7 +134,7 @@ void ShenandoahControlThread::run_service() { } } else if (explicit_gc_requested) { - cause = _requested_gc_cause; + cause = requested_gc_cause; log_info(gc)("Trigger: Explicit GC request (%s)", GCCause::to_string(cause)); heuristics->record_requested_gc(); @@ -147,7 +149,7 @@ void ShenandoahControlThread::run_service() { mode = stw_full; } } else if (implicit_gc_requested) { - cause = _requested_gc_cause; + cause = requested_gc_cause; log_info(gc)("Trigger: Implicit GC request (%s)", GCCause::to_string(cause)); heuristics->record_requested_gc(); @@ -505,8 +507,11 @@ void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) { size_t current_gc_id = get_gc_id(); size_t required_gc_id = current_gc_id + 1; while (current_gc_id < required_gc_id) { - _gc_requested.set(); + // Although setting gc request is under _gc_waiters_lock, but read side (run_service()) + // does not take the lock. We need to enforce following order, so that read side sees + // latest requested gc cause when the flag is set. _requested_gc_cause = cause; + _gc_requested.set(); if (cause != GCCause::_wb_breakpoint) { ml.wait(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index b37b488cc18d3f0421c41736fcef5a91c995527a..893e8238aaa585d3867ff3636eec77355582d319 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -28,6 +28,7 @@ #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/preservedMarks.inline.hpp" #include "gc/shared/tlab_globals.hpp" +#include "gc/shared/workerThread.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" @@ -59,7 +60,6 @@ #include "utilities/copy.hpp" #include "utilities/events.hpp" #include "utilities/growableArray.hpp" -#include "gc/shared/workgroup.hpp" ShenandoahFullGC::ShenandoahFullGC() : _gc_timer(ShenandoahHeap::heap()->gc_timer()), @@ -363,7 +363,7 @@ public: } }; -class ShenandoahPrepareForCompactionTask : public AbstractGangTask { +class ShenandoahPrepareForCompactionTask : public WorkerTask { private: PreservedMarksSet* const _preserved_marks; ShenandoahHeap* const _heap; @@ -371,7 +371,7 @@ private: public: ShenandoahPrepareForCompactionTask(PreservedMarksSet *preserved_marks, ShenandoahHeapRegionSet **worker_slices) : - AbstractGangTask("Shenandoah Prepare For Compaction"), + WorkerTask("Shenandoah Prepare For Compaction"), _preserved_marks(preserved_marks), _heap(ShenandoahHeap::heap()), _worker_slices(worker_slices) { } @@ -757,14 +757,14 @@ public: } }; -class ShenandoahAdjustPointersTask : public AbstractGangTask { +class ShenandoahAdjustPointersTask : public WorkerTask { private: ShenandoahHeap* const _heap; ShenandoahRegionIterator _regions; public: ShenandoahAdjustPointersTask() : - AbstractGangTask("Shenandoah Adjust Pointers"), + WorkerTask("Shenandoah Adjust Pointers"), _heap(ShenandoahHeap::heap()) { } @@ -781,13 +781,13 @@ public: } }; -class ShenandoahAdjustRootPointersTask : public AbstractGangTask { +class ShenandoahAdjustRootPointersTask : public WorkerTask { private: ShenandoahRootAdjuster* _rp; PreservedMarksSet* _preserved_marks; public: ShenandoahAdjustRootPointersTask(ShenandoahRootAdjuster* rp, PreservedMarksSet* preserved_marks) : - AbstractGangTask("Shenandoah Adjust Root Pointers"), + WorkerTask("Shenandoah Adjust Root Pointers"), _rp(rp), _preserved_marks(preserved_marks) {} @@ -805,7 +805,7 @@ void ShenandoahFullGC::phase3_update_references() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - WorkGang* workers = heap->workers(); + WorkerThreads* workers = heap->workers(); uint nworkers = workers->active_workers(); { #if COMPILER2_OR_JVMCI @@ -834,7 +834,7 @@ public: void do_object(oop p) { assert(_heap->complete_marking_context()->is_marked(p), "must be marked"); - size_t size = (size_t)p->size(); + size_t size = p->size(); if (p->is_forwarded()) { HeapWord* compact_from = cast_from_oop(p); HeapWord* compact_to = cast_from_oop(p->forwardee()); @@ -845,14 +845,14 @@ public: } }; -class ShenandoahCompactObjectsTask : public AbstractGangTask { +class ShenandoahCompactObjectsTask : public WorkerTask { private: ShenandoahHeap* const _heap; ShenandoahHeapRegionSet** const _worker_slices; public: ShenandoahCompactObjectsTask(ShenandoahHeapRegionSet** worker_slices) : - AbstractGangTask("Shenandoah Compact Objects"), + WorkerTask("Shenandoah Compact Objects"), _heap(ShenandoahHeap::heap()), _worker_slices(worker_slices) { } @@ -995,13 +995,13 @@ void ShenandoahFullGC::compact_humongous_objects() { // cannot be iterated over using oop->size(). The only way to safely iterate over those is using // a valid marking bitmap and valid TAMS pointer. This class only resets marking // bitmaps for un-pinned regions, and later we only reset TAMS for unpinned regions. -class ShenandoahMCResetCompleteBitmapTask : public AbstractGangTask { +class ShenandoahMCResetCompleteBitmapTask : public WorkerTask { private: ShenandoahRegionIterator _regions; public: ShenandoahMCResetCompleteBitmapTask() : - AbstractGangTask("Shenandoah Reset Bitmap") { + WorkerTask("Shenandoah Reset Bitmap") { } void work(uint worker_id) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp index c0ab9a61f813c6e59aae24f8afb7012b21b1ab1a..fa1938802933b7f756d0541b278220618e65c1d5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "compiler/oopMap.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahGC.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" @@ -51,13 +51,13 @@ const char* ShenandoahGC::degen_point_to_string(ShenandoahDegenPoint point) { } } -class ShenandoahUpdateRootsTask : public AbstractGangTask { +class ShenandoahUpdateRootsTask : public WorkerTask { private: ShenandoahRootUpdater* _root_updater; bool _check_alive; public: ShenandoahUpdateRootsTask(ShenandoahRootUpdater* root_updater, bool check_alive) : - AbstractGangTask("Shenandoah Update Roots"), + WorkerTask("Shenandoah Update Roots"), _root_updater(root_updater), _check_alive(check_alive){ } @@ -95,7 +95,7 @@ void ShenandoahGC::update_roots(bool full_gc) { #endif ShenandoahHeap* const heap = ShenandoahHeap::heap(); - WorkGang* workers = heap->workers(); + WorkerThreads* workers = heap->workers(); uint nworkers = workers->active_workers(); ShenandoahRootUpdater root_updater(nworkers, p); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 34b87173290d3b441aa183d23b954f3a6887b179..7cd9a61b29f43bc226ee351aa9fabd98a47b3191 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -88,13 +88,13 @@ #include "utilities/events.hpp" #include "utilities/powerOfTwo.hpp" -class ShenandoahPretouchHeapTask : public AbstractGangTask { +class ShenandoahPretouchHeapTask : public WorkerTask { private: ShenandoahRegionIterator _regions; const size_t _page_size; public: ShenandoahPretouchHeapTask(size_t page_size) : - AbstractGangTask("Shenandoah Pretouch Heap"), + WorkerTask("Shenandoah Pretouch Heap"), _page_size(page_size) {} virtual void work(uint worker_id) { @@ -108,7 +108,7 @@ public: } }; -class ShenandoahPretouchBitmapTask : public AbstractGangTask { +class ShenandoahPretouchBitmapTask : public WorkerTask { private: ShenandoahRegionIterator _regions; char* _bitmap_base; @@ -116,7 +116,7 @@ private: const size_t _page_size; public: ShenandoahPretouchBitmapTask(char* bitmap_base, size_t bitmap_size, size_t page_size) : - AbstractGangTask("Shenandoah Pretouch Bitmap"), + WorkerTask("Shenandoah Pretouch Bitmap"), _bitmap_base(bitmap_base), _bitmap_size(bitmap_size), _page_size(page_size) {} @@ -495,7 +495,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : BarrierSet::set_barrier_set(new ShenandoahBarrierSet(this)); _max_workers = MAX2(_max_workers, 1U); - _workers = new ShenandoahWorkGang("Shenandoah GC Threads", _max_workers); + _workers = new ShenandoahWorkerThreads("Shenandoah GC Threads", _max_workers); if (_workers == NULL) { vm_exit_during_initialization("Failed necessary allocation."); } else { @@ -503,7 +503,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : } if (ParallelGCThreads > 1) { - _safepoint_workers = new ShenandoahWorkGang("Safepoint Cleanup Thread", + _safepoint_workers = new ShenandoahWorkerThreads("Safepoint Cleanup Thread", ParallelGCThreads); _safepoint_workers->initialize_workers(); } @@ -513,13 +513,13 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : #pragma warning( pop ) #endif -class ShenandoahResetBitmapTask : public AbstractGangTask { +class ShenandoahResetBitmapTask : public WorkerTask { private: ShenandoahRegionIterator _regions; public: ShenandoahResetBitmapTask() : - AbstractGangTask("Shenandoah Reset Bitmap") {} + WorkerTask("Shenandoah Reset Bitmap") {} void work(uint worker_id) { ShenandoahHeapRegion* region = _regions.next(); @@ -612,7 +612,7 @@ void ShenandoahHeap::post_initialize() { _workers->threads_do(&init_gclabs); // gclab can not be initialized early during VM startup, as it can not determinate its max_size. - // Now, we will let WorkGang to initialize gclab when new worker is created. + // Now, we will let WorkerThreads to initialize gclab when new worker is created. _workers->set_initialize_gclab(); if (_safepoint_workers != NULL) { _safepoint_workers->threads_do(&init_gclabs); @@ -953,7 +953,7 @@ public: } }; -class ShenandoahEvacuationTask : public AbstractGangTask { +class ShenandoahEvacuationTask : public WorkerTask { private: ShenandoahHeap* const _sh; ShenandoahCollectionSet* const _cs; @@ -962,7 +962,7 @@ public: ShenandoahEvacuationTask(ShenandoahHeap* sh, ShenandoahCollectionSet* cs, bool concurrent) : - AbstractGangTask("Shenandoah Evacuation"), + WorkerTask("Shenandoah Evacuation"), _sh(sh), _cs(cs), _concurrent(concurrent) @@ -1483,7 +1483,7 @@ void ShenandoahHeap::heap_region_iterate(ShenandoahHeapRegionClosure* blk) const } } -class ShenandoahParallelHeapRegionTask : public AbstractGangTask { +class ShenandoahParallelHeapRegionTask : public WorkerTask { private: ShenandoahHeap* const _heap; ShenandoahHeapRegionClosure* const _blk; @@ -1494,7 +1494,7 @@ private: public: ShenandoahParallelHeapRegionTask(ShenandoahHeapRegionClosure* blk) : - AbstractGangTask("Shenandoah Parallel Region Operation"), + WorkerTask("Shenandoah Parallel Region Operation"), _heap(ShenandoahHeap::heap()), _blk(blk), _index(0) {} void work(uint worker_id) { @@ -2010,13 +2010,13 @@ ShenandoahVerifier* ShenandoahHeap::verifier() { } template -class ShenandoahUpdateHeapRefsTask : public AbstractGangTask { +class ShenandoahUpdateHeapRefsTask : public WorkerTask { private: ShenandoahHeap* _heap; ShenandoahRegionIterator* _regions; public: ShenandoahUpdateHeapRefsTask(ShenandoahRegionIterator* regions) : - AbstractGangTask("Shenandoah Update References"), + WorkerTask("Shenandoah Update References"), _heap(ShenandoahHeap::heap()), _regions(regions) { } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 3eea61e16914cf625e3a1d5e5d236a07d544f083..79afeb37ab59ffacbcf9bf25276e71840794779e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -61,7 +61,7 @@ class ShenandoahMonitoringSupport; class ShenandoahPacer; class ShenandoahReferenceProcessor; class ShenandoahVerifier; -class ShenandoahWorkGang; +class ShenandoahWorkerThreads; class VMStructs; // Used for buffering per-region liveness data. @@ -207,15 +207,15 @@ public: // private: uint _max_workers; - ShenandoahWorkGang* _workers; - ShenandoahWorkGang* _safepoint_workers; + ShenandoahWorkerThreads* _workers; + ShenandoahWorkerThreads* _safepoint_workers; public: uint max_workers(); void assert_gc_workers(uint nworker) NOT_DEBUG_RETURN; - WorkGang* workers() const; - WorkGang* safepoint_workers(); + WorkerThreads* workers() const; + WorkerThreads* safepoint_workers(); void gc_threads_do(ThreadClosure* tcl) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index be00a01118515b3edce2ee934364d82a9f1b3636..1e5cddd49d81f5a6e533996688074d210729b0ad 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -64,11 +64,11 @@ inline bool ShenandoahHeap::has_forwarded_objects() const { return _gc_state.is_set(HAS_FORWARDED); } -inline WorkGang* ShenandoahHeap::workers() const { +inline WorkerThreads* ShenandoahHeap::workers() const { return _workers; } -inline WorkGang* ShenandoahHeap::safepoint_workers() { +inline WorkerThreads* ShenandoahHeap::safepoint_workers() { return _safepoint_workers; } @@ -513,7 +513,7 @@ inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, oop obj = cast_to_oop(cs); assert(oopDesc::is_oop(obj), "sanity"); assert(ctx->is_marked(obj), "object expected to be marked"); - int size = obj->size(); + size_t size = obj->size(); cl->do_object(obj); cs += size; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index a35d54259dd4c495d702dff87bc4968685df23f6..755e29ce57e53215930224c1d47b53cdf14b629e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -493,7 +493,7 @@ void ShenandoahNMethodTableSnapshot::parallel_blobs_do(CodeBlobClosure *f) { size_t max = (size_t)_limit; while (_claimed < max) { - size_t cur = Atomic::fetch_and_add(&_claimed, stride); + size_t cur = Atomic::fetch_and_add(&_claimed, stride, memory_order_relaxed); size_t start = cur; size_t end = MIN2(cur + stride, max); if (start >= max) break; @@ -520,7 +520,7 @@ void ShenandoahNMethodTableSnapshot::concurrent_nmethods_do(NMethodClosure* cl) ShenandoahNMethod** list = _list->list(); size_t max = (size_t)_limit; while (_claimed < max) { - size_t cur = Atomic::fetch_and_add(&_claimed, stride); + size_t cur = Atomic::fetch_and_add(&_claimed, stride, memory_order_relaxed); size_t start = cur; size_t end = MIN2(cur + stride, max); if (start >= max) break; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp index a42c8d0bbcdf1c6df7abe41f94376c43d6ab1cf6..faf5172bec0a5f04b0c52551a9c3c032f898cf67 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp @@ -67,7 +67,7 @@ public: _heap(heap), _last_time(os::elapsedTime()), _progress_history(new TruncatedSeq(5)), - _wait_monitor(new Monitor(Mutex::leaf, "_wait_monitor", Monitor::_safepoint_check_always, true)), + _wait_monitor(new Monitor(Mutex::safepoint-1, "ShenandoahWaitMonitor_lock", true)), _epoch(0), _tax_rate(1), _budget(0), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp index 8f4659c3eb2964e49783210359983719ac6a1bd9..2390c4470f467362f5ecaa13d03da982675789d0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.cpp @@ -35,7 +35,7 @@ ShenandoahClassUnloadingTask::ShenandoahClassUnloadingTask(ShenandoahPhaseTiming BoolObjectClosure* is_alive, uint num_workers, bool unloading_occurred) : - AbstractGangTask("Shenandoah Class Unloading"), + WorkerTask("Shenandoah Class Unloading"), _phase(phase), _unloading_occurred(unloading_occurred), _code_cache_task(num_workers, is_alive, unloading_occurred), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp index c4ca0afb9bb8ad0853c99f8e2b467049f3c8d488..9e0ebe548b916bb5c4361f1b6895716a03b1d543 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.hpp @@ -27,13 +27,13 @@ #include "gc/shared/parallelCleaning.hpp" #include "gc/shared/weakProcessor.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "memory/iterator.hpp" // Perform weak root cleaning at a pause template -class ShenandoahParallelWeakRootsCleaningTask : public AbstractGangTask { +class ShenandoahParallelWeakRootsCleaningTask : public WorkerTask { protected: ShenandoahPhaseTimings::Phase const _phase; WeakProcessor::Task _weak_processing_task; @@ -51,7 +51,7 @@ public: }; // Perform class unloading at a pause -class ShenandoahClassUnloadingTask : public AbstractGangTask { +class ShenandoahClassUnloadingTask : public WorkerTask { private: ShenandoahPhaseTimings::Phase const _phase; bool _unloading_occurred; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp index f88e9bb394cb1985f78606181277ccf0305d85ce..f764c7f97632ef1683d84b6771168fed393fc5dd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp @@ -38,7 +38,7 @@ ShenandoahParallelWeakRootsCleaningTask::ShenandoahParallelW IsAlive* is_alive, KeepAlive* keep_alive, uint num_workers) : - AbstractGangTask("Shenandoah Weak Root Cleaning"), + WorkerTask("Shenandoah Weak Root Cleaning"), _phase(phase), _weak_processing_task(num_workers), _is_alive(is_alive), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 2d185e2066adf4427778a081059ec92ab3f5d00a..72a87a9d3dee264403b17e373c9dfd08fbb32add 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -453,18 +453,18 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLoc void ShenandoahReferenceProcessor::work() { // Process discovered references uint max_workers = ShenandoahHeap::heap()->max_workers(); - uint worker_id = Atomic::add(&_iterate_discovered_list_id, 1U) - 1; + uint worker_id = Atomic::add(&_iterate_discovered_list_id, 1U, memory_order_relaxed) - 1; while (worker_id < max_workers) { if (UseCompressedOops) { process_references(_ref_proc_thread_locals[worker_id], worker_id); } else { process_references(_ref_proc_thread_locals[worker_id], worker_id); } - worker_id = Atomic::add(&_iterate_discovered_list_id, 1U) - 1; + worker_id = Atomic::add(&_iterate_discovered_list_id, 1U, memory_order_relaxed) - 1; } } -class ShenandoahReferenceProcessorTask : public AbstractGangTask { +class ShenandoahReferenceProcessorTask : public WorkerTask { private: bool const _concurrent; ShenandoahPhaseTimings::Phase const _phase; @@ -472,7 +472,7 @@ private: public: ShenandoahReferenceProcessorTask(ShenandoahPhaseTimings::Phase phase, bool concurrent, ShenandoahReferenceProcessor* reference_processor) : - AbstractGangTask("ShenandoahReferenceProcessorTask"), + WorkerTask("ShenandoahReferenceProcessorTask"), _concurrent(concurrent), _phase(phase), _reference_processor(reference_processor) { @@ -491,7 +491,7 @@ public: } }; -void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Phase phase, WorkGang* workers, bool concurrent) { +void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Phase phase, WorkerThreads* workers, bool concurrent) { Atomic::release_store_fence(&_iterate_discovered_list_id, 0U); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp index b1c26972c2163fb9a60ed75ec5b7a01688e04e9e..597b0a8ba5b9a53ccb22ce47bc84a98fa074139e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp @@ -33,7 +33,7 @@ #include "memory/allocation.hpp" class ShenandoahMarkRefsSuperClosure; -class WorkGang; +class WorkerThreads; static const size_t reference_type_count = REF_PHANTOM + 1; typedef size_t Counters[reference_type_count]; @@ -179,7 +179,7 @@ public: bool discover_reference(oop obj, ReferenceType type) override; - void process_references(ShenandoahPhaseTimings::Phase phase, WorkGang* workers, bool concurrent); + void process_references(ShenandoahPhaseTimings::Phase phase, WorkerThreads* workers, bool concurrent); const ReferenceProcessorStats& reference_process_stats() { return _stats; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index 09fea5b848779851357dcab69a73dbaca4139ca3..7e64c55647683b4944f0e185d6756f3d9ed8b0ec 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -27,7 +27,7 @@ #include "gc/shared/strongRootsScope.hpp" #include "gc/shared/taskTerminator.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahOopClosures.inline.hpp" @@ -60,7 +60,7 @@ void ShenandoahInitMarkRootsClosure::do_oop_work(T* p) { ShenandoahMark::mark_through_ref(p, _queue, _mark_context, false); } -class ShenandoahSTWMarkTask : public AbstractGangTask { +class ShenandoahSTWMarkTask : public WorkerTask { private: ShenandoahSTWMark* const _mark; @@ -70,7 +70,7 @@ public: }; ShenandoahSTWMarkTask::ShenandoahSTWMarkTask(ShenandoahSTWMark* mark) : - AbstractGangTask("Shenandoah STW mark"), + WorkerTask("Shenandoah STW mark"), _mark(mark) { } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index bc38a0936eb48f118c09820da41617dbf0429225..5ce731e6fccba9da70bed8282bfd4255a4fb19d1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -337,7 +337,7 @@ T* ParallelClaimableQueueSet::claim_next() { return NULL; } - jint index = Atomic::add(&_claimed_index, 1); + jint index = Atomic::add(&_claimed_index, 1, memory_order_relaxed); if (index <= size) { return GenericTaskQueueSet::queue((uint)index - 1); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 4748688b2da78a12faf092e64748300a5c6cc150..07f1dec2b17b281b095547fd3cac82f917fa0b6c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -421,7 +421,7 @@ public: } }; -class ShenandoahVerifierReachableTask : public AbstractGangTask { +class ShenandoahVerifierReachableTask : public WorkerTask { private: const char* _label; ShenandoahVerifier::VerifyOptions _options; @@ -435,7 +435,7 @@ public: ShenandoahLivenessData* ld, const char* label, ShenandoahVerifier::VerifyOptions options) : - AbstractGangTask("Shenandoah Verifier Reachable Objects"), + WorkerTask("Shenandoah Verifier Reachable Objects"), _label(label), _options(options), _heap(ShenandoahHeap::heap()), @@ -484,7 +484,7 @@ public: } }; -class ShenandoahVerifierMarkedRegionTask : public AbstractGangTask { +class ShenandoahVerifierMarkedRegionTask : public WorkerTask { private: const char* _label; ShenandoahVerifier::VerifyOptions _options; @@ -499,7 +499,7 @@ public: ShenandoahLivenessData* ld, const char* label, ShenandoahVerifier::VerifyOptions options) : - AbstractGangTask("Shenandoah Verifier Marked Objects"), + WorkerTask("Shenandoah Verifier Marked Objects"), _label(label), _options(options), _heap(ShenandoahHeap::heap()), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.cpp b/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.cpp index f41331d1d8240aa70bd775375466e5ac10cbc5a3..34202dac6574b8345ce61a4d6d081d0d21770714 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.cpp @@ -31,11 +31,11 @@ #include "logging/log.hpp" -ShenandoahWorkerScope::ShenandoahWorkerScope(WorkGang* workers, uint nworkers, const char* msg, bool check) : +ShenandoahWorkerScope::ShenandoahWorkerScope(WorkerThreads* workers, uint nworkers, const char* msg, bool check) : _workers(workers) { assert(msg != NULL, "Missing message"); - _n_workers = _workers->update_active_workers(nworkers); + _n_workers = _workers->set_active_workers(nworkers); assert(_n_workers <= nworkers, "Must be"); log_info(gc, task)("Using %u of %u workers for %s", @@ -51,10 +51,10 @@ ShenandoahWorkerScope::~ShenandoahWorkerScope() { "Active workers can not be changed within this scope"); } -ShenandoahPushWorkerScope::ShenandoahPushWorkerScope(WorkGang* workers, uint nworkers, bool check) : +ShenandoahPushWorkerScope::ShenandoahPushWorkerScope(WorkerThreads* workers, uint nworkers, bool check) : _old_workers(workers->active_workers()), _workers(workers) { - _n_workers = _workers->update_active_workers(nworkers); + _n_workers = _workers->set_active_workers(nworkers); assert(_n_workers <= nworkers, "Must be"); // bypass concurrent/parallel protocol check for non-regular paths, e.g. verifier, etc. @@ -67,12 +67,12 @@ ShenandoahPushWorkerScope::~ShenandoahPushWorkerScope() { assert(_workers->active_workers() == _n_workers, "Active workers can not be changed within this scope"); // Restore old worker value - uint nworkers = _workers->update_active_workers(_old_workers); + uint nworkers = _workers->set_active_workers(_old_workers); assert(nworkers == _old_workers, "Must be able to restore"); } -GangWorker* ShenandoahWorkGang::install_worker(uint which) { - GangWorker* worker = WorkGang::install_worker(which); +WorkerThread* ShenandoahWorkerThreads::create_worker(uint id) { + WorkerThread* worker = WorkerThreads::create_worker(id); ShenandoahThreadLocalData::create(worker); if (_initialize_gclab) { ShenandoahThreadLocalData::initialize_gclab(worker); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.hpp b/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.hpp index 5ccd87773a3b432287c150acb136b64b6609100d..f5bd3ea0e481b760a7a37ec93f3ce7829476ea0d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHWORKGROUP_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHWORKGROUP_HPP -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "gc/shenandoah/shenandoahTaskqueue.hpp" #include "memory/allocation.hpp" @@ -34,9 +34,9 @@ class ShenandoahObjToScanQueueSet; class ShenandoahWorkerScope : public StackObj { private: uint _n_workers; - WorkGang* _workers; + WorkerThreads* _workers; public: - ShenandoahWorkerScope(WorkGang* workers, uint nworkers, const char* msg, bool do_check = true); + ShenandoahWorkerScope(WorkerThreads* workers, uint nworkers, const char* msg, bool do_check = true); ~ShenandoahWorkerScope(); }; @@ -44,25 +44,25 @@ class ShenandoahPushWorkerScope : StackObj { protected: uint _n_workers; uint _old_workers; - WorkGang* _workers; + WorkerThreads* _workers; public: - ShenandoahPushWorkerScope(WorkGang* workers, uint nworkers, bool do_check = true); + ShenandoahPushWorkerScope(WorkerThreads* workers, uint nworkers, bool do_check = true); ~ShenandoahPushWorkerScope(); }; -class ShenandoahWorkGang : public WorkGang { +class ShenandoahWorkerThreads : public WorkerThreads { private: bool _initialize_gclab; public: - ShenandoahWorkGang(const char* name, + ShenandoahWorkerThreads(const char* name, uint workers) : - WorkGang(name, workers), _initialize_gclab(false) { + WorkerThreads(name, workers), _initialize_gclab(false) { } - // Create a GC worker and install it into the work gang. + // Create a GC worker. // We need to initialize gclab for dynamic allocated workers - GangWorker* install_worker(uint which); + WorkerThread* create_worker(uint id); void set_initialize_gclab() { assert(!_initialize_gclab, "Can only enable once"); _initialize_gclab = true; } }; diff --git a/src/hotspot/share/gc/z/zCollectedHeap.cpp b/src/hotspot/share/gc/z/zCollectedHeap.cpp index 0e31f859275a09e028f9318000fff6193c1ac854..e07ec3e80c775a43db7bcc9398d396b6604300db 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.cpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp @@ -136,7 +136,7 @@ HeapWord* ZCollectedHeap::allocate_new_tlab(size_t min_size, size_t requested_si return (HeapWord*)addr; } -oop ZCollectedHeap::array_allocate(Klass* klass, int size, int length, bool do_zero, TRAPS) { +oop ZCollectedHeap::array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS) { if (!do_zero) { return CollectedHeap::array_allocate(klass, size, length, false /* do_zero */, THREAD); } @@ -266,7 +266,7 @@ void ZCollectedHeap::verify_nmethod(nmethod* nm) { // Does nothing } -WorkGang* ZCollectedHeap::safepoint_workers() { +WorkerThreads* ZCollectedHeap::safepoint_workers() { return _runtime_workers.workers(); } diff --git a/src/hotspot/share/gc/z/zCollectedHeap.hpp b/src/hotspot/share/gc/z/zCollectedHeap.hpp index 91c78420995b2cd4818ee5e45cf5f99599beab12..236609361d02d0d9e8601f628e79668b4b1ffe94 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.hpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.hpp @@ -75,7 +75,7 @@ public: virtual uint32_t hash_oop(oop obj) const; - virtual oop array_allocate(Klass* klass, int size, int length, bool do_zero, TRAPS); + virtual oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS); virtual HeapWord* mem_allocate(size_t size, bool* gc_overhead_limit_was_exceeded); virtual MetaWord* satisfy_failed_metadata_allocation(ClassLoaderData* loader_data, size_t size, @@ -104,7 +104,7 @@ public: virtual void flush_nmethod(nmethod* nm); virtual void verify_nmethod(nmethod* nmethod); - virtual WorkGang* safepoint_workers(); + virtual WorkerThreads* safepoint_workers(); virtual void gc_threads_do(ThreadClosure* tc) const; diff --git a/src/hotspot/share/gc/z/zLiveMap.inline.hpp b/src/hotspot/share/gc/z/zLiveMap.inline.hpp index bf0fd808a8bbca29bdcfdff2e70273d4e6038da8..1d3052473c6cfb4223bc984a11e600ff526a12a8 100644 --- a/src/hotspot/share/gc/z/zLiveMap.inline.hpp +++ b/src/hotspot/share/gc/z/zLiveMap.inline.hpp @@ -98,9 +98,9 @@ inline BitMap::idx_t ZLiveMap::index_to_segment(BitMap::idx_t index) const { inline bool ZLiveMap::get(size_t index) const { BitMap::idx_t segment = index_to_segment(index); - return is_marked() && // Page is marked - is_segment_live(segment) && // Segment is marked - _bitmap.at(index); // Object is marked + return is_marked() && // Page is marked + is_segment_live(segment) && // Segment is marked + _bitmap.par_at(index, memory_order_relaxed); // Object is marked } inline bool ZLiveMap::set(size_t index, bool finalizable, bool& inc_live) { diff --git a/src/hotspot/share/gc/z/zMessagePort.inline.hpp b/src/hotspot/share/gc/z/zMessagePort.inline.hpp index fd036ebc983363136aa2400fd7ac94c061b39aab..e9e9ba539a2047ec73658f2a2867d9ed736cfbe5 100644 --- a/src/hotspot/share/gc/z/zMessagePort.inline.hpp +++ b/src/hotspot/share/gc/z/zMessagePort.inline.hpp @@ -66,9 +66,7 @@ public: template inline ZMessagePort::ZMessagePort() : - _monitor(Monitor::nosafepoint, - "ZMessagePort_lock", - Monitor::_safepoint_check_never), + _monitor(Monitor::nosafepoint, "ZMessagePort_lock"), _has_message(false), _seqnum(0), _queue() {} diff --git a/src/hotspot/share/gc/z/zMetronome.cpp b/src/hotspot/share/gc/z/zMetronome.cpp index 937533770651afe38461434f12e3b7a077db41e5..b8e1b69a5ab7f2c2d917e5b32c4cefb48ffaef1e 100644 --- a/src/hotspot/share/gc/z/zMetronome.cpp +++ b/src/hotspot/share/gc/z/zMetronome.cpp @@ -28,7 +28,7 @@ #include "utilities/ticks.hpp" ZMetronome::ZMetronome(uint64_t hz) : - _monitor(Monitor::nosafepoint, "ZMetronome_lock", Monitor::_safepoint_check_never), + _monitor(Monitor::nosafepoint, "ZMetronome_lock"), _interval_ms(MILLIUNITS / hz), _start_ms(0), _nticks(0), diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp index d492a1b66091f9e41db12d0d26cd76f3c9ccb7b2..71f510c2e81b2ae693615a7e557c908cdf465bbd 100644 --- a/src/hotspot/share/gc/z/zNMethod.cpp +++ b/src/hotspot/share/gc/z/zNMethod.cpp @@ -126,8 +126,10 @@ void ZNMethod::log_register(const nmethod* nm) { oop* const begin = nm->oops_begin(); oop* const end = nm->oops_end(); for (oop* p = begin; p < end; p++) { + const oop o = Atomic::load(p); // C1 PatchingStub may replace it concurrently. + const char* external_name = (o == nullptr) ? "N/A" : o->klass()->external_name(); log_oops.print(" Oop[" SIZE_FORMAT "] " PTR_FORMAT " (%s)", - (p - begin), p2i(*p), (*p)->klass()->external_name()); + (p - begin), p2i(o), external_name); } } diff --git a/src/hotspot/share/gc/z/zRuntimeWorkers.cpp b/src/hotspot/share/gc/z/zRuntimeWorkers.cpp index 59d3029fec51cdcca013f45b26e6b714cf6140ac..568b9b778ce90520d909adbf9270131cf29b5a78 100644 --- a/src/hotspot/share/gc/z/zRuntimeWorkers.cpp +++ b/src/hotspot/share/gc/z/zRuntimeWorkers.cpp @@ -30,7 +30,7 @@ #include "gc/z/zThread.hpp" #include "runtime/java.hpp" -class ZRuntimeWorkersInitializeTask : public AbstractGangTask { +class ZRuntimeWorkersInitializeTask : public WorkerTask { private: const uint _nworkers; uint _started; @@ -38,7 +38,7 @@ private: public: ZRuntimeWorkersInitializeTask(uint nworkers) : - AbstractGangTask("ZRuntimeWorkersInitializeTask"), + WorkerTask("ZRuntimeWorkersInitializeTask"), _nworkers(nworkers), _started(0), _lock() {} @@ -61,22 +61,22 @@ ZRuntimeWorkers::ZRuntimeWorkers() : _workers("RuntimeWorker", ParallelGCThreads) { - log_info_p(gc, init)("Runtime Workers: %u", _workers.total_workers()); + log_info_p(gc, init)("Runtime Workers: %u", _workers.max_workers()); // Initialize worker threads _workers.initialize_workers(); - _workers.update_active_workers(_workers.total_workers()); - if (_workers.active_workers() != _workers.total_workers()) { + _workers.set_active_workers(_workers.max_workers()); + if (_workers.active_workers() != _workers.max_workers()) { vm_exit_during_initialization("Failed to create ZRuntimeWorkers"); } // Execute task to reduce latency in early safepoints, // which otherwise would have to take on any warmup costs. - ZRuntimeWorkersInitializeTask task(_workers.total_workers()); + ZRuntimeWorkersInitializeTask task(_workers.max_workers()); _workers.run_task(&task); } -WorkGang* ZRuntimeWorkers::workers() { +WorkerThreads* ZRuntimeWorkers::workers() { return &_workers; } diff --git a/src/hotspot/share/gc/z/zRuntimeWorkers.hpp b/src/hotspot/share/gc/z/zRuntimeWorkers.hpp index 8ff19c7b699160685dd4f94a1f0f6ac7afcf8220..dede5b0b2d9bf38862a28634712558a7476570d5 100644 --- a/src/hotspot/share/gc/z/zRuntimeWorkers.hpp +++ b/src/hotspot/share/gc/z/zRuntimeWorkers.hpp @@ -24,18 +24,18 @@ #ifndef SHARE_GC_Z_ZRUNTIMEWORKERS_HPP #define SHARE_GC_Z_ZRUNTIMEWORKERS_HPP -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" class ThreadClosure; class ZRuntimeWorkers { private: - WorkGang _workers; + WorkerThreads _workers; public: ZRuntimeWorkers(); - WorkGang* workers(); + WorkerThreads* workers(); void threads_do(ThreadClosure* tc) const; }; diff --git a/src/hotspot/share/gc/z/zTask.cpp b/src/hotspot/share/gc/z/zTask.cpp index 37905ebc6df29f48ae374af2e986c12b5c851836..7a0503a4fb8a4019df7384da47e6ced618f998b0 100644 --- a/src/hotspot/share/gc/z/zTask.cpp +++ b/src/hotspot/share/gc/z/zTask.cpp @@ -25,23 +25,23 @@ #include "gc/z/zTask.hpp" #include "gc/z/zThread.hpp" -ZTask::GangTask::GangTask(ZTask* ztask, const char* name) : - AbstractGangTask(name), - _ztask(ztask) {} +ZTask::Task::Task(ZTask* task, const char* name) : + WorkerTask(name), + _task(task) {} -void ZTask::GangTask::work(uint worker_id) { +void ZTask::Task::work(uint worker_id) { ZThread::set_worker_id(worker_id); - _ztask->work(); + _task->work(); ZThread::clear_worker_id(); } ZTask::ZTask(const char* name) : - _gang_task(this, name) {} + _worker_task(this, name) {} const char* ZTask::name() const { - return _gang_task.name(); + return _worker_task.name(); } -AbstractGangTask* ZTask::gang_task() { - return &_gang_task; +WorkerTask* ZTask::worker_task() { + return &_worker_task; } diff --git a/src/hotspot/share/gc/z/zTask.hpp b/src/hotspot/share/gc/z/zTask.hpp index a4a116b7c2270c42751162013840995c8639672a..80836aedcac53fbe0282960e6356c3c16803dc24 100644 --- a/src/hotspot/share/gc/z/zTask.hpp +++ b/src/hotspot/share/gc/z/zTask.hpp @@ -24,28 +24,28 @@ #ifndef SHARE_GC_Z_ZTASK_HPP #define SHARE_GC_Z_ZTASK_HPP -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" class ZTask : public StackObj { private: - class GangTask : public AbstractGangTask { + class Task : public WorkerTask { private: - ZTask* const _ztask; + ZTask* const _task; public: - GangTask(ZTask* ztask, const char* name); + Task(ZTask* task, const char* name); virtual void work(uint worker_id); }; - GangTask _gang_task; + Task _worker_task; public: ZTask(const char* name); const char* name() const; - AbstractGangTask* gang_task(); + WorkerTask* worker_task(); virtual void work() = 0; }; diff --git a/src/hotspot/share/gc/z/zWorkers.cpp b/src/hotspot/share/gc/z/zWorkers.cpp index 782d91fd703ce531c8d7eb506779621c109e1f39..84f17f299fbb7d242bfdfa712bff78eb80248d27 100644 --- a/src/hotspot/share/gc/z/zWorkers.cpp +++ b/src/hotspot/share/gc/z/zWorkers.cpp @@ -31,7 +31,7 @@ #include "gc/z/zWorkers.hpp" #include "runtime/java.hpp" -class ZWorkersInitializeTask : public AbstractGangTask { +class ZWorkersInitializeTask : public WorkerTask { private: const uint _nworkers; uint _started; @@ -39,7 +39,7 @@ private: public: ZWorkersInitializeTask(uint nworkers) : - AbstractGangTask("ZWorkersInitializeTask"), + WorkerTask("ZWorkersInitializeTask"), _nworkers(nworkers), _started(0), _lock() {} @@ -66,20 +66,20 @@ ZWorkers::ZWorkers() : UseDynamicNumberOfGCThreads ? ConcGCThreads : MAX2(ConcGCThreads, ParallelGCThreads)) { if (UseDynamicNumberOfGCThreads) { - log_info_p(gc, init)("GC Workers: %u (dynamic)", _workers.total_workers()); + log_info_p(gc, init)("GC Workers: %u (dynamic)", _workers.max_workers()); } else { - log_info_p(gc, init)("GC Workers: %u/%u (static)", ConcGCThreads, _workers.total_workers()); + log_info_p(gc, init)("GC Workers: %u/%u (static)", ConcGCThreads, _workers.max_workers()); } // Initialize worker threads _workers.initialize_workers(); - _workers.update_active_workers(_workers.total_workers()); - if (_workers.active_workers() != _workers.total_workers()) { + _workers.set_active_workers(_workers.max_workers()); + if (_workers.active_workers() != _workers.max_workers()) { vm_exit_during_initialization("Failed to create ZWorkers"); } // Execute task to register threads as workers - ZWorkersInitializeTask task(_workers.total_workers()); + ZWorkersInitializeTask task(_workers.max_workers()); _workers.run_task(&task); } @@ -89,13 +89,13 @@ uint ZWorkers::active_workers() const { void ZWorkers::set_active_workers(uint nworkers) { log_info(gc, task)("Using %u workers", nworkers); - _workers.update_active_workers(nworkers); + _workers.set_active_workers(nworkers); } void ZWorkers::run(ZTask* task) { log_debug(gc, task)("Executing Task: %s, Active Workers: %u", task->name(), active_workers()); ZStatWorkers::at_start(); - _workers.run_task(task->gang_task()); + _workers.run_task(task->worker_task()); ZStatWorkers::at_end(); } @@ -104,12 +104,12 @@ void ZWorkers::run_all(ZTask* task) { const uint prev_active_workers = _workers.active_workers(); // Execute task using all workers - _workers.update_active_workers(_workers.total_workers()); + _workers.set_active_workers(_workers.max_workers()); log_debug(gc, task)("Executing Task: %s, Active Workers: %u", task->name(), active_workers()); - _workers.run_task(task->gang_task()); + _workers.run_task(task->worker_task()); // Restore number of active workers - _workers.update_active_workers(prev_active_workers); + _workers.set_active_workers(prev_active_workers); } void ZWorkers::threads_do(ThreadClosure* tc) const { diff --git a/src/hotspot/share/gc/z/zWorkers.hpp b/src/hotspot/share/gc/z/zWorkers.hpp index 9618f00b087c70d159a82efd6fb700f6a116ebd3..3ee14ece6bea0651d35b06cb35f1a9aa6db332ab 100644 --- a/src/hotspot/share/gc/z/zWorkers.hpp +++ b/src/hotspot/share/gc/z/zWorkers.hpp @@ -24,14 +24,14 @@ #ifndef SHARE_GC_Z_ZWORKERS_HPP #define SHARE_GC_Z_ZWORKERS_HPP -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" class ThreadClosure; class ZTask; class ZWorkers { private: - WorkGang _workers; + WorkerThreads _workers; public: ZWorkers(); diff --git a/src/hotspot/share/include/cds.h b/src/hotspot/share/include/cds.h index b12e25dad722eb6d3a3761408c70272c7f1e7ee4..69f9758ea284865918fd7a6a1c3266ef048a56d5 100644 --- a/src/hotspot/share/include/cds.h +++ b/src/hotspot/share/include/cds.h @@ -38,10 +38,9 @@ #define NUM_CDS_REGIONS 7 // this must be the same as MetaspaceShared::n_regions #define CDS_ARCHIVE_MAGIC 0xf00baba2 #define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8 -#define CURRENT_CDS_ARCHIVE_VERSION 11 -#define INVALID_CDS_ARCHIVE_VERSION -1 +#define CURRENT_CDS_ARCHIVE_VERSION 12 -struct CDSFileMapRegion { +typedef struct CDSFileMapRegion { int _crc; // CRC checksum of this region. int _read_only; // read only region? int _allow_exec; // executable code in this region? @@ -58,15 +57,37 @@ struct CDSFileMapRegion { size_t _oopmap_offset; // Bitmap for relocating embedded oops (offset from SharedBaseAddress). size_t _oopmap_size_in_bits; char* _mapped_base; // Actually mapped address (NULL if this region is not mapped). -}; +} CDSFileMapRegion; -struct CDSFileMapHeaderBase { - unsigned int _magic; // identify file type - int _crc; // header crc checksum - int _version; // must be CURRENT_CDS_ARCHIVE_VERSION - struct CDSFileMapRegion _space[NUM_CDS_REGIONS]; -}; +// This portion of the archive file header must remain unchanged for _version >= 12. +// This makes it possible to read important information from a CDS archive created by +// a different version of HotSpot, so that we can automatically regenerate the archive as necessary. +typedef struct GenericCDSFileMapHeader { + unsigned int _magic; // identification of file type + int _crc; // header crc checksum + int _version; // CURRENT_CDS_ARCHIVE_VERSION of the jdk that dumped the this archive + unsigned int _header_size; // total size of the header, in bytes + unsigned int _base_archive_path_offset; // offset where the base archive name is stored + // static archive: 0 + // dynamic archive: + // 0 for default base archive + // non-zero for non-default base archive + // (char*)this + _base_archive_path_offset + // points to a 0-terminated string for the base archive name + unsigned int _base_archive_name_size; // size of base archive name including ending '\0' + // static: 0 + // dynamic: + // 0 for default base archive + // non-zero for non-default base archive +} GenericCDSFileMapHeader; -typedef struct CDSFileMapHeaderBase CDSFileMapHeaderBase; +// This type is used by the Serviceability Agent to access the contents of +// a memory-mapped CDS archive. +typedef struct CDSFileMapHeaderBase { + // We cannot inherit from GenericCDSFileMapHeader as this type may be used + // by both C and C++ code. + GenericCDSFileMapHeader _generic_header; + CDSFileMapRegion _space[NUM_CDS_REGIONS]; +} CDSFileMapHeaderBase; #endif // SHARE_INCLUDE_CDS_H diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 7b80d72b562d3fedfd53239513341319231b7c87..00035a6807235ffca5418a3645fd5f40ed486828 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -753,6 +753,12 @@ JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused); JNIEXPORT jboolean JNICALL JVM_SupportsCX8(void); +/* + * java.lang.ref.Finalizer + */ +JNIEXPORT void JNICALL +JVM_ReportFinalizationComplete(JNIEnv *env, jobject finalizee); + /************************************************************************* PART 2: Support for the Verifier and Class File Format Checker ************************************************************************/ diff --git a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp index a02f30d9b5214a9c0b8219a850974c838b5c8403..e24cc6209388dda47f51fa65144133b3a5e55548 100644 --- a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp +++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp @@ -383,7 +383,7 @@ static void read_specialized_field(JavaValue* result, const Handle& h_oop, field } } -static bool find_field(InstanceKlass* ik, +static bool find_field(const InstanceKlass* ik, Symbol* name_symbol, Symbol* signature_symbol, fieldDescriptor* fd, @@ -395,29 +395,32 @@ static bool find_field(InstanceKlass* ik, return ik->find_local_field(name_symbol, signature_symbol, fd); } -static void lookup_field(JfrJavaArguments* args, InstanceKlass* klass, fieldDescriptor* fd, bool static_field) { +static void lookup_field(JfrJavaArguments* args, const InstanceKlass* ik, fieldDescriptor* fd, bool static_field) { assert(args != NULL, "invariant"); - assert(klass != NULL, "invariant"); - assert(klass->is_initialized(), "invariant"); + assert(ik != NULL, "invariant"); + assert(ik->is_initialized(), "invariant"); assert(fd != NULL, "invariant"); - find_field(klass, args->name(), args->signature(), fd, static_field, true); + find_field(ik, args->name(), args->signature(), fd, static_field, true); +} + +static void read_field(JfrJavaArguments* args, JavaValue* result, Thread* thread) { + const bool static_field = !args->has_receiver(); + fieldDescriptor fd; + const InstanceKlass* const ik = static_cast(args->klass()); + lookup_field(args, ik, &fd, static_field); + assert(fd.offset() > 0, "invariant"); + HandleMark hm(thread); + Handle h_oop(static_field ? Handle(thread, ik->java_mirror()) : Handle(thread, args->receiver())); + read_specialized_field(result, h_oop, &fd); } static void read_field(JfrJavaArguments* args, JavaValue* result, TRAPS) { assert(args != NULL, "invariant"); assert(result != NULL, "invariant"); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); - InstanceKlass* const klass = static_cast(args->klass()); klass->initialize(CHECK); - const bool static_field = !args->has_receiver(); - fieldDescriptor fd; - lookup_field(args, klass, &fd, static_field); - assert(fd.offset() > 0, "invariant"); - - HandleMark hm(THREAD); - Handle h_oop(static_field ? Handle(THREAD, klass->java_mirror()) : Handle(THREAD, args->receiver())); - read_specialized_field(result, h_oop, &fd); + read_field(args, result, static_cast(THREAD)); } static void write_field(JfrJavaArguments* args, JavaValue* result, TRAPS) { @@ -448,6 +451,11 @@ void JfrJavaSupport::get_field(JfrJavaArguments* args, TRAPS) { read_field(args, args->result(), THREAD); } +void JfrJavaSupport::get_field(JfrJavaArguments* args, Thread* thread) { + assert(args != NULL, "invariant"); + read_field(args, args->result(), thread); +} + void JfrJavaSupport::get_field_local_ref(JfrJavaArguments* args, TRAPS) { assert(args != NULL, "invariant"); DEBUG_ONLY(check_java_thread_in_vm(THREAD)); @@ -487,20 +495,18 @@ Klass* JfrJavaSupport::klass(const jobject handle) { return obj->klass(); } -static char* allocate_string(bool c_heap, int length, JavaThread* jt) { +static char* allocate_string(bool c_heap, int length, Thread* thread) { return c_heap ? NEW_C_HEAP_ARRAY(char, length, mtTracing) : - NEW_RESOURCE_ARRAY_IN_THREAD(jt, char, length); + NEW_RESOURCE_ARRAY_IN_THREAD(thread, char, length); } -const char* JfrJavaSupport::c_str(oop string, JavaThread* t, bool c_heap /* false */) { - DEBUG_ONLY(check_java_thread_in_vm(t)); +const char* JfrJavaSupport::c_str(oop string, Thread* thread, bool c_heap /* false */) { char* str = NULL; const typeArrayOop value = java_lang_String::value(string); if (value != NULL) { const int length = java_lang_String::utf8_length(string, value); - str = allocate_string(c_heap, length + 1, t); + str = allocate_string(c_heap, length + 1, thread); if (str == NULL) { - JfrJavaSupport::throw_out_of_memory_error("Unable to allocate native memory", t); return NULL; } java_lang_String::as_utf8_string(string, value, str, length + 1); @@ -508,9 +514,8 @@ const char* JfrJavaSupport::c_str(oop string, JavaThread* t, bool c_heap /* fals return str; } -const char* JfrJavaSupport::c_str(jstring string, JavaThread* t, bool c_heap /* false */) { - DEBUG_ONLY(check_java_thread_in_vm(t)); - return string != NULL ? c_str(resolve_non_null(string), t, c_heap) : NULL; +const char* JfrJavaSupport::c_str(jstring string, Thread* thread, bool c_heap /* false */) { + return string != NULL ? c_str(resolve_non_null(string), thread, c_heap) : NULL; } /* diff --git a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp index 6a2f200322ccf045cd665d8e21b324fd69d12117..ca97d90dd78a0ef52d7be251bf6ff6f8ce388d80 100644 --- a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp +++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp @@ -56,6 +56,7 @@ class JfrJavaSupport : public AllStatic { static void set_field(JfrJavaArguments* args, TRAPS); static void get_field(JfrJavaArguments* args, TRAPS); + static void get_field(JfrJavaArguments* args, Thread* thread); static void new_object(JfrJavaArguments* args, TRAPS); // global jni handle result @@ -75,8 +76,8 @@ class JfrJavaSupport : public AllStatic { // misc static Klass* klass(const jobject handle); - static const char* c_str(jstring string, JavaThread* jt, bool c_heap = false); - static const char* c_str(oop string, JavaThread* jt, bool c_heap = false); + static const char* c_str(jstring string, Thread* thread, bool c_heap = false); + static const char* c_str(oop string, Thread* thread, bool c_heap = false); // exceptions static void throw_illegal_state_exception(const char* message, TRAPS); diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp b/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp index 359de35d04af5a5ece47caaa6b4e028ff8229bbd..7616d615a45d21e66f6b31ee43f5c34008edf6d6 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ BitSet::BitSet() : _bitmap_fragments(32), _fragment_list(NULL), _last_fragment_bits(NULL), - _last_fragment_granule(0) { + _last_fragment_granule(UINTPTR_MAX) { } BitSet::~BitSet() { diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp index 011dce70da4f4541a66e9e1d6ddb3a420d297463..8ba9409df56406d6fd923efc23e5d6e6336a4e07 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,10 @@ #include "precompiled.hpp" #include "jfr/leakprofiler/chains/edgeStore.hpp" #include "jfr/leakprofiler/chains/edgeUtils.hpp" +#include "jfr/leakprofiler/sampling/objectSample.hpp" #include "jfr/leakprofiler/utilities/unifiedOopRef.inline.hpp" #include "oops/oop.inline.hpp" +#include "runtime/safepoint.hpp" StoredEdge::StoredEdge(const Edge* parent, UnifiedOopRef reference) : Edge(parent, reference), _gc_root_id(0), _skip_length(0) {} @@ -36,15 +38,6 @@ StoredEdge::StoredEdge(const StoredEdge& edge) : Edge(edge), _gc_root_id(edge._g traceid EdgeStore::_edge_id_counter = 0; -EdgeStore::EdgeStore() : _edges(NULL) { - _edges = new EdgeHashTable(this); -} - -EdgeStore::~EdgeStore() { - assert(_edges != NULL, "invariant"); - delete _edges; -} - bool EdgeStore::is_empty() const { return !_edges->has_entries(); } @@ -224,15 +217,80 @@ bool EdgeStore::put_edges(StoredEdge** previous, const Edge** current, size_t li return NULL == *current; } -// Install the immediate edge into the mark word of the leak candidate object +static GrowableArray* _leak_context_edges = nullptr; + +EdgeStore::EdgeStore() : _edges(new EdgeHashTable(this)) {} + +EdgeStore::~EdgeStore() { + assert(_edges != NULL, "invariant"); + delete _edges; + delete _leak_context_edges; + _leak_context_edges = nullptr; +} + +static int leak_context_edge_idx(const ObjectSample* sample) { + assert(sample != nullptr, "invariant"); + return static_cast(sample->object()->mark().value()) >> markWord::lock_bits; +} + +bool EdgeStore::has_leak_context(const ObjectSample* sample) const { + return leak_context_edge_idx(sample) != 0; +} + +const StoredEdge* EdgeStore::get(const ObjectSample* sample) const { + assert(sample != nullptr, "invariant"); + if (_leak_context_edges != nullptr) { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + const int idx = leak_context_edge_idx(sample); + if (idx > 0) { + return _leak_context_edges->at(idx); + } + } + return get(UnifiedOopRef::encode_in_native(sample->object_addr())); +} + +#ifdef ASSERT +// max_idx to ensure idx fit in lower 32-bits of markword together with lock bits. +static constexpr const int max_idx = right_n_bits(32 - markWord::lock_bits); + +static void store_idx_precondition(oop sample_object, int idx) { + assert(sample_object != NULL, "invariant"); + assert(sample_object->mark().is_marked(), "invariant"); + assert(idx > 0, "invariant"); + assert(idx <= max_idx, "invariant"); +} +#endif + +static void store_idx_in_markword(oop sample_object, int idx) { + DEBUG_ONLY(store_idx_precondition(sample_object, idx);) + const markWord idx_mark_word(sample_object->mark().value() | idx << markWord::lock_bits); + sample_object->set_mark(idx_mark_word); + assert(sample_object->mark().is_marked(), "must still be marked"); +} + +static const int initial_size = 64; + +static int save(const StoredEdge* edge) { + assert(edge != nullptr, "invariant"); + if (_leak_context_edges == nullptr) { + _leak_context_edges = new (ResourceObj::C_HEAP, mtTracing)GrowableArray(initial_size, mtTracing); + _leak_context_edges->append(nullptr); // next idx now at 1, for disambiguation in markword. + } + return _leak_context_edges->append(edge); +} + +// We associate the leak context edge with the leak candidate object by saving the +// edge in an array and storing the array idx (shifted) into the markword of the candidate object. +static void associate_with_candidate(const StoredEdge* leak_context_edge) { + assert(leak_context_edge != nullptr, "invariant"); + store_idx_in_markword(leak_context_edge->pointee(), save(leak_context_edge)); +} + StoredEdge* EdgeStore::associate_leak_context_with_candidate(const Edge* edge) { assert(edge != NULL, "invariant"); assert(!contains(edge->reference()), "invariant"); StoredEdge* const leak_context_edge = put(edge->reference()); - oop sample_object = edge->pointee(); - assert(sample_object != NULL, "invariant"); - assert(sample_object->mark().is_marked(), "invariant"); - sample_object->set_mark(markWord::from_pointer(leak_context_edge)); + associate_with_candidate(leak_context_edge); return leak_context_edge; } diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp index f948525a5b9808ae69f12cbd963df12e07d6c7cf..e920fd64ea99f60402983f29ba78fa017bc70680 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ #include "memory/allocation.hpp" typedef u8 traceid; +class ObjectSample; class StoredEdge : public Edge { private: @@ -79,6 +80,7 @@ class EdgeStore : public CHeapObj { void on_unlink(EdgeEntry* entry); StoredEdge* get(UnifiedOopRef reference) const; + const StoredEdge* get(const ObjectSample* sample) const; StoredEdge* put(UnifiedOopRef reference); traceid gc_root_id(const Edge* edge) const; @@ -90,6 +92,7 @@ class EdgeStore : public CHeapObj { void store_gc_root_id_in_leak_context_edge(StoredEdge* leak_context_edge, const Edge* root) const; StoredEdge* link_new_edge(StoredEdge** previous, const Edge** current); void link_with_existing_chain(const StoredEdge* current_stored, StoredEdge** previous, size_t previous_length); + bool has_leak_context(const ObjectSample* sample) const; template void iterate(T& functor) const { _edges->iterate_value(functor); } diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp index dff2e1442201495e75483f33db777faa497a7e4a..ad71690535167a4af3e6200b3f1f830e7856a0ad 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp @@ -35,10 +35,6 @@ #include "oops/oopsHierarchy.hpp" #include "runtime/handles.inline.hpp" -bool EdgeUtils::is_leak_edge(const Edge& edge) { - return (const Edge*)edge.pointee()->mark().to_pointer() == &edge; -} - static bool is_static_field(const oop ref_owner, const InstanceKlass* ik, int offset) { assert(ref_owner != NULL, "invariant"); assert(ik != NULL, "invariant"); @@ -52,9 +48,10 @@ static int field_offset(const Edge& edge, const oop ref_owner) { assert(ref_owner->is_instance(), "invariant"); UnifiedOopRef reference = edge.reference(); assert(!reference.is_null(), "invariant"); - const int offset = (int)(reference.addr() - cast_from_oop(ref_owner)); + const size_t offset = (reference.addr() - cast_from_oop(ref_owner)); assert(offset < ref_owner->size() * HeapWordSize, "invariant"); - return offset; + assert(offset <= size_t(INT_MAX), "invariant"); + return (int)offset; } const Symbol* EdgeUtils::field_name(const Edge& edge, jshort* modifiers) { diff --git a/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.hpp b/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.hpp index 36b447ff75aaf02b87328ab247cd2f663624c7c6..c9a169e6edb53cc6f8d3567927595fabea62495d 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.hpp @@ -36,7 +36,6 @@ class EdgeUtils : public AllStatic { static const size_t root_context = 100; static const size_t max_ref_chain_depth = leak_context + root_context; - static bool is_leak_edge(const Edge& edge); static const Edge* root(const Edge& edge); static const Edge* ancestor(const Edge& edge, size_t distance); diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/eventEmitter.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/eventEmitter.cpp index b2f9d36e159f4f9819c4ee6e932112c5de6ebc73..5ba92d7f641f1b0a8f33b1438d92a24c7652d7e0 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/eventEmitter.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/eventEmitter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, Datadog, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -121,7 +121,7 @@ void EventEmitter::link_sample_with_edge(const ObjectSample* sample, EdgeStore* assert(!sample->is_dead(), "invariant"); assert(edge_store != NULL, "invariant"); if (SafepointSynchronize::is_at_safepoint()) { - if (!sample->object()->mark().is_marked()) { + if (edge_store->has_leak_context(sample)) { // Associated with an edge (chain) already during heap traversal. return; } @@ -138,21 +138,12 @@ void EventEmitter::write_event(const ObjectSample* sample, EdgeStore* edge_store assert(edge_store != NULL, "invariant"); assert(_jfr_thread_local != NULL, "invariant"); - traceid gc_root_id = 0; - const Edge* edge = NULL; - if (SafepointSynchronize::is_at_safepoint()) { - if (!sample->object()->mark().is_marked()) { - edge = (const Edge*)(sample->object())->mark().to_pointer(); - } - } - if (edge == NULL) { - edge = edge_store->get(UnifiedOopRef::encode_in_native(sample->object_addr())); - } else { - gc_root_id = edge_store->gc_root_id(edge); - } + const StoredEdge* const edge = edge_store->get(sample); assert(edge != NULL, "invariant"); + assert(edge->pointee() == sample->object(), "invariant"); const traceid object_id = edge_store->get_id(edge); assert(object_id != 0, "invariant"); + const traceid gc_root_id = edge->gc_root_id(); Tickspan object_age = Ticks(_start_time.value()) - sample->allocation_time(); diff --git a/src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.cpp b/src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.cpp deleted file mode 100644 index 9de7d82f60d1a15075acf9e92045966c231c8246..0000000000000000000000000000000000000000 --- a/src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "classfile/classLoaderDataGraph.hpp" -#include "jfr/leakprofiler/utilities/saveRestore.hpp" -#include "oops/oop.inline.hpp" - -MarkWordContext::MarkWordContext() : _obj(NULL), _mark_word(markWord::zero()) {} - -MarkWordContext::MarkWordContext(const oop obj) : _obj(obj), _mark_word(obj->mark()) { - assert(_obj->mark() == _mark_word, "invariant"); - // now we will "poison" the mark word of the object - // to the intermediate monitor INFLATING state. - // This is an "impossible" state during a safepoint, - // hence we will use it to quickly identify objects - // during the reachability search from gc roots. - assert(markWord::zero() == markWord::INFLATING(), "invariant"); - _obj->set_mark(markWord::INFLATING()); - assert(markWord::zero() == obj->mark(), "invariant"); -} - -MarkWordContext::~MarkWordContext() { - if (_obj != NULL) { - _obj->set_mark(_mark_word); - assert(_obj->mark() == _mark_word, "invariant"); - } -} - -MarkWordContext::MarkWordContext(const MarkWordContext& rhs) : _obj(NULL), _mark_word(markWord::zero()) { - swap(const_cast(rhs)); -} - -void MarkWordContext::operator=(MarkWordContext rhs) { - swap(rhs); -} - -void MarkWordContext::swap(MarkWordContext& rhs) { - oop temp_obj = rhs._obj; - markWord temp_mark_word = rhs._mark_word; - rhs._obj = _obj; - rhs._mark_word = _mark_word; - _obj = temp_obj; - _mark_word = temp_mark_word; -} - -CLDClaimContext::CLDClaimContext() : _cld(NULL) {} - -CLDClaimContext::CLDClaimContext(ClassLoaderData* cld) : _cld(cld) { - assert(_cld->claimed(), "invariant"); - _cld->clear_claim(); -} - -CLDClaimContext::~CLDClaimContext() { - if (_cld != NULL) { - _cld->try_claim(ClassLoaderData::_claim_strong); - assert(_cld->claimed(), "invariant"); - } -} - -CLDClaimContext::CLDClaimContext(const CLDClaimContext& rhs) : _cld(NULL) { - swap(const_cast(rhs)); -} - -void CLDClaimContext::operator=(CLDClaimContext rhs) { - swap(rhs); -} - -void CLDClaimContext::swap(CLDClaimContext& rhs) { - ClassLoaderData* temp_cld = rhs._cld; - rhs._cld = _cld; - _cld = temp_cld; -} - -CLDClaimStateClosure::CLDClaimStateClosure() : CLDClosure(), _state() {} - -void CLDClaimStateClosure::do_cld(ClassLoaderData* cld) { - assert(cld != NULL, "invariant"); - if (cld->claimed()) { - _state.save(cld); - } -} - -SaveRestoreCLDClaimBits::SaveRestoreCLDClaimBits() : _claim_state_closure() { - // interferes with GC, so walk all oops that GC would. - ClassLoaderDataGraph::cld_do(&_claim_state_closure); -} - -SaveRestoreCLDClaimBits::~SaveRestoreCLDClaimBits() { - ClassLoaderDataGraph::clear_claimed_marks(); -} diff --git a/src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.hpp b/src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.hpp deleted file mode 100644 index 3e4013f86e1ab6e3fe26bfbdc70cfc0bde861de9..0000000000000000000000000000000000000000 --- a/src/hotspot/share/jfr/leakprofiler/utilities/saveRestore.hpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_JFR_LEAKPROFILER_UTILITIES_SAVERESTORE_HPP -#define SHARE_JFR_LEAKPROFILER_UTILITIES_SAVERESTORE_HPP - -#include "memory/allocation.hpp" -#include "memory/iterator.hpp" -#include "oops/markWord.hpp" -#include "utilities/growableArray.hpp" - -template -class SaveRestore { - private: - Impl _impl; - public: - SaveRestore() : _impl() { - _impl.setup(); - } - - void save(T const& value) { - _impl.save(value); - } - - ~SaveRestore() { - _impl.restore(); - } -}; - -template -class ContextStore { -private: - GrowableArray* _storage; -public: - ContextStore() : _storage(NULL) {} - - void setup() { - assert(_storage == NULL, "invariant"); - _storage = new GrowableArray(16); - } - - void save(T const& value) { - _storage->push(Context(value)); - } - - void restore() { - for (int i = 0; i < _storage->length(); ++i) { - _storage->at(i).~Context(); - } - } -}; - -/* -* This class will save the original mark oop of an object sample object. -* It will then install an "identifier" mark oop to be used for -* identification purposes in the search for reference chains. -* The destructor will restore the original mark oop. -*/ - -class MarkWordContext { - private: - oop _obj; - markWord _mark_word; - void swap(MarkWordContext& rhs); - public: - MarkWordContext(); - MarkWordContext(const oop obj); - MarkWordContext(const MarkWordContext& rhs); - void operator=(MarkWordContext rhs); - ~MarkWordContext(); -}; - -typedef SaveRestore > SaveRestoreMarkWords; - -class ClassLoaderData; - -class CLDClaimContext { - private: - ClassLoaderData* _cld; - void swap(CLDClaimContext& rhs); - public: - CLDClaimContext(); - CLDClaimContext(ClassLoaderData* cld); - CLDClaimContext(const CLDClaimContext& rhs); - void operator=(CLDClaimContext rhs); - ~CLDClaimContext(); -}; - -typedef SaveRestore > SaveRestoreCLDClaimState; - -class CLDClaimStateClosure : public CLDClosure { - private: - SaveRestoreCLDClaimState _state; - public: - CLDClaimStateClosure(); - void do_cld(ClassLoaderData* cld); -}; - -class SaveRestoreCLDClaimBits : public StackObj { - private: - CLDClaimStateClosure _claim_state_closure; - public: - SaveRestoreCLDClaimBits(); - ~SaveRestoreCLDClaimBits(); -}; - -#endif // SHARE_JFR_LEAKPROFILER_UTILITIES_SAVERESTORE_HPP diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 1a19b76331489ccf58c6a1b8b310692167a6460b..6a1f426785c9d692d132a89d0dc1d4e2e38f6aab 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -1081,6 +1081,13 @@ + + + + + + + diff --git a/src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.cpp b/src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f68d7b5de0fd57c9f87e2721d6f7e6a23e1fb64 --- /dev/null +++ b/src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "utilities/macros.hpp" +#if INCLUDE_MANAGEMENT +#include "classfile/classLoaderDataGraph.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/periodic/jfrFinalizerStatisticsEvent.hpp" +#include "jfr/support/jfrSymbolTable.hpp" +#include "jfr/utilities/jfrTime.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/thread.inline.hpp" +#include "services/finalizerService.hpp" + +static void send_event(const FinalizerEntry* fe, const InstanceKlass* ik, const JfrTicks& timestamp, Thread* thread) { + assert(ik != NULL, "invariant"); + assert(ik->has_finalizer(), "invariant"); + assert(thread != NULL, "invariant"); + const char* const url = fe != nullptr ? fe->codesource() : nullptr; + const traceid url_symbol_id = url != NULL ? JfrSymbolTable::add(url) : 0; + EventFinalizerStatistics event(UNTIMED); + event.set_endtime(timestamp); + event.set_finalizableClass(ik); + event.set_codeSource(url_symbol_id); + if (fe == NULL) { + event.set_objects(0); + event.set_totalFinalizersRun(0); + } else { + assert(fe->klass() == ik, "invariant"); + event.set_objects(fe->objects_on_heap()); + event.set_totalFinalizersRun(fe->total_finalizers_run()); + } + event.commit(); +} + +void JfrFinalizerStatisticsEvent::send_unload_event(const InstanceKlass* ik) { + Thread* const thread = Thread::current(); + ResourceMark rm(thread); + send_event(FinalizerService::lookup(ik, thread), ik, JfrTicks::now(), thread); +} + +// Finalizer events generated by the periodic task will all have the same timestamp. + +class FinalizerStatisticsEventClosure : public FinalizerEntryClosure { + private: + Thread* _thread; + const JfrTicks _timestamp; + public: + FinalizerStatisticsEventClosure(Thread* thread) : _thread(thread), _timestamp(JfrTicks::now()) {} + virtual bool do_entry(const FinalizerEntry* fe) { + assert(fe != NULL, "invariant"); + send_event(fe, fe->klass(), _timestamp, _thread); + return true; + } +}; + +void JfrFinalizerStatisticsEvent::generate_events() { + Thread* const thread = Thread::current(); + ResourceMark rm(thread); + FinalizerStatisticsEventClosure fsec(thread); + MutexLocker lock(ClassLoaderDataGraph_lock); // To prevent entries from being removed by class unloading. + FinalizerService::do_entries(&fsec, thread); +} + +#endif // INCLUDE_MANAGEMENT diff --git a/src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.hpp b/src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fe739495718d43c682771a7fa934dbb69ba9d90b --- /dev/null +++ b/src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_JFR_PERIODIC_JFRFINALIZERSTATISTICSEVENT_HPP +#define SHARE_JFR_PERIODIC_JFRFINALIZERSTATISTICSEVENT_HPP + +#include "memory/allocation.hpp" + +class InstanceKlass; + +class JfrFinalizerStatisticsEvent : AllStatic { + public: + static void send_unload_event(const InstanceKlass* ik) NOT_MANAGEMENT_RETURN; + static void generate_events() NOT_MANAGEMENT_RETURN; +}; + +#endif // SHARE_JFR_PERIODIC_JFRFINALIZERSTATISTICSEVENT_HPP diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp index 905f1e53ac0a99ec5d3e7d6abdf9e43da1c56a85..5b0ce8e23283da72e0067657d517cad660a2cf7a 100644 --- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp +++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp @@ -37,6 +37,7 @@ #include "gc/shared/gcVMOperations.hpp" #include "gc/shared/objectCountEventSender.hpp" #include "jfr/jfrEvents.hpp" +#include "jfr/periodic/jfrFinalizerStatisticsEvent.hpp" #include "jfr/periodic/jfrModuleEvent.hpp" #include "jfr/periodic/jfrOSInterface.hpp" #include "jfr/periodic/jfrThreadCPULoadEvent.hpp" @@ -472,10 +473,14 @@ TRACE_REQUEST_FUNC(JavaThreadStatistics) { } TRACE_REQUEST_FUNC(ClassLoadingStatistics) { +#if INCLUDE_MANAGEMENT EventClassLoadingStatistics event; event.set_loadedClassCount(ClassLoadingService::loaded_class_count()); event.set_unloadedClassCount(ClassLoadingService::unloaded_class_count()); event.commit(); +#else + log_debug(jfr, system)("Unable to generate requestable event ClassLoadingStatistics. The required jvm feature 'management' is missing."); +#endif } class JfrClassLoaderStatsClosure : public ClassLoaderStatsClosure { @@ -635,7 +640,6 @@ TRACE_REQUEST_FUNC(CodeSweeperConfiguration) { event.commit(); } - TRACE_REQUEST_FUNC(ShenandoahHeapRegionInformation) { #if INCLUDE_SHENANDOAHGC if (UseShenandoahGC) { @@ -644,3 +648,11 @@ TRACE_REQUEST_FUNC(ShenandoahHeapRegionInformation) { } #endif } + +TRACE_REQUEST_FUNC(FinalizerStatistics) { +#if INCLUDE_MANAGEMENT + JfrFinalizerStatisticsEvent::generate_events(); +#else + log_debug(jfr, system)("Unable to generate requestable event FinalizerStatistics. The required jvm feature 'management' is missing."); +#endif +} diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 0d338187775839cda3f604bb355e7d6a7496ce9b..fb593adb8a80734812341191760efd5e9fd91d33 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -55,8 +55,8 @@ typedef const ModuleEntry* ModPtr; typedef const ClassLoaderData* CldPtr; typedef const Method* MethodPtr; typedef const Symbol* SymbolPtr; -typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr; -typedef const JfrSymbolId::CStringEntry* CStringEntryPtr; +typedef const JfrSymbolTable::SymbolEntry* SymbolEntryPtr; +typedef const JfrSymbolTable::StringEntry* StringEntryPtr; static JfrCheckpointWriter* _writer = NULL; static JfrCheckpointWriter* _leakp_writer = NULL; @@ -64,18 +64,7 @@ static JfrArtifactSet* _artifacts = NULL; static JfrArtifactClosure* _subsystem_callback = NULL; static bool _class_unload = false; static bool _flushpoint = false; -static bool _clear_artifacts = false; - -// incremented on each rotation -static u8 checkpoint_id = 1; - -// creates a unique id by combining a checkpoint relative symbol id (2^24) -// with the current checkpoint id (2^40) -#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id))) - -static traceid create_symbol_id(traceid artifact_id) { - return artifact_id != 0 ? CREATE_SYMBOL_ID(artifact_id) : 0; -} +static bool _initial_type_set = true; static bool current_epoch() { return _class_unload || _flushpoint; @@ -86,7 +75,7 @@ static bool previous_epoch() { } static bool is_initial_typeset_for_chunk() { - return _clear_artifacts && !_class_unload; + return _initial_type_set && !_class_unload; } static bool is_complete() { @@ -94,15 +83,15 @@ static bool is_complete() { } static traceid mark_symbol(KlassPtr klass, bool leakp) { - return klass != NULL ? create_symbol_id(_artifacts->mark(klass, leakp)) : 0; + return klass != NULL ? _artifacts->mark(klass, leakp) : 0; } static traceid mark_symbol(Symbol* symbol, bool leakp) { - return symbol != NULL ? create_symbol_id(_artifacts->mark(symbol, leakp)) : 0; + return symbol != NULL ? _artifacts->mark(symbol, leakp) : 0; } static traceid get_bootstrap_name(bool leakp) { - return create_symbol_id(_artifacts->bootstrap_name(leakp)); + return _artifacts->bootstrap_name(leakp); } static const char* primitive_name(KlassPtr type_array_klass) { @@ -924,14 +913,14 @@ static void write_methods() { } template <> -void set_serialized(SymbolEntryPtr ptr) { +void set_serialized(SymbolEntryPtr ptr) { assert(ptr != NULL, "invariant"); ptr->set_serialized(); assert(ptr->is_serialized(), "invariant"); } template <> -void set_serialized(CStringEntryPtr ptr) { +void set_serialized(StringEntryPtr ptr) { assert(ptr != NULL, "invariant"); ptr->set_serialized(); assert(ptr->is_serialized(), "invariant"); @@ -941,7 +930,7 @@ static int write_symbol(JfrCheckpointWriter* writer, SymbolEntryPtr entry, bool assert(writer != NULL, "invariant"); assert(entry != NULL, "invariant"); ResourceMark rm; - writer->write(create_symbol_id(entry->id())); + writer->write(entry->id()); writer->write(entry->value()->as_C_string()); return 1; } @@ -959,42 +948,42 @@ int write__symbol__leakp(JfrCheckpointWriter* writer, const void* e) { return write_symbol(writer, entry, true); } -static int write_cstring(JfrCheckpointWriter* writer, CStringEntryPtr entry, bool leakp) { +static int write_string(JfrCheckpointWriter* writer, StringEntryPtr entry, bool leakp) { assert(writer != NULL, "invariant"); assert(entry != NULL, "invariant"); - writer->write(create_symbol_id(entry->id())); + writer->write(entry->id()); writer->write(entry->value()); return 1; } -int write__cstring(JfrCheckpointWriter* writer, const void* e) { +int write__string(JfrCheckpointWriter* writer, const void* e) { assert(e != NULL, "invariant"); - CStringEntryPtr entry = (CStringEntryPtr)e; + StringEntryPtr entry = (StringEntryPtr)e; set_serialized(entry); - return write_cstring(writer, entry, false); + return write_string(writer, entry, false); } -int write__cstring__leakp(JfrCheckpointWriter* writer, const void* e) { +int write__string__leakp(JfrCheckpointWriter* writer, const void* e) { assert(e != NULL, "invariant"); - CStringEntryPtr entry = (CStringEntryPtr)e; - return write_cstring(writer, entry, true); + StringEntryPtr entry = (StringEntryPtr)e; + return write_string(writer, entry, true); } typedef SymbolPredicate SymPredicate; typedef JfrPredicatedTypeWriterImplHost SymbolEntryWriterImpl; typedef JfrTypeWriterHost SymbolEntryWriter; -typedef SymbolPredicate CStringPredicate; -typedef JfrPredicatedTypeWriterImplHost CStringEntryWriterImpl; -typedef JfrTypeWriterHost CStringEntryWriter; +typedef SymbolPredicate StringPredicate; +typedef JfrPredicatedTypeWriterImplHost StringEntryWriterImpl; +typedef JfrTypeWriterHost StringEntryWriter; typedef SymbolPredicate LeakSymPredicate; typedef JfrPredicatedTypeWriterImplHost LeakSymbolEntryWriterImpl; typedef JfrTypeWriterHost LeakSymbolEntryWriter; typedef CompositeFunctor CompositeSymbolWriter; -typedef SymbolPredicate LeakCStringPredicate; -typedef JfrPredicatedTypeWriterImplHost LeakCStringEntryWriterImpl; -typedef JfrTypeWriterHost LeakCStringEntryWriter; -typedef CompositeFunctor CompositeCStringWriter; +typedef SymbolPredicate LeakStringPredicate; +typedef JfrPredicatedTypeWriterImplHost LeakStringEntryWriterImpl; +typedef JfrTypeWriterHost LeakStringEntryWriter; +typedef CompositeFunctor CompositeStringWriter; static void write_symbols_with_leakp() { assert(_leakp_writer != NULL, "invariant"); @@ -1002,12 +991,12 @@ static void write_symbols_with_leakp() { LeakSymbolEntryWriter lsw(_leakp_writer, _class_unload); CompositeSymbolWriter csw(&lsw, &sw); _artifacts->iterate_symbols(csw); - CStringEntryWriter ccsw(_writer, _class_unload, true); // skip header - LeakCStringEntryWriter lccsw(_leakp_writer, _class_unload, true); // skip header - CompositeCStringWriter cccsw(&lccsw, &ccsw); - _artifacts->iterate_cstrings(cccsw); - sw.add(ccsw.count()); - lsw.add(lccsw.count()); + StringEntryWriter sew(_writer, _class_unload, true); // skip header + LeakStringEntryWriter lsew(_leakp_writer, _class_unload, true); // skip header + CompositeStringWriter csew(&lsew, &sew); + _artifacts->iterate_strings(csew); + sw.add(sew.count()); + lsw.add(lsew.count()); _artifacts->tally(sw); } @@ -1019,9 +1008,9 @@ static void write_symbols() { } SymbolEntryWriter sw(_writer, _class_unload); _artifacts->iterate_symbols(sw); - CStringEntryWriter csw(_writer, _class_unload, true); // skip header - _artifacts->iterate_cstrings(csw); - sw.add(csw.count()); + StringEntryWriter sew(_writer, _class_unload, true); // skip header + _artifacts->iterate_strings(sew); + sw.add(sew.count()); _artifacts->tally(sw); } @@ -1040,10 +1029,10 @@ static size_t teardown() { if (previous_epoch()) { clear_klasses_and_methods(); JfrKlassUnloading::clear(); - _clear_artifacts = true; - ++checkpoint_id; + _artifacts->increment_checkpoint_id(); + _initial_type_set = true; } else { - _clear_artifacts = false; + _initial_type_set = false; } return total_count; } @@ -1056,7 +1045,7 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer if (_artifacts == NULL) { _artifacts = new JfrArtifactSet(class_unload); } else { - _artifacts->initialize(class_unload, _clear_artifacts); + _artifacts->initialize(class_unload); } if (!_class_unload) { JfrKlassUnloading::sort(previous_epoch()); @@ -1091,7 +1080,9 @@ size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* l void JfrTypeSet::clear() { ResourceMark rm; JfrKlassUnloading::clear(); - _clear_artifacts = true; + if (_artifacts != NULL) { + _artifacts->clear(); + } setup(NULL, NULL, false, false); register_klasses(); clear_packages(); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp index 5eb70fb23d2df153c6014db01b6216652a9a970d..19a9fa7516b564a938fa8aaf65dbfe4de8428a44 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp @@ -30,225 +30,7 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" -static JfrSymbolId::CStringEntry* bootstrap = NULL; - -JfrSymbolId::JfrSymbolId() : - _sym_table(new SymbolTable(this)), - _cstring_table(new CStringTable(this)), - _sym_list(NULL), - _cstring_list(NULL), - _sym_query(NULL), - _cstring_query(NULL), - _symbol_id_counter(1), - _class_unload(false) { - assert(_sym_table != NULL, "invariant"); - assert(_cstring_table != NULL, "invariant"); - bootstrap = new CStringEntry(0, (const char*)&BOOTSTRAP_LOADER_NAME); - assert(bootstrap != NULL, "invariant"); - bootstrap->set_id(1); - _cstring_list = bootstrap; -} - -JfrSymbolId::~JfrSymbolId() { - clear(); - delete _sym_table; - delete _cstring_table; - delete bootstrap; -} - -void JfrSymbolId::clear() { - assert(_sym_table != NULL, "invariant"); - if (_sym_table->has_entries()) { - _sym_table->clear_entries(); - } - assert(!_sym_table->has_entries(), "invariant"); - - assert(_cstring_table != NULL, "invariant"); - if (_cstring_table->has_entries()) { - _cstring_table->clear_entries(); - } - assert(!_cstring_table->has_entries(), "invariant"); - - _sym_list = NULL; - _symbol_id_counter = 1; - - _sym_query = NULL; - _cstring_query = NULL; - - assert(bootstrap != NULL, "invariant"); - bootstrap->reset(); - _cstring_list = bootstrap; -} - -void JfrSymbolId::set_class_unload(bool class_unload) { - _class_unload = class_unload; -} - -void JfrSymbolId::on_link(const SymbolEntry* entry) { - assert(entry != NULL, "invariant"); - const_cast(entry->literal())->increment_refcount(); - assert(entry->id() == 0, "invariant"); - entry->set_id(++_symbol_id_counter); - entry->set_list_next(_sym_list); - _sym_list = entry; -} - -bool JfrSymbolId::on_equals(uintptr_t hash, const SymbolEntry* entry) { - assert(entry != NULL, "invariant"); - assert(entry->hash() == hash, "invariant"); - assert(_sym_query != NULL, "invariant"); - return _sym_query == entry->literal(); -} - -void JfrSymbolId::on_unlink(const SymbolEntry* entry) { - assert(entry != NULL, "invariant"); - const_cast(entry->literal())->decrement_refcount(); -} - -static const char* resource_to_cstring(const char* resource_str) { - assert(resource_str != NULL, "invariant"); - const size_t length = strlen(resource_str); - char* const c_string = JfrCHeapObj::new_array(length + 1); - assert(c_string != NULL, "invariant"); - strncpy(c_string, resource_str, length + 1); - return c_string; -} - -void JfrSymbolId::on_link(const CStringEntry* entry) { - assert(entry != NULL, "invariant"); - assert(entry->id() == 0, "invariant"); - entry->set_id(++_symbol_id_counter); - const_cast(entry)->set_literal(resource_to_cstring(entry->literal())); - entry->set_list_next(_cstring_list); - _cstring_list = entry; -} - -static bool string_compare(const char* query, const char* candidate) { - assert(query != NULL, "invariant"); - assert(candidate != NULL, "invariant"); - const size_t length = strlen(query); - return strncmp(query, candidate, length) == 0; -} - -bool JfrSymbolId::on_equals(uintptr_t hash, const CStringEntry* entry) { - assert(entry != NULL, "invariant"); - assert(entry->hash() == hash, "invariant"); - assert(_cstring_query != NULL, "invariant"); - return string_compare(_cstring_query, entry->literal()); -} - -void JfrSymbolId::on_unlink(const CStringEntry* entry) { - assert(entry != NULL, "invariant"); - JfrCHeapObj::free(const_cast(entry->literal()), strlen(entry->literal() + 1)); -} - -traceid JfrSymbolId::bootstrap_name(bool leakp) { - assert(bootstrap != NULL, "invariant"); - if (leakp) { - bootstrap->set_leakp(); - } - return 1; -} - -traceid JfrSymbolId::mark(const Symbol* symbol, bool leakp) { - assert(symbol != NULL, "invariant"); - return mark((uintptr_t)symbol->identity_hash(), symbol, leakp); -} - -traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) { - assert(data != NULL, "invariant"); - assert(_sym_table != NULL, "invariant"); - _sym_query = data; - const SymbolEntry& entry = _sym_table->lookup_put(hash, data); - if (_class_unload) { - entry.set_unloading(); - } - if (leakp) { - entry.set_leakp(); - } - return entry.id(); -} - -traceid JfrSymbolId::mark(uintptr_t hash, const char* str, bool leakp) { - assert(str != NULL, "invariant"); - assert(_cstring_table != NULL, "invariant"); - _cstring_query = str; - const CStringEntry& entry = _cstring_table->lookup_put(hash, str); - if (_class_unload) { - entry.set_unloading(); - } - if (leakp) { - entry.set_leakp(); - } - return entry.id(); -} - -/* -* hidden classes symbol is the external name + -* the address of its InstanceKlass slash appended: -* java.lang.invoke.LambdaForm$BMH/22626602 -* -* caller needs ResourceMark -*/ - -uintptr_t JfrSymbolId::hidden_klass_name_hash(const InstanceKlass* ik) { - assert(ik != NULL, "invariant"); - assert(ik->is_hidden(), "invariant"); - const oop mirror = ik->java_mirror_no_keepalive(); - assert(mirror != NULL, "invariant"); - return (uintptr_t)mirror->identity_hash(); -} - -static const char* create_hidden_klass_symbol(const InstanceKlass* ik, uintptr_t hash) { - assert(ik != NULL, "invariant"); - assert(ik->is_hidden(), "invariant"); - assert(hash != 0, "invariant"); - char* hidden_symbol = NULL; - const oop mirror = ik->java_mirror_no_keepalive(); - assert(mirror != NULL, "invariant"); - char hash_buf[40]; - sprintf(hash_buf, "/" UINTX_FORMAT, hash); - const size_t hash_len = strlen(hash_buf); - const size_t result_len = ik->name()->utf8_length(); - hidden_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1); - ik->name()->as_klass_external_name(hidden_symbol, (int)result_len + 1); - assert(strlen(hidden_symbol) == result_len, "invariant"); - strcpy(hidden_symbol + result_len, hash_buf); - assert(strlen(hidden_symbol) == result_len + hash_len, "invariant"); - return hidden_symbol; -} - -bool JfrSymbolId::is_hidden_klass(const Klass* k) { - assert(k != NULL, "invariant"); - return k->is_instance_klass() && ((const InstanceKlass*)k)->is_hidden(); -} - -traceid JfrSymbolId::mark_hidden_klass_name(const InstanceKlass* ik, bool leakp) { - assert(ik != NULL, "invariant"); - assert(ik->is_hidden(), "invariant"); - const uintptr_t hash = hidden_klass_name_hash(ik); - const char* const hidden_symbol = create_hidden_klass_symbol(ik, hash); - return mark(hash, hidden_symbol, leakp); -} - -traceid JfrSymbolId::mark(const Klass* k, bool leakp) { - assert(k != NULL, "invariant"); - traceid symbol_id = 0; - if (is_hidden_klass(k)) { - assert(k->is_instance_klass(), "invariant"); - symbol_id = mark_hidden_klass_name((const InstanceKlass*)k, leakp); - } - if (0 == symbol_id) { - Symbol* const sym = k->name(); - if (sym != NULL) { - symbol_id = mark(sym, leakp); - } - } - assert(symbol_id > 0, "a symbol handler must mark the symbol for writing"); - return symbol_id; -} - -JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId()), +JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_table(NULL), _klass_list(NULL), _total_count(0) { initialize(class_unload); @@ -258,47 +40,53 @@ JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId() static const size_t initial_klass_list_size = 256; const int initial_klass_loader_set_size = 64; -void JfrArtifactSet::initialize(bool class_unload, bool clear /* false */) { - assert(_symbol_id != NULL, "invariant"); - if (clear) { - _symbol_id->clear(); +void JfrArtifactSet::initialize(bool class_unload) { + if (_symbol_table == NULL) { + _symbol_table = JfrSymbolTable::create(); + assert(_symbol_table != NULL, "invariant"); } - _symbol_id->set_class_unload(class_unload); + assert(_symbol_table != NULL, "invariant"); + _symbol_table->set_class_unload(class_unload); _total_count = 0; // resource allocation _klass_list = new GrowableArray(initial_klass_list_size); _klass_loader_set = new GrowableArray(initial_klass_loader_set_size); } +void JfrArtifactSet::clear() { + if (_symbol_table != NULL) { + _symbol_table->clear(); + } +} + JfrArtifactSet::~JfrArtifactSet() { - _symbol_id->clear(); - delete _symbol_id; + delete _symbol_table; // _klass_list and _klass_loader_list will be cleared by a ResourceMark } traceid JfrArtifactSet::bootstrap_name(bool leakp) { - return _symbol_id->bootstrap_name(leakp); + return _symbol_table->bootstrap_name(leakp); } traceid JfrArtifactSet::mark_hidden_klass_name(const Klass* klass, bool leakp) { assert(klass->is_instance_klass(), "invariant"); - return _symbol_id->mark_hidden_klass_name((const InstanceKlass*)klass, leakp); + return _symbol_table->mark_hidden_klass_name((const InstanceKlass*)klass, leakp); } traceid JfrArtifactSet::mark(uintptr_t hash, const Symbol* sym, bool leakp) { - return _symbol_id->mark(hash, sym, leakp); + return _symbol_table->mark(hash, sym, leakp); } traceid JfrArtifactSet::mark(const Klass* klass, bool leakp) { - return _symbol_id->mark(klass, leakp); + return _symbol_table->mark(klass, leakp); } traceid JfrArtifactSet::mark(const Symbol* symbol, bool leakp) { - return _symbol_id->mark(symbol, leakp); + return _symbol_table->mark(symbol, leakp); } traceid JfrArtifactSet::mark(uintptr_t hash, const char* const str, bool leakp) { - return _symbol_id->mark(hash, str, leakp); + return _symbol_table->mark(hash, str, leakp); } bool JfrArtifactSet::has_klass_entries() const { @@ -324,3 +112,9 @@ void JfrArtifactSet::register_klass(const Klass* k) { size_t JfrArtifactSet::total_count() const { return _total_count; } + +void JfrArtifactSet::increment_checkpoint_id() { + assert(_symbol_table != NULL, "invariant"); + _symbol_table->increment_checkpoint_id(); +} + diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp index b93d1673b726b6c8f1a983ce6dd5c5cc3e5c6ba5..5b33bfd3266a19c4e295aebdffccca500ad67936 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp @@ -26,8 +26,8 @@ #define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESETUTILS_HPP #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" +#include "jfr/support/jfrSymbolTable.hpp" #include "jfr/utilities/jfrAllocation.hpp" -#include "jfr/utilities/jfrHashtable.hpp" #include "oops/klass.hpp" #include "oops/method.hpp" @@ -191,98 +191,6 @@ class LeakPredicate { } }; -template -class ListEntry : public JfrHashtableEntry { - public: - ListEntry(uintptr_t hash, const T& data) : JfrHashtableEntry(hash, data), - _list_next(NULL), _serialized(false), _unloading(false), _leakp(false) {} - const ListEntry* list_next() const { return _list_next; } - void reset() const { - _list_next = NULL; _serialized = false; _unloading = false; _leakp = false; - } - void set_list_next(const ListEntry* next) const { _list_next = next; } - bool is_serialized() const { return _serialized; } - void set_serialized() const { _serialized = true; } - bool is_unloading() const { return _unloading; } - void set_unloading() const { _unloading = true; } - bool is_leakp() const { return _leakp; } - void set_leakp() const { _leakp = true; } - private: - mutable const ListEntry* _list_next; - mutable bool _serialized; - mutable bool _unloading; - mutable bool _leakp; -}; - -class JfrSymbolId : public JfrCHeapObj { - template class, typename, size_t> - friend class HashTableHost; - typedef HashTableHost SymbolTable; - typedef HashTableHost CStringTable; - friend class JfrArtifactSet; - public: - typedef SymbolTable::HashEntry SymbolEntry; - typedef CStringTable::HashEntry CStringEntry; - private: - SymbolTable* _sym_table; - CStringTable* _cstring_table; - const SymbolEntry* _sym_list; - const CStringEntry* _cstring_list; - const Symbol* _sym_query; - const char* _cstring_query; - traceid _symbol_id_counter; - bool _class_unload; - - // hashtable(s) callbacks - void on_link(const SymbolEntry* entry); - bool on_equals(uintptr_t hash, const SymbolEntry* entry); - void on_unlink(const SymbolEntry* entry); - void on_link(const CStringEntry* entry); - bool on_equals(uintptr_t hash, const CStringEntry* entry); - void on_unlink(const CStringEntry* entry); - - template - void iterate(Functor& functor, const T* list) { - const T* symbol = list; - while (symbol != NULL) { - const T* next = symbol->list_next(); - functor(symbol); - symbol = next; - } - } - - traceid mark_hidden_klass_name(const InstanceKlass* k, bool leakp); - bool is_hidden_klass(const Klass* k); - uintptr_t hidden_klass_name_hash(const InstanceKlass* ik); - - public: - JfrSymbolId(); - ~JfrSymbolId(); - - void clear(); - void set_class_unload(bool class_unload); - - traceid mark(uintptr_t hash, const Symbol* sym, bool leakp); - traceid mark(const Klass* k, bool leakp); - traceid mark(const Symbol* symbol, bool leakp); - traceid mark(uintptr_t hash, const char* str, bool leakp); - traceid bootstrap_name(bool leakp); - - template - void iterate_symbols(Functor& functor) { - iterate(functor, _sym_list); - } - - template - void iterate_cstrings(Functor& functor) { - iterate(functor, _cstring_list); - } - - bool has_entries() const { return has_symbol_entries() || has_cstring_entries(); } - bool has_symbol_entries() const { return _sym_list != NULL; } - bool has_cstring_entries() const { return _cstring_list != NULL; } -}; - /** * When processing a set of artifacts, there will be a need * to track transitive dependencies originating with each artifact. @@ -299,7 +207,7 @@ class JfrSymbolId : public JfrCHeapObj { */ class JfrArtifactSet : public JfrCHeapObj { private: - JfrSymbolId* _symbol_id; + JfrSymbolTable* _symbol_table; GrowableArray* _klass_list; GrowableArray* _klass_loader_set; size_t _total_count; @@ -309,7 +217,8 @@ class JfrArtifactSet : public JfrCHeapObj { ~JfrArtifactSet(); // caller needs ResourceMark - void initialize(bool class_unload, bool clear = false); + void initialize(bool class_unload); + void clear(); traceid mark(uintptr_t hash, const Symbol* sym, bool leakp); traceid mark(const Klass* klass, bool leakp); @@ -318,15 +227,16 @@ class JfrArtifactSet : public JfrCHeapObj { traceid mark_hidden_klass_name(const Klass* klass, bool leakp); traceid bootstrap_name(bool leakp); - const JfrSymbolId::SymbolEntry* map_symbol(const Symbol* symbol) const; - const JfrSymbolId::SymbolEntry* map_symbol(uintptr_t hash) const; - const JfrSymbolId::CStringEntry* map_cstring(uintptr_t hash) const; + const JfrSymbolTable::SymbolEntry* map_symbol(const Symbol* symbol) const; + const JfrSymbolTable::SymbolEntry* map_symbol(uintptr_t hash) const; + const JfrSymbolTable::StringEntry* map_string(uintptr_t hash) const; bool has_klass_entries() const; int entries() const; size_t total_count() const; void register_klass(const Klass* k); bool should_do_loader_klass(const Klass* k); + void increment_checkpoint_id(); template void iterate_klasses(Functor& functor) const { @@ -339,12 +249,12 @@ class JfrArtifactSet : public JfrCHeapObj { template void iterate_symbols(T& functor) { - _symbol_id->iterate_symbols(functor); + _symbol_table->iterate_symbols(functor); } template - void iterate_cstrings(T& functor) { - _symbol_id->iterate_cstrings(functor); + void iterate_strings(T& functor) { + _symbol_table->iterate_strings(functor); } template diff --git a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp index 43838e815ee0cbfcc2f19615bf7b2f4185d0f5be..4001e51f5b06e06c7fc1936950217fd527700fd9 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp @@ -26,6 +26,8 @@ #include "jfr/recorder/service/jfrPostBox.hpp" #include "jfr/utilities/jfrTryLock.hpp" #include "runtime/atomic.hpp" +#include "runtime/handles.hpp" +#include "runtime/interfaceSupport.inline.hpp" #include "runtime/thread.inline.hpp" #define MSG_IS_SYNCHRONOUS ( (MSGBIT(MSG_ROTATE)) | \ @@ -110,7 +112,9 @@ void JfrPostBox::asynchronous_post(int msg) { void JfrPostBox::synchronous_post(int msg) { assert(is_synchronous(msg), "invariant"); assert(!JfrMsg_lock->owned_by_self(), "should not hold JfrMsg_lock here!"); - MonitorLocker msg_lock(JfrMsg_lock); + NoHandleMark nhm; + ThreadBlockInVM transition(JavaThread::current()); + MonitorLocker msg_lock(JfrMsg_lock, Mutex::_no_safepoint_check_flag); deposit(msg); // serial_id is used to check when what we send in has been processed. // _msg_read_serial is read under JfrMsg_lock protection. @@ -168,6 +172,6 @@ void JfrPostBox::notify_waiters() { // safeguard to ensure no threads are left waiting void JfrPostBox::notify_collection_stop() { - MutexLocker msg_lock(JfrMsg_lock); + assert(JfrMsg_lock->owned_by_self(), "invariant"); JfrMsg_lock->notify_all(); } diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp index 6cbd487d514668ceb4bf42fe71b05cfea8f48034..1145b3c21b863facce6f557e7caf7a30baec195a 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp @@ -51,23 +51,24 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) { log_debug(jfr, system)("Recorder thread STARTED"); { + // Run as _thread_in_native to minimize impact on safepoint synchronization. + NoHandleMark nhm; + ThreadToNativeFromVM transition(thread); + bool done = false; int msgs = 0; JfrRecorderService service; - MutexLocker msg_lock(JfrMsg_lock); + + MonitorLocker msg_lock(JfrMsg_lock, Mutex::_no_safepoint_check_flag); // JFR MESSAGE LOOP PROCESSING - BEGIN while (!done) { if (post_box.is_empty()) { - JfrMsg_lock->wait(); + msg_lock.wait(); } msgs = post_box.collect(); - JfrMsg_lock->unlock(); { - // Run as _thread_in_native as much a possible - // to minimize impact on safepoint synchronizations. - NoHandleMark nhm; - ThreadToNativeFromVM transition(thread); + MutexUnlocker mul(JfrMsg_lock, Mutex::_no_safepoint_check_flag); if (PROCESS_FULL_BUFFERS) { service.process_full_buffers(); } @@ -82,18 +83,16 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) { service.flushpoint(); } } - JfrMsg_lock->lock(); post_box.notify_waiters(); if (SHUTDOWN) { log_debug(jfr, system)("Request to STOP recorder"); done = true; } } // JFR MESSAGE LOOP PROCESSING - END - - } // JfrMsg_lock scope + post_box.notify_collection_stop(); + } // JfrMsg_lock scope and the thread returns to _thread_in_vm assert(!JfrMsg_lock->owned_by_self(), "invariant"); - post_box.notify_collection_stop(); JfrRecorder::on_recorder_thread_exit(); #undef START diff --git a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp index 7d7b5ca9161ab0059aca3c987493f96809d10985..b1794a7a1e1224ad1bb697a9a1fd4a7843232cb0 100644 --- a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp +++ b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp @@ -23,11 +23,14 @@ */ #include "precompiled.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/periodic/jfrFinalizerStatisticsEvent.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" #include "jfr/support/jfrKlassUnloading.hpp" #include "jfr/utilities/jfrPredicate.hpp" #include "jfr/utilities/jfrRelation.hpp" #include "runtime/mutexLocker.hpp" +#include "utilities/macros.hpp" static const int initial_array_size = 64; @@ -107,9 +110,22 @@ static bool add_to_unloaded_klass_set(traceid klass_id, bool current_epoch) { return true; } +#if INCLUDE_MANAGEMENT +static void send_finalizer_event(const Klass* k) { + if (!k->is_instance_klass()) { + return; + } + const InstanceKlass* const ik = InstanceKlass::cast(k); + if (ik->has_finalizer()) { + JfrFinalizerStatisticsEvent::send_unload_event(ik); + } +} +#endif + bool JfrKlassUnloading::on_unload(const Klass* k) { assert(k != NULL, "invariant"); assert_locked_or_safepoint(ClassLoaderDataGraph_lock); + MANAGEMENT_ONLY(send_finalizer_event(k);) if (IS_JDK_JFR_EVENT_SUBKLASS(k)) { ++event_klass_unloaded_count; } diff --git a/src/hotspot/share/jfr/support/jfrSymbolTable.cpp b/src/hotspot/share/jfr/support/jfrSymbolTable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..786fff37efdf94090398ecbe75fc2a192223de46 --- /dev/null +++ b/src/hotspot/share/jfr/support/jfrSymbolTable.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/javaClasses.inline.hpp" +#include "classfile/classLoaderData.hpp" +#include "jfr/support/jfrSymbolTable.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/oop.inline.hpp" +#include "oops/symbol.hpp" +#include "runtime/mutexLocker.hpp" + +// incremented on each rotation +static u8 checkpoint_id = 1; + +// creates a unique id by combining a checkpoint relative symbol id (2^24) +// with the current checkpoint id (2^40) +#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id))) + +static traceid create_symbol_id(traceid artifact_id) { + return artifact_id != 0 ? CREATE_SYMBOL_ID(artifact_id) : 0; +} + +static uintptr_t string_hash(const char* str) { + return java_lang_String::hash_code(reinterpret_cast(str), static_cast(strlen(str))); +} + +static JfrSymbolTable::StringEntry* bootstrap = NULL; + +static JfrSymbolTable* _instance = NULL; + +static JfrSymbolTable& instance() { + assert(_instance != NULL, "invariant"); + return *_instance; +} + +JfrSymbolTable* JfrSymbolTable::create() { + assert(_instance == NULL, "invariant"); + assert_lock_strong(ClassLoaderDataGraph_lock); + _instance = new JfrSymbolTable(); + return _instance; +} + +void JfrSymbolTable::destroy() { + assert_lock_strong(ClassLoaderDataGraph_lock); + if (_instance != NULL) { + delete _instance; + _instance = NULL; + } + assert(_instance == NULL, "invariant"); +} + +JfrSymbolTable::JfrSymbolTable() : + _symbols(new Symbols(this)), + _strings(new Strings(this)), + _symbol_list(NULL), + _string_list(NULL), + _symbol_query(NULL), + _string_query(NULL), + _id_counter(1), + _class_unload(false) { + assert(_symbols != NULL, "invariant"); + assert(_strings != NULL, "invariant"); + bootstrap = new StringEntry(0, (const char*)&BOOTSTRAP_LOADER_NAME); + assert(bootstrap != NULL, "invariant"); + bootstrap->set_id(create_symbol_id(1)); + _string_list = bootstrap; +} + +JfrSymbolTable::~JfrSymbolTable() { + clear(); + delete _symbols; + delete _strings; + delete bootstrap; +} + +void JfrSymbolTable::clear() { + assert(_symbols != NULL, "invariant"); + if (_symbols->has_entries()) { + _symbols->clear_entries(); + } + assert(!_symbols->has_entries(), "invariant"); + + assert(_strings != NULL, "invariant"); + if (_strings->has_entries()) { + _strings->clear_entries(); + } + assert(!_strings->has_entries(), "invariant"); + + _symbol_list = NULL; + _id_counter = 1; + + _symbol_query = NULL; + _string_query = NULL; + + assert(bootstrap != NULL, "invariant"); + bootstrap->reset(); + _string_list = bootstrap; +} + +void JfrSymbolTable::set_class_unload(bool class_unload) { + _class_unload = class_unload; +} + +void JfrSymbolTable::increment_checkpoint_id() { + assert_lock_strong(ClassLoaderDataGraph_lock); + clear(); + ++checkpoint_id; +} + +template +inline void JfrSymbolTable::assign_id(T* entry) { + assert(entry != NULL, "invariant"); + assert(entry->id() == 0, "invariant"); + entry->set_id(create_symbol_id(++_id_counter)); +} + +void JfrSymbolTable::on_link(const SymbolEntry* entry) { + assign_id(entry); + const_cast(entry->literal())->increment_refcount(); + entry->set_list_next(_symbol_list); + _symbol_list = entry; +} + +bool JfrSymbolTable::on_equals(uintptr_t hash, const SymbolEntry* entry) { + assert(entry != NULL, "invariant"); + assert(entry->hash() == hash, "invariant"); + assert(_symbol_query != NULL, "invariant"); + return _symbol_query == entry->literal(); +} + +void JfrSymbolTable::on_unlink(const SymbolEntry* entry) { + assert(entry != NULL, "invariant"); + const_cast(entry->literal())->decrement_refcount(); +} + +static const char* resource_to_c_heap_string(const char* resource_str) { + assert(resource_str != NULL, "invariant"); + const size_t length = strlen(resource_str); + char* const c_string = JfrCHeapObj::new_array(length + 1); + assert(c_string != NULL, "invariant"); + strncpy(c_string, resource_str, length + 1); + return c_string; +} + +void JfrSymbolTable::on_link(const StringEntry* entry) { + assign_id(entry); + const_cast(entry)->set_literal(resource_to_c_heap_string(entry->literal())); + entry->set_list_next(_string_list); + _string_list = entry; +} + +static bool string_compare(const char* query, const char* candidate) { + assert(query != NULL, "invariant"); + assert(candidate != NULL, "invariant"); + const size_t length = strlen(query); + return strncmp(query, candidate, length) == 0; +} + +bool JfrSymbolTable::on_equals(uintptr_t hash, const StringEntry* entry) { + assert(entry != NULL, "invariant"); + assert(entry->hash() == hash, "invariant"); + assert(_string_query != NULL, "invariant"); + return string_compare(_string_query, entry->literal()); +} + +void JfrSymbolTable::on_unlink(const StringEntry* entry) { + assert(entry != NULL, "invariant"); + JfrCHeapObj::free(const_cast(entry->literal()), strlen(entry->literal() + 1)); +} + +traceid JfrSymbolTable::bootstrap_name(bool leakp) { + assert(bootstrap != NULL, "invariant"); + if (leakp) { + bootstrap->set_leakp(); + } + return bootstrap->id(); +} + +traceid JfrSymbolTable::mark(const Symbol* sym, bool leakp /* false */) { + assert(sym != NULL, "invariant"); + return mark((uintptr_t)sym->identity_hash(), sym, leakp); +} + +traceid JfrSymbolTable::mark(uintptr_t hash, const Symbol* sym, bool leakp) { + assert(sym != NULL, "invariant"); + assert(_symbols != NULL, "invariant"); + _symbol_query = sym; + const SymbolEntry& entry = _symbols->lookup_put(hash, sym); + if (_class_unload) { + entry.set_unloading(); + } + if (leakp) { + entry.set_leakp(); + } + return entry.id(); +} + +traceid JfrSymbolTable::mark(const char* str, bool leakp /* false*/) { + return mark(string_hash(str), str, leakp); +} + +traceid JfrSymbolTable::mark(uintptr_t hash, const char* str, bool leakp) { + assert(str != NULL, "invariant"); + assert(_strings != NULL, "invariant"); + _string_query = str; + const StringEntry& entry = _strings->lookup_put(hash, str); + if (_class_unload) { + entry.set_unloading(); + } + if (leakp) { + entry.set_leakp(); + } + return entry.id(); +} + +/* +* hidden classes symbol is the external name + +* the address of its InstanceKlass slash appended: +* java.lang.invoke.LambdaForm$BMH/22626602 +* +* caller needs ResourceMark +*/ + +uintptr_t JfrSymbolTable::hidden_klass_name_hash(const InstanceKlass* ik) { + assert(ik != NULL, "invariant"); + assert(ik->is_hidden(), "invariant"); + const oop mirror = ik->java_mirror_no_keepalive(); + assert(mirror != NULL, "invariant"); + return (uintptr_t)mirror->identity_hash(); +} + +static const char* create_hidden_klass_symbol(const InstanceKlass* ik, uintptr_t hash) { + assert(ik != NULL, "invariant"); + assert(ik->is_hidden(), "invariant"); + assert(hash != 0, "invariant"); + char* hidden_symbol = NULL; + const oop mirror = ik->java_mirror_no_keepalive(); + assert(mirror != NULL, "invariant"); + char hash_buf[40]; + sprintf(hash_buf, "/" UINTX_FORMAT, hash); + const size_t hash_len = strlen(hash_buf); + const size_t result_len = ik->name()->utf8_length(); + hidden_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1); + ik->name()->as_klass_external_name(hidden_symbol, (int)result_len + 1); + assert(strlen(hidden_symbol) == result_len, "invariant"); + strcpy(hidden_symbol + result_len, hash_buf); + assert(strlen(hidden_symbol) == result_len + hash_len, "invariant"); + return hidden_symbol; +} + +bool JfrSymbolTable::is_hidden_klass(const Klass* k) { + assert(k != NULL, "invariant"); + return k->is_instance_klass() && ((const InstanceKlass*)k)->is_hidden(); +} + +traceid JfrSymbolTable::mark_hidden_klass_name(const InstanceKlass* ik, bool leakp) { + assert(ik != NULL, "invariant"); + assert(ik->is_hidden(), "invariant"); + const uintptr_t hash = hidden_klass_name_hash(ik); + const char* const hidden_symbol = create_hidden_klass_symbol(ik, hash); + return mark(hash, hidden_symbol, leakp); +} + +traceid JfrSymbolTable::mark(const Klass* k, bool leakp) { + assert(k != NULL, "invariant"); + traceid symbol_id = 0; + if (is_hidden_klass(k)) { + assert(k->is_instance_klass(), "invariant"); + symbol_id = mark_hidden_klass_name((const InstanceKlass*)k, leakp); + } else { + Symbol* const sym = k->name(); + if (sym != NULL) { + symbol_id = mark(sym, leakp); + } + } + assert(symbol_id > 0, "a symbol handler must mark the symbol for writing"); + return symbol_id; +} + +template +traceid JfrSymbolTable::add_impl(const T* sym) { + assert(sym != NULL, "invariant"); + assert(_instance != NULL, "invariant"); + assert_locked_or_safepoint(ClassLoaderDataGraph_lock); + return instance().mark(sym); +} + +traceid JfrSymbolTable::add(const Symbol* sym) { + return add_impl(sym); +} + +traceid JfrSymbolTable::add(const char* str) { + return add_impl(str); +} diff --git a/src/hotspot/share/jfr/support/jfrSymbolTable.hpp b/src/hotspot/share/jfr/support/jfrSymbolTable.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5a6d9beaa1776bf2b21202aead30ae164ed232af --- /dev/null +++ b/src/hotspot/share/jfr/support/jfrSymbolTable.hpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_JFR_SUPPORT_JFRSYMBOLTABLE_HPP +#define SHARE_JFR_SUPPORT_JFRSYMBOLTABLE_HPP + +#include "jfr/utilities/jfrHashtable.hpp" +#include "jfr/utilities/jfrTypes.hpp" + +template +class ListEntry : public JfrHashtableEntry { + public: + ListEntry(uintptr_t hash, const T& data) : JfrHashtableEntry(hash, data), + _list_next(NULL), _serialized(false), _unloading(false), _leakp(false) {} + const ListEntry* list_next() const { return _list_next; } + void reset() const { + _list_next = NULL; _serialized = false; _unloading = false; _leakp = false; + } + void set_list_next(const ListEntry* next) const { _list_next = next; } + bool is_serialized() const { return _serialized; } + void set_serialized() const { _serialized = true; } + bool is_unloading() const { return _unloading; } + void set_unloading() const { _unloading = true; } + bool is_leakp() const { return _leakp; } + void set_leakp() const { _leakp = true; } + private: + mutable const ListEntry* _list_next; + mutable bool _serialized; + mutable bool _unloading; + mutable bool _leakp; +}; + +/* + * This table maps an oop/Symbol* or a char* to the Jfr type 'Symbol'. + * + * It provides an interface over the corresponding constant pool (TYPE_SYMBOL), + * which is represented in the binary format as a sequence of checkpoint events. + * The returned id can be used as a foreign key, but please note that the id is + * epoch-relative, and is therefore only valid in the current epoch / chunk. + * The table is cleared as part of rotation. + * + * Caller must ensure mutual exclusion by means of the ClassLoaderDataGraph_lock or by safepointing. + */ +class JfrSymbolTable : public JfrCHeapObj { + template class, typename, size_t> + friend class HashTableHost; + typedef HashTableHost Symbols; + typedef HashTableHost Strings; + friend class JfrArtifactSet; + + public: + typedef Symbols::HashEntry SymbolEntry; + typedef Strings::HashEntry StringEntry; + + static traceid add(const Symbol* sym); + static traceid add(const char* str); + + private: + Symbols* _symbols; + Strings* _strings; + const SymbolEntry* _symbol_list; + const StringEntry* _string_list; + const Symbol* _symbol_query; + const char* _string_query; + traceid _id_counter; + bool _class_unload; + + JfrSymbolTable(); + ~JfrSymbolTable(); + static JfrSymbolTable* create(); + static void destroy(); + + void clear(); + void increment_checkpoint_id(); + void set_class_unload(bool class_unload); + + traceid mark(uintptr_t hash, const Symbol* sym, bool leakp); + traceid mark(const Klass* k, bool leakp); + traceid mark(const Symbol* sym, bool leakp = false); + traceid mark(const char* str, bool leakp = false); + traceid mark(uintptr_t hash, const char* str, bool leakp); + traceid bootstrap_name(bool leakp); + + bool has_entries() const { return has_symbol_entries() || has_string_entries(); } + bool has_symbol_entries() const { return _symbol_list != NULL; } + bool has_string_entries() const { return _string_list != NULL; } + + traceid mark_hidden_klass_name(const InstanceKlass* k, bool leakp); + bool is_hidden_klass(const Klass* k); + uintptr_t hidden_klass_name_hash(const InstanceKlass* ik); + + // hashtable(s) callbacks + void on_link(const SymbolEntry* entry); + bool on_equals(uintptr_t hash, const SymbolEntry* entry); + void on_unlink(const SymbolEntry* entry); + void on_link(const StringEntry* entry); + bool on_equals(uintptr_t hash, const StringEntry* entry); + void on_unlink(const StringEntry* entry); + + template + static traceid add_impl(const T* sym); + + template + void assign_id(T* entry); + + template + void iterate_symbols(Functor& functor) { + iterate(functor, _symbol_list); + } + + template + void iterate_strings(Functor& functor) { + iterate(functor, _string_list); + } + + template + void iterate(Functor& functor, const T* list) { + const T* symbol = list; + while (symbol != NULL) { + const T* next = symbol->list_next(); + functor(symbol); + symbol = next; + } + } +}; + +#endif // SHARE_JFR_SUPPORT_JFRSYMBOLTABLE_HPP diff --git a/src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp b/src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp index 433c2589380f252a64a1aaaffdced76627749534..c1c0775c5303941b0d0f1d9ad794cb813a652b25 100644 --- a/src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp +++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp @@ -26,6 +26,7 @@ #include "jfr/support/jfrThreadLocal.hpp" #include "jfr/utilities/jfrThreadIterator.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.inline.hpp" static bool thread_inclusion_predicate(Thread* t) { assert(t != NULL, "invariant"); @@ -40,14 +41,6 @@ static bool java_thread_inclusion_predicate(JavaThread* jt, bool live_only) { return thread_inclusion_predicate(jt); } -static JavaThread* next_java_thread(JavaThreadIteratorWithHandle& iter, bool live_only) { - JavaThread* next = iter.next(); - while (next != NULL && !java_thread_inclusion_predicate(next, live_only)) { - next = iter.next(); - } - return next; -} - static NonJavaThread* next_non_java_thread(NonJavaThread::Iterator& iter) { while (!iter.end()) { NonJavaThread* next = iter.current(); @@ -60,15 +53,29 @@ static NonJavaThread* next_non_java_thread(NonJavaThread::Iterator& iter) { return NULL; } -JfrJavaThreadIteratorAdapter::JfrJavaThreadIteratorAdapter(bool live_only /* true */) : _iter(), - _next(next_java_thread(_iter, live_only)), - _live_only(live_only) {} +JfrJavaThreadIteratorAdapter::JfrJavaThreadIteratorAdapter(bool live_only /* true */) : + _tlist(), + _it(_tlist.begin()), + _end(_tlist.end()), + _live_only(live_only) +{ + skip_excluded(); +} + +bool JfrJavaThreadIteratorAdapter::has_next() const { + return _it != _end; +} + +void JfrJavaThreadIteratorAdapter::skip_excluded() { + while (has_next() && !java_thread_inclusion_predicate(*_it, _live_only)) { + ++_it; + } +} JavaThread* JfrJavaThreadIteratorAdapter::next() { assert(has_next(), "invariant"); - Type* const temp = _next; - _next = next_java_thread(_iter, _live_only); - assert(temp != _next, "invariant"); + Type* const temp = *_it++; + skip_excluded(); return temp; } diff --git a/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp b/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp index f45ed6bf4e36d96bb58b17d68912fb7174665789..a3144c8168ae8965f4e631c3c146bc379c678fd9 100644 --- a/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp +++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp @@ -47,15 +47,17 @@ class JfrThreadIterator : public AP { class JfrJavaThreadIteratorAdapter { private: - JavaThreadIteratorWithHandle _iter; - JavaThread* _next; + ThreadsListHandle _tlist; + ThreadsListHandle::Iterator _it; + ThreadsListHandle::Iterator _end; bool _live_only; + + void skip_excluded(); + public: typedef JavaThread Type; JfrJavaThreadIteratorAdapter(bool live_only = true); - bool has_next() const { - return _next != NULL; - } + bool has_next() const; Type* next(); }; diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 45d2ad1d2e4f1ba7f66484825c428fa83ca1d23a..e4e1197d3576e7450bc44c23554a9ab9ad4b18c3 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -1045,9 +1045,10 @@ C2V_VMENTRY_NULL(jlongArray, getLineNumberTable, (JNIEnv* env, jobject, jobject int i = 0; jlong value; while (stream.read_pair()) { - value = ((long) stream.bci()); + // FIXME: Why was this long before? + value = ((jlong) stream.bci()); JVMCIENV->put_long_at(result, i, value); - value = ((long) stream.line()); + value = ((jlong) stream.line()); JVMCIENV->put_long_at(result, i + 1, value); i += 2; } @@ -1891,7 +1892,7 @@ C2V_VMENTRY_NULL(jobjectArray, getDeclaredMethods, (JNIEnv* env, jobject, jobjec return JVMCIENV->get_jobjectArray(methods); C2V_END -C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, jobject expected_type, long displacement, jboolean is_volatile, jobject kind_object)) +C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, jobject expected_type, long displacement, jobject kind_object)) if (object == NULL || kind_object == NULL) { JVMCI_THROW_0(NullPointerException); } @@ -1931,13 +1932,18 @@ C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, ShouldNotReachHere(); } - if (displacement < 0 || ((long) displacement + type2aelembytes(basic_type) > HeapWordSize * obj->size())) { + int basic_type_elemsize = type2aelembytes(basic_type); + if (displacement < 0 || ((size_t) displacement + basic_type_elemsize > HeapWordSize * obj->size())) { // Reading outside of the object bounds JVMCI_THROW_MSG_NULL(IllegalArgumentException, "reading outside object bounds"); } // Perform basic sanity checks on the read. Primitive reads are permitted to read outside the // bounds of their fields but object reads must map exactly onto the underlying oop slot. + bool aligned = (displacement % basic_type_elemsize) == 0; + if (!aligned) { + JVMCI_THROW_MSG_NULL(IllegalArgumentException, "read is unaligned"); + } if (basic_type == T_OBJECT) { if (obj->is_objArray()) { if (displacement < arrayOopDesc::base_offset_in_bytes(T_OBJECT)) { @@ -1975,15 +1981,20 @@ C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, } jlong value = 0; + + // Treat all reads as volatile for simplicity as this function can be used + // both for reading Java fields declared as volatile as well as for constant + // folding Unsafe.get* methods with volatile semantics. + switch (basic_type) { - case T_BOOLEAN: value = is_volatile ? obj->bool_field_acquire(displacement) : obj->bool_field(displacement); break; - case T_BYTE: value = is_volatile ? obj->byte_field_acquire(displacement) : obj->byte_field(displacement); break; - case T_SHORT: value = is_volatile ? obj->short_field_acquire(displacement) : obj->short_field(displacement); break; - case T_CHAR: value = is_volatile ? obj->char_field_acquire(displacement) : obj->char_field(displacement); break; + case T_BOOLEAN: value = obj->bool_field_acquire(displacement); break; + case T_BYTE: value = obj->byte_field_acquire(displacement); break; + case T_SHORT: value = obj->short_field_acquire(displacement); break; + case T_CHAR: value = obj->char_field_acquire(displacement); break; case T_FLOAT: - case T_INT: value = is_volatile ? obj->int_field_acquire(displacement) : obj->int_field(displacement); break; + case T_INT: value = obj->int_field_acquire(displacement); break; case T_DOUBLE: - case T_LONG: value = is_volatile ? obj->long_field_acquire(displacement) : obj->long_field(displacement); break; + case T_LONG: value = obj->long_field_acquire(displacement); break; case T_OBJECT: { if (displacement == java_lang_Class::component_mirror_offset() && java_lang_Class::is_instance(obj()) && @@ -1993,7 +2004,8 @@ C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); } - oop value = is_volatile ? obj->obj_field_acquire(displacement) : obj->obj_field(displacement); + oop value = obj->obj_field_acquire(displacement); + if (value == NULL) { return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); } else { @@ -2782,8 +2794,8 @@ JNINativeMethod CompilerToVM::methods[] = { {CC "boxPrimitive", CC "(" OBJECT ")" OBJECTCONSTANT, FN_PTR(boxPrimitive)}, {CC "getDeclaredConstructors", CC "(" HS_RESOLVED_KLASS ")[" RESOLVED_METHOD, FN_PTR(getDeclaredConstructors)}, {CC "getDeclaredMethods", CC "(" HS_RESOLVED_KLASS ")[" RESOLVED_METHOD, FN_PTR(getDeclaredMethods)}, - {CC "readFieldValue", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS "JZLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, - {CC "readFieldValue", CC "(" OBJECTCONSTANT HS_RESOLVED_KLASS "JZLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, + {CC "readFieldValue", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS "JLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, + {CC "readFieldValue", CC "(" OBJECTCONSTANT HS_RESOLVED_KLASS "JLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, {CC "isInstance", CC "(" HS_RESOLVED_KLASS OBJECTCONSTANT ")Z", FN_PTR(isInstance)}, {CC "isAssignableFrom", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS ")Z", FN_PTR(isAssignableFrom)}, {CC "isTrustedForIntrinsics", CC "(" HS_RESOLVED_KLASS ")Z", FN_PTR(isTrustedForIntrinsics)}, diff --git a/src/hotspot/share/jvmci/jvmciJavaClasses.cpp b/src/hotspot/share/jvmci/jvmciJavaClasses.cpp index ed99adb04505587c7751df2a6871d3f47c202d50..32b1fdfcaabbd389adf00b5e54d8c6c8876149e3 100644 --- a/src/hotspot/share/jvmci/jvmciJavaClasses.cpp +++ b/src/hotspot/share/jvmci/jvmciJavaClasses.cpp @@ -231,13 +231,13 @@ void HotSpotJVMCI::compute_offsets(TRAPS) { assert(className::klass() != NULL && className::klass()->is_linked(), "Class not yet linked: " #className); \ InstanceKlass* ik = className::klass(); \ oop base = ik->static_field_base_raw(); \ - return HeapAccess<>::load_at(base, className::_##name##_offset); \ + return *base->field_addr(className::_##name##_offset); \ } \ void HotSpotJVMCI::className::set_##name(JVMCIEnv* env, jtypename x) { \ assert(className::klass() != NULL && className::klass()->is_linked(), "Class not yet linked: " #className); \ InstanceKlass* ik = className::klass(); \ oop base = ik->static_field_base_raw(); \ - HeapAccess<>::store_at(base, _##name##_offset, x); \ + *base->field_addr(className::_##name##_offset) = x; \ } #define STATIC_INT_FIELD(className, name) STATIC_PRIMITIVE_FIELD(className, name, jint) diff --git a/src/hotspot/share/logging/logPrefix.hpp b/src/hotspot/share/logging/logPrefix.hpp index 2027bf6bc294267ce946dff44776ac685382138f..3411bfb7c790dba5e5a8335be3bfb26a4904542b 100644 --- a/src/hotspot/share/logging/logPrefix.hpp +++ b/src/hotspot/share/logging/logPrefix.hpp @@ -94,8 +94,7 @@ DEBUG_ONLY(size_t Test_log_prefix_prefixer(char* buf, size_t len);) DEBUG_ONLY(LOG_PREFIX(Test_log_prefix_prefixer, LOG_TAGS(logging, test))) \ LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, tlab)) \ LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, verify)) \ - LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, verify, start)) \ - LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, workgang)) + LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, verify, start)) // The empty prefix, used when there's no prefix defined. diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index cf86eaccc4d370df35258620c15244bd5a96855d..2042cf2b4d7e92f51323fdacf4b92fff9e75f887 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -71,6 +71,7 @@ LOG_TAG(event) \ LOG_TAG(exceptions) \ LOG_TAG(exit) \ + LOG_TAG(finalizer) \ LOG_TAG(fingerprint) \ DEBUG_ONLY(LOG_TAG(foreign)) \ LOG_TAG(free) \ @@ -196,8 +197,7 @@ LOG_TAG(vmoperation) \ LOG_TAG(vmthread) \ LOG_TAG(vtables) \ - LOG_TAG(vtablestubs) \ - LOG_TAG(workgang) + LOG_TAG(vtablestubs) #define PREFIX_LOG_TAG(T) (LogTag::_##T) diff --git a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp index a656bb3cb5d781c5f4af1bbd0095634421115f08..b024de615895c4c04a86df04206ee4af6b7b2b59 100644 --- a/src/hotspot/share/memory/heapInspection.cpp +++ b/src/hotspot/share/memory/heapInspection.cpp @@ -577,20 +577,21 @@ uintx HeapInspection::populate_table(KlassInfoTable* cit, BoolObjectClosure *fil if (parallel_thread_num > 1) { ResourceMark rm; - WorkGang* gang = Universe::heap()->safepoint_workers(); - if (gang != NULL) { - // The GC provided a WorkGang to be used during a safepoint. + WorkerThreads* workers = Universe::heap()->safepoint_workers(); + if (workers != NULL) { + // The GC provided a WorkerThreads to be used during a safepoint. - // Can't run with more threads than provided by the WorkGang. - WithUpdatedActiveWorkers update_and_restore(gang, parallel_thread_num); + // Can't run with more threads than provided by the WorkerThreads. + const uint capped_parallel_thread_num = MIN2(parallel_thread_num, workers->max_workers()); + WithActiveWorkers with_active_workers(workers, capped_parallel_thread_num); - ParallelObjectIterator* poi = Universe::heap()->parallel_object_iterator(gang->active_workers()); + ParallelObjectIterator* poi = Universe::heap()->parallel_object_iterator(workers->active_workers()); if (poi != NULL) { // The GC supports parallel object iteration. ParHeapInspectTask task(poi, cit, filter); // Run task with the active workers. - gang->run_task(&task); + workers->run_task(&task); delete poi; if (task.success()) { diff --git a/src/hotspot/share/memory/heapInspection.hpp b/src/hotspot/share/memory/heapInspection.hpp index b7017f2c4ae0d58c1c720e376663ce06feeb335b..1e07a361d54e14e067ca48e6ca033dfb1ee9912c 100644 --- a/src/hotspot/share/memory/heapInspection.hpp +++ b/src/hotspot/share/memory/heapInspection.hpp @@ -25,12 +25,12 @@ #ifndef SHARE_MEMORY_HEAPINSPECTION_HPP #define SHARE_MEMORY_HEAPINSPECTION_HPP +#include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" #include "oops/objArrayOop.hpp" #include "oops/oop.hpp" #include "oops/annotations.hpp" #include "utilities/macros.hpp" -#include "gc/shared/workgroup.hpp" class ParallelObjectIterator; @@ -153,22 +153,6 @@ class KlassInfoHisto : public StackObj { void print_elements(outputStream* st) const; bool is_selected(const char *col_name); - template static int count_bytes(T* x) { - return (HeapWordSize * ((x) ? (x)->size() : 0)); - } - - template static int count_bytes_array(T* x) { - if (x == NULL) { - return 0; - } - if (x->length() == 0) { - // This is a shared array, e.g., Universe::the_empty_int_array(). Don't - // count it to avoid double-counting. - return 0; - } - return HeapWordSize * x->size(); - } - static void print_julong(outputStream* st, int width, julong n) { int num_spaces = width - julong_width(n); if (num_spaces > 0) { @@ -226,7 +210,7 @@ class HeapInspection : public StackObj { // Parallel heap inspection task. Parallel inspection can fail due to // a native OOM when allocating memory for TL-KlassInfoTable. // _success will be set false on an OOM, and serial inspection tried. -class ParHeapInspectTask : public AbstractGangTask { +class ParHeapInspectTask : public WorkerTask { private: ParallelObjectIterator* _poi; KlassInfoTable* _shared_cit; @@ -239,13 +223,13 @@ class ParHeapInspectTask : public AbstractGangTask { ParHeapInspectTask(ParallelObjectIterator* poi, KlassInfoTable* shared_cit, BoolObjectClosure* filter) : - AbstractGangTask("Iterating heap"), + WorkerTask("Iterating heap"), _poi(poi), _shared_cit(shared_cit), _filter(filter), _missed_count(0), _success(true), - _mutex(Mutex::nosafepoint, "ParHeapInspectTask_lock", Mutex::_safepoint_check_never) {} + _mutex(Mutex::nosafepoint, "ParHeapInspectTask_lock") {} uintx missed_count() const { return _missed_count; diff --git a/src/hotspot/share/memory/metaspace/chunkManager.cpp b/src/hotspot/share/memory/metaspace/chunkManager.cpp index 1c175f0cec6dd93e138f69e6176a6527666472b7..c0c163d7b6518f24beee2a19dc33c4bd4d0e87ba 100644 --- a/src/hotspot/share/memory/metaspace/chunkManager.cpp +++ b/src/hotspot/share/memory/metaspace/chunkManager.cpp @@ -316,24 +316,11 @@ void ChunkManager::purge() { const size_t reserved_before = _vslist->reserved_words(); const size_t committed_before = _vslist->committed_words(); - int num_nodes_purged = 0; - - // We purge to return unused memory to the Operating System. We do this in - // two independent steps. - - // 1) We purge the virtual space list: any memory mappings which are - // completely deserted can be potentially unmapped. We iterate over the list - // of mappings (VirtualSpaceList::purge) and delete every node whose memory - // only contains free chunks. Deleting that node includes unmapping its memory, - // so all chunk vanish automatically. - // Of course we need to remove the chunk headers of those vanished chunks from - // the ChunkManager freelist. - num_nodes_purged = _vslist->purge(&_chunks); - InternalStats::inc_num_purges(); - - // 2) Since (1) is rather ineffective - it is rare that a whole node only contains - // free chunks - we now iterate over all remaining free chunks and - // and uncommit those which can be uncommitted (>= commit granule size). + + // We return unused memory to the Operating System: we iterate over all + // free chunks and uncommit the backing memory of those large enough to + // contain one or multiple commit granules (chunks larger than a granule + // always cover a whole number of granules and start at a granule boundary). if (Settings::uncommit_free_chunks()) { const chunklevel_t max_level = chunklevel::level_fitting_word_size(Settings::commit_granule_words()); @@ -365,7 +352,6 @@ void ChunkManager::purge() { ls.print("committed: "); print_word_size_delta(&ls, committed_before, committed_after); ls.cr(); - ls.print_cr("full nodes purged: %d", num_nodes_purged); } } DEBUG_ONLY(_vslist->verify_locked()); diff --git a/src/hotspot/share/memory/metaspace/internalStats.hpp b/src/hotspot/share/memory/metaspace/internalStats.hpp index 00d3e8f154c58cf153c81f2b7828f1844d4d511e..dff2534e3031657555c90483562d9c4db01e9ba4 100644 --- a/src/hotspot/share/memory/metaspace/internalStats.hpp +++ b/src/hotspot/share/memory/metaspace/internalStats.hpp @@ -92,9 +92,6 @@ class InternalStats : public AllStatic { /* Number of chunk in place enlargements */ \ x(num_chunks_enlarged) \ \ - /* Number of times we did a purge */ \ - x(num_purges) \ - \ /* Number of times we read inconsistent stats. */ \ x(num_inconsistent_stats) \ diff --git a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp index a3a03e5ba85e4353acab8a0e3970d2a284c93e14..0df97ef87ffc24f111bfd7f9b8c09ed0ed451da3 100644 --- a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp +++ b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp @@ -481,16 +481,6 @@ RootChunkAreaLUT::~RootChunkAreaLUT() { FREE_C_HEAP_ARRAY(RootChunkArea, _arr); } -// Returns true if all areas in this area table are free (only contain free chunks). -bool RootChunkAreaLUT::is_free() const { - for (int i = 0; i < _num; i++) { - if (!_arr[i].is_free()) { - return false; - } - } - return true; -} - #ifdef ASSERT void RootChunkAreaLUT::verify() const { diff --git a/src/hotspot/share/memory/metaspace/rootChunkArea.hpp b/src/hotspot/share/memory/metaspace/rootChunkArea.hpp index 9ba6d7360183d6e1e0b6e51d2c54ae6d4d11ebe5..ce13f282a181383b80ba6802546bcb6a3659f991 100644 --- a/src/hotspot/share/memory/metaspace/rootChunkArea.hpp +++ b/src/hotspot/share/memory/metaspace/rootChunkArea.hpp @@ -107,10 +107,6 @@ public: size_t word_size() const { return chunklevel::MAX_CHUNK_WORD_SIZE; } const MetaWord* end() const { return _base + word_size(); } - // Direct access to the first chunk (use with care) - Metachunk* first_chunk() { return _first_chunk; } - const Metachunk* first_chunk() const { return _first_chunk; } - // Returns true if this root chunk area is completely free: // In that case, it should only contain one chunk (maximally merged, so a root chunk) // and it should be free. @@ -182,20 +178,12 @@ public: return _arr + idx; } - // Access area by its index - int number_of_areas() const { return _num; } - RootChunkArea* get_area_by_index(int index) { assert(index >= 0 && index < _num, "oob"); return _arr + index; } - const RootChunkArea* get_area_by_index(int index) const { assert(index >= 0 && index < _num, "oob"); return _arr + index; } - /// range /// const MetaWord* base() const { return _base; } size_t word_size() const { return _num * chunklevel::MAX_CHUNK_WORD_SIZE; } const MetaWord* end() const { return _base + word_size(); } - // Returns true if all areas in this area table are free (only contain free chunks). - bool is_free() const; - DEBUG_ONLY(void verify() const;) void print_on(outputStream* st) const; diff --git a/src/hotspot/share/memory/metaspace/testHelpers.cpp b/src/hotspot/share/memory/metaspace/testHelpers.cpp index c1527c18192cbb92367c5db0b8195fd79c9b4a97..ef75b1446435b5c197665dc57ec06083d9bcc8c9 100644 --- a/src/hotspot/share/memory/metaspace/testHelpers.cpp +++ b/src/hotspot/share/memory/metaspace/testHelpers.cpp @@ -92,7 +92,7 @@ MetaspaceTestContext::~MetaspaceTestContext() { // Create an arena, feeding off this area. MetaspaceTestArena* MetaspaceTestContext::create_arena(Metaspace::MetaspaceType type) { const ArenaGrowthPolicy* growth_policy = ArenaGrowthPolicy::policy_for_space_type(type, false); - Mutex* lock = new Mutex(Monitor::nosafepoint, "MetaspaceTestArea_lock", Monitor::_safepoint_check_never); + Mutex* lock = new Mutex(Monitor::nosafepoint, "MetaspaceTestArea_lock"); MetaspaceArena* arena = NULL; { MutexLocker ml(lock, Mutex::_no_safepoint_check_flag); diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp index 0383b2eff978198ffc57bf55d7e438816520ddd6..f9d66b81e0cd1f7fa841ce21d132b460e9107f8c 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp @@ -134,43 +134,6 @@ Metachunk* VirtualSpaceList::allocate_root_chunk() { return c; } -// Attempts to purge nodes. This will remove and delete nodes which only contain free chunks. -// The free chunks are removed from the freelists before the nodes are deleted. -// Return number of purged nodes. -int VirtualSpaceList::purge(FreeChunkListVector* freelists) { - assert_lock_strong(Metaspace_lock); - UL(debug, "purging."); - - VirtualSpaceNode* vsn = _first_node; - VirtualSpaceNode* prev_vsn = NULL; - int num = 0, num_purged = 0; - while (vsn != NULL) { - VirtualSpaceNode* next_vsn = vsn->next(); - bool purged = vsn->attempt_purge(freelists); - if (purged) { - // Note: from now on do not dereference vsn! - UL2(debug, "purged node @" PTR_FORMAT ".", p2i(vsn)); - if (_first_node == vsn) { - _first_node = next_vsn; - } - DEBUG_ONLY(vsn = (VirtualSpaceNode*)((uintptr_t)(0xdeadbeef));) - if (prev_vsn != NULL) { - prev_vsn->set_next(next_vsn); - } - num_purged++; - _nodes_counter.decrement(); - } else { - prev_vsn = vsn; - } - vsn = next_vsn; - num ++; - } - - UL2(debug, "purged %d nodes (before: %d, now: %d)", - num_purged, num, num_nodes()); - return num_purged; -} - // Print all nodes in this space list. void VirtualSpaceList::print_on(outputStream* st) const { MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp b/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp index f105da7426eb23682a84ecbddcf670655c8cff31..2f2dd2988c2ce85703efbe983c85d520e70a2813 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceList.hpp @@ -48,10 +48,6 @@ class FreeChunkListVector; // managing a single contiguous memory region. The first node of // this list is the current node and used for allocation of new // root chunks. -// -// Beyond access to those nodes and the ability to grow new nodes -// (if expandable) it allows for purging: purging this list means -// removing and unmapping all memory regions which are unused. class VirtualSpaceList : public CHeapObj { @@ -101,11 +97,6 @@ public: // the list cannot be expanded (in practice this means we reached CompressedClassSpaceSize). Metachunk* allocate_root_chunk(); - // Attempts to purge nodes. This will remove and delete nodes which only contain free chunks. - // The free chunks are removed from the freelists before the nodes are deleted. - // Return number of purged nodes. - int purge(FreeChunkListVector* freelists); - //// Statistics //// // Return sum of reserved words in all nodes. diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index a0e280b55a93958de42cfc71d22bbf968261c345..87fc5acde25a79604d1bea3286348f91bf658ced 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -369,48 +369,6 @@ bool VirtualSpaceNode::attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* return rc; } -// Attempts to purge the node: -// -// If all chunks living in this node are free, they will all be removed from -// the freelist they currently reside in. Then, the node will be deleted. -// -// Returns true if the node has been deleted, false if not. -// !! If this returns true, do not access the node from this point on. !! -bool VirtualSpaceNode::attempt_purge(FreeChunkListVector* freelists) { - assert_lock_strong(Metaspace_lock); - - if (!_owns_rs) { - // We do not allow purging of nodes if we do not own the - // underlying ReservedSpace (CompressClassSpace case). - return false; - } - - // First find out if all areas are empty. Since empty chunks collapse to root chunk - // size, if all chunks in this node are free root chunks we are good to go. - if (!_root_chunk_area_lut.is_free()) { - return false; - } - - UL(debug, ": purging."); - - // Okay, we can purge. Before we can do this, we need to remove all chunks from the freelist. - for (int narea = 0; narea < _root_chunk_area_lut.number_of_areas(); narea++) { - RootChunkArea* ra = _root_chunk_area_lut.get_area_by_index(narea); - Metachunk* c = ra->first_chunk(); - if (c != NULL) { - UL2(trace, "removing chunk from to-be-purged node: " - METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c)); - assert(c->is_free() && c->is_root_chunk(), "Sanity"); - freelists->remove(c); - } - } - - // Now, delete the node, then right away return since this object is invalid. - delete this; - - return true; -} - void VirtualSpaceNode::print_on(outputStream* st) const { size_t scale = K; diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp index f3391cddc1a27afccf9e7c709fcf97d77639db5f..0a1726601ab900d98452a8ad2af051f48e04816d 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp @@ -208,15 +208,6 @@ public: // On success, true is returned, false otherwise. bool attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists); - // Attempts to purge the node: - // - // If all chunks living in this node are free, they will all be removed from - // the freelist they currently reside in. Then, the node will be deleted. - // - // Returns true if the node has been deleted, false if not. - // !! If this returns true, do not access the node from this point on. !! - bool attempt_purge(FreeChunkListVector* freelists); - // Attempts to uncommit free areas according to the rules set in settings. // Returns number of words uncommitted. size_t uncommit_free_areas(); diff --git a/src/hotspot/share/memory/operator_new.cpp b/src/hotspot/share/memory/operator_new.cpp index 84a4152b7c3a9e0076f40e083be082c4b9aca43f..357e26f9c88a6c9ba318f3836d7820b81b413b0a 100644 --- a/src/hotspot/share/memory/operator_new.cpp +++ b/src/hotspot/share/memory/operator_new.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,12 +89,6 @@ void operator delete [](void* p) throw() { fatal("Should not call global delete []"); } -#ifdef __GNUG__ -// Warning disabled for gcc 5.4 -PRAGMA_DIAG_PUSH -PRAGMA_DISABLE_GCC_WARNING("-Wc++14-compat") -#endif // __GNUG__ - void operator delete(void* p, size_t size) throw() { fatal("Should not call global sized delete"); } @@ -103,8 +97,4 @@ void operator delete [](void* p, size_t size) throw() { fatal("Should not call global sized delete []"); } -#ifdef __GNUG__ -PRAGMA_DIAG_POP -#endif // __GNUG__ - #endif // Non-product diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index fcc22bdb86f3c4fb03c6d151ce928461ed9e5ced..6c7f5a2fab682c0aadcd5c820b5b5f7bc5cba00e 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -523,15 +523,20 @@ static void reinitialize_vtables() { } } +static void reinitialize_itables() { -static void initialize_itable_for_klass(InstanceKlass* k) { - k->itable().initialize_itable(); -} - + class ReinitTableClosure : public KlassClosure { + public: + void do_klass(Klass* k) { + if (k->is_instance_klass()) { + InstanceKlass::cast(k)->itable().initialize_itable(); + } + } + }; -static void reinitialize_itables() { MutexLocker mcld(ClassLoaderDataGraph_lock); - ClassLoaderDataGraph::dictionary_classes_do(initialize_itable_for_klass); + ReinitTableClosure cl; + ClassLoaderDataGraph::classes_do(&cl); } diff --git a/src/hotspot/share/oops/arrayKlass.cpp b/src/hotspot/share/oops/arrayKlass.cpp index b1ad66db041a9066b7eb72f87f1ce4bb5829650b..c28ce0323b7f2b4e3e14c05e1b87c9e65c42ffc9 100644 --- a/src/hotspot/share/oops/arrayKlass.cpp +++ b/src/hotspot/share/oops/arrayKlass.cpp @@ -126,7 +126,7 @@ GrowableArray* ArrayKlass::compute_secondary_supers(int num_extra_slots, objArrayOop ArrayKlass::allocate_arrayArray(int n, int length, TRAPS) { check_array_allocation_length(length, arrayOopDesc::max_array_length(T_ARRAY), CHECK_NULL); - int size = objArrayOopDesc::object_size(length); + size_t size = objArrayOopDesc::object_size(length); Klass* k = array_klass(n+dimension(), CHECK_NULL); ArrayKlass* ak = ArrayKlass::cast(k); objArrayOop o = (objArrayOop)Universe::heap()->array_allocate(ak, size, length, @@ -135,24 +135,6 @@ objArrayOop ArrayKlass::allocate_arrayArray(int n, int length, TRAPS) { return o; } -void ArrayKlass::array_klasses_do(void f(Klass* k, TRAPS), TRAPS) { - Klass* k = this; - // Iterate over this array klass and all higher dimensions - while (k != NULL) { - f(k, CHECK); - k = ArrayKlass::cast(k)->higher_dimension(); - } -} - -void ArrayKlass::array_klasses_do(void f(Klass* k)) { - Klass* k = this; - // Iterate over this array klass and all higher dimensions - while (k != NULL) { - f(k); - k = ArrayKlass::cast(k)->higher_dimension(); - } -} - jint ArrayKlass::compute_modifier_flags() const { return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; } diff --git a/src/hotspot/share/oops/arrayKlass.hpp b/src/hotspot/share/oops/arrayKlass.hpp index a56343886b3ae1dc90cd7e395729a962de750c5a..bed87db0d2a8f6cec0007bb4f81d92303f442f05 100644 --- a/src/hotspot/share/oops/arrayKlass.hpp +++ b/src/hotspot/share/oops/arrayKlass.hpp @@ -104,10 +104,6 @@ class ArrayKlass: public Klass { virtual void metaspace_pointers_do(MetaspaceClosure* iter); - // Iterators - void array_klasses_do(void f(Klass* k)); - void array_klasses_do(void f(Klass* k, TRAPS), TRAPS); - // Return a handle. static void complete_create_array_klass(ArrayKlass* k, Klass* super_klass, ModuleEntry* module, TRAPS); diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index dd7fee01a2885d27873b7596750bbc7376364aba..2f385e53672a5caeec2ec0079bef623a27243e4e 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -83,6 +83,7 @@ #include "runtime/reflectionUtils.hpp" #include "runtime/thread.inline.hpp" #include "services/classLoadingService.hpp" +#include "services/finalizerService.hpp" #include "services/threadService.hpp" #include "utilities/dtrace.hpp" #include "utilities/events.hpp" @@ -96,7 +97,6 @@ #include "jfr/jfrEvents.hpp" #endif - #ifdef DTRACE_ENABLED @@ -583,7 +583,10 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { // Release C heap allocated data that this points to, which includes // reference counting symbol names. - release_C_heap_structures_internal(); + // Can't release the constant pool here because the constant pool can be + // deallocated separately from the InstanceKlass for default methods and + // redefine classes. + release_C_heap_structures(/* release_constant_pool */ false); deallocate_methods(loader_data, methods()); set_methods(NULL); @@ -1388,7 +1391,7 @@ bool InstanceKlass::is_same_or_direct_interface(Klass *k) const { objArrayOop InstanceKlass::allocate_objArray(int n, int length, TRAPS) { check_array_allocation_length(length, arrayOopDesc::max_array_length(T_OBJECT), CHECK_NULL); - int size = objArrayOopDesc::object_size(length); + size_t size = objArrayOopDesc::object_size(length); Klass* ak = array_klass(n, CHECK_NULL); objArrayOop o = (objArrayOop)Universe::heap()->array_allocate(ak, size, length, /* do_zero */ true, CHECK_NULL); @@ -1405,14 +1408,15 @@ instanceOop InstanceKlass::register_finalizer(instanceOop i, TRAPS) { // Pass the handle as argument, JavaCalls::call expects oop as jobjects JavaValue result(T_VOID); JavaCallArguments args(h_i); - methodHandle mh (THREAD, Universe::finalizer_register_method()); + methodHandle mh(THREAD, Universe::finalizer_register_method()); JavaCalls::call(&result, mh, &args, CHECK_NULL); + MANAGEMENT_ONLY(FinalizerService::on_register(h_i(), THREAD);) return h_i(); } instanceOop InstanceKlass::allocate_instance(TRAPS) { bool has_finalizer_flag = has_finalizer(); // Query before possible GC - int size = size_helper(); // Query before forming handle. + size_t size = size_helper(); // Query before forming handle. instanceOop i; @@ -1637,7 +1641,8 @@ bool InstanceKlass::find_field_from_offset(int offset, bool is_static, fieldDesc void InstanceKlass::methods_do(void f(Method* method)) { // Methods aren't stable until they are loaded. This can be read outside // a lock through the ClassLoaderData for profiling - if (!is_loaded()) { + // Redefined scratch classes are on the list and need to be cleaned + if (!is_loaded() && !is_scratch_class()) { return; } @@ -1720,16 +1725,6 @@ void InstanceKlass::print_nonstatic_fields(FieldClosure* cl) { } } -void InstanceKlass::array_klasses_do(void f(Klass* k, TRAPS), TRAPS) { - if (array_klasses() != NULL) - array_klasses()->array_klasses_do(f, THREAD); -} - -void InstanceKlass::array_klasses_do(void f(Klass* k)) { - if (array_klasses() != NULL) - array_klasses()->array_klasses_do(f); -} - #ifdef ASSERT static int linear_search(const Array* methods, const Symbol* name, @@ -2584,6 +2579,9 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl constants()->restore_unshareable_info(CHECK); if (array_klasses() != NULL) { + // To get a consistent list of classes we need MultiArray_lock to ensure + // array classes aren't observed while they are being restored. + MutexLocker ml(MultiArray_lock); // Array classes have null protection domain. // --> see ArrayKlass::complete_create_array_klass() array_klasses()->restore_unshareable_info(ClassLoaderData::the_null_class_loader_data(), Handle(), CHECK); @@ -2687,22 +2685,13 @@ static void method_release_C_heap_structures(Method* m) { m->release_C_heap_structures(); } -void InstanceKlass::release_C_heap_structures() { - +// Called also by InstanceKlass::deallocate_contents, with false for release_constant_pool. +void InstanceKlass::release_C_heap_structures(bool release_constant_pool) { // Clean up C heap - release_C_heap_structures_internal(); - constants()->release_C_heap_structures(); + Klass::release_C_heap_structures(); // Deallocate and call destructors for MDO mutexes methods_do(method_release_C_heap_structures); -} - -void InstanceKlass::release_C_heap_structures_internal() { - Klass::release_C_heap_structures(); - - // Can't release the constant pool here because the constant pool can be - // deallocated separately from the InstanceKlass for default methods and - // redefine classes. // Deallocate oop map cache if (_oop_map_cache != NULL) { @@ -2738,6 +2727,10 @@ void InstanceKlass::release_C_heap_structures_internal() { #endif FREE_C_HEAP_ARRAY(char, _source_debug_extension); + + if (release_constant_pool) { + constants()->release_C_heap_structures(); + } } void InstanceKlass::set_source_debug_extension(const char* array, int length) { @@ -3062,6 +3055,18 @@ InstanceKlass* InstanceKlass::compute_enclosing_class(bool* inner_is_member, TRA constantPoolHandle i_cp(THREAD, constants()); if (ooff != 0) { Klass* ok = i_cp->klass_at(ooff, CHECK_NULL); + if (!ok->is_instance_klass()) { + // If the outer class is not an instance klass then it cannot have + // declared any inner classes. + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_IncompatibleClassChangeError(), + "%s and %s disagree on InnerClasses attribute", + ok->external_name(), + external_name()); + return NULL; + } outer_klass = InstanceKlass::cast(ok); *inner_is_member = true; } @@ -3540,7 +3545,7 @@ void InstanceKlass::oop_print_on(oop obj, outputStream* st) { } } - st->print_cr(BULLET"---- fields (total size %d words):", oop_size(obj)); + st->print_cr(BULLET"---- fields (total size " SIZE_FORMAT " words):", oop_size(obj)); FieldPrinter print_field(st, obj); print_nonstatic_fields(&print_field); @@ -3550,7 +3555,7 @@ void InstanceKlass::oop_print_on(oop obj, outputStream* st) { st->cr(); Klass* real_klass = java_lang_Class::as_Klass(obj); if (real_klass != NULL && real_klass->is_instance_klass()) { - st->print_cr(BULLET"---- static fields (%d words):", java_lang_Class::static_oop_field_count(obj)); + st->print_cr(BULLET"---- static fields (%d):", java_lang_Class::static_oop_field_count(obj)); InstanceKlass::cast(real_klass)->do_local_static_fields(&print_field); } } else if (this == vmClasses::MethodType_klass()) { @@ -3982,8 +3987,6 @@ void InstanceKlass::purge_previous_version_list() { // so will be deallocated during the next phase of class unloading. log_trace(redefine, class, iklass, purge) ("previous version " INTPTR_FORMAT " is dead.", p2i(pv_node)); - // For debugging purposes. - pv_node->set_is_scratch_class(); // Unlink from previous version list. assert(pv_node->class_loader_data() == loader_data, "wrong loader_data"); InstanceKlass* next = pv_node->previous_versions(); @@ -4098,8 +4101,6 @@ void InstanceKlass::add_previous_version(InstanceKlass* scratch_class, ConstantPool* cp_ref = scratch_class->constants(); if (!cp_ref->on_stack()) { log_trace(redefine, class, iklass, add)("scratch class not added; no methods are running"); - // For debugging purposes. - scratch_class->set_is_scratch_class(); scratch_class->class_loader_data()->add_to_deallocate_list(scratch_class); return; } diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 8f9d1dca070553a1466d52f24597cbd2db02cd7a..a3e8da4c8a48d2d33c33c8755014189194cfc59e 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -393,7 +393,6 @@ class InstanceKlass: public Klass { // array klasses ObjArrayKlass* array_klasses() const { return _array_klasses; } inline ObjArrayKlass* array_klasses_acquire() const; // load with acquire semantics - void set_array_klasses(ObjArrayKlass* k) { _array_klasses = k; } inline void release_set_array_klasses(ObjArrayKlass* k); // store with release semantics // methods @@ -994,7 +993,7 @@ public: GrowableArray* compute_secondary_supers(int num_extra_slots, Array* transitive_interfaces); bool can_be_primary_super_slow() const; - int oop_size(oop obj) const { return size_helper(); } + size_t oop_size(oop obj) const { return size_helper(); } // slow because it's a virtual call and used for verifying the layout_helper. // Using the layout_helper bits, we can call is_instance_klass without a virtual call. DEBUG_ONLY(bool is_instance_klass_slow() const { return true; }) @@ -1006,8 +1005,6 @@ public: void print_nonstatic_fields(FieldClosure* cl); // including inherited and injected fields void methods_do(void f(Method* method)); - void array_klasses_do(void f(Klass* k)); - void array_klasses_do(void f(Klass* k, TRAPS), TRAPS); static InstanceKlass* cast(Klass* k) { return const_cast(cast(const_cast(k))); @@ -1104,7 +1101,7 @@ public: // callbacks for actions during class unloading static void unload_class(InstanceKlass* ik); - virtual void release_C_heap_structures(); + virtual void release_C_heap_structures(bool release_constant_pool = true); // Naming const char* signature_name() const; @@ -1221,9 +1218,6 @@ private: StaticLookupMode static_mode, PrivateLookupMode private_mode); - // Free CHeap allocated fields. - void release_C_heap_structures_internal(); - #if INCLUDE_JVMTI // RedefineClasses support void link_previous_versions(InstanceKlass* pv) { _previous_versions = pv; } diff --git a/src/hotspot/share/oops/instanceKlass.inline.hpp b/src/hotspot/share/oops/instanceKlass.inline.hpp index 8b6e2c335b61b11902c33ce3c2a7ab32fdaf3c50..3522631f59b8f0faaf47df75031a24c27ee70779 100644 --- a/src/hotspot/share/oops/instanceKlass.inline.hpp +++ b/src/hotspot/share/oops/instanceKlass.inline.hpp @@ -84,7 +84,7 @@ inline void InstanceKlass::release_set_methods_jmethod_ids(jmethodID* jmeths) { template ALWAYSINLINE void InstanceKlass::oop_oop_iterate_oop_map(OopMapBlock* map, oop obj, OopClosureType* closure) { - T* p = (T*)obj->obj_field_addr(map->offset()); + T* p = obj->field_addr(map->offset()); T* const end = p + map->count(); for (; p < end; ++p) { @@ -94,7 +94,7 @@ ALWAYSINLINE void InstanceKlass::oop_oop_iterate_oop_map(OopMapBlock* map, oop o template ALWAYSINLINE void InstanceKlass::oop_oop_iterate_oop_map_reverse(OopMapBlock* map, oop obj, OopClosureType* closure) { - T* const start = (T*)obj->obj_field_addr(map->offset()); + T* const start = obj->field_addr(map->offset()); T* p = start + map->count(); while (start < p) { @@ -105,7 +105,7 @@ ALWAYSINLINE void InstanceKlass::oop_oop_iterate_oop_map_reverse(OopMapBlock* ma template ALWAYSINLINE void InstanceKlass::oop_oop_iterate_oop_map_bounded(OopMapBlock* map, oop obj, OopClosureType* closure, MemRegion mr) { - T* p = (T*)obj->obj_field_addr(map->offset()); + T* p = obj->field_addr(map->offset()); T* end = p + map->count(); T* const l = (T*)mr.start(); diff --git a/src/hotspot/share/oops/instanceMirrorKlass.cpp b/src/hotspot/share/oops/instanceMirrorKlass.cpp index e405b10482676d7c86c4551c6c51866454ed6d4c..058e28b469a59119b15db193cc675500ac5db733 100644 --- a/src/hotspot/share/oops/instanceMirrorKlass.cpp +++ b/src/hotspot/share/oops/instanceMirrorKlass.cpp @@ -38,7 +38,7 @@ int InstanceMirrorKlass::_offset_of_static_fields = 0; -int InstanceMirrorKlass::instance_size(Klass* k) { +size_t InstanceMirrorKlass::instance_size(Klass* k) { if (k != NULL && k->is_instance_klass()) { return align_object_size(size_helper() + InstanceKlass::cast(k)->static_field_size()); } @@ -47,15 +47,15 @@ int InstanceMirrorKlass::instance_size(Klass* k) { instanceOop InstanceMirrorKlass::allocate_instance(Klass* k, TRAPS) { // Query before forming handle. - int size = instance_size(k); - assert(size > 0, "total object size must be positive: %d", size); + size_t size = instance_size(k); + assert(size > 0, "total object size must be non-zero: " SIZE_FORMAT, size); // Since mirrors can be variable sized because of the static fields, store // the size in the mirror itself. return (instanceOop)Universe::heap()->class_allocate(this, size, THREAD); } -int InstanceMirrorKlass::oop_size(oop obj) const { +size_t InstanceMirrorKlass::oop_size(oop obj) const { return java_lang_Class::oop_size(obj); } diff --git a/src/hotspot/share/oops/instanceMirrorKlass.hpp b/src/hotspot/share/oops/instanceMirrorKlass.hpp index d5a7be454a1589119debc34697541a0ba9867f97..154151932f7bbaceea60cc4c8f9185d18906d568 100644 --- a/src/hotspot/share/oops/instanceMirrorKlass.hpp +++ b/src/hotspot/share/oops/instanceMirrorKlass.hpp @@ -65,7 +65,7 @@ class InstanceMirrorKlass: public InstanceKlass { } // Returns the size of the instance including the extra static fields. - virtual int oop_size(oop obj) const; + virtual size_t oop_size(oop obj) const; // Static field offset is an offset into the Heap, should be converted by // based on UseCompressedOop for traversal @@ -86,7 +86,7 @@ class InstanceMirrorKlass: public InstanceKlass { int compute_static_oop_field_count(oop obj); // Given a Klass return the size of the instance - int instance_size(Klass* k); + size_t instance_size(Klass* k); // allocation instanceOop allocate_instance(Klass* k, TRAPS); diff --git a/src/hotspot/share/oops/instanceOop.hpp b/src/hotspot/share/oops/instanceOop.hpp index 005cf5a9e88b4f0603cc8fd7d6a263b34d95ab71..8de3b1a742cc0cf72c35a66c456673fd4c044006 100644 --- a/src/hotspot/share/oops/instanceOop.hpp +++ b/src/hotspot/share/oops/instanceOop.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #define SHARE_OOPS_INSTANCEOOP_HPP #include "oops/oop.hpp" +#include // An instanceOop is an instance of a Java Class // Evaluating "new HashTable()" will create an instanceOop. @@ -44,4 +45,7 @@ class instanceOopDesc : public oopDesc { } }; +// See similar requirement for oopDesc. +static_assert(std::is_trivially_default_constructible::value, "required"); + #endif // SHARE_OOPS_INSTANCEOOP_HPP diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index cfb4681670931dca7c2241c9403f63ff0bed5ca3..f431c2425d8758f1cd0111bd6d829330201680d6 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -105,7 +105,7 @@ bool Klass::is_subclass_of(const Klass* k) const { return false; } -void Klass::release_C_heap_structures() { +void Klass::release_C_heap_structures(bool release_constant_pool) { if (_name != NULL) _name->decrement_refcount(); } diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index 6fb94c3c8c7b4171410351d4e664140caeb3ee0f..d72d1c711036b5fc9bf368939cc2df230fe4a2cd 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -549,8 +549,8 @@ protected: // ALL FUNCTIONS BELOW THIS POINT ARE DISPATCHED FROM AN OOP // These functions describe behavior for the oop not the KLASS. - // actual oop size of obj in memory - virtual int oop_size(oop obj) const = 0; + // actual oop size of obj in memory in word size. + virtual size_t oop_size(oop obj) const = 0; // Size of klass in word size. virtual int size() const = 0; @@ -653,8 +653,6 @@ protected: clean_weak_klass_links(/*unloading_occurred*/ true , /* clean_alive_klasses */ false); } - virtual void array_klasses_do(void f(Klass* k)) {} - // Return self, except for abstract classes with exactly 1 // implementor. Then return the 1 concrete implementation. Klass *up_cast_abstract(); @@ -663,7 +661,7 @@ protected: Symbol* name() const { return _name; } void set_name(Symbol* n); - virtual void release_C_heap_structures(); + virtual void release_C_heap_structures(bool release_constant_pool = true); public: virtual jint compute_modifier_flags() const = 0; diff --git a/src/hotspot/share/oops/klassVtable.cpp b/src/hotspot/share/oops/klassVtable.cpp index 99974dce03b636cba9a40b782ecc51df6359776e..b107f5d70b2807418502c8b50a56e558e606604f 100644 --- a/src/hotspot/share/oops/klassVtable.cpp +++ b/src/hotspot/share/oops/klassVtable.cpp @@ -1511,9 +1511,6 @@ int klassItable::compute_itable_size(Array* transitive_interface // There's alway an extra itable entry so we can null-terminate it. int itable_size = calc_itable_size(cic.nof_interfaces() + 1, cic.nof_methods()); - // Statistics - update_stats(itable_size * wordSize); - return itable_size; } @@ -1628,75 +1625,4 @@ void vtableEntry::print() { tty->print("m " PTR_FORMAT " ", p2i(method())); } } - -class VtableStats : AllStatic { - public: - static int no_klasses; // # classes with vtables - static int no_array_klasses; // # array classes - static int no_instance_klasses; // # instanceKlasses - static int sum_of_vtable_len; // total # of vtable entries - static int sum_of_array_vtable_len; // total # of vtable entries in array klasses only - static int fixed; // total fixed overhead in bytes - static int filler; // overhead caused by filler bytes - static int entries; // total bytes consumed by vtable entries - static int array_entries; // total bytes consumed by array vtable entries - - static void do_class(Klass* k) { - Klass* kl = k; - klassVtable vt = kl->vtable(); - no_klasses++; - if (kl->is_instance_klass()) { - no_instance_klasses++; - kl->array_klasses_do(do_class); - } - if (kl->is_array_klass()) { - no_array_klasses++; - sum_of_array_vtable_len += vt.length(); - } - sum_of_vtable_len += vt.length(); - } - - static void compute() { - LockedClassesDo locked_do_class(&do_class); - ClassLoaderDataGraph::classes_do(&locked_do_class); - fixed = no_klasses * oopSize; // vtable length - // filler size is a conservative approximation - filler = oopSize * (no_klasses - no_instance_klasses) * (sizeof(InstanceKlass) - sizeof(ArrayKlass) - 1); - entries = sizeof(vtableEntry) * sum_of_vtable_len; - array_entries = sizeof(vtableEntry) * sum_of_array_vtable_len; - } -}; - -int VtableStats::no_klasses = 0; -int VtableStats::no_array_klasses = 0; -int VtableStats::no_instance_klasses = 0; -int VtableStats::sum_of_vtable_len = 0; -int VtableStats::sum_of_array_vtable_len = 0; -int VtableStats::fixed = 0; -int VtableStats::filler = 0; -int VtableStats::entries = 0; -int VtableStats::array_entries = 0; - -void klassVtable::print_statistics() { - ResourceMark rm; - VtableStats::compute(); - tty->print_cr("vtable statistics:"); - tty->print_cr("%6d classes (%d instance, %d array)", VtableStats::no_klasses, VtableStats::no_instance_klasses, VtableStats::no_array_klasses); - int total = VtableStats::fixed + VtableStats::filler + VtableStats::entries; - tty->print_cr("%6d bytes fixed overhead (refs + vtable object header)", VtableStats::fixed); - tty->print_cr("%6d bytes filler overhead", VtableStats::filler); - tty->print_cr("%6d bytes for vtable entries (%d for arrays)", VtableStats::entries, VtableStats::array_entries); - tty->print_cr("%6d bytes total", total); -} - -int klassItable::_total_classes; // Total no. of classes with itables -size_t klassItable::_total_size; // Total no. of bytes used for itables - -void klassItable::print_statistics() { - tty->print_cr("itable statistics:"); - tty->print_cr("%6d classes with itables", _total_classes); - tty->print_cr(SIZE_FORMAT_W(6) " K uses for itables (average by class: " SIZE_FORMAT " bytes)", - _total_size / K, _total_size / _total_classes); -} - #endif // PRODUCT diff --git a/src/hotspot/share/oops/klassVtable.hpp b/src/hotspot/share/oops/klassVtable.hpp index bf26dea7bd5e1e7a4f95a4bddd7e9488163d3ddd..d00972f4c783d6d1b836d3b107162e2d09394772 100644 --- a/src/hotspot/share/oops/klassVtable.hpp +++ b/src/hotspot/share/oops/klassVtable.hpp @@ -97,7 +97,6 @@ class klassVtable { // Debugging code void print() PRODUCT_RETURN; void verify(outputStream* st, bool force = false); - static void print_statistics() PRODUCT_RETURN; protected: friend class vtableEntry; @@ -319,8 +318,6 @@ class klassItable { static int compute_itable_size(Array* transitive_interfaces); static void setup_itable_offset_table(InstanceKlass* klass); - // Debugging/Statistics - static void print_statistics() PRODUCT_RETURN; private: intptr_t* vtable_start() const { return ((intptr_t*)_klass) + _table_offset; } intptr_t* method_start() const { return vtable_start() + _size_offset_table * itableOffsetEntry::size(); } @@ -328,11 +325,6 @@ class klassItable { // Helper methods static int calc_itable_size(int num_interfaces, int num_methods) { return (num_interfaces * itableOffsetEntry::size()) + (num_methods * itableMethodEntry::size()); } - // Statistics - NOT_PRODUCT(static int _total_classes;) // Total no. of classes with itables - NOT_PRODUCT(static size_t _total_size;) // Total no. of bytes used for itables - - static void update_stats(int size) PRODUCT_RETURN NOT_PRODUCT({ _total_classes++; _total_size += size; }) }; #endif // SHARE_OOPS_KLASSVTABLE_HPP diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index 62adf95971ab6de91f6a8528e9541a8131c85d8e..febfa7015a78ea3746c56b41907d64e4d20adde9 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -73,10 +73,13 @@ class markWord { public: explicit markWord(uintptr_t value) : _value(value) {} - markWord() { /* uninitialized */} + markWord() = default; // Doesn't initialize _value. // It is critical for performance that this class be trivially // destructable, copyable, and assignable. + ~markWord() = default; + markWord(const markWord&) = default; + markWord& operator=(const markWord&) = default; static markWord from_pointer(void* ptr) { return markWord((uintptr_t)ptr); diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index 69f7ae1b85d1fd15d49e029123d24a6abcc45c28..89d232ca094587a4e7370a4ac642e7333d2606c8 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -1206,7 +1206,8 @@ void MethodData::post_initialize(BytecodeStream* stream) { // Initialize the MethodData* corresponding to a given method. MethodData::MethodData(const methodHandle& method) : _method(method()), - _extra_data_lock(Mutex::leaf, "MDO extra data lock", Mutex::_safepoint_check_always), + // Holds Compile_lock + _extra_data_lock(Mutex::safepoint-2, "MDOExtraData_lock"), _compiler_counters(), _parameters_type_data_di(parameters_uninitialized) { initialize(); diff --git a/src/hotspot/share/oops/objArrayKlass.cpp b/src/hotspot/share/oops/objArrayKlass.cpp index da75ec35ecb79e7f1d2448f97734ebb9f38d4a9e..fab9bd35487f8c606e197924336722c1c4d252a9 100644 --- a/src/hotspot/share/oops/objArrayKlass.cpp +++ b/src/hotspot/share/oops/objArrayKlass.cpp @@ -155,20 +155,18 @@ ObjArrayKlass::ObjArrayKlass(int n, Klass* element_klass, Symbol* name) : ArrayK assert(is_objArray_klass(), "sanity"); } -int ObjArrayKlass::oop_size(oop obj) const { +size_t ObjArrayKlass::oop_size(oop obj) const { assert(obj->is_objArray(), "must be object array"); return objArrayOop(obj)->object_size(); } objArrayOop ObjArrayKlass::allocate(int length, TRAPS) { check_array_allocation_length(length, arrayOopDesc::max_array_length(T_OBJECT), CHECK_NULL); - int size = objArrayOopDesc::object_size(length); + size_t size = objArrayOopDesc::object_size(length); return (objArrayOop)Universe::heap()->array_allocate(this, size, length, /* do_zero */ true, THREAD); } -static int multi_alloc_counter = 0; - oop ObjArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) { int length = *sizes; // Call to lower_dimension uses this pointer, so most be called before a diff --git a/src/hotspot/share/oops/objArrayKlass.hpp b/src/hotspot/share/oops/objArrayKlass.hpp index 6b33e9f2da47fbbc2621566db6e2431e50098c15..e4bd54b9ed1e4da01f3675c2ea5ada7c08c7a6a0 100644 --- a/src/hotspot/share/oops/objArrayKlass.hpp +++ b/src/hotspot/share/oops/objArrayKlass.hpp @@ -72,7 +72,7 @@ class ObjArrayKlass : public ArrayKlass { GrowableArray* compute_secondary_supers(int num_extra_slots, Array* transitive_interfaces); DEBUG_ONLY(bool is_objArray_klass_slow() const { return true; }) - int oop_size(oop obj) const; + size_t oop_size(oop obj) const; // Allocation static ObjArrayKlass* allocate_objArray_klass(ClassLoaderData* loader_data, diff --git a/src/hotspot/share/oops/objArrayOop.hpp b/src/hotspot/share/oops/objArrayOop.hpp index 5952c058ef582c3c60c033db4faa993b179f50c5..e1a6226f88ae8806c127f9468e0f68b57dfe54fe 100644 --- a/src/hotspot/share/oops/objArrayOop.hpp +++ b/src/hotspot/share/oops/objArrayOop.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "oops/arrayOop.hpp" #include "utilities/align.hpp" +#include class Klass; @@ -90,15 +91,15 @@ private: // Sizing static int header_size() { return arrayOopDesc::header_size(T_OBJECT); } - int object_size() { return object_size(length()); } + size_t object_size() { return object_size(length()); } - static int object_size(int length) { + static size_t object_size(int length) { // This returns the object size in HeapWords. uint asz = array_size(length); uint osz = align_object_size(header_size() + asz); assert(osz >= asz, "no overflow"); assert((int)osz > 0, "no overflow"); - return (int)osz; + return (size_t)osz; } Klass* element_klass(); @@ -109,4 +110,7 @@ public: void oop_iterate_range(OopClosureType* blk, int start, int end); }; +// See similar requirement for oopDesc. +static_assert(std::is_trivially_default_constructible::value, "required"); + #endif // SHARE_OOPS_OBJARRAYOOP_HPP diff --git a/src/hotspot/share/oops/oop.cpp b/src/hotspot/share/oops/oop.cpp index 88601f1aa2bf5109a9f640ae528a0fe70a8b1c36..145929970914d9aac81c9df647d3a5751fa351f5 100644 --- a/src/hotspot/share/oops/oop.cpp +++ b/src/hotspot/share/oops/oop.cpp @@ -179,41 +179,41 @@ void oopDesc::obj_field_put_raw(int offset, oop value) { RawAcces void oopDesc::release_obj_field_put(int offset, oop value) { HeapAccess::oop_store_at(as_oop(), offset, value); } void oopDesc::obj_field_put_volatile(int offset, oop value) { HeapAccess::oop_store_at(as_oop(), offset, value); } -address oopDesc::address_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -address oopDesc::address_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } +address oopDesc::address_field(int offset) const { return *field_addr
      (offset); } +address oopDesc::address_field_acquire(int offset) const { return Atomic::load_acquire(field_addr
      (offset)); } -void oopDesc::address_field_put(int offset, address value) { HeapAccess<>::store_at(as_oop(), offset, value); } -void oopDesc::release_address_field_put(int offset, address value) { HeapAccess::store_at(as_oop(), offset, value); } +void oopDesc::address_field_put(int offset, address value) { *field_addr
      (offset) = value; } +void oopDesc::release_address_field_put(int offset, address value) { Atomic::release_store(field_addr
      (offset), value); } -Metadata* oopDesc::metadata_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -void oopDesc::metadata_field_put(int offset, Metadata* value) { HeapAccess<>::store_at(as_oop(), offset, value); } +Metadata* oopDesc::metadata_field(int offset) const { return *field_addr(offset); } +void oopDesc::metadata_field_put(int offset, Metadata* value) { *field_addr(offset) = value; } -Metadata* oopDesc::metadata_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_metadata_field_put(int offset, Metadata* value) { HeapAccess::store_at(as_oop(), offset, value); } +Metadata* oopDesc::metadata_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_metadata_field_put(int offset, Metadata* value) { Atomic::release_store(field_addr(offset), value); } -jbyte oopDesc::byte_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_byte_field_put(int offset, jbyte value) { HeapAccess::store_at(as_oop(), offset, value); } +jbyte oopDesc::byte_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_byte_field_put(int offset, jbyte value) { Atomic::release_store(field_addr(offset), value); } -jchar oopDesc::char_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_char_field_put(int offset, jchar value) { HeapAccess::store_at(as_oop(), offset, value); } +jchar oopDesc::char_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_char_field_put(int offset, jchar value) { Atomic::release_store(field_addr(offset), value); } -jboolean oopDesc::bool_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_bool_field_put(int offset, jboolean value) { HeapAccess::store_at(as_oop(), offset, jboolean(value & 1)); } +jboolean oopDesc::bool_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_bool_field_put(int offset, jboolean value) { Atomic::release_store(field_addr(offset), jboolean(value & 1)); } -jint oopDesc::int_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_int_field_put(int offset, jint value) { HeapAccess::store_at(as_oop(), offset, value); } +jint oopDesc::int_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_int_field_put(int offset, jint value) { Atomic::release_store(field_addr(offset), value); } -jshort oopDesc::short_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_short_field_put(int offset, jshort value) { HeapAccess::store_at(as_oop(), offset, value); } +jshort oopDesc::short_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_short_field_put(int offset, jshort value) { Atomic::release_store(field_addr(offset), value); } -jlong oopDesc::long_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_long_field_put(int offset, jlong value) { HeapAccess::store_at(as_oop(), offset, value); } +jlong oopDesc::long_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_long_field_put(int offset, jlong value) { Atomic::release_store(field_addr(offset), value); } -jfloat oopDesc::float_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_float_field_put(int offset, jfloat value) { HeapAccess::store_at(as_oop(), offset, value); } +jfloat oopDesc::float_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_float_field_put(int offset, jfloat value) { Atomic::release_store(field_addr(offset), value); } -jdouble oopDesc::double_field_acquire(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -void oopDesc::release_double_field_put(int offset, jdouble value) { HeapAccess::store_at(as_oop(), offset, value); } +jdouble oopDesc::double_field_acquire(int offset) const { return Atomic::load_acquire(field_addr(offset)); } +void oopDesc::release_double_field_put(int offset, jdouble value) { Atomic::release_store(field_addr(offset), value); } #ifdef ASSERT void oopDesc::verify_forwardee(oop forwardee) { diff --git a/src/hotspot/share/oops/oop.hpp b/src/hotspot/share/oops/oop.hpp index b30e7a2cb66a164c2989458ed8b749143c1c77dd..82335faea05efa624bd2ddef510a531748887eed 100644 --- a/src/hotspot/share/oops/oop.hpp +++ b/src/hotspot/share/oops/oop.hpp @@ -31,7 +31,9 @@ #include "oops/markWord.hpp" #include "oops/metadata.hpp" #include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" +#include // oopDesc is the top baseclass for objects classes. The {name}Desc classes describe // the format of Java objects so the fields can be accessed from C++. @@ -57,7 +59,14 @@ class oopDesc { narrowKlass _compressed_klass; } _metadata; + // There may be ordering constraints on the initialization of fields that + // make use of the C++ copy/assign incorrect. + NONCOPYABLE(oopDesc); + public: + // Must be trivial; see verifying static assert after the class. + oopDesc() = default; + inline markWord mark() const; inline markWord mark_acquire() const; inline markWord* mark_addr() const; @@ -93,11 +102,11 @@ class oopDesc { inline bool is_a(Klass* k) const; // Returns the actual oop size of the object - inline int size(); + inline size_t size(); // Sometimes (for complicated concurrency-related reasons), it is useful // to be able to figure out the size of an object knowing its klass. - inline int size_given_klass(Klass* klass); + inline size_t size_given_klass(Klass* klass); // type test operations (inlined in oop.inline.hpp) inline bool is_instance() const; @@ -115,11 +124,8 @@ class oopDesc { inline oop as_oop() const { return const_cast(this); } public: - // field addresses in oop - inline void* field_addr(int offset) const; - - // Need this as public for garbage collection. - template inline T* obj_field_addr(int offset) const; + template + inline T* field_addr(int offset) const; template inline size_t field_offset(T* p) const; @@ -266,10 +272,10 @@ class oopDesc { inline void oop_iterate(OopClosureType* cl, MemRegion mr); template - inline int oop_iterate_size(OopClosureType* cl); + inline size_t oop_iterate_size(OopClosureType* cl); template - inline int oop_iterate_size(OopClosureType* cl, MemRegion mr); + inline size_t oop_iterate_size(OopClosureType* cl, MemRegion mr); template inline void oop_iterate_backwards(OopClosureType* cl); @@ -311,4 +317,11 @@ class oopDesc { DEBUG_ONLY(bool get_UseG1GC();) }; +// An oopDesc is not initialized via a constructor. Space is allocated in +// the Java heap, and static functions provided here on HeapWord* are used +// to fill in certain parts of that memory. The allocated memory is then +// treated as referring to an oopDesc. For that to be valid, the oopDesc +// class must have a trivial default constructor (C++14 3.8/1). +static_assert(std::is_trivially_default_constructible::value, "required"); + #endif // SHARE_OOPS_OOP_HPP diff --git a/src/hotspot/share/oops/oop.inline.hpp b/src/hotspot/share/oops/oop.inline.hpp index 84b43bf8ff8c14e5799994af903af7362261f5ad..c161e479deb4f8e5c3239f43f45a9e1fa1f137f5 100644 --- a/src/hotspot/share/oops/oop.inline.hpp +++ b/src/hotspot/share/oops/oop.inline.hpp @@ -143,13 +143,13 @@ bool oopDesc::is_a(Klass* k) const { return klass()->is_subtype_of(k); } -int oopDesc::size() { +size_t oopDesc::size() { return size_given_klass(klass()); } -int oopDesc::size_given_klass(Klass* klass) { +size_t oopDesc::size_given_klass(Klass* klass) { int lh = klass->layout_helper(); - int s; + size_t s; // lh is now a value computed at class initialization that may hint // at the size. For instances, this is positive and equal to the @@ -182,7 +182,7 @@ int oopDesc::size_given_klass(Klass* klass) { // This code could be simplified, but by keeping array_header_in_bytes // in units of bytes and doing it this way we can round up just once, // skipping the intermediate round to HeapWordSize. - s = (int)(align_up(size_in_bytes, MinObjAlignmentInBytes) / HeapWordSize); + s = align_up(size_in_bytes, MinObjAlignmentInBytes) / HeapWordSize; // UseParallelGC and UseG1GC can change the length field // of an "old copy" of an object array in the young gen so it indicates @@ -198,8 +198,8 @@ int oopDesc::size_given_klass(Klass* klass) { } } - assert(s > 0, "Oop size must be greater than zero, not %d", s); - assert(is_object_aligned(s), "Oop size is not properly aligned: %d", s); + assert(s > 0, "Oop size must be greater than zero, not " SIZE_FORMAT, s); + assert(is_object_aligned(s), "Oop size is not properly aligned: " SIZE_FORMAT, s); return s; } @@ -208,10 +208,8 @@ bool oopDesc::is_array() const { return klass()->is_array_klass(); } bool oopDesc::is_objArray() const { return klass()->is_objArray_klass(); } bool oopDesc::is_typeArray() const { return klass()->is_typeArray_klass(); } -void* oopDesc::field_addr(int offset) const { return reinterpret_cast(cast_from_oop(as_oop()) + offset); } - -template -T* oopDesc::obj_field_addr(int offset) const { return (T*) field_addr(offset); } +template +T* oopDesc::field_addr(int offset) const { return reinterpret_cast(cast_from_oop(as_oop()) + offset); } template size_t oopDesc::field_offset(T* p) const { return pointer_delta((void*)p, (void*)this, 1); } @@ -222,30 +220,30 @@ inline oop oopDesc::obj_field(int offset) const { return Hea inline void oopDesc::obj_field_put(int offset, oop value) { HeapAccess<>::oop_store_at(as_oop(), offset, value); } -inline jbyte oopDesc::byte_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -inline void oopDesc::byte_field_put(int offset, jbyte value) { HeapAccess<>::store_at(as_oop(), offset, value); } +inline jbyte oopDesc::byte_field(int offset) const { return *field_addr(offset); } +inline void oopDesc::byte_field_put(int offset, jbyte value) { *field_addr(offset) = value; } -inline jchar oopDesc::char_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -inline void oopDesc::char_field_put(int offset, jchar value) { HeapAccess<>::store_at(as_oop(), offset, value); } +inline jchar oopDesc::char_field(int offset) const { return *field_addr(offset); } +inline void oopDesc::char_field_put(int offset, jchar value) { *field_addr(offset) = value; } -inline jboolean oopDesc::bool_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -inline void oopDesc::bool_field_put(int offset, jboolean value) { HeapAccess<>::store_at(as_oop(), offset, jboolean(value & 1)); } -inline jboolean oopDesc::bool_field_volatile(int offset) const { return HeapAccess::load_at(as_oop(), offset); } -inline void oopDesc::bool_field_put_volatile(int offset, jboolean value) { HeapAccess::store_at(as_oop(), offset, jboolean(value & 1)); } -inline jshort oopDesc::short_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -inline void oopDesc::short_field_put(int offset, jshort value) { HeapAccess<>::store_at(as_oop(), offset, value); } +inline jboolean oopDesc::bool_field(int offset) const { return *field_addr(offset); } +inline void oopDesc::bool_field_put(int offset, jboolean value) { *field_addr(offset) = jboolean(value & 1); } +inline jboolean oopDesc::bool_field_volatile(int offset) const { return RawAccess::load(field_addr(offset)); } +inline void oopDesc::bool_field_put_volatile(int offset, jboolean value) { RawAccess::store(field_addr(offset), jboolean(value & 1)); } +inline jshort oopDesc::short_field(int offset) const { return *field_addr(offset); } +inline void oopDesc::short_field_put(int offset, jshort value) { *field_addr(offset) = value; } -inline jint oopDesc::int_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -inline void oopDesc::int_field_put(int offset, jint value) { HeapAccess<>::store_at(as_oop(), offset, value); } +inline jint oopDesc::int_field(int offset) const { return *field_addr(offset); } +inline void oopDesc::int_field_put(int offset, jint value) { *field_addr(offset) = value; } -inline jlong oopDesc::long_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -inline void oopDesc::long_field_put(int offset, jlong value) { HeapAccess<>::store_at(as_oop(), offset, value); } +inline jlong oopDesc::long_field(int offset) const { return *field_addr(offset); } +inline void oopDesc::long_field_put(int offset, jlong value) { *field_addr(offset) = value; } -inline jfloat oopDesc::float_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -inline void oopDesc::float_field_put(int offset, jfloat value) { HeapAccess<>::store_at(as_oop(), offset, value); } +inline jfloat oopDesc::float_field(int offset) const { return *field_addr(offset); } +inline void oopDesc::float_field_put(int offset, jfloat value) { *field_addr(offset) = value; } -inline jdouble oopDesc::double_field(int offset) const { return HeapAccess<>::load_at(as_oop(), offset); } -inline void oopDesc::double_field_put(int offset, jdouble value) { HeapAccess<>::store_at(as_oop(), offset, value); } +inline jdouble oopDesc::double_field(int offset) const { return *field_addr(offset); } +inline void oopDesc::double_field_put(int offset, jdouble value) { *field_addr(offset) = value; } bool oopDesc::is_locked() const { return mark().is_locked(); @@ -324,17 +322,17 @@ void oopDesc::oop_iterate(OopClosureType* cl, MemRegion mr) { } template -int oopDesc::oop_iterate_size(OopClosureType* cl) { +size_t oopDesc::oop_iterate_size(OopClosureType* cl) { Klass* k = klass(); - int size = size_given_klass(k); + size_t size = size_given_klass(k); OopIteratorClosureDispatch::oop_oop_iterate(cl, this, k); return size; } template -int oopDesc::oop_iterate_size(OopClosureType* cl, MemRegion mr) { +size_t oopDesc::oop_iterate_size(OopClosureType* cl, MemRegion mr) { Klass* k = klass(); - int size = size_given_klass(k); + size_t size = size_given_klass(k); OopIteratorClosureDispatch::oop_oop_iterate(cl, this, k, mr); return size; } diff --git a/src/hotspot/share/oops/typeArrayKlass.cpp b/src/hotspot/share/oops/typeArrayKlass.cpp index d15afe8537d80ab5cf47a26f5166dde9e8405ef8..c79cc61e39a2b765e09235f5c2be48319523559d 100644 --- a/src/hotspot/share/oops/typeArrayKlass.cpp +++ b/src/hotspot/share/oops/typeArrayKlass.cpp @@ -90,7 +90,7 @@ typeArrayOop TypeArrayKlass::allocate_common(int length, bool do_zero, TRAPS) { assert(log2_element_size() >= 0, "bad scale"); check_array_allocation_length(length, max_length(), CHECK_NULL); size_t size = typeArrayOopDesc::object_size(layout_helper(), length); - return (typeArrayOop)Universe::heap()->array_allocate(this, (int)size, length, + return (typeArrayOop)Universe::heap()->array_allocate(this, size, length, do_zero, CHECK_NULL); } @@ -227,7 +227,7 @@ Klass* TypeArrayKlass::array_klass_or_null() { return array_klass_or_null(dimension() + 1); } -int TypeArrayKlass::oop_size(oop obj) const { +size_t TypeArrayKlass::oop_size(oop obj) const { assert(obj->is_typeArray(),"must be a type array"); typeArrayOop t = typeArrayOop(obj); return t->object_size(this); diff --git a/src/hotspot/share/oops/typeArrayKlass.hpp b/src/hotspot/share/oops/typeArrayKlass.hpp index 1d6ed457c41e66585e76dc77606243d0cb49056a..1ba4613adff1e34df62dc5ab17ba6f5def405f47 100644 --- a/src/hotspot/share/oops/typeArrayKlass.hpp +++ b/src/hotspot/share/oops/typeArrayKlass.hpp @@ -61,7 +61,7 @@ class TypeArrayKlass : public ArrayKlass { return create_klass(type, external_name(type), THREAD); } - int oop_size(oop obj) const; + size_t oop_size(oop obj) const; // Allocation typeArrayOop allocate_common(int length, bool do_zero, TRAPS); diff --git a/src/hotspot/share/oops/typeArrayOop.hpp b/src/hotspot/share/oops/typeArrayOop.hpp index 2af7be778a4a23eef937dccfad9253d498298cf5..0ca63622d1d6467ea65bb2d844b943af73161024 100644 --- a/src/hotspot/share/oops/typeArrayOop.hpp +++ b/src/hotspot/share/oops/typeArrayOop.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "oops/arrayOop.hpp" #include "oops/typeArrayKlass.hpp" +#include // A typeArrayOop is an array containing basic types (non oop elements). // It is used for arrays of {characters, singles, doubles, bytes, shorts, integers, longs} @@ -115,7 +116,7 @@ private: // Returns the number of words necessary to hold an array of "len" // elements each of the given "byte_size". private: - static int object_size(int lh, int length) { + static size_t object_size(int lh, int length) { int instance_header_size = Klass::layout_helper_header_size(lh); int element_shift = Klass::layout_helper_log2_element_size(lh); DEBUG_ONLY(BasicType etype = Klass::layout_helper_element_type(lh)); @@ -127,11 +128,14 @@ private: julong size_in_words = ((size_in_bytes + (HeapWordSize-1)) >> LogHeapWordSize); assert(size_in_words <= (julong)max_jint, "no overflow"); - return align_object_size((intptr_t)size_in_words); + return align_object_size((size_t)size_in_words); } public: - inline int object_size(const TypeArrayKlass* tk) const; + inline size_t object_size(const TypeArrayKlass* tk) const; }; +// See similar requirement for oopDesc. +static_assert(std::is_trivially_default_constructible::value, "required"); + #endif // SHARE_OOPS_TYPEARRAYOOP_HPP diff --git a/src/hotspot/share/oops/typeArrayOop.inline.hpp b/src/hotspot/share/oops/typeArrayOop.inline.hpp index cf4cfc6995ecc71000da8afa5190b57fc587fad9..43fb6a06b395ddf1460c658c0bd374395589f638 100644 --- a/src/hotspot/share/oops/typeArrayOop.inline.hpp +++ b/src/hotspot/share/oops/typeArrayOop.inline.hpp @@ -31,7 +31,7 @@ #include "oops/oop.inline.hpp" #include "oops/arrayOop.hpp" -int typeArrayOopDesc::object_size(const TypeArrayKlass* tk) const { +size_t typeArrayOopDesc::object_size(const TypeArrayKlass* tk) const { return object_size(tk->layout_helper(), length()); } @@ -90,113 +90,76 @@ inline jdouble* typeArrayOopDesc::double_at_addr(int which) const { } inline jbyte typeArrayOopDesc::byte_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *byte_at_addr(which); } inline void typeArrayOopDesc::byte_at_put(int which, jbyte contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + *byte_at_addr(which) = contents; } inline jboolean typeArrayOopDesc::bool_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *bool_at_addr(which); } inline void typeArrayOopDesc::bool_at_put(int which, jboolean contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, jboolean(contents & 1)); + *bool_at_addr(which) = jboolean(contents & 1); } inline jchar typeArrayOopDesc::char_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *char_at_addr(which); } inline void typeArrayOopDesc::char_at_put(int which, jchar contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + *char_at_addr(which) = contents; } inline jint typeArrayOopDesc::int_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *int_at_addr(which); } inline void typeArrayOopDesc::int_at_put(int which, jint contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + *int_at_addr(which) = contents; } inline jshort typeArrayOopDesc::short_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *short_at_addr(which); } inline void typeArrayOopDesc::short_at_put(int which, jshort contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + *short_at_addr(which) = contents; } inline jushort typeArrayOopDesc::ushort_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *ushort_at_addr(which); } + inline void typeArrayOopDesc::ushort_at_put(int which, jushort contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + *ushort_at_addr(which) = contents; } inline jlong typeArrayOopDesc::long_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *long_at_addr(which); } inline void typeArrayOopDesc::long_at_put(int which, jlong contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + *long_at_addr(which) = contents; } inline jfloat typeArrayOopDesc::float_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *float_at_addr(which); } + inline void typeArrayOopDesc::float_at_put(int which, jfloat contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + *float_at_addr(which) = contents; } inline jdouble typeArrayOopDesc::double_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return *double_at_addr(which); } + inline void typeArrayOopDesc::double_at_put(int which, jdouble contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + *double_at_addr(which) = contents; } inline jbyte typeArrayOopDesc::byte_at_acquire(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return HeapAccess::load_at(as_oop(), offset); + return Atomic::load_acquire(byte_at_addr(which)); } inline void typeArrayOopDesc::release_byte_at_put(int which, jbyte contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, contents); + Atomic::release_store(byte_at_addr(which), contents); } // Java thinks Symbol arrays are just arrays of either long or int, since @@ -204,25 +167,18 @@ inline void typeArrayOopDesc::release_byte_at_put(int which, jbyte contents) { // casting #ifdef _LP64 inline Symbol* typeArrayOopDesc::symbol_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return (Symbol*)(jlong) HeapAccess::load_at(as_oop(), offset); + return *reinterpret_cast(long_at_addr(which)); } + inline void typeArrayOopDesc::symbol_at_put(int which, Symbol* contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, (jlong)contents); + *reinterpret_cast(long_at_addr(which)) = contents; } #else inline Symbol* typeArrayOopDesc::symbol_at(int which) const { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - return (Symbol*)(jint) HeapAccess::load_at(as_oop(), offset); + return *reinterpret_cast(int_at_addr(which)); } inline void typeArrayOopDesc::symbol_at_put(int which, Symbol* contents) { - assert(is_within_bounds(which), "index %d out of bounds %d", which, length()); - ptrdiff_t offset = element_offset(which); - HeapAccess::store_at(as_oop(), offset, (jint)contents); + *reinterpret_cast(int_at_addr(which)) = contents; } #endif // _LP64 diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index a88513b7bd83ffeea22d4f33d15f6d0dea4f2585..598deb08ffa0b7b97c5fc2ec5a5077bdfa54b04c 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1107,6 +1107,7 @@ Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, co bool is_int = gvn.type(a)->isa_int(); assert(is_int || gvn.type(a)->isa_long(), "int or long inputs"); assert(is_int == (gvn.type(b)->isa_int() != NULL), "inconsistent inputs"); + BasicType bt = is_int ? T_INT: T_LONG; Node* hook = NULL; if (gvn.is_IterGVN()) { // Make sure a and b are not destroyed @@ -1115,48 +1116,23 @@ Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, co hook->init_req(1, b); } Node* res = NULL; - if (!is_unsigned) { + if (is_int && !is_unsigned) { if (is_max) { - if (is_int) { - res = gvn.transform(new MaxINode(a, b)); - assert(gvn.type(res)->is_int()->_lo >= t->is_int()->_lo && gvn.type(res)->is_int()->_hi <= t->is_int()->_hi, "type doesn't match"); - } else { - Node* cmp = gvn.transform(new CmpLNode(a, b)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveLNode(bol, a, b, t->is_long())); - } + res = gvn.transform(new MaxINode(a, b)); + assert(gvn.type(res)->is_int()->_lo >= t->is_int()->_lo && gvn.type(res)->is_int()->_hi <= t->is_int()->_hi, "type doesn't match"); } else { - if (is_int) { - Node* res = gvn.transform(new MinINode(a, b)); - assert(gvn.type(res)->is_int()->_lo >= t->is_int()->_lo && gvn.type(res)->is_int()->_hi <= t->is_int()->_hi, "type doesn't match"); - } else { - Node* cmp = gvn.transform(new CmpLNode(b, a)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveLNode(bol, a, b, t->is_long())); - } + Node* res = gvn.transform(new MinINode(a, b)); + assert(gvn.type(res)->is_int()->_lo >= t->is_int()->_lo && gvn.type(res)->is_int()->_hi <= t->is_int()->_hi, "type doesn't match"); } } else { + Node* cmp = NULL; if (is_max) { - if (is_int) { - Node* cmp = gvn.transform(new CmpUNode(a, b)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveINode(bol, a, b, t->is_int())); - } else { - Node* cmp = gvn.transform(new CmpULNode(a, b)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveLNode(bol, a, b, t->is_long())); - } + cmp = gvn.transform(CmpNode::make(a, b, bt, is_unsigned)); } else { - if (is_int) { - Node* cmp = gvn.transform(new CmpUNode(b, a)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveINode(bol, a, b, t->is_int())); - } else { - Node* cmp = gvn.transform(new CmpULNode(b, a)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveLNode(bol, a, b, t->is_long())); - } + cmp = gvn.transform(CmpNode::make(b, a, bt, is_unsigned)); } + Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); + res = gvn.transform(CMoveNode::make(NULL, bol, a, b, t)); } if (hook != NULL) { hook->destruct(&gvn); @@ -1168,12 +1144,8 @@ Node* MaxNode::build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const bool is_int = gvn.type(a)->isa_int(); assert(is_int || gvn.type(a)->isa_long(), "int or long inputs"); assert(is_int == (gvn.type(b)->isa_int() != NULL), "inconsistent inputs"); - Node* zero = NULL; - if (is_int) { - zero = gvn.intcon(0); - } else { - zero = gvn.longcon(0); - } + BasicType bt = is_int ? T_INT: T_LONG; + Node* zero = gvn.integercon(0, bt); Node* hook = NULL; if (gvn.is_IterGVN()) { // Make sure a and b are not destroyed @@ -1181,32 +1153,15 @@ Node* MaxNode::build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const hook->init_req(0, a); hook->init_req(1, b); } - Node* res = NULL; + Node* cmp = NULL; if (is_max) { - if (is_int) { - Node* cmp = gvn.transform(new CmpINode(a, b)); - Node* sub = gvn.transform(new SubINode(a, b)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveINode(bol, sub, zero, t->is_int())); - } else { - Node* cmp = gvn.transform(new CmpLNode(a, b)); - Node* sub = gvn.transform(new SubLNode(a, b)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveLNode(bol, sub, zero, t->is_long())); - } + cmp = gvn.transform(CmpNode::make(a, b, bt, false)); } else { - if (is_int) { - Node* cmp = gvn.transform(new CmpINode(b, a)); - Node* sub = gvn.transform(new SubINode(a, b)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveINode(bol, sub, zero, t->is_int())); - } else { - Node* cmp = gvn.transform(new CmpLNode(b, a)); - Node* sub = gvn.transform(new SubLNode(a, b)); - Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); - res = gvn.transform(new CMoveLNode(bol, sub, zero, t->is_long())); - } + cmp = gvn.transform(CmpNode::make(b, a, bt, false)); } + Node* sub = gvn.transform(SubNode::make(a, b, bt)); + Node* bol = gvn.transform(new BoolNode(cmp, BoolTest::lt)); + Node* res = gvn.transform(CMoveNode::make(NULL, bol, sub, zero, t)); if (hook != NULL) { hook->destruct(&gvn); } diff --git a/src/hotspot/share/opto/bytecodeInfo.cpp b/src/hotspot/share/opto/bytecodeInfo.cpp index 0ce5143b59afc9f6db83b58d0465806982d54261..c6bc8f8eb94dbe634936dae88d6ee8ef6f48c75c 100644 --- a/src/hotspot/share/opto/bytecodeInfo.cpp +++ b/src/hotspot/share/opto/bytecodeInfo.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "ci/ciReplay.hpp" #include "classfile/vmSymbols.hpp" +#include "compiler/compilationPolicy.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compilerEvent.hpp" #include "compiler/compileLog.hpp" @@ -152,8 +153,8 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, int inline_small_code_size = InlineSmallCode / 4; int max_inline_size = default_max_inline_size; - int call_site_count = method()->scale_count(profile.count()); - int invoke_count = method()->interpreter_invocation_count(); + int call_site_count = caller_method->scale_count(profile.count()); + int invoke_count = caller_method->interpreter_invocation_count(); assert(invoke_count != 0, "require invocation count greater than zero"); double freq = (double)call_site_count / (double)invoke_count; @@ -192,10 +193,8 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, // negative filter: should callee NOT be inlined? -bool InlineTree::should_not_inline(ciMethod *callee_method, - ciMethod* caller_method, - JVMState* jvms) { - +bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_method, + int caller_bci, ciCallProfile& profile) { const char* fail_msg = NULL; // First check all inlining restrictions which are required for correctness @@ -233,7 +232,6 @@ bool InlineTree::should_not_inline(ciMethod *callee_method, } #ifndef PRODUCT - int caller_bci = jvms->bci(); int inline_depth = inline_level()+1; if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth)) { set_msg("force inline by ciReplay"); @@ -289,7 +287,6 @@ bool InlineTree::should_not_inline(ciMethod *callee_method, // don't use counts with -Xcomp if (UseInterpreter) { - if (!callee_method->has_compiled_code() && !callee_method->was_executed_more_than(0)) { set_msg("never executed"); @@ -299,15 +296,23 @@ bool InlineTree::should_not_inline(ciMethod *callee_method, if (is_init_with_ea(callee_method, caller_method, C)) { // Escape Analysis: inline all executed constructors return false; - } else { - intx counter_high_value; - // Tiered compilation uses a different "high value" than non-tiered compilation. - // Determine the right value to use. - if (TieredCompilation) { - counter_high_value = InvocationCounter::count_limit / 2; - } else { - counter_high_value = CompileThreshold / 2; + } + + if (MinInlineFrequencyRatio > 0) { + int call_site_count = caller_method->scale_count(profile.count()); + int invoke_count = caller_method->interpreter_invocation_count(); + assert(invoke_count != 0, "require invocation count greater than zero"); + double freq = (double)call_site_count / (double)invoke_count; + double min_freq = MAX2(MinInlineFrequencyRatio, 1.0 / CompilationPolicy::min_invocations()); + + if (freq < min_freq) { + set_msg("low call site frequency"); + return true; } + } + + if (MinInliningThreshold > 0) { // Deprecated heuristic + intx counter_high_value = TieredCompilation ? InvocationCounter::count_limit / 2 : CompileThreshold / 2; if (!callee_method->was_executed_more_than(MIN2(MinInliningThreshold, counter_high_value))) { set_msg("executed < MinInliningThreshold times"); return true; @@ -367,7 +372,7 @@ bool InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_method, if (!should_inline(callee_method, caller_method, caller_bci, profile)) { return false; } - if (should_not_inline(callee_method, caller_method, jvms)) { + if (should_not_inline(callee_method, caller_method, caller_bci, profile)) { return false; } diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index d5a59a413af7586e757c6ecc8457c603b99a30d9..ad463fbbc28c50a95c9e7ec88ab96b0ff1c42ce2 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -216,6 +216,9 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_copyMemory: if (StubRoutines::unsafe_arraycopy() == NULL) return false; break; + case vmIntrinsics::_encodeAsciiArray: + if (!Matcher::match_rule_supported(Op_EncodeISOArray) || !Matcher::supports_encode_ascii_array) return false; + break; case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeByteISOArray: if (!Matcher::match_rule_supported(Op_EncodeISOArray)) return false; @@ -423,6 +426,9 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_multiplyHigh: if (!Matcher::match_rule_supported(Op_MulHiL)) return false; break; + case vmIntrinsics::_unsignedMultiplyHigh: + if (!Matcher::match_rule_supported(Op_UMulHiL)) return false; + break; case vmIntrinsics::_getCallerClass: if (vmClasses::reflect_CallerSensitive_klass() == NULL) return false; break; diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 582ec51fbde8023b4d36e4e8b1550aa426620415..6c896c4609a808134ca966650723c65a5442e728 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -238,6 +238,7 @@ macro(MoveD2L) macro(MulD) macro(MulF) macro(MulHiL) +macro(UMulHiL) macro(MulI) macro(MulL) macro(Multi) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 801a28448d14edcbcd4f040f5bd8a68de704bd3d..031f318f27e56e3142ede6df342152f30f7af3b6 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -549,6 +549,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, _do_locks_coarsening(do_locks_coarsening), _method(target), _entry_bci(osr_bci), + _ilt(NULL), _stub_function(NULL), _stub_name(NULL), _stub_entry_point(NULL), diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 40d4e690b00590995343f069312fc3704927e467..cafbc0aaa7ccc5f421f5cc1c4d7e23f45c24c4dd 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1748,14 +1748,15 @@ Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt, } //-------------------------load_array_element------------------------- -Node* GraphKit::load_array_element(Node* ctl, Node* ary, Node* idx, const TypeAryPtr* arytype) { +Node* GraphKit::load_array_element(Node* ary, Node* idx, const TypeAryPtr* arytype, bool set_ctrl) { const Type* elemtype = arytype->elem(); BasicType elembt = elemtype->array_element_basic_type(); Node* adr = array_element_address(ary, idx, elembt, arytype->size()); if (elembt == T_NARROWOOP) { elembt = T_OBJECT; // To satisfy switch in LoadNode::make() } - Node* ld = make_load(ctl, adr, elemtype, elembt, arytype, MemNode::unordered); + Node* ld = access_load_at(ary, adr, arytype, elemtype, elembt, + IN_HEAP | IS_ARRAY | (set_ctrl ? C2_CONTROL_DEPENDENT_LOAD : 0)); return ld; } @@ -4258,7 +4259,7 @@ void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* coun record_for_igvn(mem); set_control(head); set_memory(mem, TypeAryPtr::BYTES); - Node* ch = load_array_element(control(), src, i_byte, TypeAryPtr::BYTES); + Node* ch = load_array_element(src, i_byte, TypeAryPtr::BYTES, /* set_ctrl */ true); Node* st = store_to_memory(control(), array_element_address(dst, i_char, T_BYTE), AndI(ch, intcon(0xff)), T_CHAR, TypeAryPtr::BYTES, MemNode::unordered, false, false, true /* mismatched */); diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index d2a6dd8d6c809ed2e2e0d46b4e382131e626d64d..7b1ae9455e4bed8f431ff2bbd74ce045aba5b637 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -660,7 +660,7 @@ class GraphKit : public Phase { Node* ctrl = NULL); // Return a load of array element at idx. - Node* load_array_element(Node* ctl, Node* ary, Node* idx, const TypeAryPtr* arytype); + Node* load_array_element(Node* ary, Node* idx, const TypeAryPtr* arytype, bool set_ctrl); //---------------- Dtrace support -------------------- void make_dtrace_method_entry_exit(ciMethod* method, bool is_entry); diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp index 3d6e9a38d12258fa5ace75e9c48c3134f771c37f..ab8a834bb28ad7b8c8a7d01992baf1db3546a206 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -168,10 +168,14 @@ class HasNegativesNode: public StrIntrinsicNode { //------------------------------EncodeISOArray-------------------------------- -// encode char[] to byte[] in ISO_8859_1 +// encode char[] to byte[] in ISO_8859_1 or ASCII class EncodeISOArrayNode: public Node { + bool ascii; public: - EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c): Node(control, arymem, s1, s2, c) {}; + EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c, bool ascii) + : Node(control, arymem, s1, s2, c), ascii(ascii) {} + + bool is_ascii() { return ascii; } virtual int Opcode() const; virtual bool depends_only_on_test() const { return false; } virtual const Type* bottom_type() const { return TypeInt::INT; } diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 0ac5bf6d502a5b4d0aa58add8f31fe354f33ac3a..edd051c5300e09db967629c3c1bcf310b1a2e68f 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -286,6 +286,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_multiplyExactI: return inline_math_multiplyExactI(); case vmIntrinsics::_multiplyExactL: return inline_math_multiplyExactL(); case vmIntrinsics::_multiplyHigh: return inline_math_multiplyHigh(); + case vmIntrinsics::_unsignedMultiplyHigh: return inline_math_unsignedMultiplyHigh(); case vmIntrinsics::_negateExactI: return inline_math_negateExactI(); case vmIntrinsics::_negateExactL: return inline_math_negateExactL(); case vmIntrinsics::_subtractExactI: return inline_math_subtractExactI(false /* subtract */); @@ -591,7 +592,9 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeByteISOArray: - return inline_encodeISOArray(); + return inline_encodeISOArray(false); + case vmIntrinsics::_encodeAsciiArray: + return inline_encodeISOArray(true); case vmIntrinsics::_updateCRC32: return inline_updateCRC32(); @@ -1865,6 +1868,11 @@ bool LibraryCallKit::inline_math_multiplyHigh() { return true; } +bool LibraryCallKit::inline_math_unsignedMultiplyHigh() { + set_result(_gvn.transform(new UMulHiLNode(argument(0), argument(2)))); + return true; +} + Node* LibraryCallKit::generate_min_max(vmIntrinsics::ID id, Node* x0, Node* y0) { // These are the candidate return value: @@ -4882,8 +4890,8 @@ LibraryCallKit::tightly_coupled_allocation(Node* ptr) { } //-------------inline_encodeISOArray----------------------------------- -// encode char[] to byte[] in ISO_8859_1 -bool LibraryCallKit::inline_encodeISOArray() { +// encode char[] to byte[] in ISO_8859_1 or ASCII +bool LibraryCallKit::inline_encodeISOArray(bool ascii) { assert(callee()->signature()->size() == 5, "encodeISOArray has 5 parameters"); // no receiver since it is static method Node *src = argument(0); @@ -4918,7 +4926,7 @@ bool LibraryCallKit::inline_encodeISOArray() { // 'dst_start' points to dst array + scaled offset const TypeAryPtr* mtype = TypeAryPtr::BYTES; - Node* enc = new EncodeISOArrayNode(control(), memory(mtype), src_start, dst_start, length); + Node* enc = new EncodeISOArrayNode(control(), memory(mtype), src_start, dst_start, length, ascii); enc = _gvn.transform(enc); Node* res_mem = _gvn.transform(new SCMemProjNode(enc)); set_memory(res_mem, mtype); @@ -6203,7 +6211,7 @@ Node * LibraryCallKit::get_key_start_from_aescrypt_object(Node *aescrypt_object) if (objSessionK == NULL) { return (Node *) NULL; } - Node* objAESCryptKey = load_array_element(control(), objSessionK, intcon(0), TypeAryPtr::OOPS); + Node* objAESCryptKey = load_array_element(objSessionK, intcon(0), TypeAryPtr::OOPS, /* set_ctrl */ true); #else Node* objAESCryptKey = load_field_from_object(aescrypt_object, "K", "[I"); #endif // PPC64 @@ -6792,14 +6800,19 @@ bool LibraryCallKit::inline_galoisCounterMode_AESCrypt() { ciKlass* klass = ciTypeArrayKlass::make(T_LONG); Node* klass_node = makecon(TypeKlassPtr::make(klass)); - // htbl entries is set to 96 only fox x86-64 + // Does this target support this intrinsic? if (Matcher::htbl_entries == -1) return false; - // new array to hold 48 computed htbl entries - Node* subkeyHtbl_48_entries = new_array(klass_node, intcon(Matcher::htbl_entries), 0); - if (subkeyHtbl_48_entries == NULL) return false; - - Node* subkeyHtbl_48_entries_start = array_element_address(subkeyHtbl_48_entries, intcon(0), T_LONG); + Node* subkeyHtbl_48_entries_start; + if (Matcher::htbl_entries != 0) { + // new array to hold 48 computed htbl entries + Node* subkeyHtbl_48_entries = new_array(klass_node, intcon(Matcher::htbl_entries), 0); + if (subkeyHtbl_48_entries == NULL) return false; + subkeyHtbl_48_entries_start = array_element_address(subkeyHtbl_48_entries, intcon(0), T_LONG); + } else { + // This target doesn't need the extra-large Htbl. + subkeyHtbl_48_entries_start = ConvL2X(intcon(0)); + } // Call the stub, passing params Node* gcmCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index adb1cb06c957b2d5a012728f643f84784d342be5..dfbad65a7b2a241dd99c78c1e55a1137aadb94fe 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -210,6 +210,7 @@ class LibraryCallKit : public GraphKit { bool inline_math_multiplyExactI(); bool inline_math_multiplyExactL(); bool inline_math_multiplyHigh(); + bool inline_math_unsignedMultiplyHigh(); bool inline_math_negateExactI(); bool inline_math_negateExactL(); bool inline_math_subtractExactI(bool is_decrement); @@ -285,7 +286,7 @@ class LibraryCallKit : public GraphKit { Node* get_state_from_digest_object(Node *digestBase_object, const char* state_type); Node* get_digest_length_from_digest_object(Node *digestBase_object); Node* inline_digestBase_implCompressMB_predicate(int predicate); - bool inline_encodeISOArray(); + bool inline_encodeISOArray(bool ascii); bool inline_updateCRC32(); bool inline_updateBytesCRC32(); bool inline_updateByteBufferCRC32(); diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 60581759c456f49fbbb3218bb1dc9062be47cdcd..ad79fbf3b5bb864cb416f66d93aa115c66b19432 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -712,7 +712,8 @@ class Invariance : public StackObj { // Returns true if the predicate of iff is in "scale*iv + offset u< load_range(ptr)" format // Note: this function is particularly designed for loop predication. We require load_range // and offset to be loop invariant computed on the fly by "invar" -bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar DEBUG_ONLY(COMMA ProjNode *predicate_proj)) const { +bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, BasicType bt, Node *iv, Node *&range, + Node *&offset, jlong &scale) const { if (!is_loop_exit(iff)) { return false; } @@ -727,48 +728,60 @@ bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invari return false; } const CmpNode *cmp = bol->in(1)->as_Cmp(); - if (cmp->Opcode() != Op_CmpU) { + if (!(cmp->is_Cmp() && cmp->operates_on(bt, false))) { return false; } - Node* range = cmp->in(2); - if (range->Opcode() != Op_LoadRange && !iff->is_RangeCheck()) { - const TypeInt* tint = phase->_igvn.type(range)->isa_int(); - if (tint == NULL || tint->empty() || tint->_lo < 0) { + range = cmp->in(2); + if (range->Opcode() != Op_LoadRange) { + const TypeInteger* tinteger = phase->_igvn.type(range)->isa_integer(bt); + if (tinteger == NULL || tinteger->empty() || tinteger->lo_as_long() < 0) { // Allow predication on positive values that aren't LoadRanges. // This allows optimization of loops where the length of the // array is a known value and doesn't need to be loaded back // from the array. return false; } + } else { + assert(bt == T_INT, "no LoadRange for longs"); } - if (!invar.is_invariant(range)) { - return false; - } - Node *iv = _head->as_CountedLoop()->phi(); - int scale = 0; - Node *offset = NULL; - if (!phase->is_scaled_iv_plus_offset(cmp->in(1), iv, &scale, &offset)) { - return false; - } - if (offset && !invar.is_invariant(offset)) { // offset must be invariant + scale = 0; + offset = NULL; + if (!phase->is_scaled_iv_plus_offset(cmp->in(1), iv, &scale, &offset, bt)) { return false; } + return true; +} + +bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar DEBUG_ONLY(COMMA ProjNode *predicate_proj)) const { + Node* range = NULL; + Node* offset = NULL; + jlong scale = 0; + Node* iv = _head->as_BaseCountedLoop()->phi(); + if (is_range_check_if(iff, phase, T_INT, iv, range, offset, scale)) { + if (!invar.is_invariant(range)) { + return false; + } + if (offset && !invar.is_invariant(offset)) { // offset must be invariant + return false; + } #ifdef ASSERT - if (offset && phase->has_ctrl(offset)) { - Node* offset_ctrl = phase->get_ctrl(offset); - if (phase->get_loop(predicate_proj) == phase->get_loop(offset_ctrl) && - phase->is_dominator(predicate_proj, offset_ctrl)) { - // If the control of offset is loop predication promoted by previous pass, - // then it will lead to cyclic dependency. - // Previously promoted loop predication is in the same loop of predication - // point. - // This situation can occur when pinning nodes too conservatively - can we do better? - assert(false, "cyclic dependency prevents range check elimination, idx: offset %d, offset_ctrl %d, predicate_proj %d", - offset->_idx, offset_ctrl->_idx, predicate_proj->_idx); + if (offset && phase->has_ctrl(offset)) { + Node* offset_ctrl = phase->get_ctrl(offset); + if (phase->get_loop(predicate_proj) == phase->get_loop(offset_ctrl) && + phase->is_dominator(predicate_proj, offset_ctrl)) { + // If the control of offset is loop predication promoted by previous pass, + // then it will lead to cyclic dependency. + // Previously promoted loop predication is in the same loop of predication + // point. + // This situation can occur when pinning nodes too conservatively - can we do better? + assert(false, "cyclic dependency prevents range check elimination, idx: offset %d, offset_ctrl %d, predicate_proj %d", + offset->_idx, offset_ctrl->_idx, predicate_proj->_idx); + } } - } #endif - return true; + return true; + } + return false; } //------------------------------rc_predicate----------------------------------- diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 3ad5480cd66760a07a77b1019e63a0ec9664ca3a..fa97be59d7e3645875ff16e32a9cf7c0ee847e65 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1059,27 +1059,35 @@ void IdealLoopTree::policy_unroll_slp_analysis(CountedLoopNode *cl, PhaseIdealLo } } + //------------------------------policy_range_check----------------------------- // Return TRUE or FALSE if the loop should be range-check-eliminated or not. // When TRUE, the estimated node budget is also requested. // // We will actually perform iteration-splitting, a more powerful form of RCE. -bool IdealLoopTree::policy_range_check(PhaseIdealLoop *phase) const { - if (!RangeCheckElimination) return false; +bool IdealLoopTree::policy_range_check(PhaseIdealLoop* phase, bool provisional) const { + if (!provisional && !RangeCheckElimination) return false; // If nodes are depleted, some transform has miscalculated its needs. - assert(!phase->exceeding_node_budget(), "sanity"); + assert(provisional || !phase->exceeding_node_budget(), "sanity"); - CountedLoopNode *cl = _head->as_CountedLoop(); - // If we unrolled with no intention of doing RCE and we later changed our - // minds, we got no pre-loop. Either we need to make a new pre-loop, or we - // have to disallow RCE. - if (cl->is_main_no_pre_loop()) return false; // Disallowed for now. - Node *trip_counter = cl->phi(); + if (_head->is_CountedLoop()) { + CountedLoopNode *cl = _head->as_CountedLoop(); + // If we unrolled with no intention of doing RCE and we later changed our + // minds, we got no pre-loop. Either we need to make a new pre-loop, or we + // have to disallow RCE. + if (cl->is_main_no_pre_loop()) return false; // Disallowed for now. + + // check for vectorized loops, some opts are no longer needed + // RCE needs pre/main/post loops. Don't apply it on a single iteration loop. + if (cl->is_unroll_only() || (cl->is_normal_loop() && cl->trip_count() == 1)) return false; + } else { + assert(provisional, "no long counted loop expected"); + } - // check for vectorized loops, some opts are no longer needed - // RCE needs pre/main/post loops. Don't apply it on a single iteration loop. - if (cl->is_unroll_only() || (cl->is_normal_loop() && cl->trip_count() == 1)) return false; + BaseCountedLoopNode* cl = _head->as_BaseCountedLoop(); + Node *trip_counter = cl->phi(); + BasicType bt = cl->bt(); // Check loop body for tests of trip-counter plus loop-invariant vs // loop-invariant. @@ -1104,22 +1112,32 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop *phase) const { Node *rc_exp = cmp->in(1); Node *limit = cmp->in(2); - Node *limit_c = phase->get_ctrl(limit); - if (limit_c == phase->C->top()) { - return false; // Found dead test on live IF? No RCE! - } - if (is_member(phase->get_loop(limit_c))) { - // Compare might have operands swapped; commute them - rc_exp = cmp->in(2); - limit = cmp->in(1); - limit_c = phase->get_ctrl(limit); + if (provisional) { + // Try to pattern match with either cmp inputs, do not check + // whether one of the inputs is loop independent as it may not + // have had a chance to be hoisted yet. + if (!phase->is_scaled_iv_plus_offset(cmp->in(1), trip_counter, NULL, NULL, bt) && + !phase->is_scaled_iv_plus_offset(cmp->in(2), trip_counter, NULL, NULL, bt)) { + continue; + } + } else { + Node *limit_c = phase->get_ctrl(limit); + if (limit_c == phase->C->top()) { + return false; // Found dead test on live IF? No RCE! + } if (is_member(phase->get_loop(limit_c))) { - continue; // Both inputs are loop varying; cannot RCE + // Compare might have operands swapped; commute them + rc_exp = cmp->in(2); + limit = cmp->in(1); + limit_c = phase->get_ctrl(limit); + if (is_member(phase->get_loop(limit_c))) { + continue; // Both inputs are loop varying; cannot RCE + } } - } - if (!phase->is_scaled_iv_plus_offset(rc_exp, trip_counter, NULL, NULL)) { - continue; + if (!phase->is_scaled_iv_plus_offset(rc_exp, trip_counter, NULL, NULL)) { + continue; + } } // Found a test like 'trip+off vs limit'. Test is an IfNode, has two (2) // projections. If BOTH are in the loop we need loop unswitching instead @@ -1127,7 +1145,7 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop *phase) const { if (is_loop_exit(iff)) { // Found valid reason to split iterations (if there is room). // NOTE: Usually a gross overestimate. - return phase->may_require_nodes(est_loop_clone_sz(2)); + return provisional || phase->may_require_nodes(est_loop_clone_sz(2)); } } // End of is IF } @@ -2458,8 +2476,9 @@ void PhaseIdealLoop::add_constraint(jlong stride_con, jlong scale_con, Node* off //------------------------------is_scaled_iv--------------------------------- // Return true if exp is a constant times an induction var -bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, int* p_scale) { +bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, jlong* p_scale, BasicType bt) { exp = exp->uncast(); + assert(bt == T_INT || bt == T_LONG, "unexpected int type"); if (exp == iv) { if (p_scale != NULL) { *p_scale = 1; @@ -2467,23 +2486,29 @@ bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, int* p_scale) { return true; } int opc = exp->Opcode(); - if (opc == Op_MulI) { + // Can't use is_Mul() here as it's true for AndI and AndL + if ((opc == Op_MulI || opc == Op_MulL) && exp->operates_on(bt, true)) { if (exp->in(1)->uncast() == iv && exp->in(2)->is_Con()) { if (p_scale != NULL) { - *p_scale = exp->in(2)->get_int(); + *p_scale = exp->in(2)->get_integer_as_long(bt); } return true; } if (exp->in(2)->uncast() == iv && exp->in(1)->is_Con()) { if (p_scale != NULL) { - *p_scale = exp->in(1)->get_int(); + *p_scale = exp->in(1)->get_integer_as_long(bt); } return true; } - } else if (opc == Op_LShiftI) { + } else if (exp->is_LShift() && exp->operates_on(bt, true)) { if (exp->in(1)->uncast() == iv && exp->in(2)->is_Con()) { if (p_scale != NULL) { - *p_scale = 1 << exp->in(2)->get_int(); + jint shift_amount = exp->in(2)->get_int(); + if (bt == T_INT) { + *p_scale = java_shift_left((jint)1, (juint)shift_amount); + } else if (bt == T_LONG) { + *p_scale = java_shift_left((jlong)1, (julong)shift_amount); + } } return true; } @@ -2493,25 +2518,25 @@ bool PhaseIdealLoop::is_scaled_iv(Node* exp, Node* iv, int* p_scale) { //-----------------------------is_scaled_iv_plus_offset------------------------------ // Return true if exp is a simple induction variable expression: k1*iv + (invar + k2) -bool PhaseIdealLoop::is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node** p_offset, int depth) { - if (is_scaled_iv(exp, iv, p_scale)) { +bool PhaseIdealLoop::is_scaled_iv_plus_offset(Node* exp, Node* iv, jlong* p_scale, Node** p_offset, BasicType bt, int depth) { + assert(bt == T_INT || bt == T_LONG, "unexpected int type"); + if (is_scaled_iv(exp, iv, p_scale, bt)) { if (p_offset != NULL) { - Node *zero = _igvn.intcon(0); + Node *zero = _igvn.integercon(0, bt); set_ctrl(zero, C->root()); *p_offset = zero; } return true; } exp = exp->uncast(); - int opc = exp->Opcode(); - if (opc == Op_AddI) { - if (is_scaled_iv(exp->in(1), iv, p_scale)) { + if (exp->is_Add() && exp->operates_on(bt, true)) { + if (is_scaled_iv(exp->in(1), iv, p_scale, bt)) { if (p_offset != NULL) { *p_offset = exp->in(2); } return true; } - if (is_scaled_iv(exp->in(2), iv, p_scale)) { + if (is_scaled_iv(exp->in(2), iv, p_scale, bt)) { if (p_offset != NULL) { *p_offset = exp->in(1); } @@ -2521,30 +2546,34 @@ bool PhaseIdealLoop::is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node* offset2 = NULL; if (depth < 2 && is_scaled_iv_plus_offset(exp->in(1), iv, p_scale, - p_offset != NULL ? &offset2 : NULL, depth+1)) { + p_offset != NULL ? &offset2 : NULL, bt, depth+1)) { if (p_offset != NULL) { Node *ctrl_off2 = get_ctrl(offset2); - Node* offset = new AddINode(offset2, exp->in(2)); + Node* offset = AddNode::make(offset2, exp->in(2), bt); register_new_node(offset, ctrl_off2); *p_offset = offset; } return true; } } - } else if (opc == Op_SubI) { - if (is_scaled_iv(exp->in(1), iv, p_scale)) { + } else if (exp->is_Sub() && exp->operates_on(bt, true)) { + if (is_scaled_iv(exp->in(1), iv, p_scale, bt)) { if (p_offset != NULL) { - Node *zero = _igvn.intcon(0); + Node *zero = _igvn.integercon(0, bt); set_ctrl(zero, C->root()); Node *ctrl_off = get_ctrl(exp->in(2)); - Node* offset = new SubINode(zero, exp->in(2)); + Node* offset = SubNode::make(zero, exp->in(2), bt); register_new_node(offset, ctrl_off); *p_offset = offset; } return true; } - if (is_scaled_iv(exp->in(2), iv, p_scale)) { + if (is_scaled_iv(exp->in(2), iv, p_scale, bt)) { if (p_offset != NULL) { + // We can't handle a scale of min_jint (or min_jlong) here as -1 * min_jint = min_jint + if (*p_scale == min_signed_integer(bt)) { + return false; + } *p_scale *= -1; *p_offset = exp->in(1); } @@ -3411,7 +3440,7 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n // unrolling), plus any needed for RCE purposes. bool should_unroll = policy_unroll(phase); - bool should_rce = policy_range_check(phase); + bool should_rce = policy_range_check(phase, false); // If not RCE'ing (iteration splitting), then we do not need a pre-loop. // We may still need to peel an initial iteration but we will not diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 6f7e34d3d7607d35543f3e17096028f598a2adb7..f77ddeea4baf5afcabecbfe1a1226447adce8d4e 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -525,7 +525,7 @@ static bool condition_stride_ok(BoolTest::mask bt, jlong stride_con) { return true; } -void PhaseIdealLoop::long_loop_replace_long_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head) { +Node* PhaseIdealLoop::long_loop_replace_long_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head) { Node* iv_as_long = new ConvI2LNode(inner_iv, TypeLong::INT); register_new_node(iv_as_long, inner_head); Node* iv_replacement = new AddLNode(outer_phi, iv_as_long); @@ -546,6 +546,7 @@ void PhaseIdealLoop::long_loop_replace_long_iv(Node* iv_to_replace, Node* inner_ int nb = u->replace_edge(iv_to_replace, iv_replacement, &_igvn); i -= nb; } + return iv_replacement; } void PhaseIdealLoop::add_empty_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt) { @@ -828,6 +829,9 @@ bool PhaseIdealLoop::transform_long_counted_loop(IdealLoopTree* loop, Node_List Node* entry_control = x->in(LoopNode::EntryControl); Node* cmp = exit_test->cmp_node(); + Node_List range_checks; + iters_limit = extract_long_range_checks(loop, stride_con, iters_limit, phi, range_checks); + // Clone the control flow of the loop to build an outer loop Node* outer_back_branch = back_control->clone(); Node* outer_exit_test = new IfNode(exit_test->in(0), exit_test->in(1), exit_test->_prob, exit_test->_fcnt); @@ -877,22 +881,22 @@ bool PhaseIdealLoop::transform_long_counted_loop(IdealLoopTree* loop, Node_List Node* inner_iters_actual_int = new ConvL2INode(inner_iters_actual); _igvn.register_new_node_with_optimizer(inner_iters_actual_int); - Node* zero = _igvn.intcon(0); - set_ctrl(zero, C->root()); + Node* int_zero = _igvn.intcon(0); + set_ctrl(int_zero, C->root()); if (stride_con < 0) { - inner_iters_actual_int = new SubINode(zero, inner_iters_actual_int); + inner_iters_actual_int = new SubINode(int_zero, inner_iters_actual_int); _igvn.register_new_node_with_optimizer(inner_iters_actual_int); } // Clone the iv data nodes as an integer iv - Node* int_stride = _igvn.intcon((int)stride_con); + Node* int_stride = _igvn.intcon(checked_cast(stride_con)); set_ctrl(int_stride, C->root()); Node* inner_phi = new PhiNode(x->in(0), TypeInt::INT); Node* inner_incr = new AddINode(inner_phi, int_stride); Node* inner_cmp = NULL; inner_cmp = new CmpINode(inner_incr, inner_iters_actual_int); Node* inner_bol = new BoolNode(inner_cmp, exit_test->in(1)->as_Bool()->_test._test); - inner_phi->set_req(LoopNode::EntryControl, zero); + inner_phi->set_req(LoopNode::EntryControl, int_zero); inner_phi->set_req(LoopNode::LoopBackControl, inner_incr); register_new_node(inner_phi, x); register_new_node(inner_incr, x); @@ -915,7 +919,7 @@ bool PhaseIdealLoop::transform_long_counted_loop(IdealLoopTree* loop, Node_List // Replace inner loop long iv phi as inner loop int iv phi + outer // loop iv phi - long_loop_replace_long_iv(phi, inner_phi, outer_phi, head); + Node* iv_add = long_loop_replace_long_iv(phi, inner_phi, outer_phi, head); // Replace inner loop long iv incr with inner loop int incr + outer // loop iv phi @@ -979,6 +983,8 @@ bool PhaseIdealLoop::transform_long_counted_loop(IdealLoopTree* loop, Node_List // exit_branch: break; //in(0) := outer_exit_test // } + transform_long_range_checks(checked_cast(stride_con), range_checks, outer_phi, inner_iters_actual_int, + inner_phi, iv_add, inner_head); // Peel one iteration of the loop and use the safepoint at the end // of the peeled iteration to insert empty predicates. If no well // positioned safepoint peel to guarantee a safepoint in the outer @@ -1012,6 +1018,239 @@ bool PhaseIdealLoop::transform_long_counted_loop(IdealLoopTree* loop, Node_List return true; } +int PhaseIdealLoop::extract_long_range_checks(const IdealLoopTree* loop, jlong stride_con, int iters_limit, PhiNode* phi, + Node_List& range_checks) { + if (stride_con < 0) { // only for stride_con > 0 && scale > 0 for now + return iters_limit; + } + const jlong min_iters = 2; + jlong reduced_iters_limit = iters_limit; + jlong original_iters_limit = iters_limit; + for (uint i = 0; i < loop->_body.size(); i++) { + Node* c = loop->_body.at(i); + if (c->is_IfProj() && c->in(0)->is_RangeCheck()) { + CallStaticJavaNode* call = c->as_IfProj()->is_uncommon_trap_if_pattern(Deoptimization::Reason_none); + if (call != NULL) { + Node* range = NULL; + Node* offset = NULL; + jlong scale = 0; + RangeCheckNode* rc = c->in(0)->as_RangeCheck(); + if (loop->is_range_check_if(rc, this, T_LONG, phi, range, offset, scale) && + loop->is_invariant(range) && loop->is_invariant(offset) && + scale > 0 && // only for stride_con > 0 && scale > 0 for now + original_iters_limit / ABS(scale * stride_con) >= min_iters) { + reduced_iters_limit = MIN2(reduced_iters_limit, original_iters_limit/ABS(scale)); + range_checks.push(c); + } + } + } + } + + return checked_cast(reduced_iters_limit); +} + +// One execution of the inner loop covers a sub-range of the entire iteration range of the loop: [A,Z), aka [A=init, +// Z=limit). If the loop has at least one trip (which is the case here), the iteration variable i always takes A as its +// first value, followed by A+S (S is the stride), next A+2S, etc. The limit is exclusive, so that the final value B of +// i is never Z. It will be B=Z-1 if S=1, or B=Z+1 if S=-1. If |S|>1 the formula for the last value requires a floor +// operation, specifically B=floor((Z-sgn(S)-A)/S)*S+A. Thus i ranges as i:[A,B] or i:[A,Z) or i:[A,Z-U) for some U 0) to simplify the logic for clamping 32-bit bounds (L_2, R_2). +// For restrictions on S and K, see the guards in extract_long_range_checks. + +// Within the loop there may be many range checks. Each such range check (R.C.) is of the form 0 <= i*K+L < R, where K +// is a scale factor applied to the loop iteration variable i, and L is some offset; K, L, and R are loop-invariant. +// Because R is never negative, this check can always be simplified to an unsigned check i*K+L 0, +// the first C must be A itself, and the next C value is the previous C+Z_2. In each sub-loop, j counts up from zero +// and exits just before i=C+Z_2. + +// (N.B. If S<0 the formulas are different, because all the loops count downward.) + +// Returning to range checks, we see that each i*K+L =s64 0, then use this test: +// j*K + 0 =s64 0 but Q_max H, it returns L not H. +// +// Tests above can be merged into a single one: +// L_clamp = Q_min < 0 ? 0 : Q_min +// H_clamp = Q_max < Q_min ? R : Q_max +// j*K + Q_min - L_clamp root()); + + for (uint i = 0; i < range_checks.size(); i++) { + ProjNode* proj = range_checks.at(i)->as_Proj(); + ProjNode* unc_proj = proj->other_if_proj(); + RangeCheckNode* rc = proj->in(0)->as_RangeCheck(); + jlong scale = 0; + Node* offset = NULL; + Node* rc_bol = rc->in(1); + Node* rc_cmp = rc_bol->in(1); + if (rc_cmp->Opcode() == Op_CmpU) { + // could be shared and have already been taken care of + continue; + } + bool ok = is_scaled_iv_plus_offset(rc_cmp->in(1), iv_add, &scale, &offset, T_LONG); + assert(ok, "inconsistent: was tested before"); + Node* range = rc_cmp->in(2); + Node* c = rc->in(0); + Node* entry_control = inner_head->in(LoopNode::EntryControl); + + Node* R = range; + Node* K = _igvn.longcon(scale); + set_ctrl(K, this->C->root()); + Node* L = offset; + Node* C = outer_phi; + Node* Z_2 = new ConvI2LNode(inner_iters_actual_int, TypeLong::LONG); + register_new_node(Z_2, entry_control); + + // Start with 64-bit values: + // i*K + L 0 + Node* R_2 = clamp(R, L_clamp, H_clamp); + R_2 = new SubLNode(R_2, L_clamp); + register_new_node(R_2, entry_control); + R_2 = new ConvL2INode(R_2, TypeInt::POS); + register_new_node(R_2, entry_control); + + // Q = Q_min - L_clamp + // that is: Q = Q_min - 0 if Q_min < 0 + // or: Q = Q_min - Q_min = 0 if Q_min > 0 + Node* Q = new SubLNode(Q_min, L_clamp); + register_new_node(Q, entry_control); + Q = new ConvL2INode(Q, TypeInt::INT); + register_new_node(Q, entry_control); + + // Transform the range check + K = _igvn.intcon(checked_cast(scale)); + set_ctrl(K, this->C->root()); + Node* scaled_iv = new MulINode(inner_phi, K); + register_new_node(scaled_iv, c); + Node* scaled_iv_plus_offset = scaled_iv_plus_offset = new AddINode(scaled_iv, Q); + register_new_node(scaled_iv_plus_offset, c); + + Node* new_rc_cmp = new CmpUNode(scaled_iv_plus_offset, R_2); + register_new_node(new_rc_cmp, c); + + _igvn.replace_input_of(rc_bol, 1, new_rc_cmp); + } +} + +Node* PhaseIdealLoop::clamp(Node* R, Node* L, Node* H) { + Node* min = MaxNode::signed_min(R, H, TypeLong::LONG, _igvn); + set_subtree_ctrl(min, true); + Node* max = MaxNode::signed_max(L, min, TypeLong::LONG, _igvn); + set_subtree_ctrl(max, true); + return max; +} + LoopNode* PhaseIdealLoop::create_inner_head(IdealLoopTree* loop, LongCountedLoopNode* head, LongCountedLoopEndNode* exit_test) { LoopNode* new_inner_head = new LoopNode(head->in(1), head->in(2)); @@ -1417,10 +1656,10 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ Node* bol; if (stride_con > 0) { - cmp_limit = CmpNode::make(limit, _igvn.integercon(max_jint - stride_m, iv_bt), iv_bt); + cmp_limit = CmpNode::make(limit, _igvn.integercon(max_signed_integer(iv_bt) - stride_m, iv_bt), iv_bt); bol = new BoolNode(cmp_limit, BoolTest::le); } else { - cmp_limit = CmpNode::make(limit, _igvn.integercon(min_jint - stride_m, iv_bt), iv_bt); + cmp_limit = CmpNode::make(limit, _igvn.integercon(min_signed_integer(iv_bt) - stride_m, iv_bt), iv_bt); bol = new BoolNode(cmp_limit, BoolTest::ge); } @@ -1873,7 +2112,7 @@ const Type* LoopLimitNode::Value(PhaseGVN* phase) const { int stride_con = stride_t->is_int()->get_con(); if (stride_con == 1) - return NULL; // Identity + return bottom_type(); // Identity if (init_t->is_int()->is_con() && limit_t->is_int()->is_con()) { // Use jlongs to avoid integer overflow. @@ -3177,7 +3416,7 @@ void PhaseIdealLoop::replace_parallel_iv(IdealLoopTree *loop) { // Look for induction variables of the form: X += constant if (phi2->region() != loop->_head || incr2->req() != 3 || - incr2->in(1) != phi2 || + incr2->in(1)->uncast() != phi2 || incr2 == incr || incr2->Opcode() != Op_AddI || !incr2->in(2)->is_Con()) @@ -3955,24 +4194,26 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) { // Reassociate invariants and prep for split_thru_phi for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { IdealLoopTree* lpt = iter.current(); - bool is_counted = lpt->is_counted(); - if (!is_counted || !lpt->is_innermost()) continue; + if (!lpt->is_loop()) { + continue; + } + Node* head = lpt->_head; + if (!head->is_BaseCountedLoop() || !lpt->is_innermost()) continue; // check for vectorized loops, any reassociation of invariants was already done - if (is_counted && lpt->_head->as_CountedLoop()->is_unroll_only()) { - continue; - } else { - AutoNodeBudget node_budget(this); - lpt->reassociate_invariants(this); + if (head->is_CountedLoop()) { + if (head->as_CountedLoop()->is_unroll_only()) { + continue; + } else { + AutoNodeBudget node_budget(this); + lpt->reassociate_invariants(this); + } } // Because RCE opportunities can be masked by split_thru_phi, // look for RCE candidates and inhibit split_thru_phi // on just their loop-phi's for this pass of loop opts - if (SplitIfBlocks && do_split_ifs) { - AutoNodeBudget node_budget(this, AutoNodeBudget::NO_BUDGET_CHECK); - if (lpt->policy_range_check(this)) { - lpt->_rce_candidate = 1; // = true - } + if (SplitIfBlocks && do_split_ifs && lpt->policy_range_check(this, true)) { + lpt->_rce_candidate = 1; // = true } } } diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 47fa319466a2e79f25fd0bc88a6aef8b126f5fda..c851d1d9f4728f27af4b7d0254dc1516b3982395 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -734,10 +734,12 @@ public: // Return TRUE or FALSE if the loop should be range-check-eliminated. // Gather a list of IF tests that are dominated by iteration splitting; // also gather the end of the first split and the start of the 2nd split. - bool policy_range_check( PhaseIdealLoop *phase ) const; + bool policy_range_check(PhaseIdealLoop* phase, bool provisional) const; // Return TRUE if "iff" is a range check. bool is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar DEBUG_ONLY(COMMA ProjNode *predicate_proj)) const; + bool is_range_check_if(IfNode* iff, PhaseIdealLoop* phase, BasicType bt, Node* iv, Node*& range, Node*& offset, + jlong& scale) const; // Estimate the number of nodes required when cloning a loop (body). uint est_loop_clone_sz(uint factor) const; @@ -1168,7 +1170,7 @@ public: bool is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_bt); - void long_loop_replace_long_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head); + Node* long_loop_replace_long_iv(Node* iv_to_replace, Node* inner_iv, Node* outer_phi, Node* inner_head); bool transform_long_counted_loop(IdealLoopTree* loop, Node_List &old_new); #ifdef ASSERT bool convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* loop); @@ -1270,10 +1272,21 @@ public: void mark_reductions( IdealLoopTree *loop ); // Return true if exp is a constant times an induction var - bool is_scaled_iv(Node* exp, Node* iv, int* p_scale); + bool is_scaled_iv(Node* exp, Node* iv, jlong* p_scale, BasicType bt); // Return true if exp is a scaled induction var plus (or minus) constant - bool is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node** p_offset, int depth = 0); + bool is_scaled_iv_plus_offset(Node* exp, Node* iv, jlong* p_scale, Node** p_offset, BasicType bt, int depth = 0); + bool is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node** p_offset) { + jlong long_scale; + if (is_scaled_iv_plus_offset(exp, iv, &long_scale, p_offset, T_INT)) { + int int_scale = checked_cast(long_scale); + if (p_scale != NULL) { + *p_scale = int_scale; + } + return true; + } + return false; + } // Enum to determine the action to be performed in create_new_if_for_predicate() when processing phis of UCT regions. enum class UnswitchingAction { @@ -1623,6 +1636,14 @@ public: LoopNode* create_inner_head(IdealLoopTree* loop, LongCountedLoopNode* head, LongCountedLoopEndNode* exit_test); + + int extract_long_range_checks(const IdealLoopTree* loop, jlong stride_con, int iters_limit, PhiNode* phi, + Node_List &range_checks); + + void transform_long_range_checks(int stride_con, const Node_List &range_checks, Node* outer_phi, + Node* inner_iters_actual_int, Node* inner_phi, + Node* iv_add, LoopNode* inner_head); + Node* get_late_ctrl_with_anti_dep(LoadNode* n, Node* early, Node* LCA); bool ctrl_of_use_out_of_loop(const Node* n, Node* n_ctrl, IdealLoopTree* n_loop, Node* ctrl); @@ -1632,6 +1653,10 @@ public: Node* compute_early_ctrl(Node* n, Node* n_ctrl); void try_sink_out_of_loop(Node* n); + + Node* clamp(Node* R, Node* L, Node* H); + + bool safe_for_if_replacement(const Node* dom) const; }; diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 3073ac1a307a2296334c586d22d424eb64cc5a9b..8dff310c569d6c22e6e3d212c846e05a9890f35f 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1074,7 +1074,7 @@ Node *PhaseIdealLoop::split_if_with_blocks_pre( Node *n ) { // If the loop is a candidate for range check elimination, // delay splitting through it's phi until a later loop optimization - if (n_blk->is_CountedLoop()) { + if (n_blk->is_BaseCountedLoop()) { IdealLoopTree *lp = get_loop(n_blk); if (lp && lp->_rce_candidate) { return n; @@ -1392,7 +1392,8 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { Node *prevdom = n; Node *dom = idom(prevdom); while (dom != cutoff) { - if (dom->req() > 1 && dom->in(1) == bol && prevdom->in(0) == dom) { + if (dom->req() > 1 && dom->in(1) == bol && prevdom->in(0) == dom && + safe_for_if_replacement(dom)) { // It's invalid to move control dependent data nodes in the inner // strip-mined loop, because: // 1) break validation of LoopNode::verify_strip_mined() @@ -1430,6 +1431,25 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { } } +bool PhaseIdealLoop::safe_for_if_replacement(const Node* dom) const { + if (!dom->is_CountedLoopEnd()) { + return true; + } + CountedLoopEndNode* le = dom->as_CountedLoopEnd(); + CountedLoopNode* cl = le->loopnode(); + if (cl == NULL) { + return true; + } + if (!cl->is_main_loop()) { + return true; + } + if (cl->is_canonical_loop_entry() == NULL) { + return true; + } + // Further unrolling is possible so loop exit condition might change + return false; +} + // See if a shared loop-varying computation has no loop-varying uses. // Happens if something is only used for JVM state in uncommon trap exits, // like various versions of induction variable+offset. Clone the @@ -1445,7 +1465,8 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { !n->is_MergeMem() && !n->is_CMove() && !is_raw_to_oop_cast && // don't extend live ranges of raw oops - n->Opcode() != Op_Opaque4) { + n->Opcode() != Op_Opaque4 && + !n->is_Type()) { Node *n_ctrl = get_ctrl(n); IdealLoopTree *n_loop = get_loop(n_ctrl); @@ -1454,8 +1475,8 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { if (n_loop != loop_ctrl && n_loop->is_member(loop_ctrl)) { // n has a control input inside a loop but get_ctrl() is member of an outer loop. This could happen, for example, // for Div nodes inside a loop (control input inside loop) without a use except for an UCT (outside the loop). - // Rewire control of n to get_ctrl(n) to move it out of the loop, regardless if its input(s) are later sunk or not. - _igvn.replace_input_of(n, 0, n_ctrl); + // Rewire control of n to right outside of the loop, regardless if its input(s) are later sunk or not. + _igvn.replace_input_of(n, 0, place_outside_loop(n_ctrl, loop_ctrl)); } } if (n_loop != _ltree_root && n->outcnt() > 1) { diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 38a9d9b96f8855799c9f1828a370cb3d2f275540..4c999ea55242db75d96b9f290f49e469646e45f4 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -59,9 +59,9 @@ Node* MulNode::Identity(PhaseGVN* phase) { // We also canonicalize the Node, moving constants to the right input, // and flatten expressions (so that 1+x+2 becomes x+3). Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { - const Type *t1 = phase->type( in(1) ); - const Type *t2 = phase->type( in(2) ); - Node *progress = NULL; // Progress flag + Node* in1 = in(1); + Node* in2 = in(2); + Node* progress = NULL; // Progress flag // This code is used by And nodes too, but some conversions are // only valid for the actual Mul nodes. @@ -70,8 +70,6 @@ Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { (op == Op_MulF) || (op == Op_MulD); // Convert "(-a)*(-b)" into "a*b". - Node *in1 = in(1); - Node *in2 = in(2); if (real_mul && in1->is_Sub() && in2->is_Sub()) { if (phase->type(in1->in(1))->is_zero_type() && phase->type(in2->in(1))->is_zero_type()) { @@ -82,6 +80,8 @@ Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { igvn->_worklist.push(in1); igvn->_worklist.push(in2); } + in1 = in(1); + in2 = in(2); progress = this; } } @@ -104,10 +104,15 @@ Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { igvn->_worklist.push(in1); igvn->_worklist.push(in2); } + in1 = in(1); + in2 = in(2); progress = this; } } + const Type* t1 = phase->type(in1); + const Type* t2 = phase->type(in2); + // We are OK if right is a constant, or right is a load and // left is a non-constant. if( !(t2->singleton() || @@ -427,14 +432,26 @@ const Type *MulDNode::mul_ring(const Type *t0, const Type *t1) const { //============================================================================= //------------------------------Value------------------------------------------ const Type* MulHiLNode::Value(PhaseGVN* phase) const { - // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); + const Type *bot = bottom_type(); + return MulHiValue(t1, t2, bot); +} + +const Type* UMulHiLNode::Value(PhaseGVN* phase) const { + const Type *t1 = phase->type( in(1) ); + const Type *t2 = phase->type( in(2) ); + const Type *bot = bottom_type(); + return MulHiValue(t1, t2, bot); +} + +// A common routine used by UMulHiLNode and MulHiLNode +const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) { + // Either input is TOP ==> the result is TOP if( t1 == Type::TOP ) return Type::TOP; if( t2 == Type::TOP ) return Type::TOP; // Either input is BOTTOM ==> the result is the local BOTTOM - const Type *bot = bottom_type(); if( (t1 == bot) || (t2 == bot) || (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) ) return bot; diff --git a/src/hotspot/share/opto/mulnode.hpp b/src/hotspot/share/opto/mulnode.hpp index d399af5a990d192f1641715236ed2d6f4c4b1aae..25bd6ebf1503bca2955ec2c9ee37b71acf33a3f2 100644 --- a/src/hotspot/share/opto/mulnode.hpp +++ b/src/hotspot/share/opto/mulnode.hpp @@ -41,7 +41,7 @@ class PhaseTransform; class MulNode : public Node { virtual uint hash() const; public: - MulNode( Node *in1, Node *in2 ): Node(0,in1,in2) { + MulNode(Node *in1, Node *in2): Node(NULL,in1,in2) { init_class_id(Class_Mul); } @@ -75,6 +75,11 @@ public: // Supplied function to return the multiplicative opcode virtual int mul_opcode() const = 0; + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return false; + } + // Supplied function to return the additive opcode virtual int max_opcode() const = 0; @@ -98,6 +103,10 @@ public: int min_opcode() const { return Op_MinI; } const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return bt == T_INT; + } }; //------------------------------MulLNode--------------------------------------- @@ -116,6 +125,10 @@ public: int min_opcode() const { return Op_MinL; } const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return bt == T_LONG; + } }; @@ -154,6 +167,8 @@ public: }; //-------------------------------MulHiLNode------------------------------------ +const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot); + // Upper 64 bits of a 64 bit by 64 bit multiply class MulHiLNode : public Node { public: @@ -162,6 +177,18 @@ public: virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } + friend const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot); +}; + +// Upper 64 bits of a 64 bit by 64 bit unsigned multiply +class UMulHiLNode : public Node { +public: + UMulHiLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + virtual int Opcode() const; + virtual const Type* Value(PhaseGVN* phase) const; + const Type *bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } + friend const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot); }; //------------------------------AndINode--------------------------------------- @@ -202,30 +229,49 @@ public: virtual uint ideal_reg() const { return Op_RegL; } }; +class LShiftNode : public Node { +public: + LShiftNode(Node *in1, Node *in2) : Node(NULL,in1,in2) { + init_class_id(Class_LShift); + } + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return false; + } +}; + //------------------------------LShiftINode------------------------------------ // Logical shift left -class LShiftINode : public Node { +class LShiftINode : public LShiftNode { public: - LShiftINode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + LShiftINode(Node *in1, Node *in2) : LShiftNode(in1,in2) {} virtual int Opcode() const; virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return bt == T_INT; + } }; //------------------------------LShiftLNode------------------------------------ // Logical shift left -class LShiftLNode : public Node { +class LShiftLNode : public LShiftNode { public: - LShiftLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} + LShiftLNode(Node *in1, Node *in2) : LShiftNode(in1,in2) {} virtual int Opcode() const; virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return bt == T_LONG; + } }; diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 5406428cbb4343627b0482438ffef4802518b4cb..078cbd03423c95ba576dd4b0d9eb34b1873e67c9 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -98,6 +98,7 @@ class LockNode; class LongCountedLoopNode; class LongCountedLoopEndNode; class LoopNode; +class LShiftNode; class MachBranchNode; class MachCallDynamicJavaNode; class MachCallJavaNode; @@ -754,6 +755,7 @@ public: DEFINE_CLASS_ID(Halt, Node, 15) DEFINE_CLASS_ID(Opaque1, Node, 16) DEFINE_CLASS_ID(Move, Node, 17) + DEFINE_CLASS_ID(LShift, Node, 18) _max_classes = ClassMask_Move }; @@ -883,6 +885,7 @@ public: DEFINE_CLASS_QUERY(LoadStoreConditional) DEFINE_CLASS_QUERY(Lock) DEFINE_CLASS_QUERY(Loop) + DEFINE_CLASS_QUERY(LShift) DEFINE_CLASS_QUERY(Mach) DEFINE_CLASS_QUERY(MachBranch) DEFINE_CLASS_QUERY(MachCall) diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index fbe42cbaae0b5ee4406d8846202f9c518e6310b9..efed355787b1bb758e8a776a6bae6d590d0d36cd 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -78,7 +78,8 @@ protected: ciCallProfile& profile); bool should_not_inline(ciMethod* callee_method, ciMethod* caller_method, - JVMState* jvms); + int caller_bci, + ciCallProfile& profile); bool is_not_reached(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index becd187a5edb6f526177c7a8374867b02ba11b61..ee9c67fa944c7d2f79217fa2033f61c4767748cc 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -410,8 +410,8 @@ static void merge_ranges(SwitchRange* ranges, int& rp) { void Parse::do_tableswitch() { // Get information about tableswitch int default_dest = iter().get_dest_table(0); - int lo_index = iter().get_int_table(1); - int hi_index = iter().get_int_table(2); + jint lo_index = iter().get_int_table(1); + jint hi_index = iter().get_int_table(2); int len = hi_index - lo_index + 1; if (len < 1) { @@ -438,9 +438,9 @@ void Parse::do_tableswitch() { SwitchRange* ranges = NEW_RESOURCE_ARRAY(SwitchRange, rnum); int rp = -1; if (lo_index != min_jint) { - uint cnt = 1; + float cnt = 1.0F; if (profile != NULL) { - cnt = profile->default_count() / (hi_index != max_jint ? 2 : 1); + cnt = (float)profile->default_count() / (hi_index != max_jint ? 2.0F : 1.0F); } ranges[++rp].setRange(min_jint, lo_index-1, default_dest, cnt); } @@ -448,9 +448,9 @@ void Parse::do_tableswitch() { jint match_int = lo_index+j; int dest = iter().get_dest_table(j+3); makes_backward_branch |= (dest <= bci()); - uint cnt = 1; + float cnt = 1.0F; if (profile != NULL) { - cnt = profile->count_at(j); + cnt = (float)profile->count_at(j); } if (rp < 0 || !ranges[rp].adjoin(match_int, dest, cnt, trim_ranges)) { ranges[++rp].set(match_int, dest, cnt); @@ -459,9 +459,9 @@ void Parse::do_tableswitch() { jint highest = lo_index+(len-1); assert(ranges[rp].hi() == highest, ""); if (highest != max_jint) { - uint cnt = 1; + float cnt = 1.0F; if (profile != NULL) { - cnt = profile->default_count() / (lo_index != min_jint ? 2 : 1); + cnt = (float)profile->default_count() / (lo_index != min_jint ? 2.0F : 1.0F); } if (!ranges[rp].adjoinRange(highest+1, max_jint, default_dest, cnt, trim_ranges)) { ranges[++rp].setRange(highest+1, max_jint, default_dest, cnt); @@ -487,7 +487,7 @@ void Parse::do_tableswitch() { void Parse::do_lookupswitch() { // Get information about lookupswitch int default_dest = iter().get_dest_table(0); - int len = iter().get_int_table(1); + jint len = iter().get_int_table(1); if (len < 1) { // If this is a backward branch, add safepoint maybe_add_safepoint(default_dest); @@ -513,26 +513,15 @@ void Parse::do_lookupswitch() { table[3*j+0] = iter().get_int_table(2+2*j); table[3*j+1] = iter().get_dest_table(2+2*j+1); // Handle overflow when converting from uint to jint - table[3*j+2] = (profile == NULL) ? 1 : MIN2(max_jint, profile->count_at(j)); + table[3*j+2] = (profile == NULL) ? 1 : (jint)MIN2((uint)max_jint, profile->count_at(j)); } qsort(table, len, 3*sizeof(table[0]), jint_cmp); } - float defaults = 0; - jint prev = min_jint; - for (int j = 0; j < len; j++) { - jint match_int = table[3*j+0]; - if (match_int != prev) { - defaults += (float)match_int - prev; - } - prev = match_int+1; - } - if (prev != min_jint) { - defaults += (float)max_jint - prev + 1; - } - float default_cnt = 1; + float default_cnt = 1.0F; if (profile != NULL) { - default_cnt = profile->default_count()/defaults; + juint defaults = max_juint - len; + default_cnt = (float)profile->default_count()/(float)defaults; } int rnum = len*2+1; @@ -541,25 +530,25 @@ void Parse::do_lookupswitch() { int rp = -1; for (int j = 0; j < len; j++) { jint match_int = table[3*j+0]; - int dest = table[3*j+1]; - int cnt = table[3*j+2]; - int next_lo = rp < 0 ? min_jint : ranges[rp].hi()+1; + jint dest = table[3*j+1]; + jint cnt = table[3*j+2]; + jint next_lo = rp < 0 ? min_jint : ranges[rp].hi()+1; makes_backward_branch |= (dest <= bci()); - float c = default_cnt * ((float)match_int - next_lo); + float c = default_cnt * ((float)match_int - (float)next_lo); if (match_int != next_lo && (rp < 0 || !ranges[rp].adjoinRange(next_lo, match_int-1, default_dest, c, trim_ranges))) { assert(default_dest != never_reached, "sentinel value for dead destinations"); ranges[++rp].setRange(next_lo, match_int-1, default_dest, c); } - if (rp < 0 || !ranges[rp].adjoin(match_int, dest, cnt, trim_ranges)) { + if (rp < 0 || !ranges[rp].adjoin(match_int, dest, (float)cnt, trim_ranges)) { assert(dest != never_reached, "sentinel value for dead destinations"); - ranges[++rp].set(match_int, dest, cnt); + ranges[++rp].set(match_int, dest, (float)cnt); } } jint highest = table[3*(len-1)]; assert(ranges[rp].hi() == highest, ""); if (highest != max_jint && - !ranges[rp].adjoinRange(highest+1, max_jint, default_dest, default_cnt * ((float)max_jint - highest), trim_ranges)) { - ranges[++rp].setRange(highest+1, max_jint, default_dest, default_cnt * ((float)max_jint - highest)); + !ranges[rp].adjoinRange(highest+1, max_jint, default_dest, default_cnt * ((float)max_jint - (float)highest), trim_ranges)) { + ranges[++rp].setRange(highest+1, max_jint, default_dest, default_cnt * ((float)max_jint - (float)highest)); } assert(rp < rnum, "not too many ranges"); diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index cc2a3fcae84edc6aec416eac11f43eee09b6751a..29444fffac2573982853e7980f5b8f1150fdfcc2 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -792,9 +792,7 @@ ConLNode* PhaseTransform::longcon(jlong l) { } ConNode* PhaseTransform::integercon(jlong l, BasicType bt) { if (bt == T_INT) { - jint int_con = (jint)l; - assert(((long)int_con) == l, "not an int"); - return intcon(int_con); + return intcon(checked_cast(l)); } assert(bt == T_LONG, "not an integer"); return longcon(l); diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index e3e7e690a503ac885e84f7fb0b4680da58e0ad01..7e87da8a46f7ca60eece459b720bf74edb5efad7 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -302,7 +302,7 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_nozero_C(Klass* array_type, int len if ((len > 0) && (result != NULL) && is_deoptimized_caller_frame(current)) { // Zero array here if the caller is deoptimized. - int size = TypeArrayKlass::cast(array_type)->oop_size(result); + const size_t size = TypeArrayKlass::cast(array_type)->oop_size(result); BasicType elem_type = TypeArrayKlass::cast(array_type)->element_type(); const size_t hs = arrayOopDesc::header_size(elem_type); // Align to next 8 bytes to avoid trashing arrays's length. diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index 809a446f52b53160fa86f6cd4e860c8afe52bb8e..8c0a060d86258a4848d1a98b6f9f6a5e48497bff 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -65,7 +65,8 @@ class StringConcat : public ResourceObj { StringMode, IntMode, CharMode, - StringNullCheckMode + StringNullCheckMode, + NegativeIntCheckMode }; StringConcat(PhaseStringOpts* stringopts, CallStaticJavaNode* end): @@ -122,12 +123,19 @@ class StringConcat : public ResourceObj { void push_string(Node* value) { push(value, StringMode); } + void push_string_null_check(Node* value) { push(value, StringNullCheckMode); } + + void push_negative_int_check(Node* value) { + push(value, NegativeIntCheckMode); + } + void push_int(Node* value) { push(value, IntMode); } + void push_char(Node* value) { push(value, CharMode); } @@ -502,13 +510,35 @@ StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) { #ifndef PRODUCT if (PrintOptimizeStringConcat) { tty->print("giving up because StringBuilder(null) throws exception"); - alloc->jvms()->dump_spec(tty); tty->cr(); + alloc->jvms()->dump_spec(tty); + tty->cr(); } #endif return NULL; } // StringBuilder(str) argument needs null check. sc->push_string_null_check(use->in(TypeFunc::Parms + 1)); + } else if (sig == ciSymbols::int_void_signature()) { + // StringBuilder(int) case. + Node* parm = use->in(TypeFunc::Parms + 1); + assert(parm != NULL, "must exist"); + const TypeInt* type = _gvn->type(parm)->is_int(); + if (type->_hi < 0) { + // Initial capacity argument is always negative in which case StringBuilder(int) throws + // a NegativeArraySizeException. Bail out from string opts. +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print("giving up because a negative argument is passed to StringBuilder(int) which " + "throws a NegativeArraySizeException"); + alloc->jvms()->dump_spec(tty); + tty->cr(); + } +#endif + return NULL; + } else if (type->_lo < 0) { + // Argument could be negative: We need a runtime check to throw NegativeArraySizeException in that case. + sc->push_negative_int_check(parm); + } } // The int variant takes an initial size for the backing // array so just treat it like the void version. @@ -1244,7 +1274,7 @@ Node* PhaseStringOpts::int_stringSize(GraphKit& kit, Node* arg) { kit.set_control(loop); Node* sizeTable = fetch_static_field(kit, size_table_field); - Node* value = kit.load_array_element(NULL, sizeTable, index, TypeAryPtr::INTS); + Node* value = kit.load_array_element(sizeTable, index, TypeAryPtr::INTS, /* set_ctrl */ false); C->record_for_igvn(value); Node* limit = __ CmpI(phi, value); Node* limitb = __ Bool(limit, BoolTest::le); @@ -1794,6 +1824,23 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { for (int argi = 0; argi < sc->num_arguments(); argi++) { Node* arg = sc->argument(argi); switch (sc->mode(argi)) { + case StringConcat::NegativeIntCheckMode: { + // Initial capacity argument might be negative in which case StringBuilder(int) throws + // a NegativeArraySizeException. Insert a runtime check with an uncommon trap. + const TypeInt* type = kit.gvn().type(arg)->is_int(); + assert(type->_hi >= 0 && type->_lo < 0, "no runtime int check needed"); + Node* p = __ Bool(__ CmpI(arg, kit.intcon(0)), BoolTest::ge); + IfNode* iff = kit.create_and_map_if(kit.control(), p, PROB_MIN, COUNT_UNKNOWN); + { + // Negative int -> uncommon trap. + PreserveJVMState pjvms(&kit); + kit.set_control(__ IfFalse(iff)); + kit.uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } + kit.set_control(__ IfTrue(iff)); + break; + } case StringConcat::IntMode: { Node* string_size = int_stringSize(kit, arg); @@ -1963,6 +2010,8 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { for (int argi = 0; argi < sc->num_arguments(); argi++) { Node* arg = sc->argument(argi); switch (sc->mode(argi)) { + case StringConcat::NegativeIntCheckMode: + break; // Nothing to do, was only needed to add a runtime check earlier. case StringConcat::IntMode: { start = int_getChars(kit, arg, dst_array, coder, start, string_sizes->in(argi)); break; diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index 4560c109a1ec737fe3be4dad7c54b82099ee2320..281b28237c85ce19ab58df5496c6b1af66a75cdd 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -62,6 +62,10 @@ public: virtual const Type *add_id() const = 0; static SubNode* make(Node* in1, Node* in2, BasicType bt); + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return false; + } }; @@ -77,6 +81,10 @@ public: const Type *add_id() const { return TypeInt::ZERO; } const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return bt == T_INT; + } }; //------------------------------SubLNode--------------------------------------- @@ -90,6 +98,10 @@ public: const Type *add_id() const { return TypeLong::ZERO; } const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } + virtual bool operates_on(BasicType bt, bool signed_int) const { + assert(bt == T_INT || bt == T_LONG, "unsupported"); + return bt == T_LONG; + } }; // NOTE: SubFPNode should be taken away and replaced by add and negate diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 0e00986804d64c740690e208b6334266ce7e7279..9638c828143577b89f309378c88d05668ee4a717 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -104,6 +104,9 @@ #if INCLUDE_JFR #include "jfr/jfr.hpp" #endif +#if INCLUDE_MANAGEMENT +#include "services/finalizerService.hpp" +#endif #include @@ -657,7 +660,7 @@ JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) } // Make shallow object copy - const int size = obj->size(); + const size_t size = obj->size(); oop new_obj_oop = NULL; if (obj->is_array()) { const int length = ((arrayOop)obj())->length(); @@ -681,6 +684,12 @@ JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) return JNIHandles::make_local(THREAD, new_obj()); JVM_END +// java.lang.ref.Finalizer //////////////////////////////////////////////////// + +JVM_ENTRY(void, JVM_ReportFinalizationComplete(JNIEnv * env, jobject finalizee)) + MANAGEMENT_ONLY(FinalizerService::on_complete(JNIHandles::resolve_non_null(finalizee), THREAD);) +JVM_END + // java.io.File /////////////////////////////////////////////////////////////// JVM_LEAF(char*, JVM_NativePath(char* path)) @@ -2961,7 +2970,7 @@ JVM_ENTRY(void, JVM_StopThread(JNIEnv* env, jobject jthread, jobject throwable)) THROW_OOP(java_throwable); } else { // Use a VM_Operation to throw the exception. - JavaThread::send_async_exception(java_thread, java_throwable); + JavaThread::send_async_exception(receiver, java_throwable); } } else { // Either: @@ -3851,3 +3860,4 @@ JVM_END JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) return os::get_signal_number(name); JVM_END + diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index f04dc7dd27ca2ce327b0d05bef01061194d3dde3..5680e7d167027f596123302f55ac6932bf85cffe 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -1072,7 +1072,7 @@ JvmtiEnv::StopThread(JavaThread* java_thread, jobject exception) { oop e = JNIHandles::resolve_external_guard(exception); NULL_CHECK(e, JVMTI_ERROR_NULL_POINTER); - JavaThread::send_async_exception(java_thread->threadObj(), e); + JavaThread::send_async_exception(java_thread, e); return JVMTI_ERROR_NONE; diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp index 62fdd2160f57784d345b2dffaf1d0a7a09851194..89e41730ed9e7c53441438682ee7f5d80f209ac6 100644 --- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp @@ -4404,6 +4404,9 @@ void VM_RedefineClasses::redefine_single_class(Thread* current, jclass the_jclas the_class->set_has_been_redefined(); + // Scratch class is unloaded but still needs cleaning, and skipping for CDS. + scratch_class->set_is_scratch_class(); + // keep track of previous versions of this class the_class->add_previous_version(scratch_class, emcp_method_count); diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp index 5ffefc0d36e2caf6ebedd685c36c7bec9cb09dc6..a19ea2559c73a627cf31079d657ccf9c561bd6c7 100644 --- a/src/hotspot/share/prims/jvmtiTagMap.cpp +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp @@ -72,7 +72,7 @@ bool JvmtiTagMap::_has_object_free_events = false; // create a JvmtiTagMap JvmtiTagMap::JvmtiTagMap(JvmtiEnv* env) : _env(env), - _lock(Mutex::nosafepoint, "JvmtiTagMap_lock", Mutex::_safepoint_check_never), + _lock(Mutex::nosafepoint, "JvmtiTagMap_lock"), _needs_rehashing(false), _needs_cleaning(false) { diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index ee6c218f5331cd1e2da5c8ef0a69f77dc8e7e0a9..ade9651bcb840f11cfc540a8a9663c79485eb45c 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -119,7 +119,7 @@ static inline void assert_field_offset_sane(oop p, jlong field_offset) { assert(byte_offset >= 0 && byte_offset <= (jlong)MAX_OBJECT_SIZE, "sane offset"); if (byte_offset == (jint)byte_offset) { void* ptr_plus_disp = cast_from_oop
      (p) + byte_offset; - assert(p->field_addr((jint)byte_offset) == ptr_plus_disp, + assert(p->field_addr((jint)byte_offset) == ptr_plus_disp, "raw [ptr+disp] must be consistent with oop::field_addr"); } jlong p_size = HeapWordSize * (jlong)(p->size()); @@ -218,44 +218,25 @@ public: } T get() { - if (_obj == NULL) { - GuardUnsafeAccess guard(_thread); - T ret = RawAccess<>::load(addr()); - return normalize_for_read(ret); - } else { - T ret = HeapAccess<>::load_at(_obj, _offset); - return normalize_for_read(ret); - } + GuardUnsafeAccess guard(_thread); + return normalize_for_read(*addr()); } void put(T x) { - if (_obj == NULL) { - GuardUnsafeAccess guard(_thread); - RawAccess<>::store(addr(), normalize_for_write(x)); - } else { - HeapAccess<>::store_at(_obj, _offset, normalize_for_write(x)); - } + GuardUnsafeAccess guard(_thread); + *addr() = normalize_for_write(x); } T get_volatile() { - if (_obj == NULL) { - GuardUnsafeAccess guard(_thread); - volatile T ret = RawAccess::load(addr()); - return normalize_for_read(ret); - } else { - T ret = HeapAccess::load_at(_obj, _offset); - return normalize_for_read(ret); - } + GuardUnsafeAccess guard(_thread); + volatile T ret = RawAccess::load(addr()); + return normalize_for_read(ret); } void put_volatile(T x) { - if (_obj == NULL) { - GuardUnsafeAccess guard(_thread); - RawAccess::store(addr(), normalize_for_write(x)); - } else { - HeapAccess::store_at(_obj, _offset, normalize_for_write(x)); - } + GuardUnsafeAccess guard(_thread); + RawAccess::store(addr(), normalize_for_write(x)); } }; @@ -745,24 +726,14 @@ UNSAFE_ENTRY(jobject, Unsafe_CompareAndExchangeReference(JNIEnv *env, jobject un UNSAFE_ENTRY(jint, Unsafe_CompareAndExchangeInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) { oop p = JNIHandles::resolve(obj); - if (p == NULL) { - volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset); - return RawAccess<>::atomic_cmpxchg(addr, e, x); - } else { - assert_field_offset_sane(p, offset); - return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x); - } + volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset); + return Atomic::cmpxchg(addr, e, x); } UNSAFE_END UNSAFE_ENTRY(jlong, Unsafe_CompareAndExchangeLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) { oop p = JNIHandles::resolve(obj); - if (p == NULL) { - volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset); - return RawAccess<>::atomic_cmpxchg(addr, e, x); - } else { - assert_field_offset_sane(p, offset); - return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x); - } + volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset); + return Atomic::cmpxchg(addr, e, x); } UNSAFE_END UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetReference(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h)) { @@ -776,24 +747,14 @@ UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetReference(JNIEnv *env, jobject unsafe UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) { oop p = JNIHandles::resolve(obj); - if (p == NULL) { - volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset); - return RawAccess<>::atomic_cmpxchg(addr, e, x) == e; - } else { - assert_field_offset_sane(p, offset); - return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x) == e; - } + volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset); + return Atomic::cmpxchg(addr, e, x) == e; } UNSAFE_END UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) { oop p = JNIHandles::resolve(obj); - if (p == NULL) { - volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset); - return RawAccess<>::atomic_cmpxchg(addr, e, x) == e; - } else { - assert_field_offset_sane(p, offset); - return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x) == e; - } + volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset); + return Atomic::cmpxchg(addr, e, x) == e; } UNSAFE_END static void post_thread_park_event(EventThreadPark* event, const oop obj, jlong timeout_nanos, jlong until_epoch_millis) { diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 3100361500143145185dcbc804406b39572732dc..7e154bdd3e7a32c2256392ec544b0984a1d9e53d 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -527,6 +527,7 @@ static SpecialFlag const special_jvm_flags[] = { { "AllowRedefinitionToAddDeleteMethods", JDK_Version::jdk(13), JDK_Version::undefined(), JDK_Version::undefined() }, { "FlightRecorder", JDK_Version::jdk(13), JDK_Version::undefined(), JDK_Version::undefined() }, { "FilterSpuriousWakeups", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::jdk(20) }, + { "MinInliningThreshold", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::jdk(20) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "DefaultMaxRAMFraction", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -3531,9 +3532,8 @@ bool Arguments::init_shared_archive_paths() { } if (archives == 1) { char* temp_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments); - int name_size; bool success = - FileMapInfo::get_base_archive_name_from_header(temp_archive_path, &name_size, &SharedArchivePath); + FileMapInfo::get_base_archive_name_from_header(temp_archive_path, &SharedArchivePath); if (!success) { SharedArchivePath = temp_archive_path; } else { diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 8a1c0a3f05578cc4962c4c343202ebbd92c9e1bf..d54492032518eae1275401e6ecf726cab274a93b 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -201,7 +201,7 @@ static void print_objects(JavaThread* deoptee_thread, if (obj.is_null()) { st.print(" allocation failed"); } else { - st.print(" allocated (%d bytes)", obj->size() * HeapWordSize); + st.print(" allocated (" SIZE_FORMAT " bytes)", obj->size() * HeapWordSize); } st.cr(); diff --git a/src/hotspot/share/runtime/fieldDescriptor.cpp b/src/hotspot/share/runtime/fieldDescriptor.cpp index 83f31e05085fbc660a4e78fa53813878ccda98df..4e07a456bb07bce786f6effe9428999b5f2e3aae 100644 --- a/src/hotspot/share/runtime/fieldDescriptor.cpp +++ b/src/hotspot/share/runtime/fieldDescriptor.cpp @@ -55,7 +55,7 @@ Symbol* fieldDescriptor::generic_signature() const { } } assert(false, "should never happen"); - return NULL; + return vmSymbols::void_signature(); // return a default value (for code analyzers) } bool fieldDescriptor::is_trusted_final() const { diff --git a/src/hotspot/share/runtime/flags/jvmFlagLimit.cpp b/src/hotspot/share/runtime/flags/jvmFlagLimit.cpp index 5b81444eb84abe561910d976a435f899d6432df0..bcc1565e165bd265a2b6abe7b9efaa934565ae20 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagLimit.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagLimit.cpp @@ -34,6 +34,7 @@ #include "gc/shared/referenceProcessor.hpp" #include "oops/markWord.hpp" #include "runtime/task.hpp" +#include "utilities/vmError.hpp" //---------------------------------------------------------------------- // Build flagLimitTable[] diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index d98d0ad522684cfcb5d309ca14d56cf8f437aa17..619ec338626fe8ab55254cb931bb512185a6ee76 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1091,9 +1091,6 @@ const intx ObjectAlignmentInBytes = 8; develop(bool, DebugVtables, false, \ "add debugging code to vtable dispatch") \ \ - notproduct(bool, PrintVtableStats, false, \ - "print vtables stats at end of run") \ - \ develop(bool, TraceCreateZombies, false, \ "trace creation of zombie nmethods") \ \ @@ -1336,6 +1333,10 @@ const intx ObjectAlignmentInBytes = 8; develop(intx, StackPrintLimit, 100, \ "number of stack frames to print in VM-level stack dump") \ \ + product(int, ErrorLogPrintCodeLimit, 3, DIAGNOSTIC, \ + "max number of compiled code units to print in error log") \ + range(0, VMError::max_error_log_print_code) \ + \ notproduct(intx, MaxElementPrintSize, 256, \ "maximum number of elements to print") \ \ @@ -1347,9 +1348,9 @@ const intx ObjectAlignmentInBytes = 8; "(using CompileCommand or marked w/ @ForceInline)") \ range(0, max_jint) \ \ - product(intx, MinInliningThreshold, 250, \ - "The minimum invocation count a method needs to have to be " \ - "inlined") \ + product(intx, MinInliningThreshold, 0, \ + "(Deprecated) The minimum invocation count a method needs to" \ + "have to be inlined") \ range(0, max_jint) \ \ develop(intx, MethodHistogramCutoff, 100, \ @@ -1403,6 +1404,10 @@ const intx ObjectAlignmentInBytes = 8; product(double, InlineFrequencyRatio, 0.25, DIAGNOSTIC, \ "Ratio of call site execution to caller method invocation") \ \ + product(double, MinInlineFrequencyRatio, 0.0085, DIAGNOSTIC, \ + "Minimum ratio of call site execution to caller method" \ + "invocation to be considered for inlining") \ + \ develop(intx, InlineThrowCount, 50, \ "Force inlining of interpreted methods that throw this often") \ range(0, max_jint) \ diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index 044ba38ae94b45912bf7bc5652a98d8ac20620e8..f4c9c775b021969850cd05f44beea03cf35113c9 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -408,7 +408,7 @@ void Handshake::execute(AsyncHandshakeClosure* hs_cl, JavaThread* target) { HandshakeState::HandshakeState(JavaThread* target) : _handshakee(target), _queue(), - _lock(Monitor::nosafepoint, "HandshakeState_lock", Monitor::_safepoint_check_never), + _lock(Monitor::nosafepoint, "HandshakeState_lock"), _active_handshaker(), _suspended(false), _async_suspend_handshake(false) diff --git a/src/hotspot/share/runtime/interfaceSupport.inline.hpp b/src/hotspot/share/runtime/interfaceSupport.inline.hpp index 8ba799a4baf927965686507e9bbccc918bbd5d66..82cd2c180a17337a9f02c70c44ca025a9fa31d9f 100644 --- a/src/hotspot/share/runtime/interfaceSupport.inline.hpp +++ b/src/hotspot/share/runtime/interfaceSupport.inline.hpp @@ -300,6 +300,7 @@ class VMNativeEntryWrapper { #define JRT_ENTRY(result_type, header) \ result_type header { \ + assert(current == JavaThread::current(), "Must be"); \ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \ ThreadInVMfromJava __tiv(current); \ VM_ENTRY_BASE(result_type, header, current) \ @@ -327,6 +328,7 @@ class VMNativeEntryWrapper { #define JRT_ENTRY_NO_ASYNC(result_type, header) \ result_type header { \ + assert(current == JavaThread::current(), "Must be"); \ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \ ThreadInVMfromJava __tiv(current, false /* check asyncs */); \ VM_ENTRY_BASE(result_type, header, current) \ @@ -336,17 +338,20 @@ class VMNativeEntryWrapper { // to get back into Java from the VM #define JRT_BLOCK_ENTRY(result_type, header) \ result_type header { \ + assert(current == JavaThread::current(), "Must be"); \ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \ HandleMarkCleaner __hm(current); #define JRT_BLOCK \ { \ + assert(current == JavaThread::current(), "Must be"); \ ThreadInVMfromJava __tiv(current); \ JavaThread* THREAD = current; /* For exception macros. */ \ debug_only(VMEntryWrapper __vew;) #define JRT_BLOCK_NO_ASYNC \ { \ + assert(current == JavaThread::current(), "Must be"); \ ThreadInVMfromJava __tiv(current, false /* check asyncs */); \ JavaThread* THREAD = current; /* For exception macros. */ \ debug_only(VMEntryWrapper __vew;) diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index f1c591659a5f485aaf16040c67cee1eb9467d296..59510eec897ccd273475563053b47fcbd6ca4d0d 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -306,10 +306,6 @@ void print_statistics() { CodeCache::print_internals(); } - if (PrintVtableStats) { - klassVtable::print_statistics(); - klassItable::print_statistics(); - } if (VerifyOops && Verbose) { tty->print_cr("+VerifyOops count: %d", StubRoutines::verify_oop_count()); } diff --git a/src/hotspot/share/runtime/mutex.cpp b/src/hotspot/share/runtime/mutex.cpp index 98f56a3c18be0aa17e6a1ddc3da23794235bfaad..caf14ce116493825fa931f0d9a4ef837838b4adb 100644 --- a/src/hotspot/share/runtime/mutex.cpp +++ b/src/hotspot/share/runtime/mutex.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "logging/log.hpp" +#include "memory/resourceArea.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/mutex.hpp" #include "runtime/os.inline.hpp" @@ -62,24 +63,21 @@ void Mutex::check_block_state(Thread* thread) { void Mutex::check_safepoint_state(Thread* thread) { check_block_state(thread); - // If the JavaThread checks for safepoint, verify that the lock wasn't created with safepoint_check_never. - if (thread->is_active_Java_thread()) { - assert(_safepoint_check_required != _safepoint_check_never, - "This lock should never have a safepoint check for Java threads: %s", - name()); + // If the lock acquisition checks for safepoint, verify that the lock was created with rank that + // has safepoint checks. Technically this doesn't affect NonJavaThreads since they won't actually + // check for safepoint, but let's make the rule unconditional unless there's a good reason not to. + assert(_rank > nosafepoint, + "This lock should not be taken with a safepoint check: %s", name()); + if (thread->is_active_Java_thread()) { // Also check NoSafepointVerifier, and thread state is _thread_in_vm JavaThread::cast(thread)->check_for_valid_safepoint_state(); - } else { - // If initialized with safepoint_check_never, a NonJavaThread should never ask to safepoint check either. - assert(_safepoint_check_required != _safepoint_check_never, - "NonJavaThread should not check for safepoint"); } } void Mutex::check_no_safepoint_state(Thread* thread) { check_block_state(thread); - assert(!thread->is_active_Java_thread() || _safepoint_check_required != _safepoint_check_always, + assert(!thread->is_active_Java_thread() || _rank <= nosafepoint, "This lock should always have a safepoint check for Java threads: %s", name()); } @@ -167,7 +165,7 @@ bool Mutex::try_lock_inner(bool do_rank_checks) { if (do_rank_checks) { check_rank(self); } - // Some safepoint_check_always locks use try_lock, so cannot check + // Some safepoint checking locks use try_lock, so cannot check // safepoint state, but can check blocking state. check_block_state(self); @@ -274,29 +272,21 @@ Mutex::~Mutex() { os::free(const_cast(_name)); } -Mutex::Mutex(int Rank, const char * name, SafepointCheckRequired safepoint_check_required, - bool allow_vm_block) : _owner(NULL) { +Mutex::Mutex(Rank rank, const char * name, bool allow_vm_block) : _owner(NULL) { assert(os::mutex_init_done(), "Too early!"); assert(name != NULL, "Mutex requires a name"); _name = os::strdup(name, mtInternal); #ifdef ASSERT _allow_vm_block = allow_vm_block; - _rank = Rank; - _safepoint_check_required = safepoint_check_required; + _rank = rank; _skip_rank_check = false; - assert(_rank > nosafepoint || _safepoint_check_required == _safepoint_check_never, - "Locks below nosafepoint rank should never safepoint: %s", name); - - assert(_rank <= nosafepoint || _safepoint_check_required == _safepoint_check_always, - "Locks above nosafepoint rank should safepoint: %s", name); + assert(_rank >= static_cast(0) && _rank <= safepoint, "Bad lock rank %s: %s", rank_name(), name); // The allow_vm_block also includes allowing other non-Java threads to block or // allowing Java threads to block in native. - assert(_safepoint_check_required == _safepoint_check_always || _allow_vm_block, - "Safepoint check never locks should always allow the vm to block: %s", name); - - assert(_rank >= 0, "Bad lock rank: %s", name); + assert(_rank > nosafepoint || _allow_vm_block, + "Locks that don't check for safepoint should always allow the vm to block: %s", name); #endif } @@ -312,23 +302,57 @@ void Mutex::print_on_error(outputStream* st) const { // ---------------------------------------------------------------------------------- // Non-product code +// +#ifdef ASSERT +static Mutex::Rank _ranks[] = { Mutex::event, Mutex::service, Mutex::stackwatermark, Mutex::tty, Mutex::oopstorage, + Mutex::nosafepoint, Mutex::safepoint }; + +static const char* _rank_names[] = { "event", "service", "stackwatermark", "tty", "oopstorage", + "nosafepoint", "safepoint" }; + +static const int _num_ranks = 7; + +static const char* rank_name_internal(Mutex::Rank r) { + // Find closest rank and print out the name + stringStream st; + for (int i = 0; i < _num_ranks; i++) { + if (r == _ranks[i]) { + return _rank_names[i]; + } else if (r > _ranks[i] && (i < _num_ranks-1 && r < _ranks[i+1])) { + int delta = static_cast(_ranks[i+1]) - static_cast(r); + st.print("%s-%d", _rank_names[i+1], delta); + return st.as_string(); + } + } + return "fail"; +} -#ifndef PRODUCT -const char* print_safepoint_check(Mutex::SafepointCheckRequired safepoint_check) { - switch (safepoint_check) { - case Mutex::_safepoint_check_never: return "safepoint_check_never"; - case Mutex::_safepoint_check_always: return "safepoint_check_always"; - default: return ""; +const char* Mutex::rank_name() const { + return rank_name_internal(_rank); +} + + +void Mutex::assert_no_overlap(Rank orig, Rank adjusted, int adjust) { + int i = 0; + while (_ranks[i] < orig) i++; + // underflow is caught in constructor + if (i != 0 && adjusted > event && adjusted <= _ranks[i-1]) { + ResourceMark rm; + assert(adjusted > _ranks[i-1], + "Rank %s-%d overlaps with %s", + rank_name_internal(orig), adjust, rank_name_internal(adjusted)); } } +#endif // ASSERT +#ifndef PRODUCT void Mutex::print_on(outputStream* st) const { st->print("Mutex: [" PTR_FORMAT "] %s - owner: " PTR_FORMAT, p2i(this), _name, p2i(owner())); if (_allow_vm_block) { st->print("%s", " allow_vm_block"); } - st->print(" %s", print_safepoint_check(_safepoint_check_required)); + DEBUG_ONLY(st->print(" %s", rank_name())); st->cr(); } #endif // PRODUCT @@ -384,14 +408,22 @@ void Mutex::check_rank(Thread* thread) { if (owned_by_self()) { // wait() case Mutex* least = get_least_ranked_lock_besides_this(locks_owned); - // We enforce not holding locks of rank nosafepoint or lower while waiting. + // For JavaThreads, we enforce not holding locks of rank nosafepoint or lower while waiting + // because the held lock has a NoSafepointVerifier so waiting on a lower ranked lock will not be + // able to check for safepoints first with a TBIVM. + // For all threads, we enforce not holding the tty lock or below, since this could block progress also. // Also "this" should be the monitor with lowest rank owned by this thread. - if (least != NULL && (least->rank() <= nosafepoint || least->rank() <= this->rank())) { - assert(false, "Attempting to wait on monitor %s/%d while holding lock %s/%d -- " - "possible deadlock. %s", name(), rank(), least->name(), least->rank(), + if (least != NULL && ((least->rank() <= Mutex::nosafepoint && thread->is_Java_thread()) || + least->rank() <= Mutex::tty || + least->rank() <= this->rank())) { + ResourceMark rm(thread); + assert(false, "Attempting to wait on monitor %s/%s while holding lock %s/%s -- " + "possible deadlock. %s", name(), rank_name(), least->name(), least->rank_name(), least->rank() <= this->rank() ? "Should wait on the least ranked monitor from all owned locks." : - "Should not block(wait) while holding a lock of rank nosafepoint or below."); + thread->is_Java_thread() ? + "Should not block(wait) while holding a lock of rank nosafepoint or below." : + "Should not block(wait) while holding a lock of rank tty or below."); } } else { // lock()/lock_without_safepoint_check()/try_lock() case @@ -402,14 +434,15 @@ void Mutex::check_rank(Thread* thread) { // to acquire, then deadlock prevention rules require that the rank // of m2 be less than the rank of m1. This prevents circular waits. if (least != NULL && least->rank() <= this->rank()) { + ResourceMark rm(thread); if (least->rank() > Mutex::tty) { // Printing owned locks acquires tty lock. If the least rank was below or equal // tty, then deadlock detection code would circle back here, until we run // out of stack and crash hard. Print locks only when it is safe. thread->print_owned_locks(); } - assert(false, "Attempting to acquire lock %s/%d out of order with lock %s/%d -- " - "possible deadlock", this->name(), this->rank(), least->name(), least->rank()); + assert(false, "Attempting to acquire lock %s/%s out of order with lock %s/%s -- " + "possible deadlock", this->name(), this->rank_name(), least->name(), least->rank_name()); } } } diff --git a/src/hotspot/share/runtime/mutex.hpp b/src/hotspot/share/runtime/mutex.hpp index cb98d42655cbaa406e91a89ebf1e6a8c9d6721aa..586b69f10822e4b75216b88e8cc3a2d5e336afb5 100644 --- a/src/hotspot/share/runtime/mutex.hpp +++ b/src/hotspot/share/runtime/mutex.hpp @@ -46,19 +46,41 @@ class Mutex : public CHeapObj { public: // Special low level locks are given names and ranges avoid overlap. - enum Rank { + enum class Rank { event, service = event + 6, stackwatermark = service + 3, tty = stackwatermark + 3, oopstorage = tty + 3, nosafepoint = oopstorage + 6, - leaf = nosafepoint + 6, - barrier = leaf + 10, - nonleaf = barrier + 1, - max_nonleaf = nonleaf + 900 + safepoint = nosafepoint + 20 }; + // want C++later "using enum" directives. + static const Rank event = Rank::event; + static const Rank service = Rank::service; + static const Rank stackwatermark = Rank::stackwatermark; + static const Rank tty = Rank::tty; + static const Rank oopstorage = Rank::oopstorage; + static const Rank nosafepoint = Rank::nosafepoint; + static const Rank safepoint = Rank::safepoint; + + static void assert_no_overlap(Rank orig, Rank adjusted, int adjust); + + friend Rank operator-(Rank base, int adjust) { + Rank result = static_cast(static_cast(base) - adjust); + DEBUG_ONLY(assert_no_overlap(base, result, adjust)); + return result; + } + + friend constexpr bool operator<(Rank lhs, Rank rhs) { + return static_cast(lhs) < static_cast(rhs); + } + + friend constexpr bool operator>(Rank lhs, Rank rhs) { return rhs < lhs; } + friend constexpr bool operator<=(Rank lhs, Rank rhs) { return !(lhs > rhs); } + friend constexpr bool operator>=(Rank lhs, Rank rhs) { return !(lhs < rhs); } + private: // The _owner field is only set by the current thread, either to itself after it has acquired // the low-level _lock, or to NULL before it has released the _lock. Accesses by any thread other @@ -75,7 +97,7 @@ class Mutex : public CHeapObj { bool _allow_vm_block; #endif #ifdef ASSERT - int _rank; // rank (to avoid/detect potential deadlocks) + Rank _rank; // rank (to avoid/detect potential deadlocks) Mutex* _next; // Used by a Thread to link up owned locks Thread* _last_owner; // the last thread to own the lock bool _skip_rank_check; // read only by owner when doing rank checks @@ -89,7 +111,8 @@ class Mutex : public CHeapObj { } public: - int rank() const { return _rank; } + Rank rank() const { return _rank; } + const char* rank_name() const; Mutex* next() const { return _next; } void set_next(Mutex *next) { _next = next; } #endif // ASSERT @@ -109,10 +132,10 @@ class Mutex : public CHeapObj { // the safepoint protocol when acquiring locks. // Each lock can be acquired by only JavaThreads, only NonJavaThreads, or shared between - // Java and NonJavaThreads. When the lock is initialized with _safepoint_check_always, + // Java and NonJavaThreads. When the lock is initialized with rank > nosafepoint, // that means that whenever the lock is acquired by a JavaThread, it will verify that // it is done with a safepoint check. In corollary, when the lock is initialized with - // _safepoint_check_never, that means that whenever the lock is acquired by a JavaThread + // rank <= nosafepoint, that means that whenever the lock is acquired by a JavaThread // it will verify that it is done without a safepoint check. // TODO: Locks that are shared between JavaThreads and NonJavaThreads @@ -130,26 +153,11 @@ class Mutex : public CHeapObj { static const SafepointCheckFlag _no_safepoint_check_flag = SafepointCheckFlag::_no_safepoint_check_flag; - enum class SafepointCheckRequired { - _safepoint_check_never, // Mutexes with this value will cause errors - // when acquired by a JavaThread with a safepoint check. - _safepoint_check_always // Mutexes with this value will cause errors - // when acquired by a JavaThread without a safepoint check. - }; - // Bring the enumerator names into class scope. - static const SafepointCheckRequired _safepoint_check_never = - SafepointCheckRequired::_safepoint_check_never; - static const SafepointCheckRequired _safepoint_check_always = - SafepointCheckRequired::_safepoint_check_always; - - NOT_PRODUCT(SafepointCheckRequired _safepoint_check_required;) - public: - Mutex(int rank, const char *name, SafepointCheckRequired safepoint_check_required, bool allow_vm_block); + Mutex(Rank rank, const char *name, bool allow_vm_block); - Mutex(int rank, const char *name, SafepointCheckRequired safepoint_check_required) : - Mutex(rank, name, safepoint_check_required, - safepoint_check_required == _safepoint_check_never ? true : false) {} + Mutex(Rank rank, const char *name) : + Mutex(rank, name, rank > nosafepoint ? false : true) {} ~Mutex(); @@ -190,11 +198,11 @@ class Mutex : public CHeapObj { class Monitor : public Mutex { public: - Monitor(int rank, const char *name, SafepointCheckRequired safepoint_check_required, bool allow_vm_block) : - Mutex(rank, name, safepoint_check_required, allow_vm_block) {} + Monitor(Rank rank, const char *name, bool allow_vm_block) : + Mutex(rank, name, allow_vm_block) {} - Monitor(int rank, const char *name, SafepointCheckRequired safepoint_check_required) : - Mutex(rank, name, safepoint_check_required) {} + Monitor(Rank rank, const char *name) : + Mutex(rank, name) {} // default destructor // Wait until monitor is notified (or times out). @@ -214,10 +222,8 @@ class PaddedMutex : public Mutex { }; char _padding[PADDING_LEN]; public: - PaddedMutex(int rank, const char *name, SafepointCheckRequired safepoint_check_required, bool allow_vm_block) : - Mutex(rank, name, safepoint_check_required, allow_vm_block) {}; - PaddedMutex(int rank, const char *name, SafepointCheckRequired safepoint_check_required) : - Mutex(rank, name, safepoint_check_required) {}; + PaddedMutex(Rank rank, const char *name, bool allow_vm_block) : Mutex(rank, name, allow_vm_block) {}; + PaddedMutex(Rank rank, const char *name) : Mutex(rank, name) {}; }; class PaddedMonitor : public Monitor { @@ -227,10 +233,8 @@ class PaddedMonitor : public Monitor { }; char _padding[PADDING_LEN]; public: - PaddedMonitor(int rank, const char *name, SafepointCheckRequired safepoint_check_required, bool allow_vm_block) : - Monitor(rank, name, safepoint_check_required, allow_vm_block) {}; - PaddedMonitor(int rank, const char *name, SafepointCheckRequired safepoint_check_required) : - Monitor(rank, name, safepoint_check_required) {}; + PaddedMonitor(Rank rank, const char *name, bool allow_vm_block) : Monitor(rank, name, allow_vm_block) {}; + PaddedMonitor(Rank rank, const char *name) : Monitor(rank, name) {}; }; #endif // SHARE_RUNTIME_MUTEX_HPP diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index f93a7ecbce1f2ebc1f093daa3cd3be45e63de2ed..34b19eb51ac3852b84a8db22db7b58940a18b34b 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -194,153 +194,178 @@ void assert_locked_or_safepoint_or_handshake(const Mutex* lock, const JavaThread } #endif -#define def(var, type, pri, vm_block, safepoint_check_allowed ) { \ - var = new type(Mutex::pri, #var, Mutex::safepoint_check_allowed, vm_block); \ - assert(_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX"); \ - _mutex_array[_num_mutex++] = var; \ +static void add_mutex(Mutex* var) { + assert(_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX"); + _mutex_array[_num_mutex++] = var; } +#define def(var, type, pri, ...) { \ + var = new type(Mutex::pri, #var, ##__VA_ARGS__); \ + add_mutex(var); \ +} + +// Specify relative ranked lock +#ifdef ASSERT +#define defl(var, type, held_lock, ...) { \ + var = new type(held_lock->rank()-1, #var, ##__VA_ARGS__); \ + add_mutex(var); \ +} +#else +#define defl(var, type, held_lock, ...) { \ + var = new type(Mutex::safepoint, #var, ##__VA_ARGS__); \ + add_mutex(var); \ +} +#endif + // Using Padded subclasses to prevent false sharing of these global monitors and mutexes. void mutex_init() { - def(tty_lock , PaddedMutex , tty, true, _safepoint_check_never); // allow to lock in VM + def(tty_lock , PaddedMutex , tty); // allow to lock in VM - def(STS_lock , PaddedMonitor, nosafepoint, true, _safepoint_check_never); + def(STS_lock , PaddedMonitor, nosafepoint); if (UseG1GC) { - def(CGC_lock , PaddedMonitor, nosafepoint, true, _safepoint_check_never); - def(G1OldGCCount_lock , PaddedMonitor, leaf, true, _safepoint_check_always); + def(CGC_lock , PaddedMonitor, nosafepoint); - def(G1DetachedRefinementStats_lock, PaddedMutex, nosafepoint-2, true, _safepoint_check_never); + def(G1DetachedRefinementStats_lock, PaddedMutex, nosafepoint-2); - def(FreeList_lock , PaddedMutex , service-1, true, _safepoint_check_never); - def(OldSets_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(Uncommit_lock , PaddedMutex , service-2, true, _safepoint_check_never); - def(RootRegionScan_lock , PaddedMonitor, nosafepoint-1, true, _safepoint_check_never); + def(FreeList_lock , PaddedMutex , service-1); + def(OldSets_lock , PaddedMutex , nosafepoint); + def(Uncommit_lock , PaddedMutex , service-2); + def(RootRegionScan_lock , PaddedMonitor, nosafepoint-1); - def(MarkStackFreeList_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(MarkStackChunkList_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); + def(MarkStackFreeList_lock , PaddedMutex , nosafepoint); + def(MarkStackChunkList_lock , PaddedMutex , nosafepoint); - def(MonitoringSupport_lock , PaddedMutex , service-1, true, _safepoint_check_never); // used for serviceability monitoring support + def(MonitoringSupport_lock , PaddedMutex , service-1); // used for serviceability monitoring support } - def(StringDedup_lock , PaddedMonitor, nosafepoint, true, _safepoint_check_never); - def(StringDedupIntern_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(ParGCRareEvent_lock , PaddedMutex , leaf, true, _safepoint_check_always); - def(CodeCache_lock , PaddedMonitor, nosafepoint-3, true, _safepoint_check_never); - def(CodeSweeper_lock , PaddedMonitor, nosafepoint-5, true, _safepoint_check_never); - def(RawMonitor_lock , PaddedMutex , nosafepoint-1, true, _safepoint_check_never); - def(OopMapCacheAlloc_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for oop_map_cache allocation. - - def(Metaspace_lock , PaddedMutex , nosafepoint-3, true, _safepoint_check_never); - def(ClassLoaderDataGraph_lock , PaddedMutex , nonleaf, false, _safepoint_check_always); - - def(Patching_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); // used for safepointing and code patching. - def(CompiledMethod_lock , PaddedMutex , nosafepoint-4, true, _safepoint_check_never); - def(MonitorDeflation_lock , PaddedMonitor, nosafepoint, true, _safepoint_check_never); // used for monitor deflation thread operations - def(Service_lock , PaddedMonitor, service, true, _safepoint_check_never); // used for service thread operations + def(StringDedup_lock , PaddedMonitor, nosafepoint); + def(StringDedupIntern_lock , PaddedMutex , nosafepoint); + def(ParGCRareEvent_lock , PaddedMutex , safepoint, true); + def(RawMonitor_lock , PaddedMutex , nosafepoint-1); + + def(Metaspace_lock , PaddedMutex , nosafepoint-3); + + def(Patching_lock , PaddedMutex , nosafepoint); // used for safepointing and code patching. + def(MonitorDeflation_lock , PaddedMonitor, nosafepoint); // used for monitor deflation thread operations + def(Service_lock , PaddedMonitor, service); // used for service thread operations if (UseNotificationThread) { - def(Notification_lock , PaddedMonitor, service, true, _safepoint_check_never); // used for notification thread operations + def(Notification_lock , PaddedMonitor, service); // used for notification thread operations } else { Notification_lock = Service_lock; } - def(JmethodIdCreation_lock , PaddedMutex , nosafepoint-2, true, _safepoint_check_never); // used for creating jmethodIDs. - - def(SystemDictionary_lock , PaddedMonitor, leaf, true, _safepoint_check_always); - def(SharedDictionary_lock , PaddedMutex , leaf, true, _safepoint_check_always); - def(ClassInitError_lock , PaddedMonitor, leaf+1, true, _safepoint_check_always); - def(Module_lock , PaddedMutex , leaf+2, false, _safepoint_check_always); - def(InlineCacheBuffer_lock , PaddedMutex , nosafepoint-1, true, _safepoint_check_never); - def(VMStatistic_lock , PaddedMutex , leaf, false, _safepoint_check_always); - def(ExpandHeap_lock , PaddedMutex , leaf, true, _safepoint_check_always); // Used during compilation by VM thread - def(JNIHandleBlockFreeList_lock , PaddedMutex , nosafepoint-1, true, _safepoint_check_never); // handles are used by VM thread - def(SignatureHandlerLibrary_lock , PaddedMutex , leaf, false, _safepoint_check_always); - def(SymbolArena_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(ExceptionCache_lock , PaddedMutex , leaf, false, _safepoint_check_always); + def(JmethodIdCreation_lock , PaddedMutex , nosafepoint-2); // used for creating jmethodIDs. + + def(SharedDictionary_lock , PaddedMutex , safepoint, true); + def(VMStatistic_lock , PaddedMutex , safepoint); + def(JNIHandleBlockFreeList_lock , PaddedMutex , nosafepoint-1); // handles are used by VM thread + def(SignatureHandlerLibrary_lock , PaddedMutex , safepoint); + def(SymbolArena_lock , PaddedMutex , nosafepoint); + def(ExceptionCache_lock , PaddedMutex , safepoint); #ifndef PRODUCT - def(FullGCALot_lock , PaddedMutex , leaf, false, _safepoint_check_always); // a lock to make FullGCALot MT safe + def(FullGCALot_lock , PaddedMutex , safepoint); // a lock to make FullGCALot MT safe #endif - def(BeforeExit_lock , PaddedMonitor, leaf, true, _safepoint_check_always); - def(PerfDataMemAlloc_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for allocating PerfData memory for performance data - def(PerfDataManager_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for synchronized access to PerfDataManager resources - - def(Threads_lock , PaddedMonitor, barrier, true, _safepoint_check_always); // Used for safepoint protocol. - def(NonJavaThreadsList_lock , PaddedMutex, nosafepoint-1, true, _safepoint_check_never); - def(NonJavaThreadsListSync_lock , PaddedMutex, nosafepoint, true, _safepoint_check_never); - - def(VMOperation_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always); // VM_thread allowed to block on these - def(RetData_lock , PaddedMutex , nonleaf, false, _safepoint_check_always); - def(Terminator_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always); - def(InitCompleted_lock , PaddedMonitor, nosafepoint, true, _safepoint_check_never); - def(VtableStubs_lock , PaddedMutex , nosafepoint-2, true, _safepoint_check_never); - def(Notify_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always); - def(JNICritical_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always); // used for JNI critical regions - def(AdapterHandlerLibrary_lock , PaddedMutex , nonleaf, true, _safepoint_check_always); - - def(Heap_lock , PaddedMonitor, nonleaf+1, false, _safepoint_check_always); // Doesn't safepoint check during termination. - def(JfieldIdCreation_lock , PaddedMutex , nonleaf+1, true, _safepoint_check_always); // jfieldID, Used in VM_Operation - - def(CompiledIC_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); // locks VtableStubs_lock, InlineCacheBuffer_lock - def(CompileTaskAlloc_lock , PaddedMutex , nonleaf+2, true, _safepoint_check_always); - def(CompileStatistics_lock , PaddedMutex , nonleaf+2, false, _safepoint_check_always); - def(DirectivesStack_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(MultiArray_lock , PaddedMutex , nonleaf+2, false, _safepoint_check_always); - - def(JvmtiThreadState_lock , PaddedMutex , nonleaf+2, false, _safepoint_check_always); // Used by JvmtiThreadState/JvmtiEventController - def(EscapeBarrier_lock , PaddedMonitor, nosafepoint, true, _safepoint_check_never); // Used to synchronize object reallocation/relocking triggered by JVMTI - def(Management_lock , PaddedMutex , nonleaf+2, false, _safepoint_check_always); // used for JVM management - - def(ConcurrentGCBreakpoints_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always); - def(Compile_lock , PaddedMutex , nonleaf+3, false, _safepoint_check_always); - def(MethodData_lock , PaddedMutex , nonleaf+3, false, _safepoint_check_always); - def(TouchedMethodLog_lock , PaddedMutex , nonleaf+3, false, _safepoint_check_always); - - def(MethodCompileQueue_lock , PaddedMonitor, nonleaf+4, false, _safepoint_check_always); - def(CompileThread_lock , PaddedMonitor, nonleaf+5, false, _safepoint_check_always); - def(PeriodicTask_lock , PaddedMonitor, nonleaf+5, true, _safepoint_check_always); - def(RedefineClasses_lock , PaddedMonitor, nonleaf+5, true, _safepoint_check_always); - def(Verify_lock , PaddedMutex, nonleaf+5, true, _safepoint_check_always); - def(Zip_lock , PaddedMonitor, nosafepoint-2, true, _safepoint_check_never); + def(BeforeExit_lock , PaddedMonitor, safepoint, true); + + def(NonJavaThreadsList_lock , PaddedMutex, nosafepoint-1); + def(NonJavaThreadsListSync_lock , PaddedMutex, nosafepoint); + + def(RetData_lock , PaddedMutex , safepoint); + def(Terminator_lock , PaddedMonitor, safepoint, true); + def(InitCompleted_lock , PaddedMonitor, nosafepoint); + def(Notify_lock , PaddedMonitor, safepoint, true); + def(AdapterHandlerLibrary_lock , PaddedMutex , safepoint, true); + + def(Heap_lock , PaddedMonitor, safepoint); // Doesn't safepoint check during termination. + def(JfieldIdCreation_lock , PaddedMutex , safepoint, true); // jfieldID, Used in VM_Operation + + def(CompiledIC_lock , PaddedMutex , nosafepoint); // locks VtableStubs_lock, InlineCacheBuffer_lock + def(MethodCompileQueue_lock , PaddedMonitor, safepoint); + def(CompileStatistics_lock , PaddedMutex , safepoint); + def(DirectivesStack_lock , PaddedMutex , nosafepoint); + def(MultiArray_lock , PaddedMutex , safepoint); + + def(JvmtiThreadState_lock , PaddedMutex , safepoint); // Used by JvmtiThreadState/JvmtiEventController + def(EscapeBarrier_lock , PaddedMonitor, nosafepoint); // Used to synchronize object reallocation/relocking triggered by JVMTI + def(Management_lock , PaddedMutex , safepoint); // used for JVM management + + def(ConcurrentGCBreakpoints_lock , PaddedMonitor, safepoint, true); + def(MethodData_lock , PaddedMutex , safepoint); + def(TouchedMethodLog_lock , PaddedMutex , safepoint); + + def(CompileThread_lock , PaddedMonitor, safepoint); + def(PeriodicTask_lock , PaddedMonitor, safepoint, true); + def(RedefineClasses_lock , PaddedMonitor, safepoint, true); + def(Verify_lock , PaddedMutex, safepoint, true); if (WhiteBoxAPI) { - def(Compilation_lock , PaddedMonitor, nosafepoint, true, _safepoint_check_never); + def(Compilation_lock , PaddedMonitor, nosafepoint); } #if INCLUDE_JFR - def(JfrMsg_lock , PaddedMonitor, leaf, true, _safepoint_check_always); - def(JfrBuffer_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(JfrStacktrace_lock , PaddedMutex , stackwatermark-1, true, _safepoint_check_never); - def(JfrThreadSampler_lock , PaddedMonitor, nosafepoint, true, _safepoint_check_never); + def(JfrBuffer_lock , PaddedMutex , nosafepoint); + def(JfrMsg_lock , PaddedMonitor, nosafepoint-3); + def(JfrStacktrace_lock , PaddedMutex , stackwatermark-1); + def(JfrThreadSampler_lock , PaddedMonitor, nosafepoint); #endif #ifndef SUPPORTS_NATIVE_CX8 - def(UnsafeJlong_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); + def(UnsafeJlong_lock , PaddedMutex , nosafepoint); #endif - def(CodeHeapStateAnalytics_lock , PaddedMutex , nonleaf+6, false, _safepoint_check_always); - def(NMethodSweeperStats_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(ThreadsSMRDelete_lock , PaddedMonitor, nosafepoint-3, true, _safepoint_check_never); // Holds ConcurrentHashTableResize_lock - def(ThreadIdTableCreate_lock , PaddedMutex , leaf, false, _safepoint_check_always); - def(SharedDecoder_lock , PaddedMutex , tty-1, true, _safepoint_check_never); - def(DCmdFactory_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); + def(CodeHeapStateAnalytics_lock , PaddedMutex , safepoint); + def(NMethodSweeperStats_lock , PaddedMutex , nosafepoint); + def(ThreadsSMRDelete_lock , PaddedMonitor, nosafepoint-3); // Holds ConcurrentHashTableResize_lock + def(ThreadIdTableCreate_lock , PaddedMutex , safepoint); + def(SharedDecoder_lock , PaddedMutex , tty-1); + def(DCmdFactory_lock , PaddedMutex , nosafepoint); #if INCLUDE_NMT - def(NMTQuery_lock , PaddedMutex , max_nonleaf, false, _safepoint_check_always); + def(NMTQuery_lock , PaddedMutex , safepoint); #endif #if INCLUDE_CDS #if INCLUDE_JVMTI - def(CDSClassFileStream_lock , PaddedMutex , max_nonleaf, false, _safepoint_check_always); + def(CDSClassFileStream_lock , PaddedMutex , safepoint); #endif - def(DumpTimeTable_lock , PaddedMutex , nosafepoint-1, true, _safepoint_check_never); - def(CDSLambda_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(DumpRegion_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(ClassListFile_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); - def(LambdaFormInvokers_lock , PaddedMutex , nonleaf+2, false, _safepoint_check_always); + def(DumpTimeTable_lock , PaddedMutex , nosafepoint); + def(CDSLambda_lock , PaddedMutex , nosafepoint); + def(DumpRegion_lock , PaddedMutex , nosafepoint); + def(ClassListFile_lock , PaddedMutex , nosafepoint); + def(LambdaFormInvokers_lock , PaddedMutex , safepoint); #endif // INCLUDE_CDS - def(Bootclasspath_lock , PaddedMutex , nosafepoint, true, _safepoint_check_never); + def(Bootclasspath_lock , PaddedMutex , nosafepoint); + def(Zip_lock , PaddedMonitor, nosafepoint-1); // Holds DumpTimeTable_lock #if INCLUDE_JVMCI - def(JVMCI_lock , PaddedMonitor, nonleaf+2, true, _safepoint_check_always); + def(JVMCI_lock , PaddedMonitor, safepoint, true); #endif + + // These locks have relative rankings, and inherit safepoint checking attributes from that rank. + defl(InlineCacheBuffer_lock , PaddedMutex , CompiledIC_lock); + defl(VtableStubs_lock , PaddedMutex , CompiledIC_lock); // Also holds DumpTimeTable_lock + defl(CodeCache_lock , PaddedMonitor, VtableStubs_lock); + defl(CompiledMethod_lock , PaddedMutex , CodeCache_lock); + defl(CodeSweeper_lock , PaddedMonitor, CompiledMethod_lock); + + defl(Threads_lock , PaddedMonitor, CompileThread_lock, true); + defl(Heap_lock , PaddedMonitor, MultiArray_lock); + defl(Compile_lock , PaddedMutex , MethodCompileQueue_lock); + + defl(PerfDataMemAlloc_lock , PaddedMutex , Heap_lock, true); + defl(PerfDataManager_lock , PaddedMutex , Heap_lock, true); + defl(ClassLoaderDataGraph_lock , PaddedMutex , MultiArray_lock); + defl(VMOperation_lock , PaddedMonitor, Compile_lock, true); + defl(ClassInitError_lock , PaddedMonitor, Threads_lock, true); + + if (UseG1GC) { + defl(G1OldGCCount_lock , PaddedMonitor, Threads_lock, true); + } + defl(CompileTaskAlloc_lock , PaddedMutex , MethodCompileQueue_lock, true); + defl(ExpandHeap_lock , PaddedMutex , Heap_lock, true); + defl(OopMapCacheAlloc_lock , PaddedMutex , Threads_lock, true); + defl(Module_lock , PaddedMutex , ClassLoaderDataGraph_lock); + defl(SystemDictionary_lock , PaddedMonitor, Module_lock, true); + defl(JNICritical_lock , PaddedMonitor, MultiArray_lock, true); // used for JNI critical regions } GCMutexLocker::GCMutexLocker(Mutex* mutex) { diff --git a/src/hotspot/share/runtime/nonJavaThread.hpp b/src/hotspot/share/runtime/nonJavaThread.hpp index 0556fa95044d8f60c190648e712c2f4328eeeb73..5eb26e429241a3527dbef5c0125e55a6e39e20ac 100644 --- a/src/hotspot/share/runtime/nonJavaThread.hpp +++ b/src/hotspot/share/runtime/nonJavaThread.hpp @@ -101,31 +101,6 @@ class NamedThread: public NonJavaThread { uint gc_id() { return _gc_id; } }; -// Worker threads are named and have an id of an assigned work. -class WorkerThread: public NamedThread { - private: - uint _id; - public: - static WorkerThread* current() { - return WorkerThread::cast(Thread::current()); - } - - static WorkerThread* cast(Thread* t) { - assert(t->is_Worker_thread(), "incorrect cast to WorkerThread"); - return static_cast(t); - } - - WorkerThread() : _id(0) { } - virtual bool is_Worker_thread() const { return true; } - - void set_id(uint work_id) { _id = work_id; } - uint id() const { return _id; } - - // Printing - virtual const char* type_name() const { return "WorkerThread"; } - -}; - // A single WatcherThread is used for simulating timer interrupts. class WatcherThread: public NonJavaThread { friend class VMStructs; diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index cb26598132e8120f946d6ac44c6f5e6e602142bc..c70a6b39821c46237fcac9c6b13b774b0a2b4a10 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -520,9 +520,13 @@ class os: AllStatic { // child process (ignored on AIX, which always uses vfork). static int fork_and_exec(const char *cmd, bool prefer_vfork = false); - // Call ::exit() on all platforms but Windows + // Call ::exit() on all platforms static void exit(int num); + // Call ::_exit() on all platforms. Similar semantics to die() except we never + // want a core dump. + static void _exit(int num); + // Terminate the VM, but don't exit the process static void shutdown(); diff --git a/src/hotspot/share/runtime/prefetch.hpp b/src/hotspot/share/runtime/prefetch.hpp index 0cea7db1cd574fe82e8a1db9dd9d12aabaa2679c..dac34f6cd26634a93b0ed6c19fb5e5a4cea546c3 100644 --- a/src/hotspot/share/runtime/prefetch.hpp +++ b/src/hotspot/share/runtime/prefetch.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ class Prefetch : AllStatic { }; // Prefetch anticipating read; must not fault, semantically a no-op - static void read(void* loc, intx interval); + static void read(const void* loc, intx interval); // Prefetch anticipating write; must not fault, semantically a no-op static void write(void* loc, intx interval); diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index 49046a710ee3362c9f1902fa302fe4260557d164..be371446242a178063cd812995d4c8544f63d1cf 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -37,7 +37,8 @@ #include "gc/shared/gcLocker.hpp" #include "gc/shared/oopStorage.hpp" #include "gc/shared/strongRootsScope.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" +#include "gc/shared/workerUtils.hpp" #include "interpreter/interpreter.hpp" #include "jfr/jfrEvents.hpp" #include "logging/log.hpp" @@ -515,7 +516,7 @@ public: } }; -class ParallelSPCleanupTask : public AbstractGangTask { +class ParallelSPCleanupTask : public WorkerTask { private: SubTasksDone _subtasks; uint _num_workers; @@ -539,7 +540,7 @@ private: public: ParallelSPCleanupTask(uint num_workers) : - AbstractGangTask("Parallel Safepoint Cleanup"), + WorkerTask("Parallel Safepoint Cleanup"), _subtasks(SafepointSynchronize::SAFEPOINT_CLEANUP_NUM_TASKS), _num_workers(num_workers), _do_lazy_roots(!VMThread::vm_operation()->skip_thread_oop_barriers() && @@ -602,7 +603,7 @@ void SafepointSynchronize::do_cleanup_tasks() { CollectedHeap* heap = Universe::heap(); assert(heap != NULL, "heap not initialized yet?"); - WorkGang* cleanup_workers = heap->safepoint_workers(); + WorkerThreads* cleanup_workers = heap->safepoint_workers(); if (cleanup_workers != NULL) { // Parallel cleanup using GC provided thread pool. uint num_cleanup_workers = cleanup_workers->active_workers(); diff --git a/src/hotspot/share/runtime/safepoint.hpp b/src/hotspot/share/runtime/safepoint.hpp index 2342b379b74eb4e5e22af976bb348ebee0dd23c9..a2aaf381e8d52a80ebd2ae950a5eaf595e6d0460 100644 --- a/src/hotspot/share/runtime/safepoint.hpp +++ b/src/hotspot/share/runtime/safepoint.hpp @@ -131,7 +131,6 @@ class SafepointSynchronize : AllStatic { switch(state) { case _thread_in_vm: case _thread_in_Java: // From compiled code - case _thread_in_native_trans: case _thread_blocked_trans: return true; default: diff --git a/src/hotspot/share/runtime/safepointMechanism.hpp b/src/hotspot/share/runtime/safepointMechanism.hpp index 69e1b84889371734b40335fe8f6ea56d69d92af5..46cac5e4a610fb6c91b163e187d4fd837554c43f 100644 --- a/src/hotspot/share/runtime/safepointMechanism.hpp +++ b/src/hotspot/share/runtime/safepointMechanism.hpp @@ -45,15 +45,12 @@ class SafepointMechanism : public AllStatic { static address _polling_page; - static inline void disarm_local_poll(JavaThread* thread); static inline bool global_poll(); static void process(JavaThread *thread, bool allow_suspend); - static inline bool should_process_no_suspend(JavaThread* thread); - static void default_initialize(); static void pd_initialize() NOT_AIX({ default_initialize(); }); diff --git a/src/hotspot/share/runtime/safepointMechanism.inline.hpp b/src/hotspot/share/runtime/safepointMechanism.inline.hpp index 7256cf0e5e48bc15a1f782cd1fa17f3f49684e23..9439672157f52830b715927fe3e99da184a06d0c 100644 --- a/src/hotspot/share/runtime/safepointMechanism.inline.hpp +++ b/src/hotspot/share/runtime/safepointMechanism.inline.hpp @@ -30,6 +30,7 @@ #include "runtime/atomic.hpp" #include "runtime/handshake.hpp" #include "runtime/safepoint.hpp" +#include "runtime/stackWatermarkSet.hpp" #include "runtime/thread.inline.hpp" // Caller is responsible for using a memory barrier if needed. @@ -62,26 +63,29 @@ bool SafepointMechanism::global_poll() { return (SafepointSynchronize::_state != SafepointSynchronize::_not_synchronized); } -bool SafepointMechanism::should_process_no_suspend(JavaThread* thread) { - if (global_poll() || thread->handshake_state()->has_a_non_suspend_operation()) { - return true; - } else { - // We ignore suspend requests if any and just check before returning if we need - // to fix the thread's oops and first few frames due to a possible safepoint. - StackWatermarkSet::on_safepoint(thread); - update_poll_values(thread); - OrderAccess::cross_modify_fence(); - return false; - } -} - bool SafepointMechanism::should_process(JavaThread* thread, bool allow_suspend) { if (!local_poll_armed(thread)) { return false; } else if (allow_suspend) { return true; } - return should_process_no_suspend(thread); + // We are armed but we should ignore suspend operations. + if (global_poll() || // Safepoint + thread->handshake_state()->has_a_non_suspend_operation() || // Non-suspend handshake + !StackWatermarkSet::processing_started(thread)) { // StackWatermark processing is not started + return true; + } + + // It has boiled down to two possibilities: + // 1: We have nothing to process, this just a disarm poll. + // 2: We have a suspend handshake, which cannot be processed. + // We update the poll value in case of a disarm, to reduce false positives. + update_poll_values(thread); + + // We are now about to avoid processing and thus no cross modify fence will be executed. + // In case a safepoint happened, while being blocked, we execute it here. + OrderAccess::cross_modify_fence(); + return false; } void SafepointMechanism::process_if_requested(JavaThread* thread, bool allow_suspend) { diff --git a/src/hotspot/share/runtime/serviceThread.cpp b/src/hotspot/share/runtime/serviceThread.cpp index bba0f0f3325f08d8f5c7c365f6f6b05d0aacf466..6b23755f7c329f04b0cd4f1c967c0ba7a89ef69f 100644 --- a/src/hotspot/share/runtime/serviceThread.cpp +++ b/src/hotspot/share/runtime/serviceThread.cpp @@ -47,6 +47,7 @@ #include "prims/resolvedMethodTable.hpp" #include "services/diagnosticArgument.hpp" #include "services/diagnosticFramework.hpp" +#include "services/finalizerService.hpp" #include "services/gcNotifier.hpp" #include "services/lowMemoryDetector.hpp" #include "services/threadIdTable.hpp" @@ -115,6 +116,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { bool has_dcmd_notification_event = false; bool stringtable_work = false; bool symboltable_work = false; + bool finalizerservice_work = false; bool resolved_method_table_work = false; bool thread_id_table_work = false; bool protection_domain_table_work = false; @@ -145,6 +147,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { (has_dcmd_notification_event = (!UseNotificationThread && DCmdFactory::has_pending_jmx_notification())) | (stringtable_work = StringTable::has_work()) | (symboltable_work = SymbolTable::has_work()) | + (finalizerservice_work = FinalizerService::has_work()) | (resolved_method_table_work = ResolvedMethodTable::has_work()) | (thread_id_table_work = ThreadIdTable::has_work()) | (protection_domain_table_work = SystemDictionary::pd_cache_table()->has_work()) | @@ -172,6 +175,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { SymbolTable::do_concurrent_work(jt); } + if (finalizerservice_work) { + FinalizerService::do_concurrent_work(jt); + } + if (has_jvmti_events) { _jvmti_event->post(); _jvmti_event = NULL; // reset diff --git a/src/hotspot/share/runtime/stackWatermark.cpp b/src/hotspot/share/runtime/stackWatermark.cpp index 921210c2dc312b3ec14dd2ac0507bfaf3217b724..397f9332dff90d372b7696b10cad48403465e66e 100644 --- a/src/hotspot/share/runtime/stackWatermark.cpp +++ b/src/hotspot/share/runtime/stackWatermark.cpp @@ -164,7 +164,7 @@ StackWatermark::StackWatermark(JavaThread* jt, StackWatermarkKind kind, uint32_t _next(NULL), _jt(jt), _iterator(NULL), - _lock(Mutex::stackwatermark, "StackWatermark_lock", Mutex::_safepoint_check_never), + _lock(Mutex::stackwatermark, "StackWatermark_lock"), _kind(kind), _linked_watermark(NULL) { } diff --git a/src/hotspot/share/runtime/stackWatermarkSet.cpp b/src/hotspot/share/runtime/stackWatermarkSet.cpp index 26e8a628f8616f426ad68506b5a8ddd89a3f5d92..dace3fee9669e7edc4239ffb122ef87eb87a4174 100644 --- a/src/hotspot/share/runtime/stackWatermarkSet.cpp +++ b/src/hotspot/share/runtime/stackWatermarkSet.cpp @@ -128,6 +128,15 @@ void StackWatermarkSet::start_processing(JavaThread* jt, StackWatermarkKind kind // will always update the poll values when waking up from a safepoint. } +bool StackWatermarkSet::processing_started(JavaThread* jt) { + for (StackWatermark* current = head(jt); current != NULL; current = current->next()) { + if (!current->processing_started()) { + return false; + } + } + return true; +} + void StackWatermarkSet::finish_processing(JavaThread* jt, void* context, StackWatermarkKind kind) { StackWatermark* watermark = get(jt, kind); if (watermark != NULL) { diff --git a/src/hotspot/share/runtime/stackWatermarkSet.hpp b/src/hotspot/share/runtime/stackWatermarkSet.hpp index 78cfc7f28276fd2c19090afa25a81a00ae2b3820..11c27abb85ef72f91efc07f61dbb99bc5d8022c2 100644 --- a/src/hotspot/share/runtime/stackWatermarkSet.hpp +++ b/src/hotspot/share/runtime/stackWatermarkSet.hpp @@ -79,6 +79,9 @@ public: // Called to ensure that processing of the thread is started static void start_processing(JavaThread* jt, StackWatermarkKind kind); + // Returns true if all StackWatermarks have been started. + static bool processing_started(JavaThread* jt); + // Called to finish the processing of a thread static void finish_processing(JavaThread* jt, void* context, StackWatermarkKind kind); diff --git a/src/hotspot/share/runtime/sweeper.cpp b/src/hotspot/share/runtime/sweeper.cpp index 0d270eb0b0d674cef54cce73218cb827c0bda403..31c9220ea784ee68eac6696ace2307703e38c39f 100644 --- a/src/hotspot/share/runtime/sweeper.cpp +++ b/src/hotspot/share/runtime/sweeper.cpp @@ -29,7 +29,7 @@ #include "code/nmethod.hpp" #include "compiler/compileBroker.hpp" #include "gc/shared/collectedHeap.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "jfr/jfrEvents.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 0188420b0ce1477bf3945eb545113fb26384b56b..a2a41754ede90770831a15e874eb05a2d1919755 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -1001,8 +1001,10 @@ JavaThread::JavaThread() : _monitor_chunks(nullptr), _suspend_flags(0), - _async_exception_condition(_no_async_condition), _pending_async_exception(nullptr), +#ifdef ASSERT + _is_unsafe_access_error(false), +#endif _thread_state(_thread_new), _saved_exception_pc(nullptr), @@ -1572,9 +1574,6 @@ void JavaThread::remove_monitor_chunk(MonitorChunk* chunk) { // Asynchronous exceptions support // -// Note: this function shouldn't block if it's called in -// _thread_in_native_trans state (such as from -// check_special_condition_for_native_trans()). void JavaThread::check_and_handle_async_exceptions() { if (has_last_Java_frame() && has_async_exception_condition()) { // If we are at a polling page safepoint (not a poll return) @@ -1600,21 +1599,12 @@ void JavaThread::check_and_handle_async_exceptions() { } } - AsyncExceptionCondition condition = clear_async_exception_condition(); - if (condition == _no_async_condition) { - // Conditions have changed since has_special_runtime_exit_condition() - // was called: - // - if we were here only because of an external suspend request, - // then that was taken care of above (or cancelled) so we are done - // - if we were here because of another async request, then it has - // been cleared between the has_special_runtime_exit_condition() - // and now so again we are done + if (!clear_async_exception_condition()) { return; } - // Check for pending async. exception if (_pending_async_exception != NULL) { - // Only overwrite an already pending exception, if it is not a threadDeath. + // Only overwrite an already pending exception if it is not a threadDeath. if (!has_pending_exception() || !pending_exception()->is_a(vmClasses::ThreadDeath_klass())) { // We cannot call Exceptions::_throw(...) here because we cannot block @@ -1631,25 +1621,23 @@ void JavaThread::check_and_handle_async_exceptions() { } ls.print_cr(" of type: %s", _pending_async_exception->klass()->external_name()); } - _pending_async_exception = NULL; - // Clear condition from _suspend_flags since we have finished processing it. - clear_suspend_flag(_has_async_exception); } - } + // Always null out the _pending_async_exception oop here since the async condition was + // already cleared above and thus considered handled. + _pending_async_exception = NULL; + } else { + assert(_is_unsafe_access_error, "must be"); + DEBUG_ONLY(_is_unsafe_access_error = false); - if (condition == _async_unsafe_access_error && !has_pending_exception()) { // We may be at method entry which requires we save the do-not-unlock flag. UnlockFlagSaver fs(this); switch (thread_state()) { case _thread_in_vm: { JavaThread* THREAD = this; Exceptions::throw_unsafe_access_internal_error(THREAD, __FILE__, __LINE__, "a fault occurred in an unsafe memory access operation"); - return; - } - case _thread_in_native: { - ThreadInVMfromNative tiv(this); - JavaThread* THREAD = this; - Exceptions::throw_unsafe_access_internal_error(THREAD, __FILE__, __LINE__, "a fault occurred in an unsafe memory access operation"); + // We might have blocked in a ThreadBlockInVM wrapper in the call above so make sure we process pending + // suspend requests and object reallocation operations if any since we might be going to Java after this. + SafepointMechanism::process_if_requested_with_exit_check(this, true /* check asyncs */); return; } case _thread_in_Java: { @@ -1662,8 +1650,6 @@ void JavaThread::check_and_handle_async_exceptions() { ShouldNotReachHere(); } } - - assert(has_pending_exception(), "must have handled the async condition if no exception"); } void JavaThread::handle_special_runtime_exit_condition(bool check_asyncs) { @@ -1696,9 +1682,8 @@ public: } }; -void JavaThread::send_async_exception(oop java_thread, oop java_throwable) { +void JavaThread::send_async_exception(JavaThread* target, oop java_throwable) { Handle throwable(Thread::current(), java_throwable); - JavaThread* target = java_lang_Thread::thread(java_thread); InstallAsyncExceptionClosure vm_stop(throwable); Handshake::execute(&vm_stop, target); } @@ -1847,21 +1832,17 @@ void JavaThread::check_special_condition_for_native_trans(JavaThread *thread) { assert(thread->thread_state() == _thread_in_native_trans, "wrong state"); assert(!thread->has_last_Java_frame() || thread->frame_anchor()->walkable(), "Unwalkable stack in native->Java transition"); + thread->set_thread_state(_thread_in_vm); + // Enable WXWrite: called directly from interpreter native wrapper. MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, thread)); - SafepointMechanism::process_if_requested_with_exit_check(thread, false /* check asyncs */); + SafepointMechanism::process_if_requested_with_exit_check(thread, true /* check asyncs */); // After returning from native, it could be that the stack frames are not // yet safe to use. We catch such situations in the subsequent stack watermark // barrier, which will trap unsafe stack frames. StackWatermarkSet::before_unwind(thread); - - if (thread->has_async_exception_condition(false /* check unsafe access error */)) { - // We are in _thread_in_native_trans state, don't handle unsafe - // access error since that may block. - thread->check_and_handle_async_exceptions(); - } } #ifndef PRODUCT diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 4882d3b453dacd826067f839a8e1332337e611db..9540b7855bb6348aa16cb2ba398dc11e016e615d 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -99,7 +99,6 @@ class JavaThread; // - VMThread // - ConcurrentGCThread // - WorkerThread -// - GangWorker // - WatcherThread // - JfrThreadSampler // - LogAsyncWriter @@ -802,37 +801,21 @@ class JavaThread: public Thread { // Asynchronous exceptions support private: - enum AsyncExceptionCondition { - _no_async_condition = 0, - _async_exception, - _async_unsafe_access_error - }; - AsyncExceptionCondition _async_exception_condition; - oop _pending_async_exception; - - void set_async_exception_condition(AsyncExceptionCondition aec) { _async_exception_condition = aec; } - AsyncExceptionCondition clear_async_exception_condition() { - AsyncExceptionCondition x = _async_exception_condition; - _async_exception_condition = _no_async_condition; - return x; - } + oop _pending_async_exception; +#ifdef ASSERT + bool _is_unsafe_access_error; +#endif + inline bool clear_async_exception_condition(); public: - bool has_async_exception_condition(bool check_unsafe_access_error = true) { - return check_unsafe_access_error ? _async_exception_condition != _no_async_condition - : _async_exception_condition == _async_exception; + bool has_async_exception_condition() { + return (_suspend_flags & _has_async_exception) != 0; } inline void set_pending_async_exception(oop e); - void set_pending_unsafe_access_error() { - // Don't overwrite an asynchronous exception sent by another thread - if (_async_exception_condition == _no_async_condition) { - set_async_exception_condition(_async_unsafe_access_error); - } - } - void check_and_handle_async_exceptions(); - // Installs a pending exception to be inserted later - static void send_async_exception(oop thread_oop, oop java_throwable); + inline void set_pending_unsafe_access_error(); + static void send_async_exception(JavaThread* jt, oop java_throwable); void send_thread_stop(oop throwable); + void check_and_handle_async_exceptions(); // Safepoint support public: // Expose _thread_state for SafeFetchInt() @@ -1177,8 +1160,7 @@ class JavaThread: public Thread { // Return true if JavaThread has an asynchronous condition or // if external suspension is requested. bool has_special_runtime_exit_condition() { - return (_async_exception_condition != _no_async_condition) || - (_suspend_flags & (_obj_deopt JFR_ONLY(| _trace_flag))) != 0; + return (_suspend_flags & (_has_async_exception | _obj_deopt JFR_ONLY(| _trace_flag))) != 0; } // Fast-locking support diff --git a/src/hotspot/share/runtime/thread.inline.hpp b/src/hotspot/share/runtime/thread.inline.hpp index 63101e77855924e1bb24096745467fc0f5ff3571..7d27a3bf257cacbeb075f15dac7e60611108ac35 100644 --- a/src/hotspot/share/runtime/thread.inline.hpp +++ b/src/hotspot/share/runtime/thread.inline.hpp @@ -122,15 +122,22 @@ inline void JavaThread::clear_obj_deopt_flag() { clear_suspend_flag(_obj_deopt); } +inline bool JavaThread::clear_async_exception_condition() { + bool ret = has_async_exception_condition(); + clear_suspend_flag(_has_async_exception); + return ret; +} + inline void JavaThread::set_pending_async_exception(oop e) { _pending_async_exception = e; - set_async_exception_condition(_async_exception); - // Set _suspend_flags too so we save a comparison in the transition from native to Java - // in the native wrappers. It will be cleared in check_and_handle_async_exceptions() - // when we actually install the exception. set_suspend_flag(_has_async_exception); } +inline void JavaThread::set_pending_unsafe_access_error() { + set_suspend_flag(_has_async_exception); + DEBUG_ONLY(_is_unsafe_access_error = true); +} + inline JavaThreadState JavaThread::thread_state() const { #if defined(PPC64) || defined (AARCH64) // Use membars when accessing volatile _thread_state. See diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp index ed728f1a1dadd34fab9ee49ee04709085fa8c441..a84ff5f0bb1a80613d120ea251d179cce32cc9cb 100644 --- a/src/hotspot/share/runtime/vmOperations.cpp +++ b/src/hotspot/share/runtime/vmOperations.cpp @@ -368,7 +368,7 @@ int VM_Exit::wait_for_threads_in_native_to_block() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint already"); Thread * thr_cur = Thread::current(); - Monitor timer(Mutex::nosafepoint, "VM_ExitTimer_lock", Monitor::_safepoint_check_never); + Monitor timer(Mutex::nosafepoint, "VM_ExitTimer_lock"); // Compiler threads need longer wait because they can access VM data directly // while in native. If they are active and some structures being used are diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 28d34525f1316200eb9e077fb6a823e149d29a7a..7c337f3c65f5dfd225c87daa88f85525813b5b38 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -717,7 +717,6 @@ nonstatic_field(JavaThread, _current_pending_monitor_is_from_java, bool) \ volatile_nonstatic_field(JavaThread, _current_waiting_monitor, ObjectMonitor*) \ volatile_nonstatic_field(JavaThread, _suspend_flags, uint32_t) \ - nonstatic_field(JavaThread, _async_exception_condition, JavaThread::AsyncExceptionCondition) \ nonstatic_field(JavaThread, _pending_async_exception, oop) \ volatile_nonstatic_field(JavaThread, _exception_oop, oop) \ volatile_nonstatic_field(JavaThread, _exception_pc, address) \ @@ -1671,6 +1670,7 @@ declare_c2_type(MulFNode, MulNode) \ declare_c2_type(MulDNode, MulNode) \ declare_c2_type(MulHiLNode, Node) \ + declare_c2_type(UMulHiLNode, Node) \ declare_c2_type(AndINode, MulINode) \ declare_c2_type(AndLNode, MulLNode) \ declare_c2_type(LShiftINode, Node) \ @@ -1951,7 +1951,6 @@ declare_toplevel_type(JavaThread*) \ declare_toplevel_type(JavaThread *const *const) \ declare_toplevel_type(java_lang_Class) \ - declare_integer_type(JavaThread::AsyncExceptionCondition) \ declare_integer_type(JavaThread::TerminatedTypes) \ declare_toplevel_type(jbyte*) \ declare_toplevel_type(jbyte**) \ diff --git a/src/hotspot/share/runtime/vmThread.cpp b/src/hotspot/share/runtime/vmThread.cpp index 63341d8a8ab65650f8b52a447cda991f04c4e213..08d58de97fd6f7449b9d6feeb992b6b57455a95b 100644 --- a/src/hotspot/share/runtime/vmThread.cpp +++ b/src/hotspot/share/runtime/vmThread.cpp @@ -128,8 +128,7 @@ void VMThread::create() { assert(_timeout_task == NULL, "sanity"); } - _terminate_lock = new Monitor(Mutex::nosafepoint, "VMThreadTerminate_lock", - Monitor::_safepoint_check_never); + _terminate_lock = new Monitor(Mutex::nosafepoint, "VMThreadTerminate_lock"); if (UsePerfData) { // jvmstat performance counters diff --git a/src/hotspot/share/services/classLoadingService.cpp b/src/hotspot/share/services/classLoadingService.cpp index 9da4496971c7ecaea9e449793f0b096cfef49566..16a6fd005a46332395e4cd1580c9962ab67901eb 100644 --- a/src/hotspot/share/services/classLoadingService.cpp +++ b/src/hotspot/share/services/classLoadingService.cpp @@ -119,49 +119,62 @@ void ClassLoadingService::init() { } } -void ClassLoadingService::notify_class_unloaded(InstanceKlass* k) { - DTRACE_CLASSLOAD_PROBE(unloaded, k, false); - // Classes that can be unloaded must be non-shared - _classes_unloaded_count->inc(); +bool ClassLoadingService::set_verbose(bool verbose) { + MutexLocker m(Management_lock); + // verbose will be set to the previous value + LogLevelType level = verbose ? LogLevel::Info : LogLevel::Off; + LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, load)); + reset_trace_class_unloading(); + return verbose; +} - if (UsePerfData) { - // add the class size - size_t size = compute_class_size(k); - _classbytes_unloaded->inc(size); +// Caller to this function must own Management_lock +void ClassLoadingService::reset_trace_class_unloading() { + assert(Management_lock->owned_by_self(), "Must own the Management_lock"); + bool value = MemoryService::get_verbose() || ClassLoadingService::get_verbose(); + LogLevelType level = value ? LogLevel::Info : LogLevel::Off; + LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, unload)); +} - // Compute method size & subtract from running total. - // We are called during phase 1 of mark sweep, so it's - // still ok to iterate through Method*s here. - Array* methods = k->methods(); - for (int i = 0; i < methods->length(); i++) { - _class_methods_size->inc(-methods->at(i)->size()); - } - } +jlong ClassLoadingService::loaded_class_count() { + return _classes_loaded_count->get_value() + _shared_classes_loaded_count->get_value(); } -void ClassLoadingService::notify_class_loaded(InstanceKlass* k, bool shared_class) { - DTRACE_CLASSLOAD_PROBE(loaded, k, shared_class); - PerfCounter* classes_counter = (shared_class ? _shared_classes_loaded_count - : _classes_loaded_count); - // increment the count - classes_counter->inc(); +jlong ClassLoadingService::unloaded_class_count() { + return _classes_unloaded_count->get_value() + _shared_classes_unloaded_count->get_value(); +} - if (UsePerfData) { - PerfCounter* classbytes_counter = (shared_class ? _shared_classbytes_loaded - : _classbytes_loaded); - // add the class size - size_t size = compute_class_size(k); - classbytes_counter->inc(size); - } +jlong ClassLoadingService::loaded_class_bytes() { + return UsePerfData ? _classbytes_loaded->get_value() + _shared_classbytes_loaded->get_value() : -1; } -size_t ClassLoadingService::compute_class_size(InstanceKlass* k) { - // lifted from ClassStatistics.do_class(Klass* k) +jlong ClassLoadingService::unloaded_class_bytes() { + return UsePerfData ? _classbytes_unloaded->get_value() + _shared_classbytes_unloaded->get_value() : -1; +} + +jlong ClassLoadingService::loaded_shared_class_count() { + return _shared_classes_loaded_count->get_value(); +} - size_t class_size = 0; +jlong ClassLoadingService::unloaded_shared_class_count() { + return _shared_classes_unloaded_count->get_value(); +} - class_size += k->size(); +jlong ClassLoadingService::loaded_shared_class_bytes() { + return UsePerfData ? _shared_classbytes_loaded->get_value() : -1; +} +jlong ClassLoadingService::unloaded_shared_class_bytes() { + return UsePerfData ? _shared_classbytes_unloaded->get_value() : -1; +} + +jlong ClassLoadingService::class_method_data_size() { + return UsePerfData ? _class_methods_size->get_value() : -1; +} + +static size_t compute_class_size(InstanceKlass* k) { + // lifted from ClassStatistics.do_class(Klass* k) + size_t class_size = k->size(); if (k->is_instance_klass()) { class_size += k->methods()->size(); // FIXME: Need to count the contents of methods @@ -177,21 +190,40 @@ size_t ClassLoadingService::compute_class_size(InstanceKlass* k) { return class_size * oopSize; } -bool ClassLoadingService::set_verbose(bool verbose) { - MutexLocker m(Management_lock); - // verbose will be set to the previous value - LogLevelType level = verbose ? LogLevel::Info : LogLevel::Off; - LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, load)); - reset_trace_class_unloading(); - return verbose; +void ClassLoadingService::notify_class_loaded(InstanceKlass* k, bool shared_class) { + DTRACE_CLASSLOAD_PROBE(loaded, k, shared_class); + PerfCounter* classes_counter = (shared_class ? _shared_classes_loaded_count + : _classes_loaded_count); + // increment the count + classes_counter->inc(); + + if (UsePerfData) { + PerfCounter* classbytes_counter = (shared_class ? _shared_classbytes_loaded + : _classbytes_loaded); + // add the class size + size_t size = compute_class_size(k); + classbytes_counter->inc(size); + } } -// Caller to this function must own Management_lock -void ClassLoadingService::reset_trace_class_unloading() { - assert(Management_lock->owned_by_self(), "Must own the Management_lock"); - bool value = MemoryService::get_verbose() || ClassLoadingService::get_verbose(); - LogLevelType level = value ? LogLevel::Info : LogLevel::Off; - LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, unload)); +void ClassLoadingService::notify_class_unloaded(InstanceKlass* k) { + DTRACE_CLASSLOAD_PROBE(unloaded, k, false); + // Classes that can be unloaded must be non-shared + _classes_unloaded_count->inc(); + + if (UsePerfData) { + // add the class size + size_t size = compute_class_size(k); + _classbytes_unloaded->inc(size); + + // Compute method size & subtract from running total. + // We are called during phase 1 of mark sweep, so it's + // still ok to iterate through Method*s here. + Array* methods = k->methods(); + for (int i = 0; i < methods->length(); i++) { + _class_methods_size->inc(-methods->at(i)->size()); + } + } } #endif // INCLUDE_MANAGEMENT diff --git a/src/hotspot/share/services/classLoadingService.hpp b/src/hotspot/share/services/classLoadingService.hpp index db422cbf11ff6e210e9835b31d3dfd2d64bbe5e1..f9db3da50918bc87deeccbee159da9fe2c8cd294 100644 --- a/src/hotspot/share/services/classLoadingService.hpp +++ b/src/hotspot/share/services/classLoadingService.hpp @@ -35,7 +35,7 @@ class InstanceKlass; // VM monitoring and management support for the Class Loading subsystem class ClassLoadingService : public AllStatic { -private: + private: // Counters for classes loaded from class files static PerfCounter* _classes_loaded_count; static PerfCounter* _classes_unloaded_count; @@ -50,59 +50,20 @@ private: static PerfVariable* _class_methods_size; - static size_t compute_class_size(InstanceKlass* k); - -public: - static void init(); - - static bool get_verbose() { return log_is_enabled(Info, class, load); } - static bool set_verbose(bool verbose); + public: + static void init() NOT_MANAGEMENT_RETURN; + static bool set_verbose(bool verbose) NOT_MANAGEMENT_RETURN_(false); static void reset_trace_class_unloading() NOT_MANAGEMENT_RETURN; - - static jlong loaded_class_count() { - return _classes_loaded_count->get_value() + _shared_classes_loaded_count->get_value(); - } - static jlong unloaded_class_count() { - return _classes_unloaded_count->get_value() + _shared_classes_unloaded_count->get_value(); - } - static jlong loaded_class_bytes() { - if (UsePerfData) { - return _classbytes_loaded->get_value() + _shared_classbytes_loaded->get_value(); - } else { - return -1; - } - } - static jlong unloaded_class_bytes() { - if (UsePerfData) { - return _classbytes_unloaded->get_value() + _shared_classbytes_unloaded->get_value(); - } else { - return -1; - } - } - - static jlong loaded_shared_class_count() { - return _shared_classes_loaded_count->get_value(); - } - static jlong unloaded_shared_class_count() { - return _shared_classes_unloaded_count->get_value(); - } - static jlong loaded_shared_class_bytes() { - if (UsePerfData) { - return _shared_classbytes_loaded->get_value(); - } else { - return -1; - } - } - static jlong unloaded_shared_class_bytes() { - if (UsePerfData) { - return _shared_classbytes_unloaded->get_value(); - } else { - return -1; - } - } - static jlong class_method_data_size() { - return (UsePerfData ? _class_methods_size->get_value() : -1); - } + static jlong loaded_class_count() NOT_MANAGEMENT_RETURN_(0L); + static jlong unloaded_class_count() NOT_MANAGEMENT_RETURN_(0L); + static jlong loaded_class_bytes() NOT_MANAGEMENT_RETURN_(0L); + static jlong unloaded_class_bytes() NOT_MANAGEMENT_RETURN_(0L); + static jlong loaded_shared_class_count() NOT_MANAGEMENT_RETURN_(0L); + static jlong unloaded_shared_class_count() NOT_MANAGEMENT_RETURN_(0L); + static jlong loaded_shared_class_bytes() NOT_MANAGEMENT_RETURN_(0L); + static jlong unloaded_shared_class_bytes() NOT_MANAGEMENT_RETURN_(0L); + static jlong class_method_data_size() NOT_MANAGEMENT_RETURN_(0L); + static bool get_verbose() { return log_is_enabled(Info, class, load); } static void notify_class_loaded(InstanceKlass* k, bool shared_class) NOT_MANAGEMENT_RETURN; diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 57b55091be5ac72cf40e1100b636ceead89b1631..1b3710cf004c2a47d8b17e9c8f171781745b1f18 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -964,10 +964,10 @@ void DumpSharedArchiveDCmd::execute(DCmdSource source, TRAPS) { if (strcmp(scmd, "static_dump") == 0) { is_static = JNI_TRUE; - output()->print_cr("Static dump:"); + output()->print("Static dump: "); } else if (strcmp(scmd, "dynamic_dump") == 0) { is_static = JNI_FALSE; - output()->print_cr("Dynamic dump:"); + output()->print("Dynamic dump: "); if (!UseSharedSpaces) { output()->print_cr("Dynamic dump is unsupported when base CDS archive is not loaded"); return; @@ -988,7 +988,7 @@ void DumpSharedArchiveDCmd::execute(DCmdSource source, TRAPS) { } Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS(); Klass* cds_klass = SystemDictionary::resolve_or_fail(cds_name, true /*throw error*/, CHECK); - JavaValue result(T_VOID); + JavaValue result(T_OBJECT); JavaCallArguments args; args.push_int(is_static); args.push_oop(fileh); @@ -997,6 +997,12 @@ void DumpSharedArchiveDCmd::execute(DCmdSource source, TRAPS) { vmSymbols::dumpSharedArchive(), vmSymbols::dumpSharedArchive_signature(), &args, CHECK); + if (!HAS_PENDING_EXCEPTION) { + assert(result.get_type() == T_OBJECT, "Sanity check"); + // result contains the archive name + char* archive_name = java_lang_String::as_utf8_string(result.get_oop()); + output()->print_cr("%s", archive_name); + } } #endif // INCLUDE_CDS diff --git a/src/hotspot/share/services/finalizerService.cpp b/src/hotspot/share/services/finalizerService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9efcda886d8f8c4aeecae3e8f431020ee39c3030 --- /dev/null +++ b/src/hotspot/share/services/finalizerService.cpp @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "utilities/macros.hpp" +#if INCLUDE_MANAGEMENT +#include "classfile/classLoaderDataGraph.inline.hpp" +#include "classfile/javaClasses.inline.hpp" +#include "classfile/symbolTable.hpp" +#include "memory/resourceArea.hpp" +#include "logging/log.hpp" +#include "oops/instanceKlass.inline.hpp" +#include "runtime/atomic.hpp" +#include "runtime/fieldDescriptor.inline.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/synchronizer.hpp" +#include "runtime/thread.inline.hpp" +#include "services/finalizerService.hpp" +#include "utilities/concurrentHashTableTasks.inline.hpp" +#include "utilities/debug.hpp" + +static const char* allocate(oop string) { + char* str = nullptr; + const typeArrayOop value = java_lang_String::value(string); + if (value != nullptr) { + const int length = java_lang_String::utf8_length(string, value); + str = NEW_C_HEAP_ARRAY(char, length + 1, mtServiceability); + java_lang_String::as_utf8_string(string, value, str, length + 1); + } + return str; +} + +static int compute_field_offset(const Klass* klass, const char* field_name, const char* field_signature) { + assert(klass != nullptr, "invariant"); + Symbol* const name = SymbolTable::new_symbol(field_name); + assert(name != nullptr, "invariant"); + Symbol* const signature = SymbolTable::new_symbol(field_signature); + assert(signature != nullptr, "invariant"); + assert(klass->is_instance_klass(), "invariant"); + fieldDescriptor fd; + InstanceKlass::cast(klass)->find_field(name, signature, false, &fd); + return fd.offset(); +} + +static const char* location_no_frag_string(oop codesource) { + assert(codesource != nullptr, "invariant"); + static int loc_no_frag_offset = compute_field_offset(codesource->klass(), "locationNoFragString", "Ljava/lang/String;"); + oop string = codesource->obj_field(loc_no_frag_offset); + return string != nullptr ? allocate(string) : nullptr; +} + +static oop codesource(oop pd) { + assert(pd != nullptr, "invariant"); + static int codesource_offset = compute_field_offset(pd->klass(), "codesource", "Ljava/security/CodeSource;"); + return pd->obj_field(codesource_offset); +} + +static const char* get_codesource(const InstanceKlass* ik) { + assert(ik != nullptr, "invariant"); + oop pd = java_lang_Class::protection_domain(ik->java_mirror()); + if (pd == nullptr) { + return nullptr; + } + oop cs = codesource(pd); + return cs != nullptr ? location_no_frag_string(cs) : nullptr; +} + +FinalizerEntry::FinalizerEntry(const InstanceKlass* ik) : + _ik(ik), + _codesource(get_codesource(ik)), + _objects_on_heap(0), + _total_finalizers_run(0) {} + +FinalizerEntry::~FinalizerEntry() { + FREE_C_HEAP_ARRAY(char, _codesource); +} + +const InstanceKlass* FinalizerEntry::klass() const { + return _ik; +} + +const char* FinalizerEntry::codesource() const { + return _codesource; +} + +uintptr_t FinalizerEntry::objects_on_heap() const { + return Atomic::load(&_objects_on_heap); +} + +uintptr_t FinalizerEntry::total_finalizers_run() const { + return Atomic::load(&_total_finalizers_run); +} + +void FinalizerEntry::on_register() { + Atomic::inc(&_objects_on_heap, memory_order_relaxed); +} + +void FinalizerEntry::on_complete() { + Atomic::inc(&_total_finalizers_run, memory_order_relaxed); + Atomic::dec(&_objects_on_heap, memory_order_relaxed); +} + +static inline uintx hash_function(const InstanceKlass* ik) { + assert(ik != nullptr, "invariant"); + return primitive_hash(ik); +} + +static inline uintx hash_function(const FinalizerEntry* fe) { + return hash_function(fe->klass()); +} + +class FinalizerEntryLookup : StackObj { + private: + const InstanceKlass* const _ik; + public: + FinalizerEntryLookup(const InstanceKlass* ik) : _ik(ik) {} + uintx get_hash() const { return hash_function(_ik); } + bool equals(FinalizerEntry** value, bool* is_dead) { + assert(value != nullptr, "invariant"); + assert(*value != nullptr, "invariant"); + return (*value)->klass() == _ik; + } +}; + +class FinalizerTableConfig : public AllStatic { + public: + typedef FinalizerEntry* Value; // value of the Node in the hashtable + + static uintx get_hash(Value const& value, bool* is_dead) { + return hash_function(value); + } + static void* allocate_node(void* context, size_t size, Value const& value) { + return AllocateHeap(size, mtServiceability); + } + static void free_node(void* context, void* memory, Value const& value) { + FreeHeap(memory); + } +}; + +typedef ConcurrentHashTable FinalizerHashtable; +static FinalizerHashtable* _table = nullptr; +static const size_t DEFAULT_TABLE_SIZE = 2048; +// 2^24 is max size, like StringTable. +static const size_t MAX_SIZE = 24; +static volatile bool _has_work = false; + +static size_t ceil_log2(size_t value) { + size_t ret; + for (ret = 1; ((size_t)1 << ret) < value; ++ret); + return ret; +} + +class FinalizerEntryLookupResult { + private: + FinalizerEntry* _result; + public: + FinalizerEntryLookupResult() : _result(nullptr) {} + void operator()(FinalizerEntry* node) { + assert(node != nullptr, "invariant"); + _result = node; + } + FinalizerEntry* result() const { return _result; } +}; + +class FinalizerEntryLookupGet { + private: + FinalizerEntry* _result; + public: + FinalizerEntryLookupGet() : _result(nullptr) {} + void operator()(FinalizerEntry** node) { + assert(node != nullptr, "invariant"); + _result = *node; + } + FinalizerEntry* result() const { return _result; } +}; + +static inline void set_has_work(bool value) { + Atomic::store(&_has_work, value); +} + +static inline bool has_work() { + return Atomic::load(&_has_work); +} + +static void request_resize() { + if (!has_work()) { + MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + if (!has_work()) { + set_has_work(true); + Service_lock->notify_all(); + } + } +} + +static FinalizerEntry* add_to_table_if_needed(const InstanceKlass* ik, Thread* thread) { + FinalizerEntryLookup lookup(ik); + FinalizerEntry* entry = nullptr; + bool grow_hint = false; + do { + // We have looked up the entry once, proceed with insertion. + entry = new FinalizerEntry(ik); + if (_table->insert(thread, lookup, entry, &grow_hint)) { + break; + } + // In case another thread did a concurrent add, return value already in the table. + // This could fail if the entry got deleted concurrently, so loop back until success. + FinalizerEntryLookupGet felg; + if (_table->get(thread, lookup, felg, &grow_hint)) { + entry = felg.result(); + break; + } + } while (true); + if (grow_hint) { + request_resize(); + } + assert(entry != nullptr, "invariant"); + return entry; +} + +static void do_table_concurrent_work(JavaThread* jt) { + if (!_table->is_max_size_reached()) { + FinalizerHashtable::GrowTask gt(_table); + if (!gt.prepare(jt)) { + return; + } + while (gt.do_task(jt)) { + gt.pause(jt); + { + ThreadBlockInVM tbivm(jt); + } + gt.cont(jt); + } + gt.done(jt); + } + set_has_work(false); +} + +bool FinalizerService::has_work() { + return ::has_work(); +} + +void FinalizerService::do_concurrent_work(JavaThread* service_thread) { + assert(service_thread != nullptr, "invariant"); + assert(has_work(), "invariant"); + do_table_concurrent_work(service_thread); +} + +void FinalizerService::init() { + assert(_table == nullptr, "invariant"); + const size_t start_size_log_2 = ceil_log2(DEFAULT_TABLE_SIZE); + _table = new FinalizerHashtable(start_size_log_2, MAX_SIZE); +} + +static FinalizerEntry* lookup_entry(const InstanceKlass* ik, Thread* thread) { + FinalizerEntryLookup lookup(ik); + FinalizerEntryLookupGet felg; + _table->get(thread, lookup, felg); + return felg.result(); +} + +const FinalizerEntry* FinalizerService::lookup(const InstanceKlass* ik, Thread* thread) { + assert(ik != nullptr, "invariant"); + assert(thread != nullptr, "invariant"); + assert(ik->has_finalizer(), "invariant"); + return lookup_entry(ik, thread); +} + +// Add if not exist. +static FinalizerEntry* get_entry(const InstanceKlass* ik, Thread* thread) { + assert(ik != nullptr, "invariant"); + assert(ik->has_finalizer(), "invariant"); + FinalizerEntry* const entry = lookup_entry(ik, thread); + return entry != nullptr ? entry : add_to_table_if_needed(ik, thread); +} + +static FinalizerEntry* get_entry(oop finalizee, Thread* thread) { + assert(finalizee != nullptr, "invariant"); + assert(finalizee->is_instance(), "invariant"); + return get_entry(InstanceKlass::cast(finalizee->klass()), thread); +} + +static void log_registered(oop finalizee, Thread* thread) { + ResourceMark rm(thread); + const intptr_t identity_hash = ObjectSynchronizer::FastHashCode(thread, finalizee); + log_info(finalizer)("Registered object (" INTPTR_FORMAT ") of class %s as finalizable", identity_hash, finalizee->klass()->external_name()); +} + +void FinalizerService::on_register(oop finalizee, Thread* thread) { + FinalizerEntry* const fe = get_entry(finalizee, thread); + assert(fe != nullptr, "invariant"); + fe->on_register(); + if (log_is_enabled(Info, finalizer)) { + log_registered(finalizee, thread); + } +} + +static void log_completed(oop finalizee, Thread* thread) { + ResourceMark rm(thread); + const intptr_t identity_hash = ObjectSynchronizer::FastHashCode(thread, finalizee); + log_info(finalizer)("Finalizer was run for object (" INTPTR_FORMAT ") of class %s", identity_hash, finalizee->klass()->external_name()); +} + +void FinalizerService::on_complete(oop finalizee, JavaThread* finalizer_thread) { + FinalizerEntry* const fe = get_entry(finalizee, finalizer_thread); + assert(fe != nullptr, "invariant"); + fe->on_complete(); + if (log_is_enabled(Info, finalizer)) { + log_completed(finalizee, finalizer_thread); + } +} + +class FinalizerScan : public StackObj { + private: + FinalizerEntryClosure* _closure; + public: + FinalizerScan(FinalizerEntryClosure* closure) : _closure(closure) {} + bool operator()(FinalizerEntry** fe) { + return _closure->do_entry(*fe); + } +}; + +void FinalizerService::do_entries(FinalizerEntryClosure* closure, Thread* thread) { + assert(closure != nullptr, "invariant"); + FinalizerScan scan(closure); + _table->do_scan(thread, scan); +} + +static bool remove_entry(const InstanceKlass* ik) { + assert(ik != nullptr, "invariant"); + FinalizerEntryLookup lookup(ik); + return _table->remove(Thread::current(), lookup); +} + +static void on_unloading(Klass* klass) { + assert(klass != nullptr, "invariant"); + if (!klass->is_instance_klass()) { + return; + } + const InstanceKlass* const ik = InstanceKlass::cast(klass); + if (ik->has_finalizer()) { + remove_entry(ik); + } +} + +void FinalizerService::purge_unloaded() { + assert_locked_or_safepoint(ClassLoaderDataGraph_lock); + ClassLoaderDataGraph::classes_unloading_do(&on_unloading); +} + +#endif // INCLUDE_MANAGEMENT diff --git a/src/hotspot/share/services/finalizerService.hpp b/src/hotspot/share/services/finalizerService.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fbf1d5311de276b45d280e4fa154aa06e0bd8b8f --- /dev/null +++ b/src/hotspot/share/services/finalizerService.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_SERVICES_FINALIZERSERVICE_HPP +#define SHARE_SERVICES_FINALIZERSERVICE_HPP + +#include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" + +class InstanceKlass; +class JavaThread; +class Thread; + +class FinalizerEntry : public CHeapObj { + private: + const InstanceKlass* const _ik; + const char* _codesource; + uintptr_t _objects_on_heap; + uintptr_t _total_finalizers_run; + public: + FinalizerEntry(const InstanceKlass* ik); + ~FinalizerEntry(); + const InstanceKlass* klass() const NOT_MANAGEMENT_RETURN_(nullptr); + const char* codesource() const NOT_MANAGEMENT_RETURN_(nullptr); + uintptr_t objects_on_heap() const NOT_MANAGEMENT_RETURN_(0L); + uintptr_t total_finalizers_run() const NOT_MANAGEMENT_RETURN_(0L); + void on_register() NOT_MANAGEMENT_RETURN; + void on_complete() NOT_MANAGEMENT_RETURN; +}; + +class FinalizerEntryClosure : public StackObj { + public: + virtual bool do_entry(const FinalizerEntry* fe) = 0; +}; + +class FinalizerService : AllStatic { + friend class ServiceThread; + private: + static bool has_work() NOT_MANAGEMENT_RETURN_(false); + static void do_concurrent_work(JavaThread* service_thread) NOT_MANAGEMENT_RETURN; + public: + static void init() NOT_MANAGEMENT_RETURN; + static void purge_unloaded() NOT_MANAGEMENT_RETURN; + static void on_register(oop finalizee, Thread* thread) NOT_MANAGEMENT_RETURN; + static void on_complete(oop finalizee, JavaThread* finalizer_thread) NOT_MANAGEMENT_RETURN; + static void do_entries(FinalizerEntryClosure* closure, Thread* thread) NOT_MANAGEMENT_RETURN; + static const FinalizerEntry* lookup(const InstanceKlass* ik, Thread* thread) NOT_MANAGEMENT_RETURN_(nullptr); +}; + +#endif // SHARE_SERVICES_FINALIZERSERVICE_HPP diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index ac40f0704e8003fdb2f1b40df3cddeb6f03bb218..348ed2c3746a1fba1b98c9f26783270d0e66217a 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -32,7 +32,7 @@ #include "classfile/vmSymbols.hpp" #include "gc/shared/gcLocker.hpp" #include "gc/shared/gcVMOperations.hpp" -#include "gc/shared/workgroup.hpp" +#include "gc/shared/workerThread.hpp" #include "jfr/jfrEvents.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" @@ -748,7 +748,7 @@ class ParDumpWriter : public AbstractDumpWriter { static void before_work() { assert(_lock == NULL, "ParDumpWriter lock must be initialized only once"); - _lock = new (std::nothrow) PaddedMonitor(Mutex::leaf, "Parallel HProf writer lock", Mutex::_safepoint_check_never); + _lock = new (std::nothrow) PaddedMonitor(Mutex::safepoint, "ParallelHProfWriter_lock"); } static void after_work() { @@ -1626,7 +1626,7 @@ class JNIGlobalsDumper : public OopClosure { }; void JNIGlobalsDumper::do_oop(oop* obj_p) { - oop o = *obj_p; + oop o = NativeAccess::oop_load(obj_p); // ignore these if (o == NULL) return; @@ -1814,8 +1814,7 @@ class DumperController : public CHeapObj { public: DumperController(uint number) : _started(false), - _lock(new (std::nothrow) PaddedMonitor(Mutex::leaf, "Dumper Controller lock", - Mutex::_safepoint_check_never)), + _lock(new (std::nothrow) PaddedMonitor(Mutex::safepoint, "DumperController_lock")), _dumper_number(number), _complete_number(0) { } @@ -1854,7 +1853,7 @@ class DumperController : public CHeapObj { }; // The VM operation that performs the heap dump -class VM_HeapDumper : public VM_GC_Operation, public AbstractGangTask { +class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { private: static VM_HeapDumper* _global_dumper; static DumpWriter* _global_writer; @@ -1910,11 +1909,11 @@ class VM_HeapDumper : public VM_GC_Operation, public AbstractGangTask { _num_writer_threads = 1; _num_dumper_threads = num_total - _num_writer_threads; } - // Number of dumper threads that only iterate heap. - uint _heap_only_dumper_threads = _num_dumper_threads - 1 /* VMDumper thread */; // Prepare parallel writer. if (_num_dumper_threads > 1) { ParDumpWriter::before_work(); + // Number of dumper threads that only iterate heap. + uint _heap_only_dumper_threads = _num_dumper_threads - 1 /* VMDumper thread */; _dumper_controller = new (std::nothrow) DumperController(_heap_only_dumper_threads); _poi = Universe::heap()->parallel_object_iterator(_num_dumper_threads); } @@ -1974,7 +1973,7 @@ class VM_HeapDumper : public VM_GC_Operation, public AbstractGangTask { GCCause::_heap_dump /* GC Cause */, 0 /* total full collections, dummy, ignored */, gc_before_heap_dump), - AbstractGangTask("dump heap") { + WorkerTask("dump heap") { _local_writer = writer; _gc_before_heap_dump = gc_before_heap_dump; _klass_map = new (ResourceObj::C_HEAP, mtServiceability) GrowableArray(INITIAL_CLASS_COUNT, mtServiceability); @@ -2249,13 +2248,16 @@ void VM_HeapDumper::doit() { set_global_dumper(); set_global_writer(); - WorkGang* gang = ch->safepoint_workers(); + WorkerThreads* workers = ch->safepoint_workers(); - if (gang == NULL) { + if (workers == NULL) { + // Use serial dump, set dumper threads and writer threads number to 1. + _num_dumper_threads=1; + _num_writer_threads=1; work(0); } else { - prepare_parallel_dump(gang->active_workers()); - gang->run_task(this); + prepare_parallel_dump(workers->active_workers()); + workers->run_task(this); finish_parallel_dump(); } @@ -2315,7 +2317,6 @@ void VM_HeapDumper::work(uint worker_id) { // technically not jni roots, but global roots // for things like preallocated throwable backtraces Universe::vm_global()->oops_do(&jni_dumper); - // HPROF_GC_ROOT_STICKY_CLASS // These should be classes in the NULL class loader data, and not all classes // if !ClassUnloading @@ -2353,10 +2354,8 @@ void VM_HeapDumper::work(uint worker_id) { _dumper_controller->wait_all_dumpers_complete(); // clear internal buffer; pw.finish_dump_segment(true); - // refresh the global_writer's buffer and position; writer()->refresh(); - } else { pw.finish_dump_segment(true); _dumper_controller->dumper_complete(); diff --git a/src/hotspot/share/services/heapDumperCompression.cpp b/src/hotspot/share/services/heapDumperCompression.cpp index 8063528b6f81740f791f6fa4eb35e065df7ccccd..0d6426017974012c0918d745672dcb3403c1c704 100644 --- a/src/hotspot/share/services/heapDumperCompression.cpp +++ b/src/hotspot/share/services/heapDumperCompression.cpp @@ -200,8 +200,7 @@ CompressionBackend::CompressionBackend(AbstractWriter* writer, _written(0), _writer(writer), _compressor(compressor), - _lock(new (std::nothrow) PaddedMonitor(Mutex::nosafepoint, "HProfCompressionBackend_lock", - Mutex::_safepoint_check_never)) { + _lock(new (std::nothrow) PaddedMonitor(Mutex::nosafepoint, "HProfCompressionBackend_lock")) { if (_writer == NULL) { set_error("Could not allocate writer"); } else if (_lock == NULL) { diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index df4c20cebf1575d6450a1cab53da2910449c6bce..7f103ea784974bb34c6508051a65872ec8956f8c 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -55,6 +55,7 @@ #include "services/classLoadingService.hpp" #include "services/diagnosticCommand.hpp" #include "services/diagnosticFramework.hpp" +#include "services/finalizerService.hpp" #include "services/writeableFlags.hpp" #include "services/heapDumper.hpp" #include "services/lowMemoryDetector.hpp" @@ -94,6 +95,7 @@ void management_init() { ThreadService::init(); RuntimeService::init(); ClassLoadingService::init(); + FinalizerService::init(); #else ThreadService::init(); #endif // INCLUDE_MANAGEMENT @@ -206,6 +208,15 @@ InstanceKlass* Management::initialize_klass(Klass* k, TRAPS) { return ik; } + +void Management::record_vm_init_completed() { + // Initialize the timestamp to get the current time + _vm_init_done_time->set_value(os::javaTimeMillis()); + + // Update the timestamp to the vm init done time + _stamp.update(); +} + void Management::record_vm_startup_time(jlong begin, jlong duration) { // if the performance counter is not initialized, // then vm initialization failed; simply return. @@ -216,6 +227,14 @@ void Management::record_vm_startup_time(jlong begin, jlong duration) { PerfMemory::set_accessible(true); } +jlong Management::begin_vm_creation_time() { + return _begin_vm_creation_time->get_value(); +} + +jlong Management::vm_init_done_time() { + return _vm_init_done_time->get_value(); +} + jlong Management::timestamp() { TimeStamp t; t.update(); diff --git a/src/hotspot/share/services/management.hpp b/src/hotspot/share/services/management.hpp index 96b50eb3e611c173e3d47035f8c6436d21573bc5..8c5be096b3c738c32f6457c99c97f7fbad956203 100644 --- a/src/hotspot/share/services/management.hpp +++ b/src/hotspot/share/services/management.hpp @@ -70,20 +70,10 @@ public: static void record_vm_startup_time(jlong begin, jlong duration) NOT_MANAGEMENT_RETURN; - static void record_vm_init_completed() { - // Initialize the timestamp to get the current time - _vm_init_done_time->set_value(os::javaTimeMillis()); + static void record_vm_init_completed() NOT_MANAGEMENT_RETURN; - // Update the timestamp to the vm init done time - _stamp.update(); - } - - static jlong begin_vm_creation_time() { - return _begin_vm_creation_time->get_value(); - } - static jlong vm_init_done_time() { - return _vm_init_done_time->get_value(); - } + static jlong begin_vm_creation_time() NOT_MANAGEMENT_RETURN_(0L); + static jlong vm_init_done_time() NOT_MANAGEMENT_RETURN_(0L); // methods to return a Klass*. static InstanceKlass* java_lang_management_ThreadInfo_klass(TRAPS); diff --git a/src/hotspot/share/services/memoryManager.cpp b/src/hotspot/share/services/memoryManager.cpp index 30a14f77745362236d9cfcab618fc768c6352216..11abafa94fe7f91a986a30d042aec1d37adc9558 100644 --- a/src/hotspot/share/services/memoryManager.cpp +++ b/src/hotspot/share/services/memoryManager.cpp @@ -174,8 +174,7 @@ GCMemoryManager::GCMemoryManager(const char* name, const char* gc_end_message) : MemoryManager(name), _gc_end_message(gc_end_message) { _num_collections = 0; _last_gc_stat = NULL; - _last_gc_lock = new Mutex(Mutex::nosafepoint, "GCMemoryManager_lock", - Mutex::_safepoint_check_never); + _last_gc_lock = new Mutex(Mutex::nosafepoint, "GCMemoryManager_lock"); _current_gc_stat = NULL; _num_gc_threads = 1; _notification_enabled = false; diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index fcfb350dfe2012ebd04752bc69f78f9d3aa0e373..85a899c57b6a6fc3dae4bf9591dc3ffef70561b1 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -1014,8 +1014,7 @@ inline ConcurrentHashTable:: { _stats_rate = TableRateStatistics(); _resize_lock = - new Mutex(Mutex::nosafepoint-2, "ConcurrentHashTableResize_lock", - Mutex::_safepoint_check_never); + new Mutex(Mutex::nosafepoint-2, "ConcurrentHashTableResize_lock"); _table = new InternalTable(log2size); assert(log2size_limit >= log2size, "bad ergo"); _size_limit_reached = _table->_log2_size == _log2_size_limit; diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 0cc83bd9e40c85d85a9cc3cf334f7ffec4aafbc3..778d1a1b4a4ef8759a3359111b8ba325ceffdabe 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -366,7 +366,7 @@ void report_java_out_of_memory(const char* message) { if (ExitOnOutOfMemoryError) { tty->print_cr("Terminating due to java.lang.OutOfMemoryError: %s", message); - os::exit(3); + os::_exit(3); // quick exit with no cleanup hooks run } } } diff --git a/src/hotspot/share/utilities/debug.hpp b/src/hotspot/share/utilities/debug.hpp index 11b79f3cb6c950cb654da3182f6b732abc5f6834..7e3f71a9dff98a062900573b6f82e4003f20d421 100644 --- a/src/hotspot/share/utilities/debug.hpp +++ b/src/hotspot/share/utilities/debug.hpp @@ -174,22 +174,7 @@ void report_untested(const char* file, int line, const char* message); void warning(const char* format, ...) ATTRIBUTE_PRINTF(1, 2); -// Compile-time asserts. Cond must be a compile-time constant expression that -// is convertible to bool. STATIC_ASSERT() can be used anywhere a declaration -// may appear. -// -// Implementation Note: STATIC_ASSERT_FAILURE provides a value member -// rather than type member that could be used directly in the typedef, because -// a type member would require conditional use of "typename", depending on -// whether Cond is dependent or not. The use of a value member leads to the -// use of an array type. - -template struct STATIC_ASSERT_FAILURE; -template<> struct STATIC_ASSERT_FAILURE { enum { value = 1 }; }; - -#define STATIC_ASSERT(Cond) \ - typedef char PASTE_TOKENS(STATIC_ASSERT_DUMMY_TYPE_, __LINE__)[ \ - STATIC_ASSERT_FAILURE< (Cond) >::value ] +#define STATIC_ASSERT(Cond) static_assert((Cond), #Cond) // out of memory reporting void report_java_out_of_memory(const char* message); diff --git a/src/hotspot/share/utilities/events.cpp b/src/hotspot/share/utilities/events.cpp index 77cc16e174e919261c0c62768715f663668073ec..09e4ea89781cb33c6c88acc570fb6f7dece80e8f 100644 --- a/src/hotspot/share/utilities/events.cpp +++ b/src/hotspot/share/utilities/events.cpp @@ -39,6 +39,7 @@ StringEventLog* Events::_vm_operations = NULL; ExceptionsEventLog* Events::_exceptions = NULL; StringEventLog* Events::_redefinitions = NULL; UnloadingEventLog* Events::_class_unloading = NULL; +StringEventLog* Events::_class_loading = NULL; StringEventLog* Events::_deopt_messages = NULL; EventLog::EventLog() { @@ -96,6 +97,7 @@ void Events::init() { _exceptions = new ExceptionsEventLog("Internal exceptions", "exc"); _redefinitions = new StringEventLog("Classes redefined", "redef"); _class_unloading = new UnloadingEventLog("Classes unloaded", "unload"); + _class_loading = new StringEventLog("Classes loaded", "load"); _deopt_messages = new StringEventLog("Deoptimization events", "deopt"); } } diff --git a/src/hotspot/share/utilities/events.hpp b/src/hotspot/share/utilities/events.hpp index 7f9ae6909f7609b91695cc9c8cd187ced8f1842f..a98ee5b796952846e348742520459ce053df9666 100644 --- a/src/hotspot/share/utilities/events.hpp +++ b/src/hotspot/share/utilities/events.hpp @@ -100,7 +100,7 @@ template class EventLogBase : public EventLog { public: EventLogBase(const char* name, const char* handle, int length = LogEventsBufferEntries): - _mutex(Mutex::event, name, Mutex::_safepoint_check_never), + _mutex(Mutex::event, name), _name(name), _handle(handle), _length(length), @@ -235,6 +235,9 @@ class Events : AllStatic { // Class unloading events static UnloadingEventLog* _class_unloading; + + // Class loading events + static StringEventLog* _class_loading; public: // Print all event logs; limit number of events per event log to be printed with max @@ -260,6 +263,8 @@ class Events : AllStatic { static void log_class_unloading(Thread* thread, InstanceKlass* ik); + static void log_class_loading(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + static void log_deopt_message(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); // Register default loggers @@ -314,6 +319,15 @@ inline void Events::log_class_unloading(Thread* thread, InstanceKlass* ik) { } } +inline void Events::log_class_loading(Thread* thread, const char* format, ...) { + if (LogEvents && _class_loading != NULL) { + va_list ap; + va_start(ap, format); + _class_loading->logv(thread, format, ap); + va_end(ap); + } +} + inline void Events::log_deopt_message(Thread* thread, const char* format, ...) { if (LogEvents && _deopt_messages != NULL) { va_list ap; @@ -473,4 +487,7 @@ typedef EventMarkWithLogFunction EventMark; // These end up in the vm_operation log. typedef EventMarkWithLogFunction EventMarkVMOperation; +// These end up in the class loading log. +typedef EventMarkWithLogFunction EventMarkClassLoading; + #endif // SHARE_UTILITIES_EVENTS_HPP diff --git a/src/hotspot/share/utilities/hashtable.cpp b/src/hotspot/share/utilities/hashtable.cpp index 5b99bba8bec181b749d00223915d2d271668bc94..5e8cd7dfde1c391a64e8e1fccddd29b5242cd255 100644 --- a/src/hotspot/share/utilities/hashtable.cpp +++ b/src/hotspot/share/utilities/hashtable.cpp @@ -75,24 +75,28 @@ template void BasicHashtable::free_buckets() { } // Default overload, for types that are uninteresting. -template static int literal_size(T) { return 0; } +template static size_t literal_size(T) { return 0; } -static int literal_size(Symbol *symbol) { +static size_t literal_size(Symbol *symbol) { return symbol->size() * HeapWordSize; } -static int literal_size(oop obj) { +static size_t literal_size(oop obj) { if (obj == NULL) { return 0; - } else if (obj->klass() == vmClasses::String_klass()) { + } + + size_t word_size = obj->size(); + + if (obj->klass() == vmClasses::String_klass()) { // This may overcount if String.value arrays are shared. - return (obj->size() + java_lang_String::value(obj)->size()) * HeapWordSize; - } else { - return obj->size(); + word_size += java_lang_String::value(obj)->size(); } + + return word_size * HeapWordSize; } -static int literal_size(WeakHandle v) { +static size_t literal_size(WeakHandle v) { return literal_size(v.peek()); } @@ -175,7 +179,7 @@ template bool BasicHashtable::maybe_grow(int max_size, int load_ template TableStatistics Hashtable::statistics_calculate(T (*literal_load_barrier)(HashtableEntry*)) { NumberSeq summary; - int literal_bytes = 0; + size_t literal_bytes = 0; for (int i = 0; i < this->table_size(); ++i) { int count = 0; for (HashtableEntry* e = this->bucket(i); diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index 501ba0dbabc255b1a604fcdf077a7bb990f76fac..9b362b88c11911a2c89360d0c7e08cc5d4678523 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -126,9 +126,11 @@ #if INCLUDE_MANAGEMENT #define NOT_MANAGEMENT_RETURN /* next token must be ; */ #define NOT_MANAGEMENT_RETURN_(code) /* next token must be ; */ +#define MANAGEMENT_ONLY(x) x #else #define NOT_MANAGEMENT_RETURN {} #define NOT_MANAGEMENT_RETURN_(code) { return code; } +#define MANAGEMENT_ONLY(x) #endif // INCLUDE_MANAGEMENT #ifndef INCLUDE_EPSILONGC diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index ec14e2095cae5b1b87da1682b77f70537e2dfc82..d6152164dbb852308f5f0660a00132d4b5b6de09 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -87,6 +87,7 @@ Thread* VMError::_thread; address VMError::_pc; void* VMError::_siginfo; void* VMError::_context; +bool VMError::_print_native_stack_used = false; const char* VMError::_filename; int VMError::_lineno; size_t VMError::_size; @@ -241,6 +242,108 @@ void VMError::print_stack_trace(outputStream* st, JavaThread* jt, #endif // ZERO } +/** + * Adds `value` to `list` iff it's not already present and there is sufficient + * capacity (i.e. length(list) < `list_capacity`). The length of the list + * is the index of the first nullptr entry or `list_capacity` if there are + * no nullptr entries. + * + * @ return true if the value was added, false otherwise + */ +static bool add_if_absent(address value, address* list, int list_capacity) { + for (int i = 0; i < list_capacity; i++) { + if (list[i] == value) { + return false; + } + if (list[i] == nullptr) { + list[i] = value; + if (i + 1 < list_capacity) { + list[i + 1] = nullptr; + } + return true; + } + } + return false; +} + +/** + * Prints the VM generated code unit, if any, containing `pc` if it has not already + * been printed. If the code unit is an InterpreterCodelet or StubCodeDesc, it is + * only printed if `is_crash_pc` is true. + * + * @param printed array of code units that have already been printed (delimited by NULL entry) + * @param printed_capacity the capacity of `printed` + * @return true if the code unit was printed, false otherwise + */ +static bool print_code(outputStream* st, Thread* thread, address pc, bool is_crash_pc, + address* printed, int printed_capacity) { + if (Interpreter::contains(pc)) { + if (is_crash_pc) { + // The interpreter CodeBlob is very large so try to print the codelet instead. + InterpreterCodelet* codelet = Interpreter::codelet_containing(pc); + if (codelet != nullptr) { + if (add_if_absent((address) codelet, printed, printed_capacity)) { + codelet->print_on(st); + Disassembler::decode(codelet->code_begin(), codelet->code_end(), st); + return true; + } + } + } + } else { + StubCodeDesc* desc = StubCodeDesc::desc_for(pc); + if (desc != nullptr) { + if (is_crash_pc) { + if (add_if_absent((address) desc, printed, printed_capacity)) { + desc->print_on(st); + Disassembler::decode(desc->begin(), desc->end(), st); + return true; + } + } + } else if (thread != nullptr) { + CodeBlob* cb = CodeCache::find_blob(pc); + if (cb != nullptr && add_if_absent((address) cb, printed, printed_capacity)) { + // Disassembling nmethod will incur resource memory allocation, + // only do so when thread is valid. + ResourceMark rm(thread); + Disassembler::decode(cb, st); + st->cr(); + return true; + } + } + } + return false; +} + +/** + * Gets the caller frame of `fr`. + * + * @returns an invalid frame (i.e. fr.pc() === 0) if the caller cannot be obtained + */ +static frame next_frame(frame fr, Thread* t) { + // Compiled code may use EBP register on x86 so it looks like + // non-walkable C frame. Use frame.sender() for java frames. + frame invalid; + if (t != nullptr && t->is_Java_thread()) { + // Catch very first native frame by using stack address. + // For JavaThread stack_base and stack_size should be set. + if (!t->is_in_full_stack((address)(fr.real_fp() + 1))) { + return invalid; + } + if (fr.is_java_frame() || fr.is_native_frame() || fr.is_runtime_frame()) { + RegisterMap map(JavaThread::cast(t), false); // No update + return fr.sender(&map); + } else { + // is_first_C_frame() does only simple checks for frame pointer, + // it will pass if java compiled code has a pointer in EBP. + if (os::is_first_C_frame(&fr)) return invalid; + return os::get_sender_for_C_frame(&fr); + } + } else { + if (os::is_first_C_frame(&fr)) return invalid; + return os::get_sender_for_C_frame(&fr); + } +} + void VMError::print_native_stack(outputStream* st, frame fr, Thread* t, char* buf, int buf_size) { // see if it's a valid frame @@ -258,26 +361,9 @@ void VMError::print_native_stack(outputStream* st, frame fr, Thread* t, char* bu } } st->cr(); - // Compiled code may use EBP register on x86 so it looks like - // non-walkable C frame. Use frame.sender() for java frames. - if (t && t->is_Java_thread()) { - // Catch very first native frame by using stack address. - // For JavaThread stack_base and stack_size should be set. - if (!t->is_in_full_stack((address)(fr.real_fp() + 1))) { - break; - } - if (fr.is_java_frame() || fr.is_native_frame() || fr.is_runtime_frame()) { - RegisterMap map(JavaThread::cast(t), false); // No update - fr = fr.sender(&map); - } else { - // is_first_C_frame() does only simple checks for frame pointer, - // it will pass if java compiled code has a pointer in EBP. - if (os::is_first_C_frame(&fr)) break; - fr = os::get_sender_for_C_frame(&fr); - } - } else { - if (os::is_first_C_frame(&fr)) break; - fr = os::get_sender_for_C_frame(&fr); + fr = next_frame(fr, t); + if (fr.pc() == nullptr) { + break; } } @@ -747,6 +833,7 @@ void VMError::report(outputStream* st, bool _verbose) { : os::current_frame(); print_native_stack(st, fr, _thread, buf, sizeof(buf)); + _print_native_stack_used = true; } } @@ -821,29 +908,46 @@ void VMError::report(outputStream* st, bool _verbose) { st->cr(); } - STEP("printing code blob if possible") + STEP("printing code blobs if possible") if (_verbose && _context) { - CodeBlob* cb = CodeCache::find_blob(_pc); - if (cb != NULL) { - if (Interpreter::contains(_pc)) { - // The interpreter CodeBlob is very large so try to print the codelet instead. - InterpreterCodelet* codelet = Interpreter::codelet_containing(_pc); - if (codelet != NULL) { - codelet->print_on(st); - Disassembler::decode(codelet->code_begin(), codelet->code_end(), st); + const int printed_capacity = max_error_log_print_code; + address printed[printed_capacity]; + printed[0] = nullptr; + int printed_len = 0; + // Even though ErrorLogPrintCodeLimit is ranged checked + // during argument parsing, there's no way to prevent it + // subsequently (i.e., after parsing) being set to a + // value outside the range. + int limit = MIN2(ErrorLogPrintCodeLimit, printed_capacity); + if (limit > 0) { + // Scan the native stack + if (!_print_native_stack_used) { + // Only try to print code of the crashing frame since + // the native stack cannot be walked with next_frame. + if (print_code(st, _thread, _pc, true, printed, printed_capacity)) { + printed_len++; } } else { - StubCodeDesc* desc = StubCodeDesc::desc_for(_pc); - if (desc != NULL) { - desc->print_on(st); - Disassembler::decode(desc->begin(), desc->end(), st); - } else if (_thread != NULL) { - // Disassembling nmethod will incur resource memory allocation, - // only do so when thread is valid. - ResourceMark rm(_thread); - Disassembler::decode(cb, st); - st->cr(); + frame fr = os::fetch_frame_from_context(_context); + while (printed_len < limit && fr.pc() != nullptr) { + if (print_code(st, _thread, fr.pc(), fr.pc() == _pc, printed, printed_capacity)) { + printed_len++; + } + fr = next_frame(fr, _thread); + } + } + + // Scan the Java stack + if (_thread != nullptr && _thread->is_Java_thread()) { + JavaThread* jt = JavaThread::cast(_thread); + if (jt->has_last_Java_frame()) { + for (StackFrameStream sfs(jt, true /* update */, true /* process_frames */); printed_len < limit && !sfs.is_done(); sfs.next()) { + address pc = sfs.current()->pc(); + if (print_code(st, _thread, pc, pc == _pc, printed, printed_capacity)) { + printed_len++; + } + } } } } diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index 4311830d5521d57c76a8b5374035761ab42f2c07..736e354677c70f2a328f2c97a4f415f62a91fa96 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -51,6 +51,10 @@ class VMError : public AllStatic { static void* _context; // ContextRecord on Windows, // ucontext_t on Solaris/Linux + // records if VMError::print_native_stack was used to + // print the native stack instead of os::platform_print_native_stack + static bool _print_native_stack_used; + // additional info for VM internal errors static const char* _filename; static int _lineno; @@ -181,6 +185,9 @@ public: // which is not NULL and contains bits in every word. static const intptr_t segfault_address = LP64_ONLY(0xABC0000000000ABCULL) NOT_LP64(0x00000ABC); + // Max value for the ErrorLogPrintCodeLimit flag. + static const int max_error_log_print_code = 10; + // Needed when printing signal handlers. NOT_WINDOWS(static const void* crash_handler_address;) diff --git a/src/java.base/macosx/native/libjli/java_md_macosx.m b/src/java.base/macosx/native/libjli/java_md_macosx.m index b83ebf91f13304037659713fe83814696dab2ff5..9e00c0f07c0ab7b8ef61f47f85d4f8e47653068b 100644 --- a/src/java.base/macosx/native/libjli/java_md_macosx.m +++ b/src/java.base/macosx/native/libjli/java_md_macosx.m @@ -793,7 +793,7 @@ SetXDockArgForAWT(const char *arg) * change drastically between update release, and it may even be * removed or replaced with another mechanism. * - * NOTE: It is used by SWT, and JavaFX. + * NOTE: It is used by SWT */ snprintf(envVar, sizeof(envVar), "APP_NAME_%d", getpid()); setenv(envVar, (arg + 12), 1); @@ -828,40 +828,78 @@ SetMainClassForAWT(JNIEnv *env, jclass mainClass) { jmethodID getCanonicalNameMID = NULL; NULL_CHECK(getCanonicalNameMID = (*env)->GetMethodID(env, classClass, "getCanonicalName", "()Ljava/lang/String;")); + jclass strClass = NULL; + NULL_CHECK(strClass = (*env)->FindClass(env, "java/lang/String")); + + jmethodID lastIndexMID = NULL; + NULL_CHECK(lastIndexMID = (*env)->GetMethodID(env, strClass, "lastIndexOf", "(I)I")); + + jmethodID subStringMID = NULL; + NULL_CHECK(subStringMID = (*env)->GetMethodID(env, strClass, "substring", "(I)Ljava/lang/String;")); + jstring mainClassString = (*env)->CallObjectMethod(env, mainClass, getCanonicalNameMID); if ((*env)->ExceptionCheck(env) || NULL == mainClassString) { - /* - * Clears all errors caused by getCanonicalName() on the mainclass and - * leaves the JAVA_MAIN_CLASS__ empty. - */ (*env)->ExceptionClear(env); return; } - const char *mainClassName = NULL; - NULL_CHECK(mainClassName = (*env)->GetStringUTFChars(env, mainClassString, NULL)); + jint lastPeriod = (*env)->CallIntMethod(env, mainClassString, lastIndexMID, (jint)'.'); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + return; + } - char envVar[80]; - /* - * The JAVA_MAIN_CLASS_ environment variable is used to pass - * the name of a Java class whose main() method is invoked by - * the Java launcher code to start the application, to the AWT code - * in order to assign the name to the Apple menu bar when the app - * is active on the Mac. - * The _ part is added to avoid collisions with child processes. - * - * WARNING: This environment variable is an implementation detail and - * isn't meant for use outside of the core platform. The mechanism for - * passing this information from Java launcher to other modules may - * change drastically between update release, and it may even be - * removed or replaced with another mechanism. + if (lastPeriod != -1) { + mainClassString = (*env)->CallObjectMethod(env, mainClassString, subStringMID, lastPeriod+1); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + return; + } + } + + /* There are multiple apple.awt.*" system properties that AWT(the desktop module) + * references that are inherited from Apple JDK. + * This inherited AWT code looks for this property and uses it for the name + * of the app as it appears in the system menu bar. * - * NOTE: It is used by SWT, and JavaFX. + * No idea if how much external code ever sets it, but use it if set, else + * if not set (the high probability event) set it to the application class name. */ - snprintf(envVar, sizeof(envVar), "JAVA_MAIN_CLASS_%d", getpid()); - setenv(envVar, mainClassName, 1); + const char* propName = "apple.awt.application.name"; + jstring jKey = NULL; + NULL_CHECK(jKey = (*env)->NewStringUTF(env, propName)); + + jclass sysClass = NULL; + NULL_CHECK(sysClass = (*env)->FindClass(env, "java/lang/System")); + + jmethodID getPropertyMID = NULL; + NULL_CHECK(getPropertyMID = (*env)->GetStaticMethodID(env, sysClass, + "getProperty", "(Ljava/lang/String;)Ljava/lang/String;")); + + jmethodID setPropertyMID = NULL; + NULL_CHECK(setPropertyMID = (*env)->GetStaticMethodID(env, sysClass, + "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")); + + jstring jValue = (*env)->CallStaticObjectMethod(env, sysClass, getPropertyMID, jKey); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + (*env)->DeleteLocalRef(env, jKey); + return; + } + if (jValue == NULL) { + (*env)->CallStaticObjectMethod(env, sysClass, setPropertyMID, + jKey, mainClassString); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + (*env)->DeleteLocalRef(env, jKey); + return; + } + } else { + (*env)->DeleteLocalRef(env, jValue); + } - (*env)->ReleaseStringUTFChars(env, mainClassString, mainClassName); + (*env)->DeleteLocalRef(env, jKey); } void diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java index c082dde20b8713cfe38d1b07cba4ed55102aae97..4c9ac6768d92932a30b54d3fd2ddb016ee5f1b3a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java @@ -72,9 +72,7 @@ abstract class AESCipher extends CipherSpi { engineSetPadding(padding); } catch (GeneralSecurityException gse) { // internal error; re-throw as provider exception - ProviderException pe =new ProviderException("Internal Error"); - pe.initCause(gse); - throw pe; + throw new ProviderException("Internal Error", gse); } } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ConstructKeys.java b/src/java.base/share/classes/com/sun/crypto/provider/ConstructKeys.java index 68c2c830df6650a9ecab59a0581e0cb007eaa752..e8375d1eb818290218ac34c1d6de8718a1a6ff23 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ConstructKeys.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ConstructKeys.java @@ -76,16 +76,10 @@ final class ConstructKeys { encodedKeyAlgorithm + "algorithm"); } catch (InvalidKeySpecException ikse2) { - InvalidKeyException ike = - new InvalidKeyException("Cannot construct public key"); - ike.initCause(ikse2); - throw ike; + throw new InvalidKeyException("Cannot construct public key", ikse2); } } catch (InvalidKeySpecException ikse) { - InvalidKeyException ike = - new InvalidKeyException("Cannot construct public key"); - ike.initCause(ikse); - throw ike; + throw new InvalidKeyException("Cannot construct public key", ikse); } return key; @@ -116,16 +110,10 @@ final class ConstructKeys { encodedKeyAlgorithm + "algorithm"); } catch (InvalidKeySpecException ikse2) { - InvalidKeyException ike = - new InvalidKeyException("Cannot construct private key"); - ike.initCause(ikse2); - throw ike; + throw new InvalidKeyException("Cannot construct private key", ikse2); } } catch (InvalidKeySpecException ikse) { - InvalidKeyException ike = - new InvalidKeyException("Cannot construct private key"); - ike.initCause(ikse); - throw ike; + throw new InvalidKeyException("Cannot construct private key", ikse); } finally { SharedSecrets.getJavaSecuritySpecAccess().clearEncodedKeySpec(keySpec); if (keyBytes != encodedKey) { diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java index 23846d14fd1aa154dfcee0cbc653a1f28ae3a696..8ba473e467e953dcb7949504d961d5067668a1f1 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java @@ -181,10 +181,7 @@ public final class DESedeWrapCipher extends CipherSpi { engineInit(opmode, key, (AlgorithmParameterSpec) null, random); } catch (InvalidAlgorithmParameterException iape) { // should never happen - InvalidKeyException ike = - new InvalidKeyException("Parameters required"); - ike.initCause(iape); - throw ike; + throw new InvalidKeyException("Parameters required", iape); } } @@ -285,11 +282,8 @@ public final class DESedeWrapCipher extends CipherSpi { paramsEng.engineInit(params.getEncoded()); ivSpec = paramsEng.engineGetParameterSpec(IvParameterSpec.class); } catch (Exception ex) { - InvalidAlgorithmParameterException iape = - new InvalidAlgorithmParameterException - ("Wrong parameter type: IV expected"); - iape.initCause(ex); - throw iape; + throw new InvalidAlgorithmParameterException + ("Wrong parameter type: IV expected", ex); } } engineInit(opmode, key, ivSpec, random); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java index dcce2c4efa5a5af8bddf88e80fd4dda3a346a305..7b8ab570a0857528e2c27d4d1dbb85aae7e92987 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java @@ -291,10 +291,8 @@ final class DHPrivateKey implements PrivateKey, DerInputStream in = new DerInputStream(this.key); this.x = in.getBigInteger(); } catch (IOException e) { - InvalidKeyException ike = new InvalidKeyException( - "Error parsing key encoding: " + e.getMessage()); - ike.initCause(e); - throw ike; + throw new InvalidKeyException( + "Error parsing key encoding: " + e.getMessage(), e); } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java index db56dfcd50538f9b10e084ff3ab08f4096c51cb6..a4d6ac58c255dc43a111747a197a021b34104813 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java @@ -162,10 +162,7 @@ abstract class PBES2Core extends CipherSpi { try { engineInit(opmode, key, (AlgorithmParameterSpec) null, random); } catch (InvalidAlgorithmParameterException ie) { - InvalidKeyException ike = - new InvalidKeyException("requires PBE parameters"); - ike.initCause(ie); - throw ike; + throw new InvalidKeyException("requires PBE parameters", ie); } } @@ -279,10 +276,7 @@ abstract class PBES2Core extends CipherSpi { try { s = (PBKDF2KeyImpl)kdf.engineGenerateSecret(pbeSpec); } catch (InvalidKeySpecException ikse) { - InvalidKeyException ike = - new InvalidKeyException("Cannot construct PBE key"); - ike.initCause(ikse); - throw ike; + throw new InvalidKeyException("Cannot construct PBE key", ikse); } finally { pbeSpec.clearPassword(); } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndDESCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndDESCipher.java index 80a27b625dd1c8252b8f891b74aeb9a4ce1f2c0d..5034434024d0501eff37e0ba305d5fd54fc3df56 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndDESCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndDESCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ package com.sun.crypto.provider; import java.security.*; import java.security.spec.*; import javax.crypto.*; -import javax.crypto.spec.*; /** * This class represents password-based encryption as defined by the PKCS #5 @@ -183,10 +182,7 @@ public final class PBEWithMD5AndDESCipher extends CipherSpi { try { engineInit(opmode, key, (AlgorithmParameterSpec) null, random); } catch (InvalidAlgorithmParameterException ie) { - InvalidKeyException ike = - new InvalidKeyException("requires PBE parameters"); - ike.initCause(ie); - throw ike; + throw new InvalidKeyException("requires PBE parameters", ie); } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndTripleDESCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndTripleDESCipher.java index 18fee3b355f19974db69797172bc86e2f1299c8e..16784afc8970e7e06cd6089fdc970940456801af 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndTripleDESCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndTripleDESCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ package com.sun.crypto.provider; import java.security.*; import java.security.spec.*; import javax.crypto.*; -import javax.crypto.spec.*; /** * This class implements a proprietary password-based encryption algorithm. @@ -195,10 +194,7 @@ public final class PBEWithMD5AndTripleDESCipher extends CipherSpi { try { core.init(opmode, key, (AlgorithmParameterSpec) null, random); } catch (InvalidAlgorithmParameterException ie) { - InvalidKeyException ike = - new InvalidKeyException("requires PBE parameters"); - ike.initCause(ie); - throw ike; + throw new InvalidKeyException("requires PBE parameters", ie); } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2Core.java b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2Core.java index 78c7b72b081fc31ad2af1ab3f522ccbb08a99f71..87207c778ecb43e3684219296d0ffdcac46a7c92 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2Core.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2Core.java @@ -151,10 +151,8 @@ abstract class PBKDF2Core extends SecretKeyFactorySpi { try { return new PBKDF2KeyImpl(spec, prfAlgo); } catch (InvalidKeySpecException re) { - InvalidKeyException ike = new InvalidKeyException - ("Invalid key component(s)"); - ike.initCause(re); - throw ike; + throw new InvalidKeyException + ("Invalid key component(s)", re); } finally { if (password != null) { Arrays.fill(password, (char) 0); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2HmacSHA1Factory.java b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2HmacSHA1Factory.java index 6f260ee7b6245956aab5abd8f34c4b2ad8b2e336..d8070e968b66e18625173781a95221a0c4c55a42 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2HmacSHA1Factory.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2HmacSHA1Factory.java @@ -151,10 +151,8 @@ public final class PBKDF2HmacSHA1Factory extends SecretKeyFactorySpi { try { return new PBKDF2KeyImpl(spec, "HmacSHA1"); } catch (InvalidKeySpecException re) { - InvalidKeyException ike = new InvalidKeyException - ("Invalid key component(s)"); - ike.initCause(re); - throw ike; + throw new InvalidKeyException + ("Invalid key component(s)", re); } finally { if (password != null) { Arrays.fill(password, (char) 0); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java index 1588385d6531b23893076fd246f6762b825343b2..b6827e22e2b928229588881269a7cd71a6088d8d 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java @@ -64,6 +64,8 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey { private int iterCount; private byte[] key; + @SuppressWarnings("serial") // Type of field is not Serializable; + // see writeReplace method private Mac prf; private static byte[] getPasswordBytes(char[] passwd) { @@ -119,9 +121,7 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey { this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength); } catch (NoSuchAlgorithmException nsae) { // not gonna happen; re-throw just in case - InvalidKeySpecException ike = new InvalidKeySpecException(); - ike.initCause(nsae); - throw ike; + throw new InvalidKeySpecException(nsae); } finally { Arrays.fill(passwdBytes, (byte) 0x00); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBMAC1Core.java b/src/java.base/share/classes/com/sun/crypto/provider/PBMAC1Core.java index bd16a20a9ee5dfa69c987766e7e596ea0804962f..a3d896aa0348727f1e112d87211d1254b86283a8 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBMAC1Core.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBMAC1Core.java @@ -26,9 +26,7 @@ package com.sun.crypto.provider; import java.util.Arrays; -import java.nio.ByteBuffer; -import javax.crypto.MacSpi; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.PBEKeySpec; @@ -181,10 +179,7 @@ abstract class PBMAC1Core extends HmacCore { s = (PBKDF2KeyImpl)kdf.engineGenerateSecret(pbeSpec); derivedKey = s.getEncoded(); } catch (InvalidKeySpecException ikse) { - InvalidKeyException ike = - new InvalidKeyException("Cannot construct PBE key"); - ike.initCause(ikse); - throw ike; + throw new InvalidKeyException("Cannot construct PBE key", ikse); } finally { pbeSpec.clearPassword(); if (s != null) { diff --git a/src/java.base/share/classes/com/sun/crypto/provider/RSACipher.java b/src/java.base/share/classes/com/sun/crypto/provider/RSACipher.java index 196acef1511e3503afce76510bb6ecc7d30d95e9..f9c910e2ff1a8e2d928b4371f4301812753d8785 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/RSACipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/RSACipher.java @@ -211,10 +211,7 @@ public final class RSACipher extends CipherSpi { } catch (InvalidAlgorithmParameterException iape) { // never thrown when null parameters are used; // but re-throw it just in case - InvalidKeyException ike = - new InvalidKeyException("Wrong parameters"); - ike.initCause(iape); - throw ike; + throw new InvalidKeyException("Wrong parameters", iape); } } @@ -237,10 +234,7 @@ public final class RSACipher extends CipherSpi { params.getParameterSpec(OAEPParameterSpec.class); init(opmode, key, random, spec); } catch (InvalidParameterSpecException ipse) { - InvalidAlgorithmParameterException iape = - new InvalidAlgorithmParameterException("Wrong parameter"); - iape.initCause(ipse); - throw iape; + throw new InvalidAlgorithmParameterException("Wrong parameter", ipse); } } } diff --git a/src/java.base/share/classes/java/io/Console.java b/src/java.base/share/classes/java/io/Console.java index 683d836f85f2924a07a304fd92ea86c53309a2d6..c355710da792bbe29d93960496029ac55ffccac8 100644 --- a/src/java.base/share/classes/java/io/Console.java +++ b/src/java.base/share/classes/java/io/Console.java @@ -582,17 +582,12 @@ public final class Console implements Flushable csname = GetPropertyAction.privilegedGetProperty("sun.stdout.encoding"); } if (csname != null) { - try { - cs = Charset.forName(csname); - } catch (Exception ignored) { } + cs = Charset.forName(csname, null); } } if (cs == null) { - try { - cs = Charset.forName(StaticProperty.nativeEncoding()); - } catch (Exception ignored) { - cs = Charset.defaultCharset(); - } + cs = Charset.forName(StaticProperty.nativeEncoding(), + Charset.defaultCharset()); } CHARSET = cs; diff --git a/src/java.base/share/classes/java/io/FileInputStream.java b/src/java.base/share/classes/java/io/FileInputStream.java index 8f65ad6238c2c1483ed7206d01fd3f948c174e3f..c7b547d12a05f5d0ef3106c11f8e6e73523ac402 100644 --- a/src/java.base/share/classes/java/io/FileInputStream.java +++ b/src/java.base/share/classes/java/io/FileInputStream.java @@ -339,7 +339,7 @@ public class FileInputStream extends InputStream int n; do { n = read(buf, nread, remaining); - if (n > 0 ) { + if (n > 0) { nread += n; remaining -= n; } else if (n == 0) { diff --git a/src/java.base/share/classes/java/io/FilePermission.java b/src/java.base/share/classes/java/io/FilePermission.java index 83a1e98dc5598c6a7108d47af857b180b0699209..0371ef7e32acb93b2959b081350f19a38f09a57c 100644 --- a/src/java.base/share/classes/java/io/FilePermission.java +++ b/src/java.base/share/classes/java/io/FilePermission.java @@ -179,7 +179,7 @@ public final class FilePermission extends Permission implements Serializable { private static final char WILD_CHAR = '*'; // public String toString() { -// StringBuffer sb = new StringBuffer(); +// StringBuilder sb = new StringBuilder(); // sb.append("*** FilePermission on " + getName() + " ***"); // for (Field f : FilePermission.class.getDeclaredFields()) { // if (!Modifier.isStatic(f.getModifiers())) { diff --git a/src/java.base/share/classes/java/io/ObjectInputFilter.java b/src/java.base/share/classes/java/io/ObjectInputFilter.java index 50a3e76d5b0b3b3e7a81e75b42014a5cc758265e..f339411593d0978ac05df628e8a7859112ca7eb9 100644 --- a/src/java.base/share/classes/java/io/ObjectInputFilter.java +++ b/src/java.base/share/classes/java/io/ObjectInputFilter.java @@ -636,7 +636,9 @@ public interface ObjectInputFilter { filter = createFilter(filterString); } catch (RuntimeException re) { configLog.log(ERROR, - "Error configuring filter: {0}", re); + "Error configuring filter: {0}", (Object) re); + // Do not continue if configuration not initialized + throw re; } } serialFilter = filter; diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java index 315df0369490a5c18fb549d2f7cbed96a01acaaa..d58bf1d91c9460b02b8bb559c9867e913456f46e 100644 --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -145,8 +145,8 @@ import sun.security.action.GetIntegerAction; * entire graphs. * *

      Serializable classes that require special handling during the - * serialization and deserialization process should implement the following - * methods: + * serialization and deserialization process should implement methods + * with the following signatures: * *

        * private void writeObject(java.io.ObjectOutputStream stream)
      @@ -157,6 +157,12 @@ import sun.security.action.GetIntegerAction;
        *     throws ObjectStreamException;
        * 
      * + *

      The method name, modifiers, return type, and number and type of + * parameters must match exactly for the method to be used by + * serialization or deserialization. The methods should only be + * declared to throw checked exceptions consistent with these + * signatures. + * *

      The readObject method is responsible for reading and restoring the state * of the object for its particular class using data written to the stream by * the corresponding writeObject method. The method does not need to concern diff --git a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java index a64186c7ed7f9beb7da3c0d7af4e17333b7790a4..8d34fcec48396aa50ec167c4a814cb524b6a1bac 100644 --- a/src/java.base/share/classes/java/io/ObjectOutputStream.java +++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java @@ -83,9 +83,10 @@ import sun.reflect.misc.ReflectUtil; * oos.close(); * * - *

      Classes that require special handling during the serialization and - * deserialization process must implement special methods with these exact - * signatures: + *

      Serializable classes that require special handling during the + * serialization and deserialization process should implement methods + * with the following signatures: + * *
      *

        * private void readObject(java.io.ObjectInputStream stream)
      @@ -96,6 +97,12 @@ import sun.reflect.misc.ReflectUtil;
        *     throws ObjectStreamException;
        * 
      * + *

      The method name, modifiers, return type, and number and type of + * parameters must match exactly for the method to be used by + * serialization or deserialization. The methods should only be + * declared to throw checked exceptions consistent with these + * signatures. + * *

      The writeObject method is responsible for writing the state of the object * for its particular class so that the corresponding readObject method can * restore it. The method does not need to concern itself with the state diff --git a/src/java.base/share/classes/java/io/ObjectStreamClass.java b/src/java.base/share/classes/java/io/ObjectStreamClass.java index 1eff6c272781f0c973f8528043bd60d0afc24e66..82ba054f61f590e248a44076f6628bbff21d6010 100644 --- a/src/java.base/share/classes/java/io/ObjectStreamClass.java +++ b/src/java.base/share/classes/java/io/ObjectStreamClass.java @@ -1712,9 +1712,7 @@ public class ObjectStreamClass implements Serializable { } else if (th instanceof Error) { throw (Error) th; } else { - IOException ex = new IOException("unexpected exception type"); - ex.initCause(th); - throw ex; + throw new IOException("unexpected exception type", th); } } diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index 66c6c57d2d5646be8c92ee53eca1d3136b17f25b..fce54fcb3d1c6c47d6d293468311178ece75e17b 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -231,21 +231,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } } - /** - * The maximum size of array to allocate (unless necessary). - * Some VMs reserve some header words in an array. - * Attempts to allocate larger arrays may result in - * OutOfMemoryError: Requested array size exceeds VM limit - */ - private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; - /** * Returns a capacity at least as large as the given minimum capacity. * Returns the current capacity increased by the current length + 2 if * that suffices. * Will not return a capacity greater than - * {@code (MAX_ARRAY_SIZE >> coder)} unless the given minimum capacity - * is greater than that. + * {@code (SOFT_MAX_ARRAY_LENGTH >> coder)} + * unless the given minimum capacity is greater than that. * * @param minCapacity the desired minimum capacity * @throws OutOfMemoryError if minCapacity is less than zero or diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 8eb030482a0a021ca2b786c0e2df36d1aee6a3ed..7341aa56a6ac870f6d05dfbac3adca52675f58c5 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -1390,6 +1390,7 @@ public final class Math { * @see #multiplyHigh * @since 18 */ + @IntrinsicCandidate public static long unsignedMultiplyHigh(long x, long y) { // Compute via multiplyHigh() to leverage the intrinsic long result = Math.multiplyHigh(x, y); diff --git a/src/java.base/share/classes/java/lang/SecurityManager.java b/src/java.base/share/classes/java/lang/SecurityManager.java index 2facf5ff9e6a1efc76566cd3816836fef718c3c4..7414e1c4a969f3ab64a21784abafab89c640d1f2 100644 --- a/src/java.base/share/classes/java/lang/SecurityManager.java +++ b/src/java.base/share/classes/java/lang/SecurityManager.java @@ -28,7 +28,6 @@ package java.lang; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Exports; import java.lang.module.ModuleDescriptor.Opens; -import java.lang.reflect.Member; import java.io.FileDescriptor; import java.io.File; import java.io.FilePermission; @@ -48,7 +47,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import jdk.internal.module.ModuleLoaderMap; -import jdk.internal.reflect.CallerSensitive; import sun.security.util.SecurityConstants; /** @@ -77,7 +75,7 @@ import sun.security.util.SecurityConstants; * manager routine simply returns if the operation is permitted, but * throws a {@code SecurityException} if the operation is not * permitted. - *

      + *

      Setting a Security Manager

      * Environments using a security manager will typically set the security * manager at startup. In the JDK implementation, this is done by setting the * system property {@systemProperty java.security.manager} on the command line @@ -96,13 +94,13 @@ import sun.security.util.SecurityConstants; * {@link System#setSecurityManager(SecurityManager) setSecurityManager} method. * In the JDK implementation, if the Java virtual machine is started with * the {@code java.security.manager} system property set to the special token - * "{@code disallow}" then a security manager will not be set at startup and - * cannot be set dynamically (the + * "{@code allow}", then a security manager will not be set at startup but can + * be set dynamically. If the Java virtual machine is started with the + * {@code java.security.manager} system property not set or set to the special + * token "{@code disallow}", then a security manager will not be set at startup + * and cannot be set dynamically (the * {@link System#setSecurityManager(SecurityManager) setSecurityManager} - * method will throw an {@code UnsupportedOperationException}). If the - * {@code java.security.manager} system property is not set or is set to the - * special token "{@code allow}", then a security manager will not be set at - * startup but can be set dynamically. Finally, if the + * method will throw an {@code UnsupportedOperationException}). Finally, if the * {@code java.security.manager} system property is set to the class name of * the security manager, or to the empty String ("") or the special token * "{@code default}", then a security manager is set at startup (as described @@ -127,8 +125,7 @@ import sun.security.util.SecurityConstants; * * null * None - * Success or throws {@code SecurityException} if not permitted by - * the currently installed security manager + * Throws {@code UnsupportedOperationException} * * * @@ -148,7 +145,7 @@ import sun.security.util.SecurityConstants; * * "disallow" * None - * Always throws {@code UnsupportedOperationException} + * Throws {@code UnsupportedOperationException} * * * @@ -167,12 +164,10 @@ import sun.security.util.SecurityConstants; * * * - *

      A future release of the JDK may change the default value of the - * {@code java.security.manager} system property to "{@code disallow}". *

      * The current security manager is returned by the * {@link System#getSecurityManager() getSecurityManager} method. - *

      + *

      Checking Permissions

      * The special method * {@link SecurityManager#checkPermission(java.security.Permission)} * determines whether an access request indicated by a specified diff --git a/src/java.base/share/classes/java/lang/StringCoding.java b/src/java.base/share/classes/java/lang/StringCoding.java index c8d691675430e6bea78b2204f60f9bc513b91332..ec81c3795799f1f91a41d3c373053007e8b565cc 100644 --- a/src/java.base/share/classes/java/lang/StringCoding.java +++ b/src/java.base/share/classes/java/lang/StringCoding.java @@ -46,7 +46,7 @@ class StringCoding { @IntrinsicCandidate public static int implEncodeISOArray(byte[] sa, int sp, - byte[] da, int dp, int len) { + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = StringUTF16.getChar(sa, sp++); @@ -57,4 +57,18 @@ class StringCoding { return i; } + @IntrinsicCandidate + public static int implEncodeAsciiArray(char[] sa, int sp, + byte[] da, int dp, int len) + { + int i = 0; + for (; i < len; i++) { + char c = sa[sp++]; + if (c >= '\u0080') + break; + da[dp++] = (byte)c; + } + return i; + } + } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index edb636bfe210c6427f78bd4db0cfe85e667d3c66..fac6716243572516c6da2a351764ff0af73375f1 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -361,9 +361,11 @@ public final class System { * the method simply returns. * * @implNote In the JDK implementation, if the Java virtual machine is - * started with the system property {@code java.security.manager} set to + * started with the system property {@code java.security.manager} not set or set to * the special token "{@code disallow}" then the {@code setSecurityManager} - * method cannot be used to set a security manager. + * method cannot be used to set a security manager. See the following + * section of the + * {@code SecurityManager} class specification for more details. * * @param sm the security manager or {@code null} * @throws SecurityException @@ -2237,7 +2239,7 @@ public final class System { allowSecurityManager = MAYBE; } } else { - allowSecurityManager = MAYBE; + allowSecurityManager = NEVER; } if (needWarning) { @@ -2419,6 +2421,10 @@ public final class System { return String.decodeASCII(src, srcOff, dst, dstOff, len); } + public int encodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len) { + return StringCoding.implEncodeAsciiArray(src, srcOff, dst, dstOff, len); + } + public void setCause(Throwable t, Throwable cause) { t.setCause(cause); } diff --git a/src/java.base/share/classes/java/lang/Throwable.java b/src/java.base/share/classes/java/lang/Throwable.java index 26c584bc8651fab965beb9c02eaa85ae7d4b80f4..22ad94e0b4eea6a8ea0ca112e73b72aada8abd1b 100644 --- a/src/java.base/share/classes/java/lang/Throwable.java +++ b/src/java.base/share/classes/java/lang/Throwable.java @@ -608,12 +608,12 @@ public class Throwable implements Serializable { * *
            * Exception in thread "main" java.lang.Exception: Something happened
      -     *  at Foo.bar(Foo.java:10)
      -     *  at Foo.main(Foo.java:5)
      -     *  Suppressed: Resource$CloseFailException: Resource ID = 0
      -     *          at Resource.close(Resource.java:26)
      -     *          at Foo.bar(Foo.java:9)
      -     *          ... 1 more
      +     *         at Foo.bar(Foo.java:10)
      +     *         at Foo.main(Foo.java:5)
      +     *         Suppressed: Resource$CloseFailException: Resource ID = 0
      +     *                 at Resource.close(Resource.java:26)
      +     *                 at Foo.bar(Foo.java:9)
      +     *                 ... 1 more
            * 
      * Note that the "... n more" notation is used on suppressed exceptions * just as it is used on causes. Unlike causes, suppressed exceptions are @@ -623,26 +623,26 @@ public class Throwable implements Serializable { * exceptions: *
            * Exception in thread "main" java.lang.Exception: Main block
      -     *  at Foo3.main(Foo3.java:7)
      -     *  Suppressed: Resource$CloseFailException: Resource ID = 2
      -     *          at Resource.close(Resource.java:26)
      -     *          at Foo3.main(Foo3.java:5)
      -     *  Suppressed: Resource$CloseFailException: Resource ID = 1
      -     *          at Resource.close(Resource.java:26)
      -     *          at Foo3.main(Foo3.java:5)
      +     *         at Foo3.main(Foo3.java:7)
      +     *         Suppressed: Resource$CloseFailException: Resource ID = 2
      +     *                 at Resource.close(Resource.java:26)
      +     *                 at Foo3.main(Foo3.java:5)
      +     *         Suppressed: Resource$CloseFailException: Resource ID = 1
      +     *                 at Resource.close(Resource.java:26)
      +     *                 at Foo3.main(Foo3.java:5)
            * Caused by: java.lang.Exception: I did it
      -     *  at Foo3.main(Foo3.java:8)
      +     *         at Foo3.main(Foo3.java:8)
            * 
      * Likewise, a suppressed exception can have a cause: *
            * Exception in thread "main" java.lang.Exception: Main block
      -     *  at Foo4.main(Foo4.java:6)
      -     *  Suppressed: Resource2$CloseFailException: Resource ID = 1
      -     *          at Resource2.close(Resource2.java:20)
      -     *          at Foo4.main(Foo4.java:5)
      -     *  Caused by: java.lang.Exception: Rats, you caught me
      -     *          at Resource2$CloseFailException.<init>(Resource2.java:45)
      -     *          ... 2 more
      +     *         at Foo4.main(Foo4.java:6)
      +     *         Suppressed: Resource2$CloseFailException: Resource ID = 1
      +     *                 at Resource2.close(Resource2.java:20)
      +     *                 at Foo4.main(Foo4.java:5)
      +     *         Caused by: java.lang.Exception: Rats, you caught me
      +     *                 at Resource2$CloseFailException.<init>(Resource2.java:45)
      +     *                 ... 2 more
            * 
      */ public void printStackTrace() { diff --git a/src/java.base/share/classes/java/lang/constant/ClassDesc.java b/src/java.base/share/classes/java/lang/constant/ClassDesc.java index 435d992fa500c6ed57e78335dd0dfacd31630e75..14923594c0e1bc24cbfbce13bc91596dcd0495ac 100644 --- a/src/java.base/share/classes/java/lang/constant/ClassDesc.java +++ b/src/java.base/share/classes/java/lang/constant/ClassDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -99,7 +99,7 @@ public sealed interface ClassDesc } validateMemberName(requireNonNull(className), false); return ofDescriptor("L" + binaryToInternal(packageName) + - (packageName.length() > 0 ? "/" : "") + className + ";"); + "/" + className + ";"); } /** diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java index dd0a2417e19a97a258ab7a17aee716af61df931b..81491819d7e2f2ac0525859de4efcd6edc081ac7 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -258,7 +258,6 @@ class MethodHandleNatives { * The JVM is linking an invokedynamic instruction. Create a reified call site for it. */ static MemberName linkCallSite(Object callerObj, - int indexInCP, Object bootstrapMethodObj, Object nameObj, Object typeObj, Object staticArguments, @@ -317,7 +316,6 @@ class MethodHandleNatives { // this implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant: static Object linkDynamicConstant(Object callerObj, - int indexInCP, Object bootstrapMethodObj, Object nameObj, Object typeObj, Object staticArguments) { diff --git a/src/java.base/share/classes/java/lang/ref/Finalizer.java b/src/java.base/share/classes/java/lang/ref/Finalizer.java index 9b402caca4423db8d693b9f39d9245254ecea755..d5838b7a6b1b461d7dff80b400895ffbf089e606 100644 --- a/src/java.base/share/classes/java/lang/ref/Finalizer.java +++ b/src/java.base/share/classes/java/lang/ref/Finalizer.java @@ -86,6 +86,7 @@ final class Finalizer extends FinalReference { /* Package-private; must assert finalizee != null; if (!(finalizee instanceof java.lang.Enum)) { jla.invokeFinalize(finalizee); + reportComplete(finalizee); // Clear stack slot containing this variable, to decrease // the chances of false retention with a conservative GC @@ -95,6 +96,8 @@ final class Finalizer extends FinalReference { /* Package-private; must super.clear(); } + private static native void reportComplete(Object finalizee); + /* Create a privileged secondary finalizer thread in the system thread * group for the given Runnable, and wait for it to complete. * diff --git a/src/java.base/share/classes/java/lang/reflect/Constructor.java b/src/java.base/share/classes/java/lang/reflect/Constructor.java index eec4512b6320ef837195b302cd35b6d29b7435b4..400a8990f8d22f6b2f77e02b1b0b406d6e3cc47c 100644 --- a/src/java.base/share/classes/java/lang/reflect/Constructor.java +++ b/src/java.base/share/classes/java/lang/reflect/Constructor.java @@ -30,6 +30,7 @@ import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.ConstructorAccessor; import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; import sun.reflect.generics.repository.ConstructorRepository; @@ -62,10 +63,12 @@ import java.util.StringJoiner; * @since 1.1 */ public final class Constructor extends Executable { + @Stable private Class clazz; private int slot; private Class[] parameterTypes; private Class[] exceptionTypes; + @Stable private int modifiers; // Generics and annotations support private transient String signature; @@ -94,7 +97,8 @@ public final class Constructor extends Executable { return genericInfo; //return cached repository } - private volatile ConstructorAccessor constructorAccessor; + @Stable + private ConstructorAccessor constructorAccessor; // For sharing of ConstructorAccessors. This branching structure // is currently only two levels deep (i.e., one root Constructor // and potentially many Constructor objects pointing to it.) @@ -491,7 +495,7 @@ public final class Constructor extends Executable { if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); - ConstructorAccessor ca = constructorAccessor; // read volatile + ConstructorAccessor ca = constructorAccessor; // read @Stable if (ca == null) { ca = acquireConstructorAccessor(); } @@ -532,8 +536,8 @@ public final class Constructor extends Executable { private ConstructorAccessor acquireConstructorAccessor() { // First check to see if one has been created yet, and take it // if so. - ConstructorAccessor tmp = null; - if (root != null) tmp = root.getConstructorAccessor(); + Constructor root = this.root; + ConstructorAccessor tmp = root == null ? null : root.getConstructorAccessor(); if (tmp != null) { constructorAccessor = tmp; } else { @@ -556,6 +560,7 @@ public final class Constructor extends Executable { void setConstructorAccessor(ConstructorAccessor accessor) { constructorAccessor = accessor; // Propagate up + Constructor root = this.root; if (root != null) { root.setConstructorAccessor(accessor); } diff --git a/src/java.base/share/classes/java/lang/reflect/Field.java b/src/java.base/share/classes/java/lang/reflect/Field.java index 9cbca96942f7704d8331b00ed9445cd15c7ddbca..e8d0e68671b25f939affe3ae1aae7b2deec20072 100644 --- a/src/java.base/share/classes/java/lang/reflect/Field.java +++ b/src/java.base/share/classes/java/lang/reflect/Field.java @@ -30,6 +30,7 @@ import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.FieldAccessor; import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; import sun.reflect.generics.repository.FieldRepository; import sun.reflect.generics.factory.CoreReflectionFactory; import sun.reflect.generics.factory.GenericsFactory; @@ -65,12 +66,15 @@ import sun.reflect.annotation.TypeAnnotationParser; public final class Field extends AccessibleObject implements Member { + @Stable private Class clazz; private int slot; // This is guaranteed to be interned by the VM in the 1.4 // reflection implementation private String name; + @Stable private Class type; + @Stable private int modifiers; private boolean trustedFinal; // Generics and annotations support @@ -79,8 +83,10 @@ class Field extends AccessibleObject implements Member { private transient FieldRepository genericInfo; private byte[] annotations; // Cached field accessor created without override + @Stable private FieldAccessor fieldAccessor; // Cached field accessor created with override + @Stable private FieldAccessor overrideFieldAccessor; // For sharing of FieldAccessors. This branching structure is // currently only two levels deep (i.e., one root Field and @@ -421,8 +427,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().get(obj); + } else { + return getOverrideFieldAccessor().get(obj); } - return getFieldAccessor(obj).get(obj); } /** @@ -455,8 +463,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().getBoolean(obj); + } else { + return getOverrideFieldAccessor().getBoolean(obj); } - return getFieldAccessor(obj).getBoolean(obj); } /** @@ -489,8 +499,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().getByte(obj); + } else { + return getOverrideFieldAccessor().getByte(obj); } - return getFieldAccessor(obj).getByte(obj); } /** @@ -525,8 +537,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().getChar(obj); + } else { + return getOverrideFieldAccessor().getChar(obj); } - return getFieldAccessor(obj).getChar(obj); } /** @@ -561,8 +575,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().getShort(obj); + } else { + return getOverrideFieldAccessor().getShort(obj); } - return getFieldAccessor(obj).getShort(obj); } /** @@ -597,8 +613,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().getInt(obj); + } else { + return getOverrideFieldAccessor().getInt(obj); } - return getFieldAccessor(obj).getInt(obj); } /** @@ -633,8 +651,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().getLong(obj); + } else { + return getOverrideFieldAccessor().getLong(obj); } - return getFieldAccessor(obj).getLong(obj); } /** @@ -669,8 +689,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().getFloat(obj); + } else { + return getOverrideFieldAccessor().getFloat(obj); } - return getFieldAccessor(obj).getFloat(obj); } /** @@ -705,8 +727,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + return getFieldAccessor().getDouble(obj); + } else { + return getOverrideFieldAccessor().getDouble(obj); } - return getFieldAccessor(obj).getDouble(obj); } /** @@ -795,8 +819,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().set(obj, value); + } else { + getOverrideFieldAccessor().set(obj, value); } - getFieldAccessor(obj).set(obj, value); } /** @@ -832,8 +858,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().setBoolean(obj, z); + } else { + getOverrideFieldAccessor().setBoolean(obj, z); } - getFieldAccessor(obj).setBoolean(obj, z); } /** @@ -869,8 +897,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().setByte(obj, b); + } else { + getOverrideFieldAccessor().setByte(obj, b); } - getFieldAccessor(obj).setByte(obj, b); } /** @@ -906,8 +936,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().setChar(obj, c); + } else { + getOverrideFieldAccessor().setChar(obj, c); } - getFieldAccessor(obj).setChar(obj, c); } /** @@ -943,8 +975,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().setShort(obj, s); + } else { + getOverrideFieldAccessor().setShort(obj, s); } - getFieldAccessor(obj).setShort(obj, s); } /** @@ -980,8 +1014,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().setInt(obj, i); + } else { + getOverrideFieldAccessor().setInt(obj, i); } - getFieldAccessor(obj).setInt(obj, i); } /** @@ -1017,8 +1053,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().setLong(obj, l); + } else { + getOverrideFieldAccessor().setLong(obj, l); } - getFieldAccessor(obj).setLong(obj, l); } /** @@ -1054,8 +1092,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().setFloat(obj, f); + } else { + getOverrideFieldAccessor().setFloat(obj, f); } - getFieldAccessor(obj).setFloat(obj, f); } /** @@ -1091,8 +1131,10 @@ class Field extends AccessibleObject implements Member { if (!override) { Class caller = Reflection.getCallerClass(); checkAccess(caller, obj); + getFieldAccessor().setDouble(obj, d); + } else { + getOverrideFieldAccessor().setDouble(obj, d); } - getFieldAccessor(obj).setDouble(obj, d); } // check access to field @@ -1105,53 +1147,69 @@ class Field extends AccessibleObject implements Member { } // security check is done before calling this method - private FieldAccessor getFieldAccessor(Object obj) - throws IllegalAccessException - { - boolean ov = override; - FieldAccessor a = (ov) ? overrideFieldAccessor : fieldAccessor; - return (a != null) ? a : acquireFieldAccessor(ov); + private FieldAccessor getFieldAccessor() { + FieldAccessor a = fieldAccessor; + return (a != null) ? a : acquireFieldAccessor(); + } + + private FieldAccessor getOverrideFieldAccessor() { + FieldAccessor a = overrideFieldAccessor; + return (a != null) ? a : acquireOverrideFieldAccessor(); } // NOTE that there is no synchronization used here. It is correct // (though not efficient) to generate more than one FieldAccessor // for a given Field. However, avoiding synchronization will // probably make the implementation more scalable. - private FieldAccessor acquireFieldAccessor(boolean overrideFinalCheck) { + private FieldAccessor acquireFieldAccessor() { // First check to see if one has been created yet, and take it // if so - FieldAccessor tmp = null; - if (root != null) tmp = root.getFieldAccessor(overrideFinalCheck); + Field root = this.root; + FieldAccessor tmp = root == null ? null : root.fieldAccessor; if (tmp != null) { - if (overrideFinalCheck) - overrideFieldAccessor = tmp; - else - fieldAccessor = tmp; + fieldAccessor = tmp; } else { // Otherwise fabricate one and propagate it up to the root - tmp = reflectionFactory.newFieldAccessor(this, overrideFinalCheck); - setFieldAccessor(tmp, overrideFinalCheck); + tmp = reflectionFactory.newFieldAccessor(this, false); + setFieldAccessor(tmp); } + return tmp; + } + private FieldAccessor acquireOverrideFieldAccessor() { + // First check to see if one has been created yet, and take it + // if so + Field root = this.root; + FieldAccessor tmp = root == null ? null : root.overrideFieldAccessor; + if (tmp != null) { + overrideFieldAccessor = tmp; + } else { + // Otherwise fabricate one and propagate it up to the root + tmp = reflectionFactory.newFieldAccessor(this, true); + setOverrideFieldAccessor(tmp); + } return tmp; } - // Returns FieldAccessor for this Field object, not looking up - // the chain to the root - private FieldAccessor getFieldAccessor(boolean overrideFinalCheck) { - return (overrideFinalCheck)? overrideFieldAccessor : fieldAccessor; + // Sets the fieldAccessor for this Field object and + // (recursively) its root + private void setFieldAccessor(FieldAccessor accessor) { + fieldAccessor = accessor; + // Propagate up + Field root = this.root; + if (root != null) { + root.setFieldAccessor(accessor); + } } - // Sets the FieldAccessor for this Field object and + // Sets the overrideFieldAccessor for this Field object and // (recursively) its root - private void setFieldAccessor(FieldAccessor accessor, boolean overrideFinalCheck) { - if (overrideFinalCheck) - overrideFieldAccessor = accessor; - else - fieldAccessor = accessor; + private void setOverrideFieldAccessor(FieldAccessor accessor) { + overrideFieldAccessor = accessor; // Propagate up + Field root = this.root; if (root != null) { - root.setFieldAccessor(accessor, overrideFinalCheck); + root.setOverrideFieldAccessor(accessor); } } diff --git a/src/java.base/share/classes/java/lang/reflect/Method.java b/src/java.base/share/classes/java/lang/reflect/Method.java index dedf2d5dd91fd69b0c89fe372752111741b6e6e2..037c4c7008a2a91441651a20e3fccbaf7e6b5d41 100644 --- a/src/java.base/share/classes/java/lang/reflect/Method.java +++ b/src/java.base/share/classes/java/lang/reflect/Method.java @@ -85,7 +85,8 @@ public final class Method extends Executable { private byte[] annotations; private byte[] parameterAnnotations; private byte[] annotationDefault; - private volatile MethodAccessor methodAccessor; + @Stable + private MethodAccessor methodAccessor; // For sharing of MethodAccessors. This branching structure is // currently only two levels deep (i.e., one root Method and // potentially many Method objects pointing to it.) @@ -665,8 +666,8 @@ public final class Method extends Executable { private MethodAccessor acquireMethodAccessor() { // First check to see if one has been created yet, and take it // if so - MethodAccessor tmp = null; - if (root != null) tmp = root.getMethodAccessor(); + Method root = this.root; + MethodAccessor tmp = root == null ? null : root.getMethodAccessor(); if (tmp != null) { methodAccessor = tmp; } else { @@ -689,6 +690,7 @@ public final class Method extends Executable { void setMethodAccessor(MethodAccessor accessor) { methodAccessor = accessor; // Propagate up + Method root = this.root; if (root != null) { root.setMethodAccessor(accessor); } diff --git a/src/java.base/share/classes/java/net/Inet4AddressImpl.java b/src/java.base/share/classes/java/net/Inet4AddressImpl.java index 04c3273be55878e247d6c5586d19c3ce5b714458..4cbe28f998b1e1f022beb827cf7df80d2adc548d 100644 --- a/src/java.base/share/classes/java/net/Inet4AddressImpl.java +++ b/src/java.base/share/classes/java/net/Inet4AddressImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ import java.io.IOException; * * @since 1.4 */ -class Inet4AddressImpl implements InetAddressImpl { +final class Inet4AddressImpl implements InetAddressImpl { public native String getLocalHostName() throws UnknownHostException; public native InetAddress[] lookupAllHostAddr(String hostname) throws UnknownHostException; diff --git a/src/java.base/share/classes/java/net/Inet6AddressImpl.java b/src/java.base/share/classes/java/net/Inet6AddressImpl.java index fa03fca7bf3991fc7133807d4d17425b9450f949..f956a50e311229b900362aeb35c95a6f5b2b202f 100644 --- a/src/java.base/share/classes/java/net/Inet6AddressImpl.java +++ b/src/java.base/share/classes/java/net/Inet6AddressImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ import static java.net.InetAddress.PREFER_SYSTEM_VALUE; * * @since 1.4 */ -class Inet6AddressImpl implements InetAddressImpl { +final class Inet6AddressImpl implements InetAddressImpl { public native String getLocalHostName() throws UnknownHostException; diff --git a/src/java.base/share/classes/java/net/InetAddress.java b/src/java.base/share/classes/java/net/InetAddress.java index a0189aa6ae947327d67246cd3159a976d18b0cc2..b43c88e4f0e62150fb8badd31c49fbb3ea515e07 100644 --- a/src/java.base/share/classes/java/net/InetAddress.java +++ b/src/java.base/share/classes/java/net/InetAddress.java @@ -1668,51 +1668,6 @@ public class InetAddress implements java.io.Serializable { return impl.anyLocalAddress(); } - /* - * Load and instantiate an underlying impl class - */ - static InetAddressImpl loadImpl(String implName) { - Object impl = null; - - /* - * Property "impl.prefix" will be prepended to the classname - * of the implementation object we instantiate, to which we - * delegate the real work (like native methods). This - * property can vary across implementations of the java. - * classes. The default is an empty String "". - */ - String prefix = GetPropertyAction.privilegedGetProperty("impl.prefix", ""); - try { - @SuppressWarnings("deprecation") - Object tmp = Class.forName("java.net." + prefix + implName).newInstance(); - impl = tmp; - } catch (ClassNotFoundException e) { - System.err.println("Class not found: java.net." + prefix + - implName + ":\ncheck impl.prefix property " + - "in your properties file."); - } catch (InstantiationException e) { - System.err.println("Could not instantiate: java.net." + prefix + - implName + ":\ncheck impl.prefix property " + - "in your properties file."); - } catch (IllegalAccessException e) { - System.err.println("Cannot access class: java.net." + prefix + - implName + ":\ncheck impl.prefix property " + - "in your properties file."); - } - - if (impl == null) { - try { - @SuppressWarnings("deprecation") - Object tmp = Class.forName(implName).newInstance(); - impl = tmp; - } catch (Exception e) { - throw new Error("System property impl.prefix incorrect"); - } - } - - return (InetAddressImpl) impl; - } - /** * Initializes an empty InetAddress. */ @@ -1793,8 +1748,8 @@ public class InetAddress implements java.io.Serializable { class InetAddressImplFactory { static InetAddressImpl create() { - return InetAddress.loadImpl(isIPv6Supported() ? - "Inet6AddressImpl" : "Inet4AddressImpl"); + return isIPv6Supported() ? + new Inet6AddressImpl() : new Inet4AddressImpl(); } static native boolean isIPv6Supported(); diff --git a/src/java.base/share/classes/java/net/InetAddressImpl.java b/src/java.base/share/classes/java/net/InetAddressImpl.java index bb67962d844f869fe320134432de26cf024d807e..a2f8ea010528e11d736f0be388a034f4ad66383a 100644 --- a/src/java.base/share/classes/java/net/InetAddressImpl.java +++ b/src/java.base/share/classes/java/net/InetAddressImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ import java.io.IOException; * * @since 1.4 */ -interface InetAddressImpl { +sealed interface InetAddressImpl permits Inet4AddressImpl, Inet6AddressImpl { String getLocalHostName() throws UnknownHostException; InetAddress[] diff --git a/src/java.base/share/classes/java/net/SocketPermission.java b/src/java.base/share/classes/java/net/SocketPermission.java index b28969d144d4b04e2bb32979d81bbfa94dba8a59..a3101c50ca368bdf0f1437ff066b6da2b7645149 100644 --- a/src/java.base/share/classes/java/net/SocketPermission.java +++ b/src/java.base/share/classes/java/net/SocketPermission.java @@ -1297,7 +1297,7 @@ public final class SocketPermission extends Permission /* public String toString() { - StringBuffer s = new StringBuffer(super.toString() + "\n" + + StringBuilder s = new StringBuilder(super.toString() + "\n" + "cname = " + cname + "\n" + "wildcard = " + wildcard + "\n" + "invalid = " + invalid + "\n" + diff --git a/src/java.base/share/classes/java/net/URI.java b/src/java.base/share/classes/java/net/URI.java index 5df726966dbc245dcbe807415383a13cb36f0382..0485e97e4f7f6f4b9b19208bb8baed945eaea44b 100644 --- a/src/java.base/share/classes/java/net/URI.java +++ b/src/java.base/share/classes/java/net/URI.java @@ -2207,7 +2207,6 @@ public final class URI ru.authority = child.authority; ru.host = child.host; ru.userInfo = child.userInfo; - ru.host = child.host; ru.port = child.port; ru.path = child.path; } @@ -3490,14 +3489,12 @@ public final class URI if (q <= p) break; l = p; + p = q; + q = scan(p, n, L_ALPHANUM | L_DASH, H_ALPHANUM | H_DASH); if (q > p) { + if (input.charAt(q - 1) == '-') + fail("Illegal character in hostname", q - 1); p = q; - q = scan(p, n, L_ALPHANUM | L_DASH, H_ALPHANUM | H_DASH); - if (q > p) { - if (input.charAt(q - 1) == '-') - fail("Illegal character in hostname", q - 1); - p = q; - } } q = scan(p, n, '.'); if (q <= p) diff --git a/src/java.base/share/classes/java/net/URL.java b/src/java.base/share/classes/java/net/URL.java index 35d9503df19122e1e4bbb3a594af0002b59937d5..4ab1f5740a330c8dc37695e2dd2f8d1d7ee3871d 100644 --- a/src/java.base/share/classes/java/net/URL.java +++ b/src/java.base/share/classes/java/net/URL.java @@ -1762,7 +1762,7 @@ final class UrlDeserializedState { String reconstituteUrlString() { - // pre-compute length of StringBuffer + // pre-compute length of StringBuilder int len = protocol.length() + 1; if (authority != null && !authority.isEmpty()) len += 2 + authority.length(); diff --git a/src/java.base/share/classes/java/net/URLClassLoader.java b/src/java.base/share/classes/java/net/URLClassLoader.java index 30f0040195ac4e7b123f959ec17ec8dbf4cc56ba..97c95bc9f597b2d9c39fdadfe12faa351dd2933f 100644 --- a/src/java.base/share/classes/java/net/URLClassLoader.java +++ b/src/java.base/share/classes/java/net/URLClassLoader.java @@ -427,6 +427,11 @@ public class URLClassLoader extends SecureClassLoader implements Closeable { return defineClass(name, res); } catch (IOException e) { throw new ClassNotFoundException(name, e); + } catch (ClassFormatError e2) { + if (res.getDataError() != null) { + e2.addSuppressed(res.getDataError()); + } + throw e2; } } else { return null; diff --git a/src/java.base/share/classes/java/nio/channels/Channels.java b/src/java.base/share/classes/java/nio/channels/Channels.java index 900b4336e25bb614b45d47b0d60714a82f050507..2d1637c53694973f6b193fd337080198cc0ac118 100644 --- a/src/java.base/share/classes/java/nio/channels/Channels.java +++ b/src/java.base/share/classes/java/nio/channels/Channels.java @@ -295,6 +295,9 @@ public final class Channels { if (!isOpen()) { throw new ClosedChannelException(); } + if (dst.isReadOnly()) { + throw new IllegalArgumentException(); + } int len = dst.remaining(); int totalRead = 0; diff --git a/src/java.base/share/classes/java/nio/charset/Charset.java b/src/java.base/share/classes/java/nio/charset/Charset.java index f44ae78bae5f6550aa6d80338b4746160c39315e..e2afa2bd62ed741dcf37e1fb4791f9ee243d34e0 100644 --- a/src/java.base/share/classes/java/nio/charset/Charset.java +++ b/src/java.base/share/classes/java/nio/charset/Charset.java @@ -527,6 +527,39 @@ public abstract class Charset throw new UnsupportedCharsetException(charsetName); } + /** + * Returns a charset object for the named charset. If the charset object + * for the named charset is not available or {@code charsetName} is not a + * legal charset name, then {@code fallback} is returned. + * + * @param charsetName + * The name of the requested charset; may be either + * a canonical name or an alias + * + * @param fallback + * fallback charset in case the charset object for the named + * charset is not available or {@code charsetName} is not a legal + * charset name. May be {@code null} + * + * @return A charset object for the named charset, or {@code fallback} + * in case the charset object for the named charset is not + * available or {@code charsetName} is not a legal charset name + * + * @throws IllegalArgumentException + * If the given {@code charsetName} is {@code null} + * + * @since 18 + */ + public static Charset forName(String charsetName, + Charset fallback) { + try { + Charset cs = lookup(charsetName); + return cs != null ? cs : fallback; + } catch (IllegalCharsetNameException icne) { + return fallback; + } + } + // Fold charsets from the given iterator into the given map, ignoring // charsets whose names already have entries in the map. // diff --git a/src/java.base/share/classes/java/nio/file/Files.java b/src/java.base/share/classes/java/nio/file/Files.java index d78773db4752c514a38b68604d49cbee1e7c17f7..98efa051d11841d4cd00f2b0636dbc2db31d87b2 100644 --- a/src/java.base/share/classes/java/nio/file/Files.java +++ b/src/java.base/share/classes/java/nio/file/Files.java @@ -2514,6 +2514,7 @@ public final class Files { * read access to the file. * * @see #notExists + * @see FileSystemProvider#checkAccess */ public static boolean exists(Path path, LinkOption... options) { if (options.length == 0) { @@ -3110,7 +3111,7 @@ public final class Files { * @throws DirectoryNotEmptyException * the {@code REPLACE_EXISTING} option is specified but the file * cannot be replaced because it is a non-empty directory - * (optional specific exception) * + * (optional specific exception) * @throws UnsupportedOperationException * if {@code options} contains a copy option that is not supported * @throws SecurityException diff --git a/src/java.base/share/classes/java/nio/file/Path.java b/src/java.base/share/classes/java/nio/file/Path.java index d9d2cfb32a50f6230eeee93b1a8cf1e24e6aaba5..d7446f53c2be007fbdc816db6158ab58ea2073ed 100644 --- a/src/java.base/share/classes/java/nio/file/Path.java +++ b/src/java.base/share/classes/java/nio/file/Path.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -911,7 +911,7 @@ public interface Path * {@code getName(index)}, where {@code index} ranges from zero to * {@code getNameCount() - 1}, inclusive. * - * @return an iterator over the name elements of this path. + * @return an iterator over the name elements of this path */ @Override default Iterator iterator() { diff --git a/src/java.base/share/classes/java/security/cert/TrustAnchor.java b/src/java.base/share/classes/java/security/cert/TrustAnchor.java index 2fee634ec41e70327b2b1ed55b8cb43bc6d7d9d2..f026bea41790b726be764ceb703003e3e37500cf 100644 --- a/src/java.base/share/classes/java/security/cert/TrustAnchor.java +++ b/src/java.base/share/classes/java/security/cert/TrustAnchor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,6 @@ import javax.security.auth.x500.X500Principal; import sun.security.util.AnchorCertificates; import sun.security.x509.NameConstraintsExtension; -import sun.security.x509.X500Name; /** * A trust anchor or most-trusted Certification Authority (CA). @@ -286,10 +285,7 @@ public class TrustAnchor { try { nc = new NameConstraintsExtension(Boolean.FALSE, bytes); } catch (IOException ioe) { - IllegalArgumentException iae = - new IllegalArgumentException(ioe.getMessage()); - iae.initCause(ioe); - throw iae; + throw new IllegalArgumentException(ioe.getMessage(), ioe); } } } diff --git a/src/java.base/share/classes/java/security/cert/X509CRLSelector.java b/src/java.base/share/classes/java/security/cert/X509CRLSelector.java index ad2947c58855db100f841fdf1390f1400a435442..444100cbdccd1c105ac8db164c1ec8565d452e7f 100644 --- a/src/java.base/share/classes/java/security/cert/X509CRLSelector.java +++ b/src/java.base/share/classes/java/security/cert/X509CRLSelector.java @@ -371,7 +371,7 @@ public class X509CRLSelector implements CRLSelector { try { x500Principals.add(new X500Principal((byte[])nameObject)); } catch (IllegalArgumentException e) { - throw (IOException)new IOException("Invalid name").initCause(e); + throw new IOException("Invalid name", e); } } } diff --git a/src/java.base/share/classes/java/text/AttributedString.java b/src/java.base/share/classes/java/text/AttributedString.java index 087753429c063a2bd25c51dd0fccf0cd670baa6e..48044b0ea5d1abed88ade230df1d80b6bbca4e6f 100644 --- a/src/java.base/share/classes/java/text/AttributedString.java +++ b/src/java.base/share/classes/java/text/AttributedString.java @@ -78,7 +78,7 @@ public class AttributedString { } else { // Build the String contents - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); for (int counter = 0; counter < iterators.length; counter++) { appendContents(buffer, iterators[counter]); } @@ -668,9 +668,9 @@ public class AttributedString { /** * Appends the contents of the CharacterIterator iterator into the - * StringBuffer buf. + * StringBuilder buf. */ - private final void appendContents(StringBuffer buf, + private final void appendContents(StringBuilder buf, CharacterIterator iterator) { int index = iterator.getBeginIndex(); int end = iterator.getEndIndex(); diff --git a/src/java.base/share/classes/java/text/ChoiceFormat.java b/src/java.base/share/classes/java/text/ChoiceFormat.java index dc1037e231b4ae5d0f5582b2c821e6a1f43866c4..40fd2ad7800046065c090d272c1efe716f5e5979 100644 --- a/src/java.base/share/classes/java/text/ChoiceFormat.java +++ b/src/java.base/share/classes/java/text/ChoiceFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -178,9 +178,9 @@ public class ChoiceFormat extends NumberFormat { * is {@code null} */ public void applyPattern(String newPattern) { - StringBuffer[] segments = new StringBuffer[2]; + StringBuilder[] segments = new StringBuilder[2]; for (int i = 0; i < segments.length; ++i) { - segments[i] = new StringBuffer(); + segments[i] = new StringBuilder(); } double[] newChoiceLimits = new double[30]; String[] newChoiceFormats = new String[30]; diff --git a/src/java.base/share/classes/java/text/CompactNumberFormat.java b/src/java.base/share/classes/java/text/CompactNumberFormat.java index cb94ecb80904c1bb344901cf70623c55314e80eb..0449133d5b8a237bcf0562546cb0e39beafd2c1b 100644 --- a/src/java.base/share/classes/java/text/CompactNumberFormat.java +++ b/src/java.base/share/classes/java/text/CompactNumberFormat.java @@ -1264,8 +1264,8 @@ public final class CompactNumberFormat extends NumberFormat { String zeros = ""; for (int j = 1; j >= 0 && start < pattern.length(); --j) { - StringBuffer prefix = new StringBuffer(); - StringBuffer suffix = new StringBuffer(); + StringBuilder prefix = new StringBuilder(); + StringBuilder suffix = new StringBuilder(); boolean inQuote = false; // The phase ranges from 0 to 2. Phase 0 is the prefix. Phase 1 is // the section of the pattern with digits. Phase 2 is the suffix. @@ -1275,7 +1275,7 @@ public final class CompactNumberFormat extends NumberFormat { int phase = 0; // The affix is either the prefix or the suffix. - StringBuffer affix = prefix; + StringBuilder affix = prefix; for (int pos = start; pos < pattern.length(); ++pos) { char ch = pattern.charAt(pos); diff --git a/src/java.base/share/classes/java/text/DecimalFormat.java b/src/java.base/share/classes/java/text/DecimalFormat.java index ec6d1b84d48940af846e348de291f3a064addc54..ea125b7d14bbb938756055fda5e351adda03f5b5 100644 --- a/src/java.base/share/classes/java/text/DecimalFormat.java +++ b/src/java.base/share/classes/java/text/DecimalFormat.java @@ -2961,8 +2961,8 @@ public class DecimalFormat extends NumberFormat { * the expanded affix strings up to date. */ private void expandAffixes() { - // Reuse one StringBuffer for better performance - StringBuffer buffer = new StringBuffer(); + // Reuse one StringBuilder for better performance + StringBuilder buffer = new StringBuilder(); if (posPrefixPattern != null) { positivePrefix = expandAffix(posPrefixPattern, buffer); positivePrefixFieldPositions = null; @@ -2992,10 +2992,10 @@ public class DecimalFormat extends NumberFormat { * itself at the end of the pattern. * * @param pattern the non-null, possibly empty pattern - * @param buffer a scratch StringBuffer; its contents will be lost + * @param buffer a scratch StringBuilder; its contents will be lost * @return the expanded equivalent of pattern */ - private String expandAffix(String pattern, StringBuffer buffer) { + private String expandAffix(String pattern, StringBuilder buffer) { buffer.setLength(0); for (int i=0; i= 0 @@ -3198,7 +3198,7 @@ public class DecimalFormat extends NumberFormat { /** * Does the real work of generating a pattern. */ private String toPattern(boolean localized) { - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); for (int j = 1; j >= 0; --j) { if (j == 1) appendAffix(result, posPrefixPattern, positivePrefix, localized); @@ -3341,8 +3341,8 @@ public class DecimalFormat extends NumberFormat { int start = 0; for (int j = 1; j >= 0 && start < pattern.length(); --j) { boolean inQuote = false; - StringBuffer prefix = new StringBuffer(); - StringBuffer suffix = new StringBuffer(); + StringBuilder prefix = new StringBuilder(); + StringBuilder suffix = new StringBuilder(); int decimalPos = -1; int multiplier = 1; int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0; @@ -3358,7 +3358,7 @@ public class DecimalFormat extends NumberFormat { int phase = 0; // The affix is either the prefix or the suffix. - StringBuffer affix = prefix; + StringBuilder affix = prefix; for (int pos = start; pos < pattern.length(); ++pos) { char ch = pattern.charAt(pos); diff --git a/src/java.base/share/classes/java/text/RBTableBuilder.java b/src/java.base/share/classes/java/text/RBTableBuilder.java index d8072d56da7f26e0edb7f4bd2712b8a85ad9d51d..4aea5e781c68ed2320d9c95c44b58c97e0213bc9 100644 --- a/src/java.base/share/classes/java/text/RBTableBuilder.java +++ b/src/java.base/share/classes/java/text/RBTableBuilder.java @@ -401,7 +401,7 @@ final class RBTableBuilder { // can work right if (fwd && groupChars.length() > 1) { addContractFlags(groupChars); - addContractOrder(new StringBuffer(groupChars).reverse().toString(), + addContractOrder(new StringBuilder(groupChars).reverse().toString(), anOrder, false); } } diff --git a/src/java.base/share/classes/java/time/Duration.java b/src/java.base/share/classes/java/time/Duration.java index e998d04eecdb7528344ae477afc4139140e7e9e5..6455d34d6702d6cae60d4fb93d7dd9746b057c0e 100644 --- a/src/java.base/share/classes/java/time/Duration.java +++ b/src/java.base/share/classes/java/time/Duration.java @@ -411,7 +411,7 @@ public final class Duration try { return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos); } catch (ArithmeticException ex) { - throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0).initCause(ex); + throw new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0, ex); } } } @@ -432,7 +432,7 @@ public final class Duration long val = Long.parseLong(text, start, end, 10); return Math.multiplyExact(val, multiplier); } catch (NumberFormatException | ArithmeticException ex) { - throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0).initCause(ex); + throw new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0, ex); } } @@ -451,7 +451,7 @@ public final class Duration } return fraction * negate; } catch (NumberFormatException | ArithmeticException ex) { - throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0).initCause(ex); + throw new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0, ex); } } diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index 5eec62ddd752ba8ef7ba1c6127ea5bbd25c68734..b68de65f15f6285c97c7c166bfd9c490359b4328 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -5182,6 +5182,16 @@ public class Collections { return element; } + @Override + public void forEach(Consumer action) { + Objects.requireNonNull(action); + int n = this.n; + E element = this.element; + for (int i = 0; i < n; i++) { + action.accept(element); + } + } + public Object[] toArray() { final Object[] a = new Object[n]; if (element != null) diff --git a/src/java.base/share/classes/java/util/DualPivotQuicksort.java b/src/java.base/share/classes/java/util/DualPivotQuicksort.java index ac96b34c5fb32d21380c5a80d34bac40e50e7b3a..3dcc7fee1f5258534916e9a449428ae2c4ceff3b 100644 --- a/src/java.base/share/classes/java/util/DualPivotQuicksort.java +++ b/src/java.base/share/classes/java/util/DualPivotQuicksort.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3997,6 +3997,7 @@ final class DualPivotQuicksort { */ private static final class Sorter extends CountedCompleter { private static final long serialVersionUID = 20180818L; + @SuppressWarnings("serial") private final Object a, b; private final int low, size, offset, depth; @@ -4066,6 +4067,7 @@ final class DualPivotQuicksort { */ private static final class Merger extends CountedCompleter { private static final long serialVersionUID = 20180818L; + @SuppressWarnings("serial") private final Object dst, a1, a2; private final int k, lo1, hi1, lo2, hi2; @@ -4115,6 +4117,7 @@ final class DualPivotQuicksort { */ private static final class RunMerger extends RecursiveTask { private static final long serialVersionUID = 20180818L; + @SuppressWarnings("serial") private final Object a, b; private final int[] run; private final int offset, aim, lo, hi; diff --git a/src/java.base/share/classes/java/util/HashMap.java b/src/java.base/share/classes/java/util/HashMap.java index 9715b6ea9b13b5687d96982d5a7c3f15728aaee7..24fcd9516ca475245ffb470f79849faa52eb14f4 100644 --- a/src/java.base/share/classes/java/util/HashMap.java +++ b/src/java.base/share/classes/java/util/HashMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ package java.util; import java.io.IOException; import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -1504,23 +1505,28 @@ public class HashMap extends AbstractMap * @throws IOException if an I/O error occurs */ @java.io.Serial - private void readObject(java.io.ObjectInputStream s) + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { - // Read in the threshold (ignored), loadfactor, and any hidden stuff - s.defaultReadObject(); + + ObjectInputStream.GetField fields = s.readFields(); + + // Read loadFactor (ignore threshold) + float lf = fields.get("loadFactor", 0.75f); + if (lf <= 0 || Float.isNaN(lf)) + throw new InvalidObjectException("Illegal load factor: " + lf); + + lf = Math.min(Math.max(0.25f, lf), 4.0f); + HashMap.UnsafeHolder.putLoadFactor(this, lf); + reinitialize(); - if (loadFactor <= 0 || Float.isNaN(loadFactor)) - throw new InvalidObjectException("Illegal load factor: " + - loadFactor); + s.readInt(); // Read and ignore number of buckets int mappings = s.readInt(); // Read number of mappings (size) - if (mappings < 0) - throw new InvalidObjectException("Illegal mappings count: " + - mappings); - else if (mappings > 0) { // (if zero, use defaults) - // Size the table using given load factor only if within - // range of 0.25...4.0 - float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f); + if (mappings < 0) { + throw new InvalidObjectException("Illegal mappings count: " + mappings); + } else if (mappings == 0) { + // use defaults + } else if (mappings > 0) { float fc = (float)mappings / lf + 1.0f; int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ? DEFAULT_INITIAL_CAPACITY : @@ -1549,6 +1555,18 @@ public class HashMap extends AbstractMap } } + // Support for resetting final field during deserializing + private static final class UnsafeHolder { + private UnsafeHolder() { throw new InternalError(); } + private static final jdk.internal.misc.Unsafe unsafe + = jdk.internal.misc.Unsafe.getUnsafe(); + private static final long LF_OFFSET + = unsafe.objectFieldOffset(HashMap.class, "loadFactor"); + static void putLoadFactor(HashMap map, float lf) { + unsafe.putFloat(map, LF_OFFSET, lf); + } + } + /* ------------------------------------------------------------ */ // iterators diff --git a/src/java.base/share/classes/java/util/HashSet.java b/src/java.base/share/classes/java/util/HashSet.java index d370e5d67104f21fc227d26e478f9c6c55531937..e28fc6d5c21dded93a3d36ee779a6dd9ebdc2094 100644 --- a/src/java.base/share/classes/java/util/HashSet.java +++ b/src/java.base/share/classes/java/util/HashSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -297,8 +297,8 @@ public class HashSet @java.io.Serial private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { - // Read in any hidden serialization magic - s.defaultReadObject(); + // Consume and ignore stream fields (currently zero). + s.readFields(); // Read capacity and verify non-negative. int capacity = s.readInt(); @@ -313,12 +313,13 @@ public class HashSet throw new InvalidObjectException("Illegal load factor: " + loadFactor); } + // Clamp load factor to range of 0.25...4.0. + loadFactor = Math.min(Math.max(0.25f, loadFactor), 4.0f); // Read size and verify non-negative. int size = s.readInt(); if (size < 0) { - throw new InvalidObjectException("Illegal size: " + - size); + throw new InvalidObjectException("Illegal size: " + size); } // Set the capacity according to the size and load factor ensuring that diff --git a/src/java.base/share/classes/java/util/Properties.java b/src/java.base/share/classes/java/util/Properties.java index 06999bc7bf37740f7a78070f7bbc57f6fa52a320..789710d6a76399c78baecf9f99ee0667cce5fc1f 100644 --- a/src/java.base/share/classes/java/util/Properties.java +++ b/src/java.base/share/classes/java/util/Properties.java @@ -46,6 +46,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; +import jdk.internal.util.StaticProperty; import sun.nio.cs.ISO_8859_1; import sun.nio.cs.UTF_8; @@ -807,17 +808,25 @@ public class Properties extends Hashtable { * If the comments argument is not null, then an ASCII {@code #} * character, the comments string, and a line separator are first written * to the output stream. Thus, the {@code comments} can serve as an - * identifying comment. Any one of a line feed ('\n'), a carriage - * return ('\r'), or a carriage return followed immediately by a line feed - * in comments is replaced by a line separator generated by the {@code Writer} - * and if the next character in comments is not character {@code #} or - * character {@code !} then an ASCII {@code #} is written out - * after that line separator. + * identifying comment. Any one of a line feed ({@code \n}), a carriage + * return ({@code \r}), or a carriage return followed immediately by a line feed + * ({@code \r\n}) in comments is replaced by a + * {@link System#lineSeparator() line separator} and if the next + * character in comments is not character {@code #} or character {@code !} then + * an ASCII {@code #} is written out after that line separator. *

      - * Next, a comment line is always written, consisting of an ASCII - * {@code #} character, the current date and time (as if produced - * by the {@code toString} method of {@code Date} for the - * current time), and a line separator as generated by the {@code Writer}. + * If the {@systemProperty java.properties.date} is set on the command line + * and is non-empty (as determined by {@link String#isEmpty() String.isEmpty}), + * a comment line is written as follows. + * First, a {@code #} character is written, followed by the contents + * of the property, followed by a line separator. Any line terminator characters + * in the value of the system property are treated the same way as noted above + * for the comments argument. + * If the system property is not set or is empty, a comment line is written + * as follows. + * First, a {@code #} character is written, followed by the current date and time + * formatted as if by the {@link Date#toString() Date.toString} method, + * followed by a line separator. *

      * Then every entry in this {@code Properties} table is * written out, one per line. For each entry the key string is @@ -833,6 +842,10 @@ public class Properties extends Hashtable { * After the entries have been written, the output stream is flushed. * The output stream remains open after this method returns. * + * @implSpec The keys and elements are written in the natural sort order + * of the keys in the {@code entrySet()} unless {@code entrySet()} is + * overridden by a subclass to return a different value than {@code super.entrySet()}. + * * @param writer an output character stream writer. * @param comments a description of the property list. * @throws IOException if writing this property list to the specified @@ -903,12 +916,25 @@ public class Properties extends Hashtable { if (comments != null) { writeComments(bw, comments); } - bw.write("#" + new Date().toString()); - bw.newLine(); + writeDateComment(bw); + synchronized (this) { - for (Map.Entry e : entrySet()) { - String key = (String)e.getKey(); - String val = (String)e.getValue(); + @SuppressWarnings("unchecked") + Collection> entries = (Set>) (Set) entrySet(); + // entrySet() can be overridden by subclasses. Here we check to see if + // the returned instance type is the one returned by the Properties.entrySet() + // implementation. If yes, then we sort those entries in the natural order + // of their key. Else, we consider that the subclassed implementation may + // potentially have returned a differently ordered entries and so we just + // use the iteration order of the returned instance. + if (entries instanceof Collections.SynchronizedSet ss + && ss.c instanceof EntrySet) { + entries = new ArrayList<>(entries); + ((List>) entries).sort(Map.Entry.comparingByKey()); + } + for (Map.Entry e : entries) { + String key = e.getKey(); + String val = e.getValue(); key = saveConvert(key, true, escUnicode); /* No need to escape embedded and trailing spaces for value, hence * pass false to flag. @@ -921,6 +947,19 @@ public class Properties extends Hashtable { bw.flush(); } + private static void writeDateComment(BufferedWriter bw) throws IOException { + // value of java.properties.date system property isn't sensitive + // and so doesn't need any security manager checks to make the value accessible + // to the callers + String sysPropVal = StaticProperty.javaPropertiesDate(); + if (sysPropVal != null && !sysPropVal.isEmpty()) { + writeComments(bw, sysPropVal); + } else { + bw.write("#" + new Date()); + bw.newLine(); + } + } + /** * Loads all of the properties represented by the XML document on the * specified input stream into this properties table. diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java index 51b16304506f82a0ab43ceaf0f4e85042f9afca8..6ef1b0815e5e5f99d38ac2ecde4c33904a7a4ad6 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java @@ -2561,7 +2561,7 @@ public class ForkJoinPool extends AbstractExecutorService { * overridden by system properties */ private ForkJoinPool(byte forCommonPoolOnly) { - int parallelism = Runtime.getRuntime().availableProcessors() - 1; + int parallelism = Math.max(1, Runtime.getRuntime().availableProcessors() - 1); ForkJoinWorkerThreadFactory fac = null; UncaughtExceptionHandler handler = null; try { // ignore exceptions in accessing/parsing properties @@ -2778,6 +2778,7 @@ public class ForkJoinPool extends AbstractExecutorService { @SuppressWarnings("serial") // Conditionally serializable volatile E result; final AtomicInteger count; // in case all throw + @SuppressWarnings("serial") final ForkJoinPool pool; // to check shutdown while collecting InvokeAnyRoot(int n, ForkJoinPool p) { pool = p; diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java index fc1bd07ea74049039dddf00b2831b5fb7a1b5756..0fe3d12aaf35b17499f632575269b8277485d80d 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java @@ -1444,8 +1444,8 @@ public abstract class ForkJoinTask implements Future, Serializable { implements RunnableFuture { @SuppressWarnings("serial") // Conditionally serializable final Callable callable; - @SuppressWarnings("serial") // Conditionally serializable transient volatile Thread runner; + @SuppressWarnings("serial") // Conditionally serializable T result; AdaptedInterruptibleCallable(Callable callable) { if (callable == null) throw new NullPointerException(); diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java index 703171a631f8bcf86940e77640aaaaa0b18de19b..f0cd0e5c3820fb7ed0bcb57aa08d8455cc8e7e54 100644 --- a/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/src/java.base/share/classes/java/util/jar/JarFile.java @@ -749,7 +749,7 @@ public class JarFile extends ZipFile { } if (mev == null) { mev = new ManifestEntryVerifier - (getManifestFromReference()); + (getManifestFromReference(), jv.manifestName); } if (name.equalsIgnoreCase(MANIFEST_NAME)) { b = jv.manifestRawBytes; @@ -801,7 +801,7 @@ public class JarFile extends ZipFile { try (InputStream is = super.getInputStream(ze)) { long uncompressedSize = ze.getSize(); if (uncompressedSize > MAX_ARRAY_SIZE) { - throw new OutOfMemoryError("Required array size too large"); + throw new IOException("Unsupported size: " + uncompressedSize); } int len = (int)uncompressedSize; int bytesRead; diff --git a/src/java.base/share/classes/java/util/jar/JarInputStream.java b/src/java.base/share/classes/java/util/jar/JarInputStream.java index 3d8c74df683d2265566f2f8e21e65e3ac8f1380e..eb51942173230f5a902583d094b29025df7ae200 100644 --- a/src/java.base/share/classes/java/util/jar/JarInputStream.java +++ b/src/java.base/share/classes/java/util/jar/JarInputStream.java @@ -95,7 +95,7 @@ public class JarInputStream extends ZipInputStream { closeEntry(); if (doVerify) { jv = new JarVerifier(e.getName(), bytes); - mev = new ManifestEntryVerifier(man); + mev = new ManifestEntryVerifier(man, jv.manifestName); } return (JarEntry)super.getNextEntry(); } diff --git a/src/java.base/share/classes/java/util/jar/JarVerifier.java b/src/java.base/share/classes/java/util/jar/JarVerifier.java index 5cd640b5e3f6f6e82dcd7dd5d8ac537365ba2cb9..280d64e9d37a95a0b337b72c1a86d231f78bf368 100644 --- a/src/java.base/share/classes/java/util/jar/JarVerifier.java +++ b/src/java.base/share/classes/java/util/jar/JarVerifier.java @@ -444,7 +444,7 @@ class JarVerifier { { this.is = Objects.requireNonNull(is); this.jv = jv; - this.mev = new ManifestEntryVerifier(man); + this.mev = new ManifestEntryVerifier(man, jv.manifestName); this.jv.beginEntry(je, mev); this.numLeft = je.getSize(); if (this.numLeft == 0) diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java index 51fdc3e31612a68681b855742cf5f68e5e7ee52c..5b2533111f4ef224bb5343d5f8d0dc20dad8c28f 100644 --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -3445,7 +3445,7 @@ loop: for(int x=0, offset=0; x= 0) { - assert (index >= 0 && index < length); + assert ((length == 0 && index == 0) || index >= 0 && index < length); for (int i = 0; x < length && i < lengthInCodePoints; i++) { if (Character.isHighSurrogate(seq.charAt(x++))) { if (x < length && Character.isLowSurrogate(seq.charAt(x))) { diff --git a/src/java.base/share/classes/java/util/zip/Deflater.java b/src/java.base/share/classes/java/util/zip/Deflater.java index 7bbb0539d5e936fd137308ddb3598f71c2c96b08..d41b8a7e12627f0e63937f7157aabc2aa01d9798 100644 --- a/src/java.base/share/classes/java/util/zip/Deflater.java +++ b/src/java.base/share/classes/java/util/zip/Deflater.java @@ -495,6 +495,7 @@ public class Deflater { * @param output the buffer for the compressed data * @return the actual number of bytes of compressed data written to the * output buffer + * @throws ReadOnlyBufferException if the given output buffer is read-only * @since 11 */ public int deflate(ByteBuffer output) { @@ -674,6 +675,7 @@ public class Deflater { * the output buffer * * @throws IllegalArgumentException if the flush mode is invalid + * @throws ReadOnlyBufferException if the given output buffer is read-only * @since 11 */ public int deflate(ByteBuffer output, int flush) { diff --git a/src/java.base/share/classes/javax/crypto/CryptoPermissions.java b/src/java.base/share/classes/javax/crypto/CryptoPermissions.java index ced2e2bd88b1cb885b6c4a79eb10e7f967e64503..703b2b67863aa1c283d34c77c91f4904da862c98 100644 --- a/src/java.base/share/classes/javax/crypto/CryptoPermissions.java +++ b/src/java.base/share/classes/javax/crypto/CryptoPermissions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,9 @@ package javax.crypto; import java.security.*; +import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; -import java.util.Vector; import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.io.Serializable; @@ -307,7 +307,7 @@ implements Serializable { */ private CryptoPermission[] getMinimum(PermissionCollection thisPc, PermissionCollection thatPc) { - Vector permVector = new Vector<>(2); + ArrayList permList = new ArrayList<>(2); Enumeration thisPcPermissions = thisPc.elements(); @@ -334,18 +334,16 @@ implements Serializable { (CryptoPermission)thatPcPermissions.nextElement(); if (thatCp.implies(thisCp)) { - permVector.addElement(thisCp); + permList.add(thisCp); break; } if (thisCp.implies(thatCp)) { - permVector.addElement(thatCp); + permList.add(thatCp); } } } - CryptoPermission[] ret = new CryptoPermission[permVector.size()]; - permVector.copyInto(ret); - return ret; + return permList.toArray(new CryptoPermission[0]); } /** @@ -363,7 +361,7 @@ implements Serializable { */ private CryptoPermission[] getMinimum(int maxKeySize, PermissionCollection pc) { - Vector permVector = new Vector<>(1); + ArrayList permList = new ArrayList<>(1); Enumeration enum_ = pc.elements(); @@ -371,16 +369,16 @@ implements Serializable { CryptoPermission cp = (CryptoPermission)enum_.nextElement(); if (cp.getMaxKeySize() <= maxKeySize) { - permVector.addElement(cp); + permList.add(cp); } else { if (cp.getCheckParam()) { - permVector.addElement( + permList.add( new CryptoPermission(cp.getAlgorithm(), maxKeySize, cp.getAlgorithmParameterSpec(), cp.getExemptionMechanism())); } else { - permVector.addElement( + permList.add( new CryptoPermission(cp.getAlgorithm(), maxKeySize, cp.getExemptionMechanism())); @@ -388,9 +386,7 @@ implements Serializable { } } - CryptoPermission[] ret = new CryptoPermission[permVector.size()]; - permVector.copyInto(ret); - return ret; + return permList.toArray(new CryptoPermission[0]); } /** diff --git a/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java b/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java index 9cc8bc48e80c83f1ccfd46cbee3d92e834e4a15e..5ccaa8143152b3b995f453f5203edd7a2f6ecd64 100644 --- a/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java +++ b/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package javax.crypto; import java.io.*; +import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; @@ -254,15 +255,15 @@ final class CryptoPolicyParser { // AlgorithmParameterSpec class name. String algParamSpecClassName = match("quoted string"); - Vector paramsV = new Vector<>(1); + ArrayList paramsV = new ArrayList<>(1); while (peek(",")) { match(","); if (peek("number")) { - paramsV.addElement(match()); + paramsV.add(match()); } else { if (peek("*")) { match("*"); - paramsV.addElement(Integer.MAX_VALUE); + paramsV.add(Integer.MAX_VALUE); } else { throw new ParsingException(st.lineno(), "Expecting an integer"); @@ -270,8 +271,7 @@ final class CryptoPolicyParser { } } - Integer[] params = new Integer[paramsV.size()]; - paramsV.copyInto(params); + Integer[] params = paramsV.toArray(new Integer[0]); e.checkParam = true; e.algParamSpec = getInstance(algParamSpecClassName, params); @@ -458,7 +458,7 @@ final class CryptoPolicyParser { } CryptoPermission[] getPermissions() { - Vector result = new Vector<>(); + ArrayList result = new ArrayList<>(); Enumeration grantEnum = grantEntries.elements(); while (grantEnum.hasMoreElements()) { @@ -469,16 +469,16 @@ final class CryptoPolicyParser { CryptoPermissionEntry pe = permEnum.nextElement(); if (pe.cryptoPermission.equals( "javax.crypto.CryptoAllPermission")) { - result.addElement(CryptoAllPermission.INSTANCE); + result.add(CryptoAllPermission.INSTANCE); } else { if (pe.checkParam) { - result.addElement(new CryptoPermission( + result.add(new CryptoPermission( pe.alg, pe.maxKeySize, pe.algParamSpec, pe.exemptionMechanism)); } else { - result.addElement(new CryptoPermission( + result.add(new CryptoPermission( pe.alg, pe.maxKeySize, pe.exemptionMechanism)); @@ -487,10 +487,7 @@ final class CryptoPolicyParser { } } - CryptoPermission[] ret = new CryptoPermission[result.size()]; - result.copyInto(ret); - - return ret; + return result.toArray(new CryptoPermission[0]); } private boolean isConsistent(String alg, String exemptionMechanism, diff --git a/src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java b/src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java index 243c848ff41c3ace54a6a3ae094068080275646d..6a3d311eb149fe05e4e56661b7e1ff76442aabb3 100644 --- a/src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/IvParameterSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,13 +76,16 @@ public class IvParameterSpec implements AlgorithmParameterSpec { if (iv == null) { throw new IllegalArgumentException("IV missing"); } - if (iv.length - offset < len) { - throw new IllegalArgumentException - ("IV buffer too short for given offset/length combination"); + if (offset < 0) { + throw new ArrayIndexOutOfBoundsException("offset is negative"); } if (len < 0) { throw new ArrayIndexOutOfBoundsException("len is negative"); } + if (iv.length - offset < len) { + throw new IllegalArgumentException + ("IV buffer too short for given offset/length combination"); + } this.iv = new byte[len]; System.arraycopy(iv, offset, this.iv, 0, len); } diff --git a/src/java.base/share/classes/javax/crypto/spec/RC5ParameterSpec.java b/src/java.base/share/classes/javax/crypto/spec/RC5ParameterSpec.java index 22d1c6a7c5800ab6adeac969e7c6f8b3c61858bd..ced87331bce6a6cee895307694e1acef19e4d481 100644 --- a/src/java.base/share/classes/javax/crypto/spec/RC5ParameterSpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/RC5ParameterSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -114,7 +114,12 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { this.version = version; this.rounds = rounds; this.wordSize = wordSize; - if (iv == null) throw new IllegalArgumentException("IV missing"); + if (iv == null) { + throw new IllegalArgumentException("IV missing"); + } + if (offset < 0) { + throw new ArrayIndexOutOfBoundsException("offset is negative"); + } int blockSize = (wordSize / 8) * 2; if (iv.length - offset < blockSize) { throw new IllegalArgumentException("IV too short"); diff --git a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java index de8245074d4ba884228e9c34f672376895b0de59..41d4acab7483cf96dd88419d032af679e4cb6e2b 100644 --- a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java @@ -157,13 +157,16 @@ public class SecretKeySpec implements KeySpec, SecretKey { if (key.length == 0) { throw new IllegalArgumentException("Empty key"); } - if (key.length-offset < len) { - throw new IllegalArgumentException - ("Invalid offset/length combination"); + if (offset < 0) { + throw new ArrayIndexOutOfBoundsException("offset is negative"); } if (len < 0) { throw new ArrayIndexOutOfBoundsException("len is negative"); } + if (key.length - offset < len) { + throw new IllegalArgumentException + ("Invalid offset/length combination"); + } this.key = new byte[len]; System.arraycopy(key, offset, this.key, 0, len); this.algorithm = algorithm; diff --git a/src/java.base/share/classes/javax/net/ssl/SSLContext.java b/src/java.base/share/classes/javax/net/ssl/SSLContext.java index c05c92969cb521509f509a0f96c1da6880197635..982a375b33177ab7da30f244f0003009e79a896d 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLContext.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLContext.java @@ -372,12 +372,9 @@ public class SSLContext { try { return contextSpi.engineCreateSSLEngine(); } catch (AbstractMethodError e) { - UnsupportedOperationException unsup = - new UnsupportedOperationException( - "Provider: " + getProvider() + - " doesn't support this operation"); - unsup.initCause(e); - throw unsup; + throw new UnsupportedOperationException( + "Provider: " + getProvider() + + " doesn't support this operation", e); } } @@ -412,12 +409,9 @@ public class SSLContext { try { return contextSpi.engineCreateSSLEngine(peerHost, peerPort); } catch (AbstractMethodError e) { - UnsupportedOperationException unsup = - new UnsupportedOperationException( - "Provider: " + getProvider() + - " does not support this operation"); - unsup.initCause(e); - throw unsup; + throw new UnsupportedOperationException( + "Provider: " + getProvider() + + " does not support this operation", e); } } diff --git a/src/java.base/share/classes/javax/security/auth/login/Configuration.java b/src/java.base/share/classes/javax/security/auth/login/Configuration.java index b4c9bc66918074369da2f9cb2ff0e734d13b1b8c..e6a0bd3166fc9d9efbea37653d5daf8d4153a4c4 100644 --- a/src/java.base/share/classes/javax/security/auth/login/Configuration.java +++ b/src/java.base/share/classes/javax/security/auth/login/Configuration.java @@ -270,17 +270,15 @@ public abstract class Configuration { } catch (PrivilegedActionException e) { Exception ee = e.getException(); if (ee instanceof InstantiationException) { - throw (SecurityException) new - SecurityException + throw new SecurityException ("Configuration error:" + ee.getCause().getMessage() + - "\n").initCause(ee.getCause()); + "\n", ee.getCause()); } else { - throw (SecurityException) new - SecurityException + throw new SecurityException ("Configuration error: " + ee.toString() + - "\n").initCause(ee); + "\n", ee); } } } diff --git a/src/java.base/share/classes/javax/security/auth/x500/X500Principal.java b/src/java.base/share/classes/javax/security/auth/x500/X500Principal.java index 72666414a2d4b4f7614d21a926c8cc70b58ee9bf..f0bf5e0ae2e1028b2ea07b88bb5c3da511412b06 100644 --- a/src/java.base/share/classes/javax/security/auth/x500/X500Principal.java +++ b/src/java.base/share/classes/javax/security/auth/x500/X500Principal.java @@ -181,10 +181,8 @@ public final class X500Principal implements Principal, java.io.Serializable { try { thisX500Name = new X500Name(name, keywordMap); } catch (Exception e) { - IllegalArgumentException iae = new IllegalArgumentException - ("improperly specified input name: " + name); - iae.initCause(e); - throw iae; + throw new IllegalArgumentException + ("improperly specified input name: " + name, e); } } @@ -226,10 +224,8 @@ public final class X500Principal implements Principal, java.io.Serializable { try { thisX500Name = new X500Name(name); } catch (Exception e) { - IllegalArgumentException iae = new IllegalArgumentException - ("improperly specified input name"); - iae.initCause(e); - throw iae; + throw new IllegalArgumentException + ("improperly specified input name", e); } } @@ -266,17 +262,13 @@ public final class X500Principal implements Principal, java.io.Serializable { try { is.reset(); } catch (IOException ioe) { - IllegalArgumentException iae = new IllegalArgumentException + throw new IllegalArgumentException ("improperly specified input stream " + - ("and unable to reset input stream")); - iae.initCause(e); - throw iae; + ("and unable to reset input stream"), e); } } - IllegalArgumentException iae = new IllegalArgumentException - ("improperly specified input stream"); - iae.initCause(e); - throw iae; + throw new IllegalArgumentException + ("improperly specified input stream", e); } } diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index f6fe5f611316699b46b3b8268aafb1806280441f..b68490ad7a39785d970e8a6731f5eee336419066 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -356,6 +356,15 @@ public interface JavaLangAccess { */ int decodeASCII(byte[] src, int srcOff, char[] dst, int dstOff, int len); + /** + * Encodes ASCII codepoints as possible from the source array into + * the destination byte array, assuming that the encoding is ASCII + * compatible + * + * @return the number of bytes successfully encoded, or 0 if none + */ + int encodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len); + /** * Set the cause of Throwable * @param cause set t's cause to new value diff --git a/src/java.base/share/classes/jdk/internal/loader/Resource.java b/src/java.base/share/classes/jdk/internal/loader/Resource.java index 55002f4c8c11dd65f90924464163a9acfb217560..d119c4b5eae9f825b4c6a16f5ea7f257dbb366c0 100644 --- a/src/java.base/share/classes/jdk/internal/loader/Resource.java +++ b/src/java.base/share/classes/jdk/internal/loader/Resource.java @@ -187,4 +187,12 @@ public abstract class Resource { public CodeSigner[] getCodeSigners() { return null; } + + /** + * Returns non-fatal reading error during data retrieval if there's any. + * For example, CRC error when reading a JAR entry. + */ + public Exception getDataError() { + return null; + } } diff --git a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java index 62c78980206e088011d445a31efe45da41ceadb6..dad2f25917bbc03e561f6d047b96826021a8e6e2 100644 --- a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java +++ b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java @@ -60,6 +60,7 @@ import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.jar.JarFile; +import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.jar.JarEntry; import java.util.jar.Manifest; @@ -597,7 +598,7 @@ public class URLClassPath { /* * Returns the base URL for this Loader. */ - URL getBaseURL() { + final URL getBaseURL() { return base; } @@ -875,6 +876,7 @@ public class URLClassPath { } return new Resource() { + private Exception dataError = null; public String getName() { return name; } public URL getURL() { return url; } public URL getCodeSourceURL() { return csu; } @@ -890,6 +892,18 @@ public class URLClassPath { { return entry.getCertificates(); }; public CodeSigner[] getCodeSigners() { return entry.getCodeSigners(); }; + public Exception getDataError() + { return dataError; } + public byte[] getBytes() throws IOException { + byte[] bytes = super.getBytes(); + CRC32 crc32 = new CRC32(); + crc32.update(bytes); + if (crc32.getValue() != entry.getCrc()) { + dataError = new IOException( + "CRC error while extracting entry from JAR file"); + } + return bytes; + } }; } @@ -1199,7 +1213,8 @@ public class URLClassPath { */ private static class FileLoader extends Loader { /* Canonicalized File */ - private File dir; + private final File dir; + private final URL normalizedBase; /* * Creates a new FileLoader for the specified URL with a file protocol. @@ -1209,6 +1224,7 @@ public class URLClassPath { String path = url.getFile().replace('/', File.separatorChar); path = ParseUtil.decode(path); dir = (new File(path)).getCanonicalFile(); + normalizedBase = new URL(getBaseURL(), "."); } /* @@ -1227,7 +1243,6 @@ public class URLClassPath { Resource getResource(final String name, boolean check) { final URL url; try { - URL normalizedBase = new URL(getBaseURL(), "."); url = new URL(getBaseURL(), ParseUtil.encodePath(name, false)); if (url.getFile().startsWith(normalizedBase.getFile()) == false) { diff --git a/src/java.base/share/classes/jdk/internal/misc/CDS.java b/src/java.base/share/classes/jdk/internal/misc/CDS.java index d3f6e4f3dd31296e49a902c6a9cc08d33c9808b0..43f6128cc0576f5e979b2b9682794c885cd9c9e2 100644 --- a/src/java.base/share/classes/jdk/internal/misc/CDS.java +++ b/src/java.base/share/classes/jdk/internal/misc/CDS.java @@ -254,8 +254,9 @@ public class CDS { * called from jcmd VM.cds to dump static or dynamic shared archive * @param isStatic true for dump static archive or false for dynnamic archive. * @param fileName user input archive name, can be null. + * @return The archive name if successfully dumped. */ - private static void dumpSharedArchive(boolean isStatic, String fileName) throws Exception { + private static String dumpSharedArchive(boolean isStatic, String fileName) throws Exception { String cwd = new File("").getAbsolutePath(); // current dir used for printing message. String currentPid = String.valueOf(ProcessHandle.current().pid()); String archiveFileName = fileName != null ? fileName : @@ -333,6 +334,8 @@ public class CDS { throw new RuntimeException("Cannot rename temp file " + tempArchiveFileName + " to archive file" + archiveFileName); } // Everyting goes well, print out the file name. - System.out.println((isStatic ? "Static" : " Dynamic") + " dump to file " + cwd + File.separator + archiveFileName); + String archiveFilePath = new File(archiveFileName).getAbsolutePath(); + System.out.println("The process was attached by jcmd and dumped a " + (isStatic ? "static" : "dynamic") + " archive " + archiveFilePath); + return archiveFilePath; } } diff --git a/src/java.base/share/classes/jdk/internal/module/ModulePath.java b/src/java.base/share/classes/jdk/internal/module/ModulePath.java index 99713872588f41443ba35bf0e73e40ed0cc518a5..47c21ed3b3d26f465c784592af0836564bb31ed2 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModulePath.java +++ b/src/java.base/share/classes/jdk/internal/module/ModulePath.java @@ -551,7 +551,7 @@ public class ModulePath implements ModuleFinder { if (!cn.isEmpty()) { String pn = packageName(cn); if (!packages.contains(pn)) { - String msg = "Provider class " + cn + " not in module"; + String msg = "Provider class " + cn + " not in JAR file " + fn; throw new InvalidModuleDescriptorException(msg); } providerClasses.add(cn); diff --git a/src/java.base/share/classes/jdk/internal/reflect/DelegatingConstructorAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/DelegatingConstructorAccessorImpl.java index 6b0f7f51b13fc654518387acfe358435f28477b7..eaa9a78639277b14540c187ddf7336ba2cf67c05 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/DelegatingConstructorAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/DelegatingConstructorAccessorImpl.java @@ -25,16 +25,25 @@ package jdk.internal.reflect; +import jdk.internal.vm.annotation.Stable; + import java.lang.reflect.InvocationTargetException; +import java.util.Objects; /** Delegates its invocation to another ConstructorAccessorImpl and can change its delegate at run time. */ class DelegatingConstructorAccessorImpl extends ConstructorAccessorImpl { - private ConstructorAccessorImpl delegate; + // initial non-null delegate + @Stable + private final ConstructorAccessorImpl initialDelegate; + // alternative delegate: starts as null; + // only single change from null -> non-null is guaranteed + @Stable + private ConstructorAccessorImpl altDelegate; DelegatingConstructorAccessorImpl(ConstructorAccessorImpl delegate) { - setDelegate(delegate); + initialDelegate = Objects.requireNonNull(delegate); } public Object newInstance(Object[] args) @@ -42,10 +51,15 @@ class DelegatingConstructorAccessorImpl extends ConstructorAccessorImpl { IllegalArgumentException, InvocationTargetException { - return delegate.newInstance(args); + return delegate().newInstance(args); + } + + private ConstructorAccessorImpl delegate() { + var d = altDelegate; + return d != null ? d : initialDelegate; } void setDelegate(ConstructorAccessorImpl delegate) { - this.delegate = delegate; + altDelegate = Objects.requireNonNull(delegate); } } diff --git a/src/java.base/share/classes/jdk/internal/reflect/DelegatingMethodAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/DelegatingMethodAccessorImpl.java index 1acab93eb42000b40c8181c20db683d557ab68da..09cb41591746726da305a50d6a02d3e1c80cd082 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/DelegatingMethodAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/DelegatingMethodAccessorImpl.java @@ -25,25 +25,38 @@ package jdk.internal.reflect; +import jdk.internal.vm.annotation.Stable; + import java.lang.reflect.InvocationTargetException; +import java.util.Objects; /** Delegates its invocation to another MethodAccessorImpl and can change its delegate at run time. */ class DelegatingMethodAccessorImpl extends MethodAccessorImpl { - private MethodAccessorImpl delegate; + // initial non-null delegate + @Stable private final MethodAccessorImpl initialDelegate; + // alternative delegate: starts as null; + // only single change from null -> non-null is guaranteed + @Stable private MethodAccessorImpl altDelegate; DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) { - setDelegate(delegate); + initialDelegate = Objects.requireNonNull(delegate); } + @Override public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { - return delegate.invoke(obj, args); + return delegate().invoke(obj, args); + } + + private MethodAccessorImpl delegate() { + var d = altDelegate; + return d != null ? d : initialDelegate; } void setDelegate(MethodAccessorImpl delegate) { - this.delegate = delegate; + altDelegate = Objects.requireNonNull(delegate); } } diff --git a/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java index b5121c860dcc507fb182c008670620039a6774f8..b703d082534e2d9ddce56cfc7ce9a1fbdef49dbb 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java @@ -25,12 +25,23 @@ package jdk.internal.reflect; +import jdk.internal.vm.annotation.Stable; + +import java.lang.reflect.Field; + /** Package-private implementation of the FieldAccessor interface which has access to all classes and all fields, regardless of language restrictions. See MagicAccessorImpl. */ abstract class FieldAccessorImpl extends MagicAccessorImpl implements FieldAccessor { + @Stable + protected final Field field; + + FieldAccessorImpl(Field field) { + this.field = field; + } + /** Matches specification in {@link java.lang.reflect.Field} */ public abstract Object get(Object obj) throws IllegalArgumentException; diff --git a/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java index 06eb4f7748fb7fe953523b465d0b6a6ca8cec031..6cd7d7d0f01ab9770900261c14255afd4994ae7a 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java @@ -25,8 +25,6 @@ package jdk.internal.reflect; -import sun.reflect.misc.ReflectUtil; - import java.lang.reflect.*; import jdk.internal.misc.Unsafe; @@ -39,12 +37,13 @@ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl { = U.objectFieldOffset(NativeConstructorAccessorImpl.class, "generated"); private final Constructor c; - private DelegatingConstructorAccessorImpl parent; + private final DelegatingConstructorAccessorImpl parent; private int numInvocations; private volatile int generated; NativeConstructorAccessorImpl(Constructor c) { this.c = c; + this.parent = new DelegatingConstructorAccessorImpl(this); } public Object newInstance(Object[] args) @@ -77,8 +76,8 @@ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl { return newInstance0(c, args); } - void setParent(DelegatingConstructorAccessorImpl parent) { - this.parent = parent; + DelegatingConstructorAccessorImpl getParent() { + return parent; } private static native Object newInstance0(Constructor c, Object[] args) diff --git a/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java index 1198b56d74687a3284cd6f7f6b93e082e0c8b120..7f57035a0dd04b6adaa335a398599b939ad24037 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java @@ -25,8 +25,6 @@ package jdk.internal.reflect; -import sun.reflect.misc.ReflectUtil; - import java.lang.reflect.*; import jdk.internal.misc.Unsafe; @@ -39,12 +37,13 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl { = U.objectFieldOffset(NativeMethodAccessorImpl.class, "generated"); private final Method method; - private DelegatingMethodAccessorImpl parent; + private final DelegatingMethodAccessorImpl parent; private int numInvocations; private volatile int generated; NativeMethodAccessorImpl(Method method) { this.method = method; + this.parent = new DelegatingMethodAccessorImpl(this); } public Object invoke(Object obj, Object[] args) @@ -77,8 +76,8 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl { return invoke0(method, obj, args); } - void setParent(DelegatingMethodAccessorImpl parent) { - this.parent = parent; + DelegatingMethodAccessorImpl getParent() { + return parent; } private static native Object invoke0(Method m, Object obj, Object[] args); diff --git a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java index a6b896df7feeb5d79f4e3dddd133a05b4b9b2117..5f1106db4725cd0e87d47a159af5485933c8ed66 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java +++ b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java @@ -44,7 +44,6 @@ import java.util.Properties; import jdk.internal.access.JavaLangReflectAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; -import sun.reflect.misc.ReflectUtil; import sun.security.action.GetPropertyAction; import sun.security.util.SecurityConstants; @@ -210,12 +209,8 @@ public class ReflectionFactory { method.getExceptionTypes(), method.getModifiers()); } else { - NativeMethodAccessorImpl acc = - new NativeMethodAccessorImpl(method); - DelegatingMethodAccessorImpl res = - new DelegatingMethodAccessorImpl(acc); - acc.setParent(res); - return res; + NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method); + return acc.getParent(); } } @@ -252,12 +247,8 @@ public class ReflectionFactory { c.getExceptionTypes(), c.getModifiers()); } else { - NativeConstructorAccessorImpl acc = - new NativeConstructorAccessorImpl(c); - DelegatingConstructorAccessorImpl res = - new DelegatingConstructorAccessorImpl(acc); - acc.setParent(res); - return res; + NativeConstructorAccessorImpl acc = new NativeConstructorAccessorImpl(c); + return acc.getParent(); } } @@ -487,7 +478,7 @@ public class ReflectionFactory { } public final MethodHandle readObjectNoDataForSerialization(Class cl) { - return findReadWriteObjectForSerialization(cl, "readObjectNoData", ObjectInputStream.class); + return findReadWriteObjectForSerialization(cl, "readObjectNoData", null); } public final MethodHandle writeObjectForSerialization(Class cl) { @@ -502,7 +493,8 @@ public class ReflectionFactory { } try { - Method meth = cl.getDeclaredMethod(methodName, streamClass); + Method meth = streamClass == null ? cl.getDeclaredMethod(methodName) + : cl.getDeclaredMethod(methodName, streamClass); int mods = meth.getModifiers(); if (meth.getReturnType() != Void.TYPE || Modifier.isStatic(mods) || diff --git a/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java index 1c6fd0046a54cf97e0e4f501925fb43fe370ef90..9f43263bf3e6bf00c338f5b2dc5413badef95784 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java @@ -28,6 +28,7 @@ package jdk.internal.reflect; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.Stable; /** Base class for jdk.internal.misc.Unsafe-based FieldAccessors. The observation is that there are only nine types of fields from the @@ -39,12 +40,12 @@ import jdk.internal.misc.Unsafe; abstract class UnsafeFieldAccessorImpl extends FieldAccessorImpl { static final Unsafe unsafe = Unsafe.getUnsafe(); - protected final Field field; + @Stable protected final long fieldOffset; protected final boolean isFinal; UnsafeFieldAccessorImpl(Field field) { - this.field = field; + super(field); if (Modifier.isStatic(field.getModifiers())) fieldOffset = unsafe.staticFieldOffset(field); else diff --git a/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticFieldAccessorImpl.java index eab39628e8896b7f095d982eaf27e5e00976c1f4..e75fffb13283598aa20bc433f79781474bfaf902 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticFieldAccessorImpl.java @@ -31,6 +31,7 @@ import java.security.AccessController; import java.util.Set; import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.Stable; /** Base class for jdk.internal.misc.Unsafe-based FieldAccessors for static fields. The observation is that there are only nine types of @@ -45,6 +46,7 @@ abstract class UnsafeStaticFieldAccessorImpl extends UnsafeFieldAccessorImpl { Set.of("base")); } + @Stable protected final Object base; // base UnsafeStaticFieldAccessorImpl(Field field) { diff --git a/src/java.base/share/classes/jdk/internal/util/StaticProperty.java b/src/java.base/share/classes/jdk/internal/util/StaticProperty.java index 011cb148af11081b5b21468ea9508cffeb359065..092f99e6b542507b3eff6429baa7aeed18d3be98 100644 --- a/src/java.base/share/classes/jdk/internal/util/StaticProperty.java +++ b/src/java.base/share/classes/jdk/internal/util/StaticProperty.java @@ -51,6 +51,7 @@ public final class StaticProperty { private static final String JAVA_IO_TMPDIR; private static final String NATIVE_ENCODING; private static final String FILE_ENCODING; + private static final String JAVA_PROPERTIES_DATE; private StaticProperty() {} @@ -67,6 +68,7 @@ public final class StaticProperty { JDK_SERIAL_FILTER_FACTORY = getProperty(props, "jdk.serialFilterFactory", null); NATIVE_ENCODING = getProperty(props, "native.encoding"); FILE_ENCODING = getProperty(props, "file.encoding"); + JAVA_PROPERTIES_DATE = getProperty(props, "java.properties.date", null); } private static String getProperty(Properties props, String key) { @@ -227,4 +229,16 @@ public final class StaticProperty { public static String fileEncoding() { return FILE_ENCODING; } + + /** + * Return the {@code java.properties.date} system property. + * + * {@link SecurityManager#checkPropertyAccess} is NOT checked + * in this method. + * + * @return the {@code java.properties.date} system property + */ + public static String javaPropertiesDate() { + return JAVA_PROPERTIES_DATE; + } } diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 19e8bcfd2550de4284e46f44777c043a93bf1f6a..9faee9cae36d957a0565267240e89b8fe21f9f3b 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -151,6 +151,7 @@ module java.base { java.management, java.naming, java.rmi, + jdk.charsets, jdk.jartool, jdk.jlink, jdk.net, diff --git a/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/src/java.base/share/classes/sun/net/www/http/HttpClient.java index 906f4626cf04bc3f1980825c1b8c43b3f9a6de9a..4b3c40b4fc01f7ed30f680409a2e1e725a48b716 100644 --- a/src/java.base/share/classes/sun/net/www/http/HttpClient.java +++ b/src/java.base/share/classes/sun/net/www/http/HttpClient.java @@ -307,7 +307,7 @@ public class HttpClient extends NetworkClient { ret = kac.get(url, null); if (ret != null && httpuc != null && httpuc.streaming() && - httpuc.getRequestMethod() == "POST") { + "POST".equals(httpuc.getRequestMethod())) { if (!ret.available()) { ret.inCache = false; ret.closeServer(); diff --git a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java index 4c21276a582a731a021a139bcbc62978a6925f86..45dfdad08e090b45c70401bc390adff823bce321 100644 --- a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java +++ b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java @@ -79,6 +79,7 @@ public class KeepAliveCache // This class is never serialized (see writeObject/readObject). private final ReentrantLock cacheLock = new ReentrantLock(); + @SuppressWarnings("serial") // Type of field is not Serializable private Thread keepAliveTimer = null; /** diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 6280cc1c8268b447b997dc1fe4b3c1a8fdef5cb3..cba385f04fa2a2621ad853b7817576f794d6f191 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -2961,7 +2961,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /* must save before calling close */ reuseClient = http; InputStream is = http.getInputStream(); - if (!method.equals("HEAD")) { + if (!method.equals("HEAD") || tunnelState == TunnelState.SETUP) { try { /* we want to read the rest of the response without using the * hurry mechanism, because that would close the connection diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java index 7861e2602aeea0a0881193060f40ff4e547a65a9..2f718e5bcc93293fee54ae224e8709b9c4e9e566 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java +++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java @@ -329,7 +329,7 @@ final class HttpsClient extends HttpClient ret = (HttpsClient) kac.get(url, sf); if (ret != null && httpuc != null && httpuc.streaming() && - httpuc.getRequestMethod() == "POST") { + "POST".equals(httpuc.getRequestMethod())) { if (!ret.available()) ret = null; } diff --git a/src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java b/src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java index 4bfb14f401ab2dc4877d167a3944fa92c01a541d..6f312cb9ed2e74bf7506ec2eb78689714b461ad7 100644 --- a/src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java +++ b/src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java @@ -25,11 +25,18 @@ package sun.nio.ch; -import java.io.*; -import java.nio.*; -import java.nio.channels.*; -import java.nio.channels.spi.*; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.channels.SelectableChannel; +import java.util.Arrays; import java.util.Objects; +import jdk.internal.util.ArraysSupport; /** * This class is defined here rather than in java.nio.channels.Channels @@ -43,13 +50,13 @@ import java.util.Objects; public class ChannelInputStream extends InputStream { + private static final int DEFAULT_BUFFER_SIZE = 8192; public static int read(ReadableByteChannel ch, ByteBuffer bb, boolean block) throws IOException { - if (ch instanceof SelectableChannel) { - SelectableChannel sc = (SelectableChannel)ch; + if (ch instanceof SelectableChannel sc) { synchronized (sc.blockingLock()) { boolean bm = sc.isBlocking(); if (!bm) @@ -107,10 +114,95 @@ public class ChannelInputStream return ChannelInputStream.read(ch, bb, true); } + @Override + public byte[] readAllBytes() throws IOException { + if (!(ch instanceof SeekableByteChannel sbc)) + return super.readAllBytes(); + + long length = sbc.size(); + long position = sbc.position(); + long size = length - position; + + if (length <= 0 || size <= 0) + return super.readAllBytes(); + + if (size > (long) Integer.MAX_VALUE) { + String msg = + String.format("Required array size too large: %d = %d - %d", + size, length, position); + throw new OutOfMemoryError(msg); + } + + int capacity = (int)size; + byte[] buf = new byte[capacity]; + + int nread = 0; + int n; + for (;;) { + // read to EOF which may read more or less than initial size, e.g., + // file is truncated while we are reading + while ((n = read(buf, nread, capacity - nread)) > 0) + nread += n; + + // if last call to read() returned -1, we are done; otherwise, + // try to read one more byte and if that fails we're done too + if (n < 0 || (n = read()) < 0) + break; + + // one more byte was read; need to allocate a larger buffer + capacity = Math.max(ArraysSupport.newLength(capacity, + 1, // min growth + capacity), // pref growth + DEFAULT_BUFFER_SIZE); + buf = Arrays.copyOf(buf, capacity); + buf[nread++] = (byte)n; + } + return (capacity == nread) ? buf : Arrays.copyOf(buf, nread); + } + + @Override + public byte[] readNBytes(int len) throws IOException { + if (len < 0) + throw new IllegalArgumentException("len < 0"); + if (len == 0) + return new byte[0]; + + if (!(ch instanceof SeekableByteChannel sbc)) + return super.readNBytes(len); + + long length = sbc.size(); + long position = sbc.position(); + long size = length - position; + + if (length <= 0 || size <= 0) + return super.readNBytes(len); + + int capacity = (int)Math.min(len, size); + byte[] buf = new byte[capacity]; + + int remaining = capacity; + int nread = 0; + int n; + do { + n = read(buf, nread, remaining); + if (n > 0) { + nread += n; + remaining -= n; + } else if (n == 0) { + // Block until a byte is read or EOF is detected + byte b = (byte)read(); + if (b == -1 ) + break; + buf[nread++] = b; + remaining--; + } + } while (n >= 0 && remaining > 0); + return (capacity == nread) ? buf : Arrays.copyOf(buf, nread); + } + public int available() throws IOException { // special case where the channel is to a file - if (ch instanceof SeekableByteChannel) { - SeekableByteChannel sbc = (SeekableByteChannel)ch; + if (ch instanceof SeekableByteChannel sbc) { long rem = Math.max(0, sbc.size() - sbc.position()); return (rem > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)rem; } @@ -119,8 +211,7 @@ public class ChannelInputStream public synchronized long skip(long n) throws IOException { // special case where the channel is to a file - if (ch instanceof SeekableByteChannel) { - SeekableByteChannel sbc = (SeekableByteChannel)ch; + if (ch instanceof SeekableByteChannel sbc) { long pos = sbc.position(); long newPos; if (n > 0) { diff --git a/src/java.base/share/classes/sun/nio/ch/IOUtil.java b/src/java.base/share/classes/sun/nio/ch/IOUtil.java index b9b320a194f2d1c46838d7468392bbcfddf94b0e..900bb4e40883dbde2efbae4b3be0a75502bc9479 100644 --- a/src/java.base/share/classes/sun/nio/ch/IOUtil.java +++ b/src/java.base/share/classes/sun/nio/ch/IOUtil.java @@ -44,6 +44,11 @@ public class IOUtil { */ static final int IOV_MAX; + /** + * Max total number of bytes that writev supports + */ + static final long WRITEV_MAX; + private IOUtil() { } // No instantiation static int write(FileDescriptor fd, ByteBuffer src, long position, @@ -172,9 +177,10 @@ public class IOUtil { Runnable handleReleasers = null; try { // Iterate over buffers to populate native iovec array. + long writevLen = 0L; int count = offset + length; int i = offset; - while (i < count && iov_len < IOV_MAX) { + while (i < count && iov_len < IOV_MAX && writevLen < WRITEV_MAX) { ByteBuffer buf = bufs[i]; var h = acquireScope(buf, async); if (h != null) { @@ -188,6 +194,10 @@ public class IOUtil { Util.checkRemainingBufferSizeAligned(rem, alignment); if (rem > 0) { + long headroom = WRITEV_MAX - writevLen; + if (headroom < rem) + rem = (int)headroom; + vec.setBuffer(iov_len, buf, pos, rem); // allocate shadow buffer to ensure I/O is done with direct buffer @@ -197,10 +207,9 @@ public class IOUtil { shadow = Util.getTemporaryAlignedDirectBuffer(rem, alignment); else shadow = Util.getTemporaryDirectBuffer(rem); - shadow.put(buf); + shadow.put(shadow.position(), buf, pos, rem); shadow.flip(); vec.setShadow(iov_len, shadow); - buf.position(pos); // temporarily restore position in user buffer buf = shadow; pos = shadow.position(); } @@ -208,6 +217,7 @@ public class IOUtil { vec.putBase(iov_len, bufferAddress(buf) + pos); vec.putLen(iov_len, rem); iov_len++; + writevLen += rem; } i++; } @@ -580,6 +590,8 @@ public class IOUtil { static native int iovMax(); + static native long writevMax(); + static native void initIDs(); /** @@ -593,6 +605,7 @@ public class IOUtil { initIDs(); IOV_MAX = iovMax(); + WRITEV_MAX = writevMax(); } } diff --git a/src/java.base/share/classes/sun/nio/cs/CESU_8.java b/src/java.base/share/classes/sun/nio/cs/CESU_8.java index b3dcebf53232d4f9996f837a3a96529ca7e0749d..f1fc69703c20c84f980ebc512216a3a81b73a050 100644 --- a/src/java.base/share/classes/sun/nio/cs/CESU_8.java +++ b/src/java.base/share/classes/sun/nio/cs/CESU_8.java @@ -76,11 +76,11 @@ class CESU_8 extends Unicode dst.position(dp - dst.arrayOffset()); } + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static class Decoder extends CharsetDecoder implements ArrayDecoder { - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - private Decoder(Charset cs) { super(cs, 1.0f, 1.0f); } @@ -434,7 +434,6 @@ class CESU_8 extends Unicode } private Surrogate.Parser sgp; - private char[] c2; private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { @@ -445,11 +444,12 @@ class CESU_8 extends Unicode byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - int dlASCII = dp + Math.min(sl - sp, dl - dp); - // ASCII only loop - while (dp < dlASCII && sa[sp] < '\u0080') - da[dp++] = (byte) sa[sp++]; + // Handle ASCII-only prefix + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + sp += n; + dp += n; + while (sp < sl) { char c = sa[sp]; if (c < 0x80) { @@ -549,11 +549,11 @@ class CESU_8 extends Unicode public int encode(char[] sa, int sp, int len, byte[] da) { int sl = sp + len; int dp = 0; - int dlASCII = dp + Math.min(len, da.length); - // ASCII only optimized loop - while (dp < dlASCII && sa[sp] < '\u0080') - da[dp++] = (byte) sa[sp++]; + // Handle ASCII-only prefix + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(len, da.length)); + sp += n; + dp += n; while (sp < sl) { char c = sa[sp++]; diff --git a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 7a9a3d40bfa066617b6c3e739e70ddbd2b5eb930..0fb8a82f74ff3f66ba1133376921c6d0dfdd4a20 100644 --- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java @@ -111,11 +111,11 @@ public class DoubleByte { Arrays.fill(B2C_UNMAPPABLE, UNMAPPABLE_DECODING); } + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + public static class Decoder extends CharsetDecoder implements DelegatableDecoder, ArrayDecoder { - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - final char[][] b2c; final char[] b2cSB; final int b2Min; @@ -601,6 +601,11 @@ public class DoubleByte { int dl = dst.arrayOffset() + dst.limit(); try { + if (isASCIICompatible) { + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + sp += n; + dp += n; + } while (sp < sl) { char c = sa[sp]; int bb = encodeChar(c); @@ -681,7 +686,11 @@ public class DoubleByte { public int encode(char[] src, int sp, int len, byte[] dst) { int dp = 0; int sl = sp + len; - int dl = dst.length; + if (isASCIICompatible) { + int n = JLA.encodeASCII(src, sp, dst, dp, len); + sp += n; + dp += n; + } while (sp < sl) { char c = src[sp++]; int bb = encodeChar(c); diff --git a/src/java.base/share/classes/sun/nio/cs/HKSCS.java b/src/java.base/share/classes/sun/nio/cs/HKSCS.java index 1b166447b78c7833a3ec54a68739eea74dad016d..f812041a295c282b186f41fe6772f3563588aec3 100644 --- a/src/java.base/share/classes/sun/nio/cs/HKSCS.java +++ b/src/java.base/share/classes/sun/nio/cs/HKSCS.java @@ -42,9 +42,9 @@ public class HKSCS { static int b2Min = 0x40; static int b2Max = 0xfe; - private char[][] b2cBmp; - private char[][] b2cSupp; - private DoubleByte.Decoder big5Dec; + private final char[][] b2cBmp; + private final char[][] b2cSupp; + private final DoubleByte.Decoder big5Dec; protected Decoder(Charset cs, DoubleByte.Decoder big5Dec, @@ -94,7 +94,6 @@ public class HKSCS { int b1 = sa[sp] & 0xff; char c = decodeSingle(b1); int inSize = 1, outSize = 1; - char[] cc = null; if (c == UNMAPPABLE_DECODING) { if (sl - sp < 2) return CoderResult.UNDERFLOW; @@ -137,7 +136,6 @@ public class HKSCS { int mark = src.position(); try { while (src.hasRemaining()) { - char[] cc = null; int b1 = src.get() & 0xff; int inSize = 1, outSize = 1; char c = decodeSingle(b1); @@ -230,9 +228,9 @@ public class HKSCS { } public static class Encoder extends DoubleByte.Encoder { - private DoubleByte.Encoder big5Enc; - private char[][] c2bBmp; - private char[][] c2bSupp; + private final DoubleByte.Encoder big5Enc; + private final char[][] c2bBmp; + private final char[][] c2bSupp; protected Encoder(Charset cs, DoubleByte.Encoder big5Enc, diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index 88f89548444243df520ee329d2f1d6b131ce1920..748659b323f153a0339032f38359992c32d48fe2 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -49,11 +49,11 @@ public class SingleByte return cr; } + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + public static final class Decoder extends CharsetDecoder implements ArrayDecoder { - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - private final char[] b2c; private final boolean isASCIICompatible; private final boolean isLatin1Decodable; @@ -214,8 +214,14 @@ public class SingleByte byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - int len = Math.min(dl - dp, sl - sp); + int len = Math.min(dl - dp, sl - sp); + if (isASCIICompatible) { + int n = JLA.encodeASCII(sa, sp, da, dp, len); + sp += n; + dp += n; + len -= n; + } while (len-- > 0) { char c = sa[sp]; int b = encode(c); diff --git a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index 04aeceb43d34addbb3dd35441cdaf8cc4fc88a24..8ff79d497fbc50ab78e6d1d09fcd54555c1d089c 100644 --- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java @@ -61,9 +61,9 @@ public class US_ASCII return new Encoder(this); } - private static class Decoder extends CharsetDecoder { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static class Decoder extends CharsetDecoder { private Decoder(Charset cs) { super(cs, 1.0f, 1.0f); @@ -159,6 +159,10 @@ public class US_ASCII assert (dp <= dl); dp = (dp <= dl ? dp : dl); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + sp += n; + dp += n; + try { while (sp < sl) { char c = sa[sp]; diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/src/java.base/share/classes/sun/nio/cs/UTF_8.java index 1a7d8c4d1e1a5d973e5abc84580dc92dd6e7ae05..a27b4690f59f52bb13433f20154b4d9b492e772a 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.java @@ -83,9 +83,9 @@ public final class UTF_8 extends Unicode { dst.position(dp - dst.arrayOffset()); } - private static class Decoder extends CharsetDecoder { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static class Decoder extends CharsetDecoder { private Decoder(Charset cs) { super(cs, 1.0f, 1.0f); @@ -443,8 +443,7 @@ public final class UTF_8 extends Unicode { private Surrogate.Parser sgp; private CoderResult encodeArrayLoop(CharBuffer src, - ByteBuffer dst) - { + ByteBuffer dst) { char[] sa = src.array(); int sp = src.arrayOffset() + src.position(); int sl = src.arrayOffset() + src.limit(); @@ -452,11 +451,22 @@ public final class UTF_8 extends Unicode { byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - int dlASCII = dp + Math.min(sl - sp, dl - dp); - // ASCII only loop - while (dp < dlASCII && sa[sp] < '\u0080') - da[dp++] = (byte) sa[sp++]; + // Handle ASCII-only prefix + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + sp += n; + dp += n; + + if (sp < sl) { + return encodeArrayLoopSlow(src, sa, sp, sl, dst, da, dp, dl); + } else { + updatePositions(src, sp, dst, dp); + return CoderResult.UNDERFLOW; + } + } + + private CoderResult encodeArrayLoopSlow(CharBuffer src, char[] sa, int sp, int sl, + ByteBuffer dst, byte[] da, int dp, int dl) { while (sp < sl) { char c = sa[sp]; if (c < 0x80) { diff --git a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java index b651f43c184b22dafc9c72f0b3b249444b493a00..1689672b7a9993af04396c38e4016c6c96f03013 100644 --- a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java +++ b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -515,6 +515,14 @@ public class SignerInfo implements DerEncoder { case "RSASSA-PSS": PSSParameterSpec spec = (PSSParameterSpec) SignatureUtil.getParamSpec(encAlg, encAlgId.getParameters()); + /* + * RFC 4056 section 3 for Signed-data: + * signatureAlgorithm MUST contain id-RSASSA-PSS. The algorithm + * parameters field MUST contain RSASSA-PSS-params. + */ + if (spec == null) { + throw new NoSuchAlgorithmException("Missing PSSParameterSpec for RSASSA-PSS algorithm"); + } if (!AlgorithmId.get(spec.getDigestAlgorithm()).equals(digAlgId)) { throw new NoSuchAlgorithmException("Incompatible digest algorithm"); } diff --git a/src/java.base/share/classes/sun/security/provider/DigestBase.java b/src/java.base/share/classes/sun/security/provider/DigestBase.java index 2d5f485c06e422707a50fd5e602c1baf65e9ecd2..dbe59396ac0b62527f544f18bdb60545e4f3ad38 100644 --- a/src/java.base/share/classes/sun/security/provider/DigestBase.java +++ b/src/java.base/share/classes/sun/security/provider/DigestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -184,8 +184,7 @@ abstract class DigestBase extends MessageDigestSpi implements Cloneable { try { engineDigest(b, 0, b.length); } catch (DigestException e) { - throw (ProviderException) - new ProviderException("Internal error").initCause(e); + throw new ProviderException("Internal error", e); } return b; } diff --git a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java index ffa547c32e293a6127e1d0f11b616bb7b7b93b9f..9f00c950433be9864200175468611a844a124590 100644 --- a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java +++ b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java @@ -810,9 +810,9 @@ public abstract class JavaKeyStore extends KeyStoreSpi { if (!MessageDigest.isEqual(computed, actual)) { Throwable t = new UnrecoverableKeyException ("Password verification failed"); - throw (IOException) new IOException + throw new IOException ("Keystore was tampered with, or " - + "password was incorrect").initCause(t); + + "password was incorrect", t); } } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java b/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java index 3ed82afaab8409e5bd9ea512233f48557ba62e68..4a79c3a3f51e783f943efd64742c50ccf8595a17 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java @@ -74,10 +74,10 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { private static final Debug debug = Debug.getInstance("certpath"); private final AlgorithmConstraints constraints; - private final PublicKey trustedPubKey; private final Date date; - private PublicKey prevPubKey; private final String variant; + private PublicKey trustedPubKey; + private PublicKey prevPubKey; private TrustAnchor anchor; private static final Set SIGNATURE_PRIMITIVE_SET = @@ -90,10 +90,6 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { CryptoPrimitive.PUBLIC_KEY_ENCRYPTION, CryptoPrimitive.KEY_AGREEMENT)); - private static final DisabledAlgorithmConstraints - certPathDefaultConstraints = - DisabledAlgorithmConstraints.certPathConstraints(); - /** * Create a new {@code AlgorithmChecker} with the given * {@code TrustAnchor} and {@code String} variant. @@ -104,7 +100,7 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { * passed will set it to Validator.GENERIC. */ public AlgorithmChecker(TrustAnchor anchor, String variant) { - this(anchor, certPathDefaultConstraints, null, variant); + this(anchor, null, null, variant); } /** @@ -141,19 +137,11 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { AlgorithmConstraints constraints, Date date, String variant) { if (anchor != null) { - if (anchor.getTrustedCert() != null) { - this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); - } else { - this.trustedPubKey = anchor.getCAPublicKey(); - } - this.anchor = anchor; - } else { - this.trustedPubKey = null; + setTrustAnchorAndKeys(anchor); } - this.prevPubKey = this.trustedPubKey; - this.constraints = (constraints == null ? certPathDefaultConstraints : - constraints); + this.constraints = constraints == null ? + DisabledAlgorithmConstraints.certPathConstraints() : constraints; this.date = date; this.variant = (variant == null ? Validator.VAR_GENERIC : variant); } @@ -172,18 +160,14 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { * passed will set it to Validator.GENERIC. */ public AlgorithmChecker(TrustAnchor anchor, Date date, String variant) { - this(anchor, certPathDefaultConstraints, date, variant); + this(anchor, null, date, variant); } @Override public void init(boolean forward) throws CertPathValidatorException { // Note that this class does not support forward mode. if (!forward) { - if (trustedPubKey != null) { - prevPubKey = trustedPubKey; - } else { - prevPubKey = null; - } + prevPubKey = trustedPubKey; } else { throw new CertPathValidatorException("forward checking not supported"); @@ -207,8 +191,8 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { Collection unresolvedCritExts) throws CertPathValidatorException { - if (!(cert instanceof X509Certificate) || constraints == null) { - // ignore the check for non-x.509 certificate or null constraints + if (!(cert instanceof X509Certificate)) { + // ignore the check for non-x.509 certificate return; } @@ -233,115 +217,114 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { PublicKey currPubKey = cert.getPublicKey(); String currSigAlg = x509Cert.getSigAlgName(); - // Check the signature algorithm and parameters against constraints. - if (!constraints.permits(SIGNATURE_PRIMITIVE_SET, currSigAlg, - currSigAlgParams)) { - throw new CertPathValidatorException( - "Algorithm constraints check failed on signature " + - "algorithm: " + currSigAlg, null, null, -1, - BasicReason.ALGORITHM_CONSTRAINED); - } - - // Assume all key usage bits are set if key usage is not present - Set primitives = KU_PRIMITIVE_SET; - - if (keyUsage != null) { - primitives = EnumSet.noneOf(CryptoPrimitive.class); - - if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) { - // keyUsage[0]: KeyUsage.digitalSignature - // keyUsage[1]: KeyUsage.nonRepudiation - // keyUsage[5]: KeyUsage.keyCertSign - // keyUsage[6]: KeyUsage.cRLSign - primitives.add(CryptoPrimitive.SIGNATURE); - } - - if (keyUsage[2]) { // KeyUsage.keyEncipherment - primitives.add(CryptoPrimitive.KEY_ENCAPSULATION); - } - - if (keyUsage[3]) { // KeyUsage.dataEncipherment - primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION); - } - - if (keyUsage[4]) { // KeyUsage.keyAgreement - primitives.add(CryptoPrimitive.KEY_AGREEMENT); - } - - // KeyUsage.encipherOnly and KeyUsage.decipherOnly are - // undefined in the absence of the keyAgreement bit. - - if (primitives.isEmpty()) { - throw new CertPathValidatorException( - "incorrect KeyUsage extension bits", - null, null, -1, PKIXReason.INVALID_KEY_USAGE); + if (constraints instanceof DisabledAlgorithmConstraints) { + DisabledAlgorithmConstraints dac = + (DisabledAlgorithmConstraints)constraints; + if (prevPubKey != null && prevPubKey == trustedPubKey) { + // check constraints of trusted public key (make sure + // algorithm and size is not restricted) + CertPathConstraintsParameters cp = + new CertPathConstraintsParameters(trustedPubKey, variant, + anchor, date); + dac.permits(trustedPubKey.getAlgorithm(), cp); } - } - - ConstraintsParameters cp = - new CertPathConstraintsParameters(x509Cert, variant, + // Check the signature algorithm and parameters against constraints + CertPathConstraintsParameters cp = + new CertPathConstraintsParameters(x509Cert, variant, anchor, date); - - // Check against local constraints if it is DisabledAlgorithmConstraints - if (constraints instanceof DisabledAlgorithmConstraints) { - ((DisabledAlgorithmConstraints)constraints).permits(currSigAlg, - currSigAlgParams, cp); - // DisabledAlgorithmsConstraints does not check primitives, so key - // additional key check. - + dac.permits(currSigAlg, currSigAlgParams, cp); } else { - // Perform the default constraints checking anyway. - certPathDefaultConstraints.permits(currSigAlg, currSigAlgParams, cp); - // Call locally set constraints to check key with primitives. - if (!constraints.permits(primitives, currPubKey)) { - throw new CertPathValidatorException( - "Algorithm constraints check failed on key " + - currPubKey.getAlgorithm() + " with size of " + - sun.security.util.KeyUtil.getKeySize(currPubKey) + - "bits", + if (prevPubKey != null) { + if (!constraints.permits(SIGNATURE_PRIMITIVE_SET, + currSigAlg, prevPubKey, currSigAlgParams)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed on " + + currSigAlg + "signature and " + + currPubKey.getAlgorithm() + " key with size of " + + sun.security.util.KeyUtil.getKeySize(currPubKey) + + "bits", + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } + } else { + if (!constraints.permits(SIGNATURE_PRIMITIVE_SET, + currSigAlg, currSigAlgParams)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed on " + + "signature algorithm: " + currSigAlg, null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } } - } - - // If there is no previous key, set one and exit - if (prevPubKey == null) { - prevPubKey = currPubKey; - return; - } + // Assume all key usage bits are set if key usage is not present + Set primitives = KU_PRIMITIVE_SET; - // Check with previous cert for signature algorithm and public key - if (!constraints.permits( - SIGNATURE_PRIMITIVE_SET, - currSigAlg, prevPubKey, currSigAlgParams)) { - throw new CertPathValidatorException( - "Algorithm constraints check failed on " + - "signature algorithm: " + currSigAlg, - null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); - } + if (keyUsage != null) { + primitives = EnumSet.noneOf(CryptoPrimitive.class); - // Inherit key parameters from previous key - if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) { - // Inherit DSA parameters from previous key - if (!(prevPubKey instanceof DSAPublicKey)) { - throw new CertPathValidatorException("Input key is not " + - "of a appropriate type for inheriting parameters"); + if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) { + // keyUsage[0]: KeyUsage.digitalSignature + // keyUsage[1]: KeyUsage.nonRepudiation + // keyUsage[5]: KeyUsage.keyCertSign + // keyUsage[6]: KeyUsage.cRLSign + primitives.add(CryptoPrimitive.SIGNATURE); + } + + if (keyUsage[2]) { // KeyUsage.keyEncipherment + primitives.add(CryptoPrimitive.KEY_ENCAPSULATION); + } + + if (keyUsage[3]) { // KeyUsage.dataEncipherment + primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION); + } + + if (keyUsage[4]) { // KeyUsage.keyAgreement + primitives.add(CryptoPrimitive.KEY_AGREEMENT); + } + + // KeyUsage.encipherOnly and KeyUsage.decipherOnly are + // undefined in the absence of the keyAgreement bit. + + if (primitives.isEmpty()) { + throw new CertPathValidatorException( + "incorrect KeyUsage extension bits", + null, null, -1, PKIXReason.INVALID_KEY_USAGE); + } } - - DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); - if (params == null) { + if (!constraints.permits(primitives, currPubKey)) { throw new CertPathValidatorException( - "Key parameters missing from public key."); + "Algorithm constraints check failed on " + + currPubKey.getAlgorithm() + " key with size of " + + sun.security.util.KeyUtil.getKeySize(currPubKey) + + "bits", + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); } + } - try { - BigInteger y = ((DSAPublicKey)currPubKey).getY(); - KeyFactory kf = KeyFactory.getInstance("DSA"); - DSAPublicKeySpec ks = new DSAPublicKeySpec(y, params.getP(), - params.getQ(), params.getG()); - currPubKey = kf.generatePublic(ks); - } catch (GeneralSecurityException e) { - throw new CertPathValidatorException("Unable to generate " + - "key with inherited parameters: " + e.getMessage(), e); + if (prevPubKey != null) { + // Inherit key parameters from previous key + if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) { + // Inherit DSA parameters from previous key + if (!(prevPubKey instanceof DSAPublicKey)) { + throw new CertPathValidatorException("Input key is not " + + "of a appropriate type for inheriting parameters"); + } + + DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); + if (params == null) { + throw new CertPathValidatorException( + "Key parameters missing from public key."); + } + + try { + BigInteger y = ((DSAPublicKey)currPubKey).getY(); + KeyFactory kf = KeyFactory.getInstance("DSA"); + DSAPublicKeySpec ks = new DSAPublicKeySpec(y, params.getP(), + params.getQ(), params.getG()); + currPubKey = kf.generatePublic(ks); + } catch (GeneralSecurityException e) { + throw new CertPathValidatorException("Unable to generate " + + "key with inherited parameters: " + + e.getMessage(), e); + } } } @@ -349,6 +332,20 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { prevPubKey = currPubKey; } + /** + * Sets the anchor, trustedPubKey and prevPubKey fields based on the + * specified trust anchor. + */ + private void setTrustAnchorAndKeys(TrustAnchor anchor) { + if (anchor.getTrustedCert() != null) { + this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); + } else { + this.trustedPubKey = anchor.getCAPublicKey(); + } + this.anchor = anchor; + this.prevPubKey = this.trustedPubKey; + } + /** * Try to set the trust anchor of the checker. *

      @@ -359,21 +356,9 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { * certificate */ void trySetTrustAnchor(TrustAnchor anchor) { - // Don't bother if the check has started or trust anchor has already - // specified. - if (prevPubKey == null) { - if (anchor == null) { - throw new IllegalArgumentException( - "The trust anchor cannot be null"); - } - - // Don't bother to change the trustedPubKey. - if (anchor.getTrustedCert() != null) { - prevPubKey = anchor.getTrustedCert().getPublicKey(); - } else { - prevPubKey = anchor.getCAPublicKey(); - } - this.anchor = anchor; + // Only set if trust anchor has not already been set. + if (this.trustedPubKey == null) { + setTrustAnchorAndKeys(anchor); } } @@ -412,9 +397,9 @@ public final class AlgorithmChecker extends PKIXCertPathChecker { static void check(PublicKey key, AlgorithmId algorithmId, String variant, TrustAnchor anchor) throws CertPathValidatorException { - certPathDefaultConstraints.permits(algorithmId.getName(), - algorithmId.getParameters(), - new CertPathConstraintsParameters(key, variant, anchor)); + DisabledAlgorithmConstraints.certPathConstraints().permits( + algorithmId.getName(), algorithmId.getParameters(), + new CertPathConstraintsParameters(key, variant, anchor, null)); } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/CertPathConstraintsParameters.java b/src/java.base/share/classes/sun/security/provider/certpath/CertPathConstraintsParameters.java index dacbd12737a6277f6add26c169dd88d641de0b7b..679e865745147a83d6345ae3c14486edf226e230 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/CertPathConstraintsParameters.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/CertPathConstraintsParameters.java @@ -50,8 +50,8 @@ public class CertPathConstraintsParameters implements ConstraintsParameters { private final Date date; // The variant or usage of this certificate private final String variant; - // The certificate being checked (may be null if a CRL or OCSPResponse is - // being checked) + // The certificate being checked (may be null if a raw public key, a CRL + // or an OCSPResponse is being checked) private final X509Certificate cert; public CertPathConstraintsParameters(X509Certificate cert, @@ -60,8 +60,8 @@ public class CertPathConstraintsParameters implements ConstraintsParameters { } public CertPathConstraintsParameters(Key key, String variant, - TrustAnchor anchor) { - this(key, variant, anchor, null, null); + TrustAnchor anchor, Date date) { + this(key, variant, anchor, date, null); } private CertPathConstraintsParameters(Key key, String variant, diff --git a/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java b/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java index 2b07e69dcfd99b22d7bd641cc6d774877cbbad95..65788b16490e198f92ed5b30268174f26de2cf67 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java @@ -35,7 +35,6 @@ import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.CRLReason; import java.security.cert.Extension; -import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Base64; import java.util.Date; @@ -46,7 +45,6 @@ import sun.security.action.GetIntegerAction; import sun.security.util.Debug; import sun.security.util.Event; import sun.security.util.IOUtils; -import sun.security.validator.Validator; import sun.security.x509.AccessDescription; import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.GeneralName; @@ -166,22 +164,26 @@ public final class OCSP { List extensions) throws IOException { OCSPRequest request = new OCSPRequest(certIds, extensions); byte[] bytes = request.encodeBytes(); + String responder = responderURI.toString(); if (debug != null) { - debug.println("connecting to OCSP service at: " + responderURI); + debug.println("connecting to OCSP service at: " + responder); } Event.report(Event.ReporterCategory.CRLCHECK, "event.ocsp.check", - responderURI.toString()); + responder); URL url; HttpURLConnection con = null; try { - String encodedGetReq = responderURI.toString() + "/" + - URLEncoder.encode(Base64.getEncoder().encodeToString(bytes), - UTF_8); + StringBuilder encodedGetReq = new StringBuilder(responder); + if (!responder.endsWith("/")) { + encodedGetReq.append("/"); + } + encodedGetReq.append(URLEncoder.encode( + Base64.getEncoder().encodeToString(bytes), UTF_8)); if (encodedGetReq.length() <= 255) { - url = new URL(encodedGetReq); + url = new URL(encodedGetReq.toString()); con = (HttpURLConnection)url.openConnection(); con.setDoOutput(true); con.setDoInput(true); diff --git a/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java b/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java index c1336719139d058f42712c4310705a5a2412ffff..e6b9c2b4992d91d20e0610d6d8e6e45eed30d943 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java @@ -638,7 +638,10 @@ public final class OCSPResponse { try { Signature respSignature = Signature.getInstance(sigAlgId.getName()); - respSignature.initVerify(cert.getPublicKey()); + SignatureUtil.initVerifyWithParam(respSignature, + cert.getPublicKey(), + SignatureUtil.getParamSpec(sigAlgId.getName(), + sigAlgId.getEncodedParams())); respSignature.update(tbsResponseData); if (respSignature.verify(signature)) { @@ -654,8 +657,8 @@ public final class OCSPResponse { } return false; } - } catch (InvalidKeyException | NoSuchAlgorithmException | - SignatureException e) + } catch (InvalidAlgorithmParameterException | InvalidKeyException + | NoSuchAlgorithmException | SignatureException e) { throw new CertPathValidatorException(e); } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java b/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java index de3923bb3bc1f9862d75eeefbc992f064379c5aa..0454d84ac465869769c6c2263b57788a67b0b0c5 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java @@ -176,8 +176,8 @@ public final class PKIXCertPathValidator extends CertPathValidatorSpi { List certPathCheckers = new ArrayList<>(); // add standard checkers that we will be using certPathCheckers.add(untrustedChecker); - certPathCheckers.add(new AlgorithmChecker(anchor, null, - params.timestamp(), params.variant())); + certPathCheckers.add(new AlgorithmChecker(anchor, params.timestamp(), + params.variant())); certPathCheckers.add(new KeyChecker(certPathLen, params.targetCertConstraints())); certPathCheckers.add(new ConstraintsChecker(certPathLen)); diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java b/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java index be275733b9be11edcc0fa8e8eea0d88053a412d6..994d9f14cce92cf443352780762c6ddf3a1f4b72 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java @@ -123,12 +123,14 @@ public class RSAPSSSignature extends SignatureSpi { @Override protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { - if (!(publicKey instanceof RSAPublicKey)) { + if (publicKey instanceof RSAPublicKey rsaPubKey) { + isPublicKeyValid(rsaPubKey); + this.pubKey = rsaPubKey; + this.privKey = null; + resetDigest(); + } else { throw new InvalidKeyException("key must be RSAPublicKey"); } - this.pubKey = (RSAPublicKey) isValid((RSAKey)publicKey); - this.privKey = null; - resetDigest(); } // initialize for signing. See JCA doc @@ -142,14 +144,16 @@ public class RSAPSSSignature extends SignatureSpi { @Override protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { - if (!(privateKey instanceof RSAPrivateKey)) { + if (privateKey instanceof RSAPrivateKey rsaPrivateKey) { + isPrivateKeyValid(rsaPrivateKey); + this.privKey = rsaPrivateKey; + this.pubKey = null; + this.random = + (random == null ? JCAUtil.getSecureRandom() : random); + resetDigest(); + } else { throw new InvalidKeyException("key must be RSAPrivateKey"); } - this.privKey = (RSAPrivateKey) isValid((RSAKey)privateKey); - this.pubKey = null; - this.random = - (random == null? JCAUtil.getSecureRandom() : random); - resetDigest(); } /** @@ -201,11 +205,56 @@ public class RSAPSSSignature extends SignatureSpi { } } + /** + * Validate the specified RSAPrivateKey + */ + private void isPrivateKeyValid(RSAPrivateKey prKey) throws InvalidKeyException { + try { + if (prKey instanceof RSAPrivateCrtKey crtKey) { + if (RSAPrivateCrtKeyImpl.checkComponents(crtKey)) { + RSAKeyFactory.checkRSAProviderKeyLengths( + crtKey.getModulus().bitLength(), + crtKey.getPublicExponent()); + } else { + throw new InvalidKeyException( + "Some of the CRT-specific components are not available"); + } + } else { + RSAKeyFactory.checkRSAProviderKeyLengths( + prKey.getModulus().bitLength(), + null); + } + } catch (InvalidKeyException ikEx) { + throw ikEx; + } catch (Exception e) { + throw new InvalidKeyException( + "Can not access private key components", e); + } + isValid(prKey); + } + + /** + * Validate the specified RSAPublicKey + */ + private void isPublicKeyValid(RSAPublicKey pKey) throws InvalidKeyException { + try { + RSAKeyFactory.checkRSAProviderKeyLengths( + pKey.getModulus().bitLength(), + pKey.getPublicExponent()); + } catch (InvalidKeyException ikEx) { + throw ikEx; + } catch (Exception e) { + throw new InvalidKeyException( + "Can not access public key components", e); + } + isValid(pKey); + } + /** * Validate the specified RSAKey and its associated parameters against * internal signature parameters. */ - private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException { + private void isValid(RSAKey rsaKey) throws InvalidKeyException { AlgorithmParameterSpec keyParams = rsaKey.getParams(); // validate key parameters if (!isCompatible(rsaKey.getParams(), this.sigParams)) { @@ -232,7 +281,6 @@ public class RSAPSSSignature extends SignatureSpi { ("Unrecognized digest algo: " + digestAlgo); } } - return rsaKey; } /** diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java index 640b221bdfe1e53af78451f8d350d623b83bba95..fbc9fda05faf23ca9594ec51310309ba00bb6a32 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java @@ -91,16 +91,11 @@ public final class RSAPrivateCrtKeyImpl RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo); // check all CRT-specific components are available, if any one // missing, return a non-CRT key instead - if ((key.getPublicExponent().signum() == 0) || - (key.getPrimeExponentP().signum() == 0) || - (key.getPrimeExponentQ().signum() == 0) || - (key.getPrimeP().signum() == 0) || - (key.getPrimeQ().signum() == 0) || - (key.getCrtCoefficient().signum() == 0)) { + if (checkComponents(key)) { + return key; + } else { return new RSAPrivateKeyImpl(key.type, key.keyParams, key.getModulus(), key.getPrivateExponent()); - } else { - return key; } case "PKCS#1": try { @@ -124,6 +119,18 @@ public final class RSAPrivateCrtKeyImpl } } + /** + * Validate if all CRT-specific components are available. + */ + static boolean checkComponents(RSAPrivateCrtKey key) { + return !((key.getPublicExponent().signum() == 0) || + (key.getPrimeExponentP().signum() == 0) || + (key.getPrimeExponentQ().signum() == 0) || + (key.getPrimeP().signum() == 0) || + (key.getPrimeQ().signum() == 0) || + (key.getCrtCoefficient().signum() == 0)); + } + /** * Generate a new key from the specified type and components. * Returns a CRT key if possible and a non-CRT key otherwise. diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java index e42f4886f0d8691756adcd67d76cec927e6e1c99..e13fcd6b9733db5abaf5a04538c47c124b5ee8ac 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java @@ -31,6 +31,7 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -333,6 +334,16 @@ final class CertificateRequest { // clean up this consumer chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id); + chc.receivedCertReq = true; + + // If we're processing this message and the server's certificate + // message consumer has not already run then this is a state + // machine violation. + if (chc.handshakeConsumers.containsKey( + SSLHandshake.CERTIFICATE.id)) { + throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected CertificateRequest handshake message"); + } SSLConsumer certStatCons = chc.handshakeConsumers.remove( SSLHandshake.CERTIFICATE_STATUS.id); @@ -659,6 +670,16 @@ final class CertificateRequest { // clean up this consumer chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id); + chc.receivedCertReq = true; + + // If we're processing this message and the server's certificate + // message consumer has not already run then this is a state + // machine violation. + if (chc.handshakeConsumers.containsKey( + SSLHandshake.CERTIFICATE.id)) { + throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected CertificateRequest handshake message"); + } SSLConsumer certStatCons = chc.handshakeConsumers.remove( SSLHandshake.CERTIFICATE_STATUS.id); @@ -709,7 +730,7 @@ final class CertificateRequest { // the SSLPossession. Instead, the choosePossession method // will use the accepted signature schemes in the message to // determine the set of acceptable certificate types to select from. - SSLPossession pos = choosePossession(chc); + SSLPossession pos = choosePossession(chc, crm); if (pos == null) { return; } @@ -719,8 +740,8 @@ final class CertificateRequest { SSLHandshake.CERTIFICATE_VERIFY); } - private static SSLPossession choosePossession(HandshakeContext hc) - throws IOException { + private static SSLPossession choosePossession(HandshakeContext hc, + T12CertificateRequestMessage crm) throws IOException { if (hc.peerRequestedCertSignSchemes == null || hc.peerRequestedCertSignSchemes.isEmpty()) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { @@ -730,6 +751,15 @@ final class CertificateRequest { return null; } + // Put the CR key type into a more friendly format for searching + List crKeyTypes = new ArrayList<>( + Arrays.asList(crm.getKeyTypes())); + // For TLS 1.2 only if RSA is a requested key type then we + // should also allow RSASSA-PSS. + if (crKeyTypes.contains("RSA")) { + crKeyTypes.add("RSASSA-PSS"); + } + Collection checkedKeyTypes = new HashSet<>(); List supportedKeyTypes = new ArrayList<>(); for (SignatureScheme ss : hc.peerRequestedCertSignSchemes) { @@ -764,6 +794,19 @@ final class CertificateRequest { "Unsupported authentication scheme: " + ss.name); } continue; + } else { + // Any auth object will have a set of allowed key types. + // This set should share at least one common algorithm with + // the CR's allowed key types. + if (Collections.disjoint(crKeyTypes, + Arrays.asList(ka.keyTypes))) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Unsupported authentication scheme: " + + ss.name); + } + continue; + } } supportedKeyTypes.add(ss.keyAlgorithm); } @@ -920,6 +963,15 @@ final class CertificateRequest { // clean up this consumer chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id); + chc.receivedCertReq = true; + + // Ensure that the CertificateRequest has not been sent prior + // to EncryptedExtensions + if (chc.handshakeConsumers.containsKey( + SSLHandshake.ENCRYPTED_EXTENSIONS.id)) { + throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected CertificateRequest handshake message"); + } T13CertificateRequestMessage crm = new T13CertificateRequestMessage(chc, message); diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java index 0ca4224c51786a4bf33a469538b81012cc76a47b..ff7690966bb394605b66fb22ce8cf0ef62d1d4ea 100644 --- a/src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -90,6 +90,11 @@ class ClientHandshakeContext extends HandshakeContext { ClientHelloMessage initialClientHelloMsg = null; + // Flag to indicate receipt of a CertificateRequest message from + // the server. Because this is optional, we cannot guarantee + // the handshakeConsumers Map will always have it present there. + boolean receivedCertReq = false; + // PSK identity is selected in first Hello and used again after HRR byte[] pskIdentity; diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java index 9fe425c49c8cde03809a9a67627c5ac156a13994..931f45e68ff8c3ccf6adcb945cd9fc2a54b39393 100644 --- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java @@ -567,6 +567,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { HashMap> holesMap; + // A map used to check duplicated handshake messages. + HashMap messageSeqMap; + HandshakeFlight() { this.handshakeType = HF_UNKNOWN; this.flightEpoch = 0; @@ -577,6 +580,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { this.maxRecordSeq = -1; this.holesMap = new HashMap<>(5); + this.messageSeqMap = new HashMap<>(5); } boolean isRetransmitOf(HandshakeFlight hs) { @@ -598,6 +602,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { hf.maxRecordSeq = this.maxRecordSeq; hf.holesMap = new HashMap<>(this.holesMap); + hf.messageSeqMap = new HashMap<>(this.messageSeqMap); return hf; } @@ -640,7 +645,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { } // Queue up a handshake message. - void queueUpHandshake(HandshakeFragment hsf) { + void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { if (!isDesirable(hsf)) { // Not a dedired record, discard it. return; @@ -707,6 +712,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { holes.add(new HoleDescriptor(0, hsf.messageLength)); } handshakeFlight.holesMap.put(hsf.handshakeType, holes); + handshakeFlight.messageSeqMap.put(hsf.handshakeType, hsf.messageSeq); } else if (holes.isEmpty()) { // Have got the full handshake message. This record may be // a handshake message retransmission. Discard this record. @@ -778,7 +784,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { } // Queue up a ChangeCipherSpec message - void queueUpChangeCipherSpec(RecordFragment rf) { + void queueUpChangeCipherSpec(RecordFragment rf) + throws SSLProtocolException { if (!isDesirable(rf)) { // Not a dedired record, discard it. return; @@ -807,7 +814,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { // Queue up a ciphertext message. // // Note: not yet be able to decrypt the message. - void queueUpFragment(RecordFragment rf) { + void queueUpFragment(RecordFragment rf) throws SSLProtocolException { if (!isDesirable(rf)) { // Not a dedired record, discard it. return; @@ -895,7 +902,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { // Is a desired record? // // Check for retransmission and lost records. - private boolean isDesirable(RecordFragment rf) { + private boolean isDesirable(RecordFragment rf) throws SSLProtocolException { // // Discard records old than the previous epoch. // @@ -970,6 +977,25 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { return false; } + // Unexpected duplicated handshake messages. + if (rf.recordEpoch == handshakeEpoch && + // For handshake messages only. + rf instanceof HandshakeFragment hsf && + // Check on the received handshake messages. + handshakeFlight.holesMap.containsKey(hsf.handshakeType)) { + Integer cachedMsgSeq = handshakeFlight.messageSeqMap.get( + hsf.handshakeType); + if (cachedMsgSeq != null && cachedMsgSeq != hsf.messageSeq) { + // Handshake messages of the same type but with different + // message sequence numbers are not allowed. + throw new SSLProtocolException( + "Two message sequence numbers are used for the " + + "same handshake message (" + + SSLHandshake.nameOf(hsf.handshakeType) + + ")"); + } + } + return true; } @@ -1086,6 +1112,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { // cleanup holes map handshakeFlight.holesMap.clear(); + // cleanup handshake message sequence numbers map + handshakeFlight.messageSeqMap.clear(); + // Ready to accept new input record. flightIsReady = false; needToCheckFlight = false; diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java index f2cc67a793f19c21758dade920b98f6b9d0f8540..6b2264e0f131b21e21d9f89f481ec2108598d2c0 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java @@ -27,6 +27,7 @@ package sun.security.ssl; import java.io.IOException; import java.nio.ByteBuffer; +import java.security.CryptoPrimitive; import java.security.GeneralSecurityException; import java.security.PublicKey; import java.security.interfaces.ECPublicKey; @@ -35,6 +36,7 @@ import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.NamedParameterSpec; import java.text.MessageFormat; +import java.util.EnumSet; import java.util.Locale; import javax.crypto.SecretKey; import sun.security.ssl.SSLHandshake.HandshakeMessage; @@ -317,12 +319,19 @@ final class ECDHClientKeyExchange { // create the credentials try { - NamedGroup ng = namedGroup; // "effectively final" the lambda - // AlgorithmConstraints are checked internally. - SSLCredentials sslCredentials = namedGroup.decodeCredentials( - cke.encodedPoint, shc.algorithmConstraints, - s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, - "ClientKeyExchange " + ng + ": " + s)); + SSLCredentials sslCredentials = + namedGroup.decodeCredentials(cke.encodedPoint); + if (shc.algorithmConstraints != null && + sslCredentials instanceof + NamedGroupCredentials namedGroupCredentials) { + if (!shc.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCredentials.getPublicKey())) { + shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "ClientKeyExchange for " + namedGroup + + " does not comply with algorithm constraints"); + } + } shc.handshakeCredentials.add(sslCredentials); } catch (GeneralSecurityException e) { @@ -497,12 +506,19 @@ final class ECDHClientKeyExchange { // create the credentials try { - NamedGroup ng = namedGroup; // "effectively final" the lambda - // AlgorithmConstraints are checked internally. - SSLCredentials sslCredentials = namedGroup.decodeCredentials( - cke.encodedPoint, shc.algorithmConstraints, - s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, - "ClientKeyExchange " + ng + ": " + s)); + SSLCredentials sslCredentials = + namedGroup.decodeCredentials(cke.encodedPoint); + if (shc.algorithmConstraints != null && + sslCredentials instanceof + NamedGroupCredentials namedGroupCredentials) { + if (!shc.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCredentials.getPublicKey())) { + shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "ClientKeyExchange for " + namedGroup + + " does not comply with algorithm constraints"); + } + } shc.handshakeCredentials.add(sslCredentials); } catch (GeneralSecurityException e) { diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java index 11efab71c85c995787a38af44ac9a2d3fa6cba86..09b7a6bdca2e245f44040f7b6b5f43f0eebb7de0 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java @@ -27,6 +27,7 @@ package sun.security.ssl; import java.io.IOException; import java.nio.ByteBuffer; +import java.security.CryptoPrimitive; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -37,6 +38,7 @@ import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.text.MessageFormat; +import java.util.EnumSet; import java.util.Locale; import java.util.Map; import sun.security.ssl.SSLHandshake.HandshakeMessage; @@ -214,10 +216,19 @@ final class ECDHServerKeyExchange { } try { - sslCredentials = namedGroup.decodeCredentials( - publicPoint, handshakeContext.algorithmConstraints, - s -> chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, - "ServerKeyExchange " + namedGroup + ": " + (s))); + sslCredentials = + namedGroup.decodeCredentials(publicPoint); + if (handshakeContext.algorithmConstraints != null && + sslCredentials instanceof + NamedGroupCredentials namedGroupCredentials) { + if (!handshakeContext.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCredentials.getPublicKey())) { + chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "ServerKeyExchange for " + namedGroup + + " does not comply with algorithm constraints"); + } + } } catch (GeneralSecurityException ex) { throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, "Cannot decode named group: " + diff --git a/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java b/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java index 6880c929ab6fa89342246f34c923cfe9ff4d9470..f2f7b0d5da44e3c635e9ec81aa445c2411944663 100644 --- a/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java +++ b/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java @@ -208,7 +208,7 @@ abstract class HelloCookieManager { byte[] target = md.digest(secret); // 32 bytes target[0] = cookie[0]; - return Arrays.equals(target, cookie); + return MessageDigest.isEqual(target, cookie); } } @@ -361,7 +361,7 @@ abstract class HelloCookieManager { md.update(headerBytes); byte[] headerCookie = md.digest(secret); - if (!Arrays.equals(headerCookie, prevHeadCookie)) { + if (!MessageDigest.isEqual(headerCookie, prevHeadCookie)) { return false; } diff --git a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java index a0d9b98a77a365ab13bc47981e1b65a8c8ee8e0e..df430c557c9bc203d804f06e9d4203a2dec511ca 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java @@ -27,6 +27,7 @@ package sun.security.ssl; import java.io.IOException; import java.nio.ByteBuffer; +import java.security.CryptoPrimitive; import java.security.GeneralSecurityException; import java.text.MessageFormat; import java.util.Collections; @@ -349,7 +350,8 @@ final class KeyShareExtension { NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId); if (ng == null || !SupportedGroups.isActivatable( shc.algorithmConstraints, ng)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unsupported named group: " + NamedGroup.nameOf(entry.namedGroupId)); @@ -359,16 +361,33 @@ final class KeyShareExtension { try { SSLCredentials kaCred = - ng.decodeCredentials(entry.keyExchange, - shc.algorithmConstraints, - s -> SSLLogger.warning(s)); + ng.decodeCredentials(entry.keyExchange); + if (shc.algorithmConstraints != null && + kaCred instanceof + NamedGroupCredentials namedGroupCredentials) { + if (!shc.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCredentials.getPublicKey())) { + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "key share entry of " + ng + " does not " + + " comply with algorithm constraints"); + } + + kaCred = null; + } + } + if (kaCred != null) { credentials.add(kaCred); } } catch (GeneralSecurityException ex) { - SSLLogger.warning( - "Cannot decode named group: " + - NamedGroup.nameOf(entry.namedGroupId)); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Cannot decode named group: " + + NamedGroup.nameOf(entry.namedGroupId)); + } } } @@ -646,9 +665,20 @@ final class KeyShareExtension { SSLCredentials credentials = null; try { - SSLCredentials kaCred = ng.decodeCredentials( - keyShare.keyExchange, chc.algorithmConstraints, - s -> chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, s)); + SSLCredentials kaCred = + ng.decodeCredentials(keyShare.keyExchange); + if (chc.algorithmConstraints != null && + kaCred instanceof + NamedGroupCredentials namedGroupCredentials) { + if (!chc.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCredentials.getPublicKey())) { + chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "key share entry of " + ng + " does not " + + " comply with algorithm constraints"); + } + } + if (kaCred != null) { credentials = kaCred; } diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java index 8febedbe9894e9edd3d5d20d01eeba0fa5d5e9a5..7afa619ad5df38acb4ab8a01aca2ad452f87f22f 100644 --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -419,12 +419,9 @@ enum NamedGroup { return spec.encodePossessionPublicKey(namedGroupPossession); } - SSLCredentials decodeCredentials(byte[] encoded, - AlgorithmConstraints constraints, - ExceptionSupplier onConstraintFail) - throws IOException, GeneralSecurityException { - return spec.decodeCredentials( - this, encoded, constraints, onConstraintFail); + SSLCredentials decodeCredentials( + byte[] encoded) throws IOException, GeneralSecurityException { + return spec.decodeCredentials(this, encoded); } SSLPossession createPossession(SecureRandom random) { @@ -436,30 +433,13 @@ enum NamedGroup { return spec.createKeyDerivation(hc); } - interface ExceptionSupplier { - void apply(String s) throws SSLException; - } - // A list of operations related to named groups. private interface NamedGroupScheme { - default void checkConstraints(PublicKey publicKey, - AlgorithmConstraints constraints, - ExceptionSupplier onConstraintFail) throws SSLException { - if (!constraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) { - onConstraintFail.apply("key share entry does not " - + "comply with algorithm constraints"); - } - } - byte[] encodePossessionPublicKey( NamedGroupPossession namedGroupPossession); - SSLCredentials decodeCredentials( - NamedGroup ng, byte[] encoded, - AlgorithmConstraints constraints, - ExceptionSupplier onConstraintFail - ) throws IOException, GeneralSecurityException; + SSLCredentials decodeCredentials(NamedGroup ng, + byte[] encoded) throws IOException, GeneralSecurityException; SSLPossession createPossession(NamedGroup ng, SecureRandom random); @@ -524,13 +504,10 @@ enum NamedGroup { } @Override - public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded, - AlgorithmConstraints constraints, - ExceptionSupplier onConstraintFail - ) throws IOException, GeneralSecurityException { + public SSLCredentials decodeCredentials(NamedGroup ng, + byte[] encoded) throws IOException, GeneralSecurityException { if (scheme != null) { - return scheme.decodeCredentials( - ng, encoded, constraints, onConstraintFail); + return scheme.decodeCredentials(ng, encoded); } return null; @@ -567,18 +544,9 @@ enum NamedGroup { } @Override - public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded, - AlgorithmConstraints constraints, - ExceptionSupplier onConstraintFail - ) throws IOException, GeneralSecurityException { - - DHKeyExchange.DHECredentials result - = DHKeyExchange.DHECredentials.valueOf(ng, encoded); - - checkConstraints(result.getPublicKey(), constraints, - onConstraintFail); - - return result; + public SSLCredentials decodeCredentials(NamedGroup ng, + byte[] encoded) throws IOException, GeneralSecurityException { + return DHKeyExchange.DHECredentials.valueOf(ng, encoded); } @Override @@ -605,18 +573,9 @@ enum NamedGroup { } @Override - public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded, - AlgorithmConstraints constraints, - ExceptionSupplier onConstraintFail - ) throws IOException, GeneralSecurityException { - - ECDHKeyExchange.ECDHECredentials result - = ECDHKeyExchange.ECDHECredentials.valueOf(ng, encoded); - - checkConstraints(result.getPublicKey(), constraints, - onConstraintFail); - - return result; + public SSLCredentials decodeCredentials(NamedGroup ng, + byte[] encoded) throws IOException, GeneralSecurityException { + return ECDHKeyExchange.ECDHECredentials.valueOf(ng, encoded); } @Override @@ -641,18 +600,9 @@ enum NamedGroup { } @Override - public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded, - AlgorithmConstraints constraints, - ExceptionSupplier onConstraintFail - ) throws IOException, GeneralSecurityException { - - XDHKeyExchange.XDHECredentials result - = XDHKeyExchange.XDHECredentials.valueOf(ng, encoded); - - checkConstraints(result.getPublicKey(), constraints, - onConstraintFail); - - return result; + public SSLCredentials decodeCredentials(NamedGroup ng, + byte[] encoded) throws IOException, GeneralSecurityException { + return XDHKeyExchange.XDHECredentials.valueOf(ng, encoded); } @Override diff --git a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java index 2d134f303fee8f10e6182e6cf7fbda5a46d61896..0b3481fbb19a37ab4c5f68257f84bc41d9a93f71 100644 --- a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java @@ -31,7 +31,6 @@ import java.text.MessageFormat; import java.util.List; import java.util.ArrayList; import java.util.Locale; -import java.util.Arrays; import java.util.Collection; import javax.crypto.Mac; import javax.crypto.SecretKey; @@ -569,7 +568,7 @@ final class PreSharedKeyExtension { SecretKey binderKey = deriveBinderKey(shc, psk, session); byte[] computedBinder = computeBinder(shc, binderKey, session, pskBinderHash); - if (!Arrays.equals(binder, computedBinder)) { + if (!MessageDigest.isEqual(binder, computedBinder)) { throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, "Incorect PSK binder value"); } diff --git a/src/java.base/share/classes/sun/security/ssl/RandomCookie.java b/src/java.base/share/classes/sun/security/ssl/RandomCookie.java index eb3d9500ca7f31460b7988c95936a7471b13a648..ef38196b2e7ba182663604532aa52634035aa2d6 100644 --- a/src/java.base/share/classes/sun/security/ssl/RandomCookie.java +++ b/src/java.base/share/classes/sun/security/ssl/RandomCookie.java @@ -25,10 +25,12 @@ package sun.security.ssl; +import sun.security.util.ByteArrays; + import java.io.*; import java.nio.ByteBuffer; +import java.security.MessageDigest; import java.security.SecureRandom; -import java.util.Arrays; /* * RandomCookie ... SSL hands standard format random cookies (nonces) @@ -111,7 +113,7 @@ final class RandomCookie { } boolean isHelloRetryRequest() { - return Arrays.equals(hrrRandomBytes, randomBytes); + return MessageDigest.isEqual(hrrRandomBytes, randomBytes); } // Used for client random validation of version downgrade protection. @@ -130,10 +132,10 @@ final class RandomCookie { } private boolean isT12Downgrade() { - return Arrays.equals(randomBytes, 24, 32, t12Protection, 0, 8); + return ByteArrays.isEqual(randomBytes, 24, 32, t12Protection, 0, 8); } private boolean isT11Downgrade() { - return Arrays.equals(randomBytes, 24, 32, t11Protection, 0, 8); + return ByteArrays.isEqual(randomBytes, 24, 32, t11Protection, 0, 8); } } diff --git a/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java b/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java index 732172a73413b73e9516027a24ab51718d7f0f8a..2dbf7d0bf914e52e3ac6ded1bb7e2f3a1107a3c0 100644 --- a/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java @@ -27,6 +27,7 @@ package sun.security.ssl; import java.io.IOException; import java.nio.ByteBuffer; +import java.security.MessageDigest; import java.text.MessageFormat; import java.util.Arrays; import java.util.Locale; @@ -37,6 +38,7 @@ import sun.security.ssl.SSLExtension.ExtensionConsumer; import static sun.security.ssl.SSLExtension.SH_RENEGOTIATION_INFO; import sun.security.ssl.SSLExtension.SSLExtensionSpec; import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.util.ByteArrays; /** * Pack of the "renegotiation_info" extensions [RFC 5746]. @@ -239,7 +241,7 @@ final class RenegoInfoExtension { "renegotiation"); } else { // verify the client_verify_data value - if (!Arrays.equals(shc.conContext.clientVerifyData, + if (!MessageDigest.isEqual(shc.conContext.clientVerifyData, spec.renegotiatedConnection)) { throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, "Invalid renegotiation_info extension data: " + @@ -459,14 +461,14 @@ final class RenegoInfoExtension { } byte[] cvd = chc.conContext.clientVerifyData; - if (!Arrays.equals(spec.renegotiatedConnection, + if (!ByteArrays.isEqual(spec.renegotiatedConnection, 0, cvd.length, cvd, 0, cvd.length)) { throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "Invalid renegotiation_info in ServerHello: " + "unmatched client_verify_data value"); } byte[] svd = chc.conContext.serverVerifyData; - if (!Arrays.equals(spec.renegotiatedConnection, + if (!ByteArrays.isEqual(spec.renegotiatedConnection, cvd.length, infoLen, svd, 0, svd.length)) { throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "Invalid renegotiation_info in ServerHello: " + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java index acc3a96de093f68c13d1922da6c1f71ad29d1ded..9f2cbaf4837bb1e33a2ff436e9f3c60f761ec1aa 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java @@ -184,7 +184,7 @@ public final class SSLLogger { } private static void log(Level level, String msg, Object... params) { - if (logger.isLoggable(level)) { + if (logger != null && logger.isLoggable(level)) { if (params == null || params.length == 0) { logger.log(level, msg); } else { diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java index bdbf7f8dba4db1485d83b620f1cfe253a8922da4..a7ba9a5e566a5aeabfae7e0083d63ec4d42e4807 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,15 @@ import javax.net.ssl.SSLHandshakeException; import sun.security.ssl.CipherSuite.HashAlg; final class SSLSecretDerivation implements SSLKeyDerivation { + + /* + * Performance optimization: + * + * Derive-Secret(Secret, Label, Messages) = + * HKDF-Expand-Label(..., Transcript-Hash(""), ...); + * + * Hardcode tha Transcript-Hash("") result and skip a digest operation. + */ private static final byte[] sha256EmptyDigest = new byte[] { (byte)0xE3, (byte)0xB0, (byte)0xC4, (byte)0x42, (byte)0x98, (byte)0xFC, (byte)0x1C, (byte)0x14, @@ -45,6 +54,7 @@ final class SSLSecretDerivation implements SSLKeyDerivation { (byte)0x78, (byte)0x52, (byte)0xB8, (byte)0x55 }; + // See above. private static final byte[] sha384EmptyDigest = new byte[] { (byte)0x38, (byte)0xB0, (byte)0x60, (byte)0xA7, (byte)0x51, (byte)0xAC, (byte)0x96, (byte)0x38, @@ -68,7 +78,6 @@ final class SSLSecretDerivation implements SSLKeyDerivation { HandshakeContext context, SecretKey secret) { this.secret = secret; this.hashAlg = context.negotiatedCipherSuite.hashAlg; - String hkdfAlg = "HKDF-Expand/Hmac" + hashAlg.name.replace("-", ""); context.handshakeHash.update(); this.transcriptHash = context.handshakeHash.digest(); } @@ -141,7 +150,7 @@ final class SSLSecretDerivation implements SSLKeyDerivation { private final byte[] label; - private SecretSchedule(String label) { + SecretSchedule(String label) { this.label = ("tls13 " + label).getBytes(); } } diff --git a/src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java index b9c8febd2385afd2147d45a5790e0b2b8bf1f7b5..be5814acc501755a66ef2c48838389bd2bd23550 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -92,6 +92,17 @@ final class ServerKeyExchange { // clean up this consumer chc.handshakeConsumers.remove(SSLHandshake.SERVER_KEY_EXCHANGE.id); + // Any receipt/consumption of the CertificateRequest before + // ServerKeyExchange is a state machine violation. We may not + // know for sure if an early CR message is a violation though until + // we have reached this point, due to other TLS features and + // optional messages. + if (chc.receivedCertReq) { + chc.receivedCertReq = false; // Reset flag + throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected ServerKeyExchange handshake message"); + } + SSLConsumer certStatCons = chc.handshakeConsumers.remove( SSLHandshake.CERTIFICATE_STATUS.id); if (certStatCons != null) { diff --git a/src/java.base/share/classes/sun/security/ssl/SessionId.java b/src/java.base/share/classes/sun/security/ssl/SessionId.java index f7dedc68fb32a44a52eb4da3bb28526858c3b58e..95a524e736cd10316dfae6d6b59e10f63cf8b3cb 100644 --- a/src/java.base/share/classes/sun/security/ssl/SessionId.java +++ b/src/java.base/share/classes/sun/security/ssl/SessionId.java @@ -25,6 +25,7 @@ package sun.security.ssl; +import java.security.MessageDigest; import java.security.SecureRandom; import java.util.Arrays; import javax.net.ssl.SSLProtocolException; @@ -89,7 +90,7 @@ final class SessionId { if (obj instanceof SessionId) { SessionId that = (SessionId)obj; - return Arrays.equals(this.sessionId, that.sessionId); + return MessageDigest.isEqual(this.sessionId, that.sessionId); } return false; diff --git a/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java b/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java index 0901edc5d97d8cf1df84d1858c89b51ded504f91..d61d6e0d3e77232590c3eb6236448d0301b50ad1 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java @@ -32,7 +32,10 @@ import java.security.cert.CertificateEncodingException; import java.security.*; import java.security.spec.ECGenParameterSpec; import java.security.spec.NamedParameterSpec; +import java.util.Calendar; import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; import sun.security.pkcs10.PKCS10; import sun.security.util.SignatureUtil; @@ -304,6 +307,12 @@ public final class CertAndKeyGen { try { lastDate = new Date (); lastDate.setTime (firstDate.getTime () + validity * 1000); + Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + c.setTime(lastDate); + if (c.get(Calendar.YEAR) > 9999) { + throw new CertificateException("Validity period ends at calendar year " + + c.get(Calendar.YEAR) + " which is greater than 9999"); + } CertificateValidity interval = new CertificateValidity(firstDate,lastDate); diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index 737d907f4b40f3115ffbe76fbca17efa4bf96e6b..2db773ed61df24d1126fcac635fe86303940e3d0 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java @@ -262,6 +262,7 @@ public final class Main { ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH, V), SHOWINFO("showinfo.command.help", TLS, V), + VERSION("Prints.the.program.version"), // Undocumented start here, KEYCLONE is used a marker in -help; @@ -717,7 +718,7 @@ public final class Main { } boolean isKeyStoreRelated(Command cmd) { - return cmd != PRINTCERTREQ && cmd != SHOWINFO; + return cmd != PRINTCERTREQ && cmd != SHOWINFO && cmd != VERSION; } /** @@ -1337,6 +1338,8 @@ public final class Main { doPrintCRL(filename, out); } else if (command == SHOWINFO) { doShowInfo(); + } else if (command == VERSION) { + doPrintVersion(); } // If we need to save the keystore, do so. @@ -1445,8 +1448,7 @@ public final class Main { X509CertInfo.DN_NAME); Date firstDate = getStartDate(startDate); - Date lastDate = new Date(); - lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L); + Date lastDate = getLastDate(firstDate, validity); CertificateValidity interval = new CertificateValidity(firstDate, lastDate); @@ -1558,12 +1560,10 @@ public final class Main { X509CertInfo.DN_NAME); Date firstDate = getStartDate(startDate); - Date lastDate = (Date) firstDate.clone(); - lastDate.setTime(lastDate.getTime() + validity*1000*24*60*60); + Date lastDate = getLastDate(firstDate, validity); CertificateValidity interval = new CertificateValidity(firstDate, lastDate); - PrivateKey privateKey = (PrivateKey)recoverKey(alias, storePass, keyPass).fst; if (sigAlgName == null) { @@ -2797,6 +2797,10 @@ public final class Main { } } + private void doPrintVersion() { + System.out.println("keytool " + System.getProperty("java.version")); + } + private Collection generateCertificates(InputStream in) throws CertificateException, IOException { byte[] data = in.readAllBytes(); @@ -3031,8 +3035,7 @@ public final class Main { // Extend its validity Date firstDate = getStartDate(startDate); - Date lastDate = new Date(); - lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L); + Date lastDate = getLastDate(firstDate, validity); CertificateValidity interval = new CertificateValidity(firstDate, lastDate); certInfo.set(X509CertInfo.VALIDITY, interval); @@ -4693,6 +4696,21 @@ public final class Main { return result; } + private Date getLastDate(Date firstDate, long validity) + throws Exception { + Date lastDate = new Date(); + lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L); + + Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + c.setTime(lastDate); + if (c.get(Calendar.YEAR) > 9999) { + throw new Exception("Validity period ends at calendar year " + + c.get(Calendar.YEAR) + " which is greater than 9999"); + } + + return lastDate; + } + private boolean isTrustedCert(Certificate cert) throws KeyStoreException { if (caks != null && caks.getCertificateAlias(cert) != null) { return true; diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Resources.java b/src/java.base/share/classes/sun/security/tools/keytool/Resources.java index 30294ad4e086959752500d3a60310bedd4314589..2733e45aeea46849ab3bf556bfa3a049a7a478b0 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Resources.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Resources.java @@ -97,6 +97,7 @@ public class Resources extends java.util.ListResourceBundle { {"Changes.the.store.password.of.a.keystore", "Changes the store password of a keystore"}, //-storepasswd {"showinfo.command.help", "Displays security-related information"}, + {"Prints.the.program.version", "Prints the program version"}, // keytool: help: options {"alias.name.of.the.entry.to.process", diff --git a/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java b/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java index ee97d461ac1278d7a35cdb4ee8f663439c617d9b..69eaa22aafb043b6548c91222238c285c27a1407 100644 --- a/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java +++ b/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ package sun.security.util; import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.Arrays; import java.util.Collection; @@ -40,6 +42,14 @@ public class AlgorithmDecomposer { private static final Pattern PATTERN = Pattern.compile("with|and|(? DECOMPOSED_DIGEST_NAMES = + Map.of("SHA-1", "SHA1", "SHA-224", "SHA224", "SHA-256", "SHA256", + "SHA-384", "SHA384", "SHA-512", "SHA512", "SHA-512/224", + "SHA512/224", "SHA-512/256", "SHA512/256"); + private static Set decomposeImpl(String algorithm) { Set elements = new HashSet<>(); @@ -93,44 +103,19 @@ public class AlgorithmDecomposer { // signature algorithm "SHA256withRSA". So we need to check both // "SHA-256" and "SHA256" to make the right constraint checking. - // handle special name: SHA-1 and SHA1 - if (elements.contains("SHA1") && !elements.contains("SHA-1")) { - elements.add("SHA-1"); - } - if (elements.contains("SHA-1") && !elements.contains("SHA1")) { - elements.add("SHA1"); + // no need to check further if algorithm doesn't contain "SHA" + if (!algorithm.contains("SHA")) { + return elements; } - // handle special name: SHA-224 and SHA224 - if (elements.contains("SHA224") && !elements.contains("SHA-224")) { - elements.add("SHA-224"); - } - if (elements.contains("SHA-224") && !elements.contains("SHA224")) { - elements.add("SHA224"); - } - - // handle special name: SHA-256 and SHA256 - if (elements.contains("SHA256") && !elements.contains("SHA-256")) { - elements.add("SHA-256"); - } - if (elements.contains("SHA-256") && !elements.contains("SHA256")) { - elements.add("SHA256"); - } - - // handle special name: SHA-384 and SHA384 - if (elements.contains("SHA384") && !elements.contains("SHA-384")) { - elements.add("SHA-384"); - } - if (elements.contains("SHA-384") && !elements.contains("SHA384")) { - elements.add("SHA384"); - } - - // handle special name: SHA-512 and SHA512 - if (elements.contains("SHA512") && !elements.contains("SHA-512")) { - elements.add("SHA-512"); - } - if (elements.contains("SHA-512") && !elements.contains("SHA512")) { - elements.add("SHA512"); + for (Map.Entry e : DECOMPOSED_DIGEST_NAMES.entrySet()) { + if (elements.contains(e.getValue()) && + !elements.contains(e.getKey())) { + elements.add(e.getKey()); + } else if (elements.contains(e.getKey()) && + !elements.contains(e.getValue())) { + elements.add(e.getValue()); + } } return elements; @@ -153,40 +138,44 @@ public class AlgorithmDecomposer { return Arrays.asList(aliases); } - private static void hasLoop(Set elements, String find, String replace) { - if (elements.contains(find)) { - if (!elements.contains(replace)) { - elements.add(replace); - } - elements.remove(find); - } - } - - /* - * This decomposes a standard name into sub-elements with a consistent - * message digest algorithm name to avoid overly complicated checking. + /** + * Decomposes a standard algorithm name into sub-elements and uses a + * consistent message digest algorithm name to avoid overly complicated + * checking. */ - public static Set decomposeOneHash(String algorithm) { + static Set decomposeName(String algorithm) { if (algorithm == null || algorithm.isEmpty()) { return new HashSet<>(); } Set elements = decomposeImpl(algorithm); - hasLoop(elements, "SHA-1", "SHA1"); - hasLoop(elements, "SHA-224", "SHA224"); - hasLoop(elements, "SHA-256", "SHA256"); - hasLoop(elements, "SHA-384", "SHA384"); - hasLoop(elements, "SHA-512", "SHA512"); + // no need to check further if algorithm doesn't contain "SHA" + if (!algorithm.contains("SHA")) { + return elements; + } + + for (Map.Entry e : DECOMPOSED_DIGEST_NAMES.entrySet()) { + if (elements.contains(e.getKey())) { + if (!elements.contains(e.getValue())) { + elements.add(e.getValue()); + } + elements.remove(e.getKey()); + } + } return elements; } - /* - * The provided message digest algorithm name will return a consistent - * naming scheme. + /** + * Decomposes a standard message digest algorithm name into a consistent + * name for matching purposes. + * + * @param algorithm the name to be decomposed + * @return the decomposed name, or the passed in algorithm name if + * it is not a digest algorithm or does not need to be decomposed */ - public static String hashName(String algorithm) { - return algorithm.replace("-", ""); + static String decomposeDigestName(String algorithm) { + return DECOMPOSED_DIGEST_NAMES.getOrDefault(algorithm, algorithm); } } diff --git a/src/java.base/share/classes/sun/security/util/ByteArrays.java b/src/java.base/share/classes/sun/security/util/ByteArrays.java new file mode 100644 index 0000000000000000000000000000000000000000..1457489f4bfc9d1c1fde3c4865c02dc892017537 --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/ByteArrays.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + * A time-instance comparison of two byte arrays. + */ +public class ByteArrays { + // See the MessageDigest.isEqual(byte[] digesta, byte[] digestb) + // implementation. This is a potential enhancement of the + // MessageDigest class. + public static boolean isEqual(byte[] a, int aFromIndex, int aToIndex, + byte[] b, int bFromIndex, int bToIndex) { + if (a == b) { + return true; + } + + if (a == null || b == null) { + return false; + } + + if (a.length == 0) { + return b.length == 0; + } + + int lenA = aToIndex - aFromIndex; + int lenB = bToIndex - bFromIndex; + + if (lenB == 0) { + return lenA == 0; + } + + int result = 0; + result |= lenA - lenB; + + // time-constant comparison + for (int indexA = 0; indexA < lenA; indexA++) { + int indexB = ((indexA - lenB) >>> 31) * indexA; + result |= a[aFromIndex + indexA] ^ b[bFromIndex + indexB]; + } + + return result == 0; + } +} diff --git a/src/java.base/share/classes/sun/security/util/Debug.java b/src/java.base/share/classes/sun/security/util/Debug.java index aca672bdb3152486b1a8da4dccc042b8dc71d2af..31c650e521da547cd8a342428b40ca1930702d7c 100644 --- a/src/java.base/share/classes/sun/security/util/Debug.java +++ b/src/java.base/share/classes/sun/security/util/Debug.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -262,7 +262,7 @@ public class Debug { private static String marshal(String args) { if (args != null) { StringBuilder target = new StringBuilder(); - StringBuffer source = new StringBuffer(args); + StringBuilder source = new StringBuilder(args); // obtain the "permission=" options // the syntax of classname: IDENTIFIER.IDENTIFIER @@ -274,7 +274,7 @@ public class Debug { "[a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)*"; Pattern pattern = Pattern.compile(reg); Matcher matcher = pattern.matcher(source); - StringBuffer left = new StringBuffer(); + StringBuilder left = new StringBuilder(); while (matcher.find()) { String matched = matcher.group(); target.append(matched.replaceFirst(keyReg, keyStr)); @@ -298,7 +298,7 @@ public class Debug { reg = keyReg + "[^, ;]*"; pattern = Pattern.compile(reg); matcher = pattern.matcher(source); - left = new StringBuffer(); + left = new StringBuilder(); while (matcher.find()) { String matched = matcher.group(); target.append(matched.replaceFirst(keyReg, keyStr)); diff --git a/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java b/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java index de8c2e5fb22935cadadfd77215f3a33403f66507..f2b88560ca38c565b3d3b8d9683e6729fad6669b 100644 --- a/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java +++ b/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,9 +31,12 @@ import java.util.ArrayList; import java.util.Arrays; /** - * A package private utility class to convert indefinite length DER + * A package private utility class to convert indefinite length BER * encoded byte arrays to definite length DER encoded byte arrays. - * + *

      + * Note: This class only substitute indefinite length octets to definite + * length octets. It does not update the contents even if they are not DER. + *

      * This assumes that the basic data structure is "tag, length, value" * triplet. In the case where the length is "indefinite", terminating * end-of-contents bytes are expected. @@ -42,26 +45,30 @@ import java.util.Arrays; */ class DerIndefLenConverter { - private static final int TAG_MASK = 0x1f; // bits 5-1 - private static final int FORM_MASK = 0x20; // bits 6 - private static final int CLASS_MASK = 0xC0; // bits 8 and 7 - private static final int LEN_LONG = 0x80; // bit 8 set private static final int LEN_MASK = 0x7f; // bits 7 - 1 - private static final int SKIP_EOC_BYTES = 2; private byte[] data, newData; private int newDataPos, dataPos, dataSize, index; private int unresolved = 0; + // A list to store each indefinite length occurrence. Whenever an indef + // length is seen, the position after the 0x80 byte is appended to the + // list as an integer. Whenever its matching EOC is seen, we know the + // actual length and the position value is substituted with a calculated + // length octets. At the end, the new DER encoding is a concatenation of + // all existing tags, existing definite length octets, existing contents, + // and the newly created definte length octets in this list. private ArrayList ndefsList = new ArrayList(); + // Length of extra bytes needed to convert indefinite encoding to definite. + // For each resolved indefinite length encoding, the starting 0x80 byte + // and the ending 00 00 bytes will be removed and a new definite length + // octets will be added. This value might be positive or negative. private int numOfTotalLenBytes = 0; - private boolean isEOC(int tag) { - return (((tag & TAG_MASK) == 0x00) && // EOC - ((tag & FORM_MASK) == 0x00) && // primitive - ((tag & CLASS_MASK) == 0x00)); // universal + private static boolean isEOC(byte[] data, int pos) { + return data[pos] == 0 && data[pos + 1] == 0; } // if bit 8 is set then it implies either indefinite length or long form @@ -70,9 +77,9 @@ class DerIndefLenConverter { } /* - * Default package private constructor + * Private constructor */ - DerIndefLenConverter() { } + private DerIndefLenConverter() { } /** * Checks whether the given length byte is of the form @@ -88,11 +95,14 @@ class DerIndefLenConverter { } /** - * Parse the tag and if it is an end-of-contents tag then - * add the current position to the eocList vector. + * Consumes the tag at {@code dataPos}. + *

      + * If it is EOC then replace the matching start position (i.e. the previous + * {@code dataPos} where an indefinite length was found by #parseLength) + * in {@code ndefsList} with a length octets for this section. */ private void parseTag() throws IOException { - if (isEOC(data[dataPos]) && (data[dataPos + 1] == 0)) { + if (isEOC(data, dataPos)) { int numOfEncapsulatedLenBytes = 0; Object elem = null; int index; @@ -103,6 +113,9 @@ class DerIndefLenConverter { if (elem instanceof Integer) { break; } else { + // For each existing converted part, 3 bytes (80 at the + // beginning and 00 00 at the end) are removed and a + // new length octets is added. numOfEncapsulatedLenBytes += ((byte[])elem).length - 3; } } @@ -114,6 +127,7 @@ class DerIndefLenConverter { numOfEncapsulatedLenBytes; byte[] sectionLenBytes = getLengthBytes(sectionLen); ndefsList.set(index, sectionLenBytes); + assert unresolved > 0; unresolved--; // Add the number of bytes required to represent this section @@ -130,34 +144,41 @@ class DerIndefLenConverter { * then skip the tag and its 1 byte length of zero. */ private void writeTag() { - if (dataPos == dataSize) + if (dataPos == dataSize) { return; - int tag = data[dataPos++]; - if (isEOC(tag) && (data[dataPos] == 0)) { - dataPos++; // skip length + } + assert dataPos + 1 < dataSize; + if (isEOC(data, dataPos)) { + dataPos += 2; // skip tag and length writeTag(); - } else - newData[newDataPos++] = (byte)tag; + } else { + newData[newDataPos++] = data[dataPos++]; + } } /** - * Parse the length and if it is an indefinite length then add - * the current position to the ndefsList vector. + * Parse the length octets started at {@code dataPos}. After this method + * is called, {@code dataPos} is placed after the length octets except + * -1 is returned. * - * @return the length of definite length data next, or -1 if there is - * not enough bytes to determine it + * @return a) the length of definite length data next + * b) -1, if it is a definite length data next but the length + * octets is not complete to determine the actual length + * c) 0, if it is an indefinite length. Also, append the current + * position to the {@code ndefsList} vector. * @throws IOException if invalid data is read */ private int parseLength() throws IOException { - int curLen = 0; - if (dataPos == dataSize) - return curLen; + if (dataPos == dataSize) { + return 0; + } int lenByte = data[dataPos++] & 0xff; if (isIndefinite(lenByte)) { ndefsList.add(dataPos); unresolved++; - return curLen; + return 0; } + int curLen = 0; if (isLongForm(lenByte)) { lenByte &= LEN_MASK; if (lenByte > 4) { @@ -179,14 +200,17 @@ class DerIndefLenConverter { } /** - * Write the length and if it is an indefinite length - * then calculate the definite length from the positions - * of the indefinite length and its matching EOC terminator. - * Then, write the value. + * Write the length and value. + *

      + * If it was definite length, just re-write the length and copy the value. + * If it was an indefinite length, copy the precalculated definite octets + * from {@code ndefsList}. There is no values here because they will be + * sub-encodings of a constructed encoding. */ private void writeLengthAndValue() throws IOException { - if (dataPos == dataSize) - return; + if (dataPos == dataSize) { + return; + } int curLen = 0; int lenByte = data[dataPos++] & 0xff; if (isIndefinite(lenByte)) { @@ -194,21 +218,21 @@ class DerIndefLenConverter { System.arraycopy(lenBytes, 0, newData, newDataPos, lenBytes.length); newDataPos += lenBytes.length; - return; - } - if (isLongForm(lenByte)) { - lenByte &= LEN_MASK; - for (int i = 0; i < lenByte; i++) { - curLen = (curLen << 8) + (data[dataPos++] & 0xff); - } - if (curLen < 0) { - throw new IOException("Invalid length bytes"); - } } else { - curLen = (lenByte & LEN_MASK); + if (isLongForm(lenByte)) { + lenByte &= LEN_MASK; + for (int i = 0; i < lenByte; i++) { + curLen = (curLen << 8) + (data[dataPos++] & 0xff); + } + if (curLen < 0) { + throw new IOException("Invalid length bytes"); + } + } else { + curLen = (lenByte & LEN_MASK); + } + writeLength(curLen); + writeValue(curLen); } - writeLength(curLen); - writeValue(curLen); } private void writeLength(int curLen) { @@ -296,19 +320,13 @@ class DerIndefLenConverter { return numOfLenBytes; } - /** - * Parse the value; - */ - private void parseValue(int curLen) { - dataPos += curLen; - } - /** * Write the value; */ private void writeValue(int curLen) { - for (int i=0; i < curLen; i++) - newData[newDataPos++] = data[dataPos++]; + System.arraycopy(data, dataPos, newData, newDataPos, curLen); + dataPos += curLen; + newDataPos += curLen; } /** @@ -323,10 +341,8 @@ class DerIndefLenConverter { */ byte[] convertBytes(byte[] indefData) throws IOException { data = indefData; - dataPos=0; index=0; + dataPos = 0; dataSize = data.length; - int len=0; - int unused = 0; // parse and set up the vectors of all the indefinite-lengths while (dataPos < dataSize) { @@ -335,14 +351,17 @@ class DerIndefLenConverter { return null; } parseTag(); - len = parseLength(); + int len = parseLength(); if (len < 0) { return null; } - parseValue(len); + dataPos += len; + if (dataPos < 0) { + // overflow + throw new IOException("Data overflow"); + } if (unresolved == 0) { - unused = dataSize - dataPos; - dataSize = dataPos; + assert !ndefsList.isEmpty() && ndefsList.get(0) instanceof byte[]; break; } } @@ -351,14 +370,18 @@ class DerIndefLenConverter { return null; } + int unused = dataSize - dataPos; + assert unused >= 0; + dataSize = dataPos; + newData = new byte[dataSize + numOfTotalLenBytes + unused]; - dataPos=0; newDataPos=0; index=0; + dataPos = 0; newDataPos = 0; index = 0; // write out the new byte array replacing all the indefinite-lengths // and EOCs while (dataPos < dataSize) { - writeTag(); - writeLengthAndValue(); + writeTag(); + writeLengthAndValue(); } System.arraycopy(indefData, dataSize, newData, dataSize + numOfTotalLenBytes, unused); @@ -395,7 +418,7 @@ class DerIndefLenConverter { if (result == null) { int next = in.read(); // This could block, but we need more if (next == -1) { - throw new IOException("not all indef len BER resolved"); + throw new IOException("not enough data to resolve indef len BER"); } int more = in.available(); // expand array to include next and more diff --git a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java index c56f9b60b750126a5eac6f3eeb27af6213baf7b9..2bf39bfae6f4668c47d1db296264b6887038340f 100644 --- a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java @@ -304,10 +304,6 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { /** * Key and Certificate Constraints * - * The complete disabling of an algorithm is not handled by Constraints or - * Constraint classes. That is addressed with - * permit(Set, String, AlgorithmParameters) - * * When passing a Key to permit(), the boolean return values follow the * same as the interface class AlgorithmConstraints.permit(). This is to * maintain compatibility: @@ -318,7 +314,6 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { * will be thrown on a failure to better identify why the operation was * disallowed. */ - private static class Constraints { private Map> constraintsMap = new HashMap<>(); @@ -341,9 +336,9 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { // Check if constraint is a complete disabling of an // algorithm or has conditions. int space = constraintEntry.indexOf(' '); - String algorithm = AlgorithmDecomposer.hashName( - ((space > 0 ? constraintEntry.substring(0, space) : - constraintEntry))); + String algorithm = AlgorithmDecomposer.decomposeDigestName( + space > 0 ? constraintEntry.substring(0, space) : + constraintEntry); List constraintList = constraintsMap.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH), @@ -497,7 +492,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { // Get all signature algorithms to check for constraints Set algorithms = new HashSet<>(); if (algorithm != null) { - algorithms.addAll(AlgorithmDecomposer.decomposeOneHash(algorithm)); + algorithms.addAll(AlgorithmDecomposer.decomposeName(algorithm)); algorithms.add(algorithm); } diff --git a/src/java.base/share/classes/sun/security/util/HostnameChecker.java b/src/java.base/share/classes/sun/security/util/HostnameChecker.java index 0183e3f4f4a2b464dd0483e0b6c23629ce6b8a0a..aacd94837c962cb92ad48c6ced680539886e84f6 100644 --- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java +++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -254,8 +254,7 @@ public class HostnameChecker { return new X500Name(subjectX500.getEncoded()); } } catch (IOException e) { - throw(CertificateParsingException) - new CertificateParsingException().initCause(e); + throw new CertificateParsingException(e); } } diff --git a/src/java.base/share/classes/sun/security/util/ManifestDigester.java b/src/java.base/share/classes/sun/security/util/ManifestDigester.java index 3920d8a6b76a3ecf29ff65b180f3070ece189964..969df7c0eb8b35bdaa51e6eb9b085ffcf51fd665 100644 --- a/src/java.base/share/classes/sun/security/util/ManifestDigester.java +++ b/src/java.base/share/classes/sun/security/util/ManifestDigester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -331,8 +331,12 @@ public class ManifestDigester { * @see #MF_MAIN_ATTRS */ public Entry getMainAttsEntry(boolean oldStyle) { - mainAttsEntry.oldStyle = oldStyle; - return mainAttsEntry; + if (mainAttsEntry != null) { + mainAttsEntry.oldStyle = oldStyle; + return mainAttsEntry; + } else { + return null; + } } public Entry get(String name) { diff --git a/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java b/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java index 062190aeddfebc174377b801efac0fb4ab28480f..43d0e751dee4501e4d6fbd939046cb56cde07238 100644 --- a/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java +++ b/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java @@ -63,7 +63,9 @@ public class ManifestEntryVerifier { ArrayList manifestHashes; private String name = null; - private Manifest man; + + private final String manifestFileName; // never null + private final Manifest man; private boolean skip = true; @@ -74,11 +76,12 @@ public class ManifestEntryVerifier { /** * Create a new ManifestEntryVerifier object. */ - public ManifestEntryVerifier(Manifest man) + public ManifestEntryVerifier(Manifest man, String manifestFileName) { createdDigests = new HashMap<>(11); digests = new ArrayList<>(); manifestHashes = new ArrayList<>(); + this.manifestFileName = manifestFileName; this.man = man; } @@ -187,7 +190,6 @@ public class ManifestEntryVerifier { * the first time we have verified this object, remove its * code signers from sigFileSigners and place in verifiedSigners. * - * */ public CodeSigner[] verify(Hashtable verifiedSigners, Hashtable sigFileSigners) @@ -209,7 +211,6 @@ public class ManifestEntryVerifier { getParams(verifiedSigners, sigFileSigners); for (int i=0; i < digests.size(); i++) { - MessageDigest digest = digests.get(i); if (params != null) { try { @@ -251,7 +252,8 @@ public class ManifestEntryVerifier { /** * Get constraints parameters for JAR. The constraints should be * checked against all code signers. Returns the parameters, - * or null if the signers for this entry have already been checked. + * or null if the signers for this entry have already been checked + * or there are no signers for this entry. */ private JarConstraintsParameters getParams( Map verifiedSigners, @@ -262,17 +264,20 @@ public class ManifestEntryVerifier { // the signers of the JAR. But if it doesn't then we need to fallback // and check verifiedSigners to see if the signers of this entry have // been checked already. - if (verifiedSigners.containsKey(JarFile.MANIFEST_NAME)) { + if (verifiedSigners.containsKey(manifestFileName)) { if (verifiedSigners.size() > 1) { // this means we already checked it previously return null; } else { return new JarConstraintsParameters( - verifiedSigners.get(JarFile.MANIFEST_NAME)); + verifiedSigners.get(manifestFileName)); } } else { + if (debug != null) { + debug.println(manifestFileName + " not present in verifiedSigners"); + } CodeSigner[] signers = sigFileSigners.get(name); - if (verifiedSigners.containsValue(signers)) { + if (signers == null || verifiedSigners.containsValue(signers)) { return null; } else { return new JarConstraintsParameters(signers); diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java index 04e6e9a085fd979135915ed1f72bef5138894d68..1af7417b7f1ada332c14bcfb68c7e439aecc541d 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -543,6 +543,10 @@ public class SignatureFileVerifier { MessageDigest digest = getDigest(algorithm); if (digest != null) { ManifestDigester.Entry mde = md.getMainAttsEntry(false); + if (mde == null) { + throw new SignatureException("Manifest Main Attribute check " + + "failed due to missing main attributes entry"); + } byte[] computedHash = mde.digest(digest); byte[] expectedHash = Base64.getMimeDecoder().decode((String)se.getValue()); diff --git a/src/java.base/share/classes/sun/security/util/SignatureUtil.java b/src/java.base/share/classes/sun/security/util/SignatureUtil.java index f65dff6d31406e3bcb7ce8be0b8cae1639489e59..6ad35d12386339084eb7c65eaa420ccafc232a87 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureUtil.java +++ b/src/java.base/share/classes/sun/security/util/SignatureUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -170,8 +170,7 @@ public class SignatureUtil { // for verification with the specified key and params (may be null) public static void initVerifyWithParam(Signature s, PublicKey key, AlgorithmParameterSpec params) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, key, params); } @@ -180,8 +179,7 @@ public class SignatureUtil { public static void initVerifyWithParam(Signature s, java.security.cert.Certificate cert, AlgorithmParameterSpec params) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, cert, params); } @@ -189,8 +187,7 @@ public class SignatureUtil { // for signing with the specified key and params (may be null) public static void initSignWithParam(Signature s, PrivateKey key, AlgorithmParameterSpec params, SecureRandom sr) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initSign(s, key, params, sr); } @@ -342,10 +339,10 @@ public class SignatureUtil { * Create a Signature that has been initialized with proper key and params. * * @param sigAlg signature algorithms - * @param key public or private key + * @param key private key * @param provider (optional) provider */ - public static Signature fromKey(String sigAlg, Key key, String provider) + public static Signature fromKey(String sigAlg, PrivateKey key, String provider) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException{ Signature sigEngine = (provider == null || provider.isEmpty()) @@ -358,10 +355,10 @@ public class SignatureUtil { * Create a Signature that has been initialized with proper key and params. * * @param sigAlg signature algorithms - * @param key public or private key + * @param key private key * @param provider (optional) provider */ - public static Signature fromKey(String sigAlg, Key key, Provider provider) + public static Signature fromKey(String sigAlg, PrivateKey key, Provider provider) throws NoSuchAlgorithmException, InvalidKeyException{ Signature sigEngine = (provider == null) ? Signature.getInstance(sigAlg) @@ -369,17 +366,12 @@ public class SignatureUtil { return autoInitInternal(sigAlg, key, sigEngine); } - private static Signature autoInitInternal(String alg, Key key, Signature s) + private static Signature autoInitInternal(String alg, PrivateKey key, Signature s) throws InvalidKeyException { AlgorithmParameterSpec params = SignatureUtil .getDefaultParamSpec(alg, key); try { - if (key instanceof PrivateKey) { - SignatureUtil.initSignWithParam(s, (PrivateKey) key, params, - null); - } else { - SignatureUtil.initVerifyWithParam(s, (PublicKey) key, params); - } + SignatureUtil.initSignWithParam(s, key, params, null); } catch (InvalidAlgorithmParameterException e) { throw new AssertionError("Should not happen", e); } diff --git a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java index 0ce9effddf2ad8fe43e1a04de01d2e7c0b78fa31..627fb503ba9c41d88b46166fc59da611f7105a1e 100644 --- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java +++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java @@ -312,7 +312,7 @@ public class AlgorithmId implements Serializable, DerEncoder { * * @return DER encoded parameters, or null not present. */ - public byte[] getEncodedParams() throws IOException { + public byte[] getEncodedParams() { return (encodedParams == null || algid.toString().equals(KnownOIDs.SpecifiedSHA2withECDSA.value())) ? null diff --git a/src/java.base/share/classes/sun/security/x509/IssuingDistributionPointExtension.java b/src/java.base/share/classes/sun/security/x509/IssuingDistributionPointExtension.java index bc0df11a8a23151bd5020221e0db70a51d850aa0..d810cf012ae4672b6fd65005f5c8aafffaafcc2e 100644 --- a/src/java.base/share/classes/sun/security/x509/IssuingDistributionPointExtension.java +++ b/src/java.base/share/classes/sun/security/x509/IssuingDistributionPointExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -393,7 +393,8 @@ public class IssuingDistributionPointExtension extends Extension if (distributionPoint != null) { DerOutputStream tmp = new DerOutputStream(); distributionPoint.encode(tmp); - tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, true, + // DistributionPointName is CHOICE. Do not writeImplicit. + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_DISTRIBUTION_POINT), tmp); } diff --git a/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java b/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java index 124231e961a01daaff37f783a1dd10ce4823eade..1523cde227d38ed6f227d24c9c47f72f8c60561a 100644 --- a/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java +++ b/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -820,13 +820,7 @@ public class X509CRLImpl extends X509CRL implements DerEncoder { * null if no parameters are present. */ public byte[] getSigAlgParams() { - if (sigAlgId == null) - return null; - try { - return sigAlgId.getEncodedParams(); - } catch (IOException e) { - return null; - } + return sigAlgId == null ? null : sigAlgId.getEncodedParams(); } /** diff --git a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java index b22091f1e62d10b1c25a1fa325eef2dd44eb058c..b658a94b8d6e4cdbeb63fb5b23e9b5691d98fc8a 100644 --- a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java +++ b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java @@ -1030,13 +1030,7 @@ public class X509CertImpl extends X509Certificate implements DerEncoder { * null if no parameters are present. */ public byte[] getSigAlgParams() { - if (algId == null) - return null; - try { - return algId.getEncodedParams(); - } catch (IOException e) { - return null; - } + return algId == null ? null : algId.getEncodedParams(); } /** diff --git a/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java b/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java index 71c7212c644084a36f506d5355fa3def47e91ca1..91a689f5f034148659fef12483a4d314887a9e91 100644 --- a/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java +++ b/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -111,7 +111,9 @@ public abstract class CalendarSystem { } } - private static final Gregorian GREGORIAN_INSTANCE = new Gregorian(); + private static final class GregorianHolder { + private static final Gregorian GREGORIAN_INSTANCE = new Gregorian(); + } /** * Returns the singleton instance of the Gregorian @@ -120,7 +122,7 @@ public abstract class CalendarSystem { * @return the Gregorian instance */ public static Gregorian getGregorianCalendar() { - return GREGORIAN_INSTANCE; + return GregorianHolder.GREGORIAN_INSTANCE; } /** @@ -135,7 +137,7 @@ public abstract class CalendarSystem { */ public static CalendarSystem forName(String calendarName) { if ("gregorian".equals(calendarName)) { - return GREGORIAN_INSTANCE; + return GregorianHolder.GREGORIAN_INSTANCE; } if (!initialized) { diff --git a/src/java.base/share/classes/sun/util/calendar/JulianCalendar.java b/src/java.base/share/classes/sun/util/calendar/JulianCalendar.java index 99759c8376040a211953472283fe57d4904ddea2..8129090d295abd886c69b3d7c7b7a3911c9a809a 100644 --- a/src/java.base/share/classes/sun/util/calendar/JulianCalendar.java +++ b/src/java.base/share/classes/sun/util/calendar/JulianCalendar.java @@ -94,7 +94,7 @@ public class JulianCalendar extends BaseCalendar { public String toString() { String time = super.toString(); time = time.substring(time.indexOf('T')); - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); Era era = getEra(); if (era != null) { String n = era.getAbbreviation(); diff --git a/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java b/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java index c26b700c8174137fef3ce88526609febba76d476..8fd582938dd4df79edff55acf05e912ed51cfbc2 100644 --- a/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java +++ b/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java @@ -123,7 +123,7 @@ public class LocalGregorianCalendar extends BaseCalendar { public String toString() { String time = super.toString(); time = time.substring(time.indexOf('T')); - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); Era era = getEra(); if (era != null) { String abbr = era.getAbbreviation(); diff --git a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java index 721b0ca91dea71004e5d6b657684a5be980193a0..9f78c46fd5b80b6d8b01cc858d80583fccf7ec5b 100644 --- a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java +++ b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java @@ -574,7 +574,7 @@ public final class ZoneInfoFile { // ZoneRulesBuilder adjusts < 0 case (-1, for last, don't have // "<=" case yet) to positive value if not February (it appears // we don't have February cutoff in tzdata table yet) - // Ideally, if JSR310 can just pass in the nagative and + // Ideally, if JSR310 can just pass in the negative and // we can then pass in the dom = -1, dow > 0 into ZoneInfo // // hacking, assume the >=24 is the result of ZRB optimization for @@ -618,34 +618,6 @@ public final class ZoneInfoFile { params[8] = endRule.secondOfDay * 1000; params[9] = toSTZTime[endRule.timeDefinition]; dstSavings = (startRule.offsetAfter - startRule.offsetBefore) * 1000; - - // Note: known mismatching -> Asia/Amman - // ZoneInfo : startDayOfWeek=5 <= Thursday - // startTime=86400000 <= 24 hours - // This: startDayOfWeek=6 - // startTime=0 - // Similar workaround needs to be applied to Africa/Cairo and - // its endDayOfWeek and endTime - // Below is the workarounds, it probably slows down everyone a little - if (params[2] == 6 && params[3] == 0 && - (zoneId.equals("Asia/Amman"))) { - params[2] = 5; - params[3] = 86400000; - } - // Additional check for startDayOfWeek=6 and starTime=86400000 - // is needed for Asia/Amman; - if (params[2] == 7 && params[3] == 0 && - (zoneId.equals("Asia/Amman"))) { - params[2] = 6; // Friday - params[3] = 86400000; // 24h - } - //endDayOfWeek and endTime workaround - if (params[7] == 6 && params[8] == 0 && - (zoneId.equals("Africa/Cairo"))) { - params[7] = 5; - params[8] = 86400000; - } - } else if (nTrans > 0) { // only do this if there is something in table already if (lastyear < LASTYEAR) { // ZoneInfo has an ending entry for 2037 @@ -894,12 +866,12 @@ public final class ZoneInfoFile { } // A simple/raw version of j.t.ZoneOffsetTransitionRule + // timeEndOfDay is included in secondOfDay as "86,400" secs. private static class ZoneOffsetTransitionRule { private final int month; private final byte dom; private final int dow; private final int secondOfDay; - private final boolean timeEndOfDay; private final int timeDefinition; private final int standardOffset; private final int offsetBefore; @@ -917,9 +889,7 @@ public final class ZoneInfoFile { this.dom = (byte)(((data & (63 << 22)) >>> 22) - 32); this.dow = dowByte == 0 ? -1 : dowByte; this.secondOfDay = timeByte == 31 ? in.readInt() : timeByte * 3600; - this.timeEndOfDay = timeByte == 24; this.timeDefinition = (data & (3 << 12)) >>> 12; - this.standardOffset = stdByte == 255 ? in.readInt() : (stdByte - 128) * 900; this.offsetBefore = beforeByte == 3 ? in.readInt() : standardOffset + beforeByte * 1800; this.offsetAfter = afterByte == 3 ? in.readInt() : standardOffset + afterByte * 1800; @@ -938,9 +908,6 @@ public final class ZoneInfoFile { epochDay = nextOrSame(epochDay, dow); } } - if (timeEndOfDay) { - epochDay += 1; - } int difference = 0; switch (timeDefinition) { case 0: // UTC diff --git a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties index 238186bf21994167834a489582b7e8855d026c8d..9f1867d2cd1ed3fbb41c2294354746815c3a3b1d 100644 --- a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties +++ b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -251,6 +251,7 @@ USS=USS UYU=UYU UZS=UZS VEB=VEB +VED=VED VEF=VEF VES=VES VND=VND @@ -474,6 +475,7 @@ uss=US Dollar (Same day) uyu=Uruguayan Peso uzs=Uzbekistan Som veb=Venezuelan Bol\u00edvar (1871-2008) +ved=Venezuelan Bol\u00edvar Soberano vef=Venezuelan Bol\u00edvar ves=Venezuelan Bol\u00edvar Soberano vnd=Vietnamese Dong diff --git a/src/java.base/share/lib/security/default.policy b/src/java.base/share/lib/security/default.policy index 8356e56367baaf465f2c9ad81d1097c310a7e321..b22f26947af7660bdd8d370c9182c8a2c432c1d1 100644 --- a/src/java.base/share/lib/security/default.policy +++ b/src/java.base/share/lib/security/default.policy @@ -111,6 +111,8 @@ grant codeBase "jrt:/jdk.accessibility" { grant codeBase "jrt:/jdk.charsets" { permission java.util.PropertyPermission "os.name", "read"; permission java.lang.RuntimePermission "charsetProvider"; + permission java.lang.RuntimePermission + "accessClassInPackage.jdk.internal.access"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; permission java.lang.RuntimePermission "accessClassInPackage.sun.nio.cs"; diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/AuthContext.java b/src/java.base/share/native/libjava/Finalizer.c similarity index 59% rename from src/java.security.jgss/share/classes/sun/security/krb5/internal/AuthContext.java rename to src/java.base/share/native/libjava/Finalizer.c index 05ed71b3229268d89d1ab9c1e37a3712689408de..d0b81f63d6e7f03290e2c680cef0ea7d5e8d73c2 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/AuthContext.java +++ b/src/java.base/share/native/libjava/Finalizer.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,31 +23,13 @@ * questions. */ -/* - * - * (C) Copyright IBM Corp. 1999 All Rights Reserved. - * Copyright 1997 The Open Group Research Institute. All rights reserved. - */ +#include "jvm.h" + +#include "java_lang_ref_Finalizer.h" -package sun.security.krb5.internal; +JNIEXPORT void JNICALL +Java_java_lang_ref_Finalizer_reportComplete(JNIEnv* env, jclass cls, jobject finalizee) { + JVM_ReportFinalizationComplete(env, finalizee); +} -import sun.security.krb5.EncryptionKey; -import java.util.BitSet; -public class AuthContext { - public HostAddress remoteAddress; - public int remotePort; - public HostAddress localAddress; - public int localPort; - public EncryptionKey keyBlock; - public EncryptionKey localSubkey; - public EncryptionKey remoteSubkey; - public BitSet authContextFlags; - public int remoteSeqNumber; - public int localSeqNumber; - public Authenticator authenticator; - public int reqCksumType; - public int safeCksumType; - public byte[] initializationVector; - //public ReplayCache replayCache; -}; diff --git a/src/java.base/share/native/libjava/System.c b/src/java.base/share/native/libjava/System.c index d2c20ff17fc705dd1b3c7ad1af630b63a9f981b8..b4a6e325feb553dc6ace892a08478bac195cc3e4 100644 --- a/src/java.base/share/native/libjava/System.c +++ b/src/java.base/share/native/libjava/System.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -146,7 +146,15 @@ Java_jdk_internal_util_SystemProps_00024Raw_platformProperties(JNIEnv *env, jcla PUTPROP(propArray, _path_separator_NDX, sprops->path_separator); PUTPROP(propArray, _line_separator_NDX, sprops->line_separator); +#ifdef MACOSX + /* + * Since sun_jnu_encoding is now hard-coded to UTF-8 on Mac, we don't + * want to use it to overwrite file.encoding + */ PUTPROP(propArray, _file_encoding_NDX, sprops->encoding); +#else + PUTPROP(propArray, _file_encoding_NDX, sprops->sun_jnu_encoding); +#endif PUTPROP(propArray, _sun_jnu_encoding_NDX, sprops->sun_jnu_encoding); /* diff --git a/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java b/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java index 8de55c57c26d9b64f771d7666f30afdbdb0b2f17..90b1fdc1d2844bf65357be6a4698fe807feb81c6 100644 --- a/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java +++ b/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java @@ -119,8 +119,10 @@ public class NTLMAuthentication extends AuthenticationInfo { }); }; + @SuppressWarnings("serial") // Type of field is not Serializable PasswordAuthentication pw; + @SuppressWarnings("serial") // Type of field is not Serializable Client client; /** * Create a NTLMAuthentication: diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 94dfc207f965204d6485f3e6154b669ac951e96e..2c82a2d678610d1d5c6776fe4d6f1beac11ff430 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ #include "jvm.h" #include "TimeZone_md.h" +#include "path_util.h" static char *isFileIdentical(char* buf, size_t size, char *pathname); @@ -77,6 +78,33 @@ static const char *ETC_ENVIRONMENT_FILE = "/etc/environment"; #if defined(__linux__) || defined(MACOSX) +/* + * remove repeated path separators ('/') in the given 'path'. + */ +static void +removeDuplicateSlashes(char *path) +{ + char *left = path; + char *right = path; + char *end = path + strlen(path); + + for (; right < end; right++) { + // Skip sequence of multiple path-separators. + while (*right == '/' && *(right + 1) == '/') { + right++; + } + + while (*right != '\0' && !(*right == '/' && *(right + 1) == '/')) { + *left++ = *right++; + } + + if (*right == '\0') { + *left = '\0'; + break; + } + } +} + /* * Returns a pointer to the zone ID portion of the given zoneinfo file * name, or NULL if the given string doesn't contain "zoneinfo/". @@ -296,6 +324,8 @@ getPlatformTimeZoneID() return NULL; } linkbuf[len] = '\0'; + removeDuplicateSlashes(linkbuf); + collapse(linkbuf); tz = getZoneName(linkbuf); if (tz != NULL) { tz = strdup(tz); diff --git a/src/java.base/unix/native/libjava/canonicalize_md.c b/src/java.base/unix/native/libjava/canonicalize_md.c index 2bb896bf32d7c8c0104f7b433289da6fb1d41e5b..0585aa3bbdf84d7c2ceb20a1c0d74c9a486025a3 100644 --- a/src/java.base/unix/native/libjava/canonicalize_md.c +++ b/src/java.base/unix/native/libjava/canonicalize_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,155 +33,13 @@ #include #include #include -#if !defined(_ALLBSD_SOURCE) -#include -#endif #include "jdk_util.h" +#include "path_util.h" /* Note: The comments in this file use the terminology defined in the java.io.File class */ - -/* Check the given name sequence to see if it can be further collapsed. - Return zero if not, otherwise return the number of names in the sequence. */ - -static int -collapsible(char *names) -{ - char *p = names; - int dots = 0, n = 0; - - while (*p) { - if ((p[0] == '.') && ((p[1] == '\0') - || (p[1] == '/') - || ((p[1] == '.') && ((p[2] == '\0') - || (p[2] == '/'))))) { - dots = 1; - } - n++; - while (*p) { - if (*p == '/') { - p++; - break; - } - p++; - } - } - return (dots ? n : 0); -} - - -/* Split the names in the given name sequence, - replacing slashes with nulls and filling in the given index array */ - -static void -splitNames(char *names, char **ix) -{ - char *p = names; - int i = 0; - - while (*p) { - ix[i++] = p++; - while (*p) { - if (*p == '/') { - *p++ = '\0'; - break; - } - p++; - } - } -} - - -/* Join the names in the given name sequence, ignoring names whose index - entries have been cleared and replacing nulls with slashes as needed */ - -static void -joinNames(char *names, int nc, char **ix) -{ - int i; - char *p; - - for (i = 0, p = names; i < nc; i++) { - if (!ix[i]) continue; - if (i > 0) { - p[-1] = '/'; - } - if (p == ix[i]) { - p += strlen(p) + 1; - } else { - char *q = ix[i]; - while ((*p++ = *q++)); - } - } - *p = '\0'; -} - - -/* Collapse "." and ".." names in the given path wherever possible. - A "." name may always be eliminated; a ".." name may be eliminated if it - follows a name that is neither "." nor "..". This is a syntactic operation - that performs no filesystem queries, so it should only be used to cleanup - after invoking the realpath() procedure. */ - -static void -collapse(char *path) -{ - char *names = (path[0] == '/') ? path + 1 : path; /* Preserve first '/' */ - int nc; - char **ix; - int i, j; - char *p, *q; - - nc = collapsible(names); - if (nc < 2) return; /* Nothing to do */ - ix = (char **)alloca(nc * sizeof(char *)); - splitNames(names, ix); - - for (i = 0; i < nc; i++) { - int dots = 0; - - /* Find next occurrence of "." or ".." */ - do { - char *p = ix[i]; - if (p[0] == '.') { - if (p[1] == '\0') { - dots = 1; - break; - } - if ((p[1] == '.') && (p[2] == '\0')) { - dots = 2; - break; - } - } - i++; - } while (i < nc); - if (i >= nc) break; - - /* At this point i is the index of either a "." or a "..", so take the - appropriate action and then continue the outer loop */ - if (dots == 1) { - /* Remove this instance of "." */ - ix[i] = 0; - } - else { - /* If there is a preceding name, remove both that name and this - instance of ".."; otherwise, leave the ".." as is */ - for (j = i - 1; j >= 0; j--) { - if (ix[j]) break; - } - if (j < 0) continue; - ix[j] = 0; - ix[i] = 0; - } - /* i will be incremented at the top of the loop */ - } - - joinNames(names, nc, ix); -} - - /* Convert a pathname to canonical form. The input path is assumed to contain no duplicate slashes. On Solaris we can use realpath() to do most of the work, though once that's done we still must collapse any remaining "." and diff --git a/src/java.base/unix/native/libjava/path_util.c b/src/java.base/unix/native/libjava/path_util.c new file mode 100644 index 0000000000000000000000000000000000000000..0a47f11666b51c1a48a1254752344a6c3fc5b7df --- /dev/null +++ b/src/java.base/unix/native/libjava/path_util.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#if !defined(_ALLBSD_SOURCE) +#include +#endif + +#include "path_util.h" + +/* Check the given name sequence to see if it can be further collapsed. + Return zero if not, otherwise return the number of names in the sequence. */ + +static int +collapsible(char *names) +{ + char *p = names; + int dots = 0, n = 0; + + while (*p) { + if ((p[0] == '.') && ((p[1] == '\0') + || (p[1] == '/') + || ((p[1] == '.') && ((p[2] == '\0') + || (p[2] == '/'))))) { + dots = 1; + } + n++; + while (*p) { + if (*p == '/') { + p++; + break; + } + p++; + } + } + return (dots ? n : 0); +} + + +/* Split the names in the given name sequence, + replacing slashes with nulls and filling in the given index array */ + +static void +splitNames(char *names, char **ix) +{ + char *p = names; + int i = 0; + + while (*p) { + ix[i++] = p++; + while (*p) { + if (*p == '/') { + *p++ = '\0'; + break; + } + p++; + } + } +} + + +/* Join the names in the given name sequence, ignoring names whose index + entries have been cleared and replacing nulls with slashes as needed */ + +static void +joinNames(char *names, int nc, char **ix) +{ + int i; + char *p; + + for (i = 0, p = names; i < nc; i++) { + if (!ix[i]) continue; + if (i > 0) { + p[-1] = '/'; + } + if (p == ix[i]) { + p += strlen(p) + 1; + } else { + char *q = ix[i]; + while ((*p++ = *q++)); + } + } + *p = '\0'; +} + +/* Collapse "." and ".." names in the given path wherever possible. + A "." name may always be eliminated; a ".." name may be eliminated if it + follows a name that is neither "." nor "..". This is a syntactic operation + that performs no filesystem queries, so it should only be used to cleanup + after invoking the realpath() procedure. */ + +void +collapse(char *path) +{ + char *names = (path[0] == '/') ? path + 1 : path; /* Preserve first '/' */ + int nc; + char **ix; + int i, j; + char *p, *q; + + nc = collapsible(names); + if (nc < 2) return; /* Nothing to do */ + ix = (char **)alloca(nc * sizeof(char *)); + splitNames(names, ix); + + for (i = 0; i < nc; i++) { + int dots = 0; + + /* Find next occurrence of "." or ".." */ + do { + char *p = ix[i]; + if (p[0] == '.') { + if (p[1] == '\0') { + dots = 1; + break; + } + if ((p[1] == '.') && (p[2] == '\0')) { + dots = 2; + break; + } + } + i++; + } while (i < nc); + if (i >= nc) break; + + /* At this point i is the index of either a "." or a "..", so take the + appropriate action and then continue the outer loop */ + if (dots == 1) { + /* Remove this instance of "." */ + ix[i] = 0; + } + else { + /* If there is a preceding name, remove both that name and this + instance of ".."; otherwise, leave the ".." as is */ + for (j = i - 1; j >= 0; j--) { + if (ix[j]) break; + } + if (j < 0) continue; + ix[j] = 0; + ix[i] = 0; + } + /* i will be incremented at the top of the loop */ + } + + joinNames(names, nc, ix); +} diff --git a/src/java.base/unix/native/libjava/path_util.h b/src/java.base/unix/native/libjava/path_util.h new file mode 100644 index 0000000000000000000000000000000000000000..7b0fd5eb1454c4f2ceabd1f3a5dee7b50e5f9a01 --- /dev/null +++ b/src/java.base/unix/native/libjava/path_util.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef PATH_UTIL_H +#define PATH_UTIL_H + +void collapse(char *path); + +#endif diff --git a/src/java.base/unix/native/libnio/ch/IOUtil.c b/src/java.base/unix/native/libnio/ch/IOUtil.c index 930137ce0314ee002ce71ce9d51b21469268acd0..dfa99658fa667515f6f98716a236ede8c41460ee 100644 --- a/src/java.base/unix/native/libnio/ch/IOUtil.c +++ b/src/java.base/unix/native/libnio/ch/IOUtil.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ #include "jlong.h" #include "sun_nio_ch_IOUtil.h" #include "java_lang_Integer.h" +#include "java_lang_Long.h" #include "nio.h" #include "nio_util.h" @@ -173,6 +174,29 @@ Java_sun_nio_ch_IOUtil_iovMax(JNIEnv *env, jclass this) return (jint)iov_max; } +JNIEXPORT jlong JNICALL +Java_sun_nio_ch_IOUtil_writevMax(JNIEnv *env, jclass this) +{ +#if defined(MACOSX) || defined(__linux__) + // + // The man pages of writev() on both Linux and macOS specify this + // constraint on the sum of all byte lengths in the iovec array: + // + // [EINVAL] The sum of the iov_len values in the iov array + // overflows a 32-bit integer. + // + // As of macOS 11 Big Sur, Darwin version 20, writev() started to + // actually enforce the constraint which had been previously ignored. + // + // In practice on Linux writev() has been observed not to write more + // than 0x7fff0000 (aarch64) or 0x7ffff000 (x64) bytes in one call. + // + return java_lang_Integer_MAX_VALUE; +#else + return java_lang_Long_MAX_VALUE; +#endif +} + /* Declared in nio_util.h for use elsewhere in NIO */ jint diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 9df8be1e62c21b5d059f1354e699679c26596fa0..ad36e6a19b4716724b5366a9fd1b55ae4a12a5b5 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -317,7 +317,7 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) /* supports extended attributes */ -#ifdef _SYS_XATTR_H_ +#if defined(_SYS_XATTR_H) || defined(_SYS_XATTR_H_) capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_XATTR; #endif @@ -1330,4 +1330,4 @@ Java_sun_nio_fs_UnixNativeDispatcher_flistxattr(JNIEnv* env, jclass clazz, if (res == (size_t)-1) throwUnixException(env, errno); return (jint)res; -} \ No newline at end of file +} diff --git a/src/java.base/windows/classes/java/io/WinNTFileSystem.java b/src/java.base/windows/classes/java/io/WinNTFileSystem.java index f30717f9ea63cccb670c67b58641b232b4bdca8b..0fc52d6299b18004fc3434ff73474fb5fc0dc2c7 100644 --- a/src/java.base/windows/classes/java/io/WinNTFileSystem.java +++ b/src/java.base/windows/classes/java/io/WinNTFileSystem.java @@ -356,7 +356,7 @@ class WinNTFileSystem extends FileSystem { if (sm != null) { sm.checkPropertyAccess("user.dir"); } - return normalize(userDir); + return userDir; } private String getDrive(String path) { diff --git a/src/java.base/windows/native/libjava/java_props_md.c b/src/java.base/windows/native/libjava/java_props_md.c index 69f068e5ebc275e9dfd92902518f68a1c86e6c1e..21532c37b2525d31736875e17b59a3ac8046ba18 100644 --- a/src/java.base/windows/native/libjava/java_props_md.c +++ b/src/java.base/windows/native/libjava/java_props_md.c @@ -471,6 +471,8 @@ GetJavaProperties(JNIEnv* env) * Windows Server 2012 6 2 (!VER_NT_WORKSTATION) * Windows Server 2012 R2 6 3 (!VER_NT_WORKSTATION) * Windows 10 10 0 (VER_NT_WORKSTATION) + * Windows 11 10 0 (VER_NT_WORKSTATION) + * where (buildNumber >= 22000) * Windows Server 2016 10 0 (!VER_NT_WORKSTATION) * Windows Server 2019 10 0 (!VER_NT_WORKSTATION) * where (buildNumber > 17762) @@ -544,7 +546,14 @@ GetJavaProperties(JNIEnv* env) } else if (majorVersion == 10) { if (is_workstation) { switch (minorVersion) { - case 0: sprops.os_name = "Windows 10"; break; + case 0: + /* Windows 11 21H2 (original release) build number is 22000 */ + if (buildNumber >= 22000) { + sprops.os_name = "Windows 11"; + } else { + sprops.os_name = "Windows 10"; + } + break; default: sprops.os_name = "Windows NT (unknown)"; } } else { diff --git a/src/java.base/windows/native/libnet/NetworkInterface.c b/src/java.base/windows/native/libnet/NetworkInterface.c index aa59b565ae02f7f3f0f793fe0f7369f1eee81a6c..c2f604b24de4741d29ff23039c70f62795fee257 100644 --- a/src/java.base/windows/native/libnet/NetworkInterface.c +++ b/src/java.base/windows/native/libnet/NetworkInterface.c @@ -112,7 +112,7 @@ MIB_IFROW *getIF(jint index) { return NULL; count = GetIfTable(tableP, &size, TRUE); - if (count == ERROR_INSUFFICIENT_BUFFER || count == ERROR_BUFFER_OVERFLOW) { + if (count == ERROR_INSUFFICIENT_BUFFER) { MIB_IFTABLE* newTableP = (MIB_IFTABLE *)realloc(tableP, size); if (newTableP == NULL) { free(tableP); @@ -187,7 +187,7 @@ int enumInterfaces(JNIEnv *env, netif **netifPP) } ret = GetIfTable(tableP, &size, TRUE); - if (ret == ERROR_INSUFFICIENT_BUFFER || ret == ERROR_BUFFER_OVERFLOW) { + if (ret == ERROR_INSUFFICIENT_BUFFER) { MIB_IFTABLE * newTableP = (MIB_IFTABLE *)realloc(tableP, size); if (newTableP == NULL) { free(tableP); @@ -200,9 +200,19 @@ int enumInterfaces(JNIEnv *env, netif **netifPP) if (ret != NO_ERROR) { free(tableP); - - JNU_ThrowByName(env, "java/lang/Error", - "IP Helper Library GetIfTable function failed"); + switch (ret) { + case ERROR_INVALID_PARAMETER: + JNU_ThrowInternalError(env, + "IP Helper Library GetIfTable function failed: " + "invalid parameter"); + break; + default: + SetLastError(ret); + JNU_ThrowByNameWithMessageAndLastError(env, + JNU_JAVANETPKG "SocketException", + "IP Helper Library GetIfTable function failed"); + break; + } // this different error code is to handle the case when we call // GetIpAddrTable in pure IPv6 environment return -2; @@ -308,8 +318,8 @@ int enumInterfaces(JNIEnv *env, netif **netifPP) // it should not fail, because we have called it once before if (MultiByteToWideChar(CP_OEMCP, 0, ifrowP->bDescr, ifrowP->dwDescrLen, curr->displayName, wlen) == 0) { - JNU_ThrowByName(env, "java/lang/Error", - "Cannot get multibyte char for interface display name"); + JNU_ThrowInternalError(env, + "Cannot get multibyte char for interface display name"); free_netif(netifP); free(tableP); free(curr->name); @@ -374,7 +384,7 @@ int lookupIPAddrTable(JNIEnv *env, MIB_IPADDRTABLE **tablePP) } ret = GetIpAddrTable(tableP, &size, FALSE); - if (ret == ERROR_INSUFFICIENT_BUFFER || ret == ERROR_BUFFER_OVERFLOW) { + if (ret == ERROR_INSUFFICIENT_BUFFER) { MIB_IPADDRTABLE * newTableP = (MIB_IPADDRTABLE *)realloc(tableP, size); if (newTableP == NULL) { free(tableP); @@ -389,8 +399,19 @@ int lookupIPAddrTable(JNIEnv *env, MIB_IPADDRTABLE **tablePP) if (tableP != NULL) { free(tableP); } - JNU_ThrowByName(env, "java/lang/Error", - "IP Helper Library GetIpAddrTable function failed"); + switch (ret) { + case ERROR_INVALID_PARAMETER: + JNU_ThrowInternalError(env, + "IP Helper Library GetIpAddrTable function failed: " + "invalid parameter"); + break; + default: + SetLastError(ret); + JNU_ThrowByNameWithMessageAndLastError(env, + JNU_JAVANETPKG "SocketException", + "IP Helper Library GetIpAddrTable function failed"); + break; + } // this different error code is to handle the case when we call // GetIpAddrTable in pure IPv6 environment return -2; diff --git a/src/java.base/windows/native/libnet/NetworkInterface_winXP.c b/src/java.base/windows/native/libnet/NetworkInterface_winXP.c index 92b015e8bb7ed2da0ac214dbb8cbbb1f7475022c..6483cd9eb80898b6403041434a9e2db76f2d45d7 100644 --- a/src/java.base/windows/native/libnet/NetworkInterface_winXP.c +++ b/src/java.base/windows/native/libnet/NetworkInterface_winXP.c @@ -110,27 +110,29 @@ int getAdapters (JNIEnv *env, int flags, IP_ADAPTER_ADDRESSES **adapters) { if (ret != ERROR_SUCCESS) { free (adapterInfo); - if (ret == ERROR_INSUFFICIENT_BUFFER) { - JNU_ThrowByName(env, "java/lang/Error", - "IP Helper Library GetAdaptersAddresses function failed " - "with ERROR_INSUFFICIENT_BUFFER"); - } else if (ret == ERROR_ADDRESS_NOT_ASSOCIATED ) { - JNU_ThrowByName(env, "java/lang/Error", - "IP Helper Library GetAdaptersAddresses function failed " - "with ERROR_ADDRESS_NOT_ASSOCIATED"); - } else { - char error_msg_buf[100]; - int _sr; - _sr = _snprintf_s(error_msg_buf, sizeof(error_msg_buf), - _TRUNCATE, "IP Helper Library GetAdaptersAddresses " - "function failed with error == %d", ret); - if (_sr != -1) { - JNU_ThrowByName(env, "java/lang/Error", error_msg_buf); - } else { - JNU_ThrowByName(env, "java/lang/Error", - "IP Helper Library GetAdaptersAddresses function failure"); - } + switch (ret) { + case ERROR_INVALID_PARAMETER: + JNU_ThrowInternalError(env, + "IP Helper Library GetAdaptersAddresses function failed: " + "invalid parameter"); + break; + case ERROR_NOT_ENOUGH_MEMORY: + JNU_ThrowOutOfMemoryError(env, + "IP Helper Library GetAdaptersAddresses function failed: " + "not enough memory"); + break; + case ERROR_NO_DATA: + // not an error + *adapters = NULL; + return ERROR_SUCCESS; + default: + SetLastError(ret); + JNU_ThrowByNameWithMessageAndLastError(env, + JNU_JAVANETPKG "SocketException", + "IP Helper Library GetAdaptersAddresses function failed"); + break; } + return -1; } *adapters = adapterInfo; @@ -179,26 +181,26 @@ IP_ADAPTER_ADDRESSES *getAdapter (JNIEnv *env, jint index) { if (val != ERROR_SUCCESS) { free (adapterInfo); - if (val == ERROR_INSUFFICIENT_BUFFER) { - JNU_ThrowByName(env, "java/lang/Error", - "IP Helper Library GetAdaptersAddresses function failed " - "with ERROR_INSUFFICIENT_BUFFER"); - } else if (val == ERROR_ADDRESS_NOT_ASSOCIATED ) { - JNU_ThrowByName(env, "java/lang/Error", - "IP Helper Library GetAdaptersAddresses function failed " - "with ERROR_ADDRESS_NOT_ASSOCIATED"); - } else { - char error_msg_buf[100]; - int _sr; - _sr = _snprintf_s(error_msg_buf, sizeof(error_msg_buf), - _TRUNCATE, "IP Helper Library GetAdaptersAddresses function failed " - "with error == %d", val); - if (_sr != -1) { - JNU_ThrowByName(env, "java/lang/Error", error_msg_buf); - } else { - JNU_ThrowByName(env, "java/lang/Error", - "IP Helper Library GetAdaptersAddresses function failure"); - } + switch (val) { + case ERROR_INVALID_PARAMETER: + JNU_ThrowInternalError(env, + "IP Helper Library GetAdaptersAddresses function failed: " + "invalid parameter"); + break; + case ERROR_NOT_ENOUGH_MEMORY: + JNU_ThrowOutOfMemoryError(env, + "IP Helper Library GetAdaptersAddresses function failed: " + "not enough memory"); + break; + case ERROR_NO_DATA: + // not an error + break; + default: + SetLastError(val); + JNU_ThrowByNameWithMessageAndLastError(env, + JNU_JAVANETPKG "SocketException", + "IP Helper Library GetAdaptersAddresses function failed"); + break; } return NULL; } @@ -237,7 +239,7 @@ static int ipinflen = 2048; */ int getAllInterfacesAndAddresses (JNIEnv *env, netif **netifPP) { - DWORD ret, flags; + int ret, flags; MIB_IPADDRTABLE *tableP; IP_ADAPTER_ADDRESSES *ptr, *adapters=NULL; ULONG len=ipinflen, count=0; diff --git a/src/java.base/windows/native/libnio/ch/IOUtil.c b/src/java.base/windows/native/libnio/ch/IOUtil.c index ff581f04806d240b6d110e56a0c632a8b62cad4a..511fcdcadb26016f2dc3221849a2e216a35b6f82 100644 --- a/src/java.base/windows/native/libnio/ch/IOUtil.c +++ b/src/java.base/windows/native/libnio/ch/IOUtil.c @@ -31,6 +31,7 @@ #include "jvm.h" #include "jlong.h" +#include "java_lang_Long.h" #include "nio.h" #include "nio_util.h" #include "net_util.h" @@ -77,6 +78,11 @@ Java_sun_nio_ch_IOUtil_iovMax(JNIEnv *env, jclass this) return 16; } +JNIEXPORT jlong JNICALL +Java_sun_nio_ch_IOUtil_writevMax(JNIEnv *env, jclass this) +{ + return java_lang_Long_MAX_VALUE; +} jint convertReturnVal(JNIEnv *env, jint n, jboolean reading) diff --git a/src/java.compiler/share/classes/javax/annotation/processing/AbstractProcessor.java b/src/java.compiler/share/classes/javax/annotation/processing/AbstractProcessor.java index 469ec4a4b8e40a86ef0df70f7c4bc9fb405bae5f..7754105a8d4c71cb9bf401d216b18eae0055332f 100644 --- a/src/java.compiler/share/classes/javax/annotation/processing/AbstractProcessor.java +++ b/src/java.compiler/share/classes/javax/annotation/processing/AbstractProcessor.java @@ -79,6 +79,7 @@ public abstract class AbstractProcessor implements Processor { * @return the options recognized by this processor, or an empty * set if none */ + @Override public Set getSupportedOptions() { SupportedOptions so = this.getClass().getAnnotation(SupportedOptions.class); return (so == null) ? @@ -101,6 +102,7 @@ public abstract class AbstractProcessor implements Processor { * @return the names of the annotation interfaces supported by * this processor, or an empty set if none */ + @Override public Set getSupportedAnnotationTypes() { SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class); boolean initialized = isInitialized(); @@ -128,6 +130,7 @@ public abstract class AbstractProcessor implements Processor { * * @return the latest source version supported by this processor */ + @Override public SourceVersion getSupportedSourceVersion() { SupportedSourceVersion ssv = this.getClass().getAnnotation(SupportedSourceVersion.class); SourceVersion sv = null; @@ -167,6 +170,7 @@ public abstract class AbstractProcessor implements Processor { /** * {@inheritDoc} */ + @Override public abstract boolean process(Set annotations, RoundEnvironment roundEnv); @@ -178,6 +182,7 @@ public abstract class AbstractProcessor implements Processor { * @param member {@inheritDoc} * @param userText {@inheritDoc} */ + @Override public Iterable getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, diff --git a/src/java.compiler/share/classes/javax/annotation/processing/Completions.java b/src/java.compiler/share/classes/javax/annotation/processing/Completions.java index 8b7aa2916457b208238d1b2674005fca45050ef0..5b476afec65ed6fc83a53fa5c71e6b47e6ed1f77 100644 --- a/src/java.compiler/share/classes/javax/annotation/processing/Completions.java +++ b/src/java.compiler/share/classes/javax/annotation/processing/Completions.java @@ -52,7 +52,6 @@ public class Completions { return value; } - public String getMessage() { return message; } diff --git a/src/java.compiler/share/classes/javax/annotation/processing/Processor.java b/src/java.compiler/share/classes/javax/annotation/processing/Processor.java index 0b2160da263a1fd413d366959ea79e82eb2a5fbd..d70910c9d49a2e8fe16affcc065ef3fbc33fbc97 100644 --- a/src/java.compiler/share/classes/javax/annotation/processing/Processor.java +++ b/src/java.compiler/share/classes/javax/annotation/processing/Processor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -322,15 +322,16 @@ public interface Processor { void init(ProcessingEnvironment processingEnv); /** - * Processes a set of annotation interfaces on type elements - * originating from the prior round and returns whether or not - * these annotation interfaces are claimed by this processor. If {@code - * true} is returned, the annotation interfaces are claimed and subsequent - * processors will not be asked to process them; if {@code false} - * is returned, the annotation interfaces are unclaimed and subsequent - * processors may be asked to process them. A processor may - * always return the same boolean value or may vary the result - * based on its own chosen criteria. + * Processes a set of annotation interfaces on {@linkplain + * RoundEnvironment#getRootElements() root elements} originating + * from the prior round and returns whether or not these + * annotation interfaces are claimed by this processor. If {@code + * true} is returned, the annotation interfaces are claimed and + * subsequent processors will not be asked to process them; if + * {@code false} is returned, the annotation interfaces are + * unclaimed and subsequent processors may be asked to process + * them. A processor may always return the same boolean value or + * may vary the result based on its own chosen criteria. * *

      The input set will be empty if the processor supports {@code * "*"} and the root elements have no annotations. A {@code diff --git a/src/java.compiler/share/classes/javax/annotation/processing/RoundEnvironment.java b/src/java.compiler/share/classes/javax/annotation/processing/RoundEnvironment.java index c50c6b787a65808d2096074effbae02442bfe1f4..6323bda133914568927acb8d95210b31d88f3914 100644 --- a/src/java.compiler/share/classes/javax/annotation/processing/RoundEnvironment.java +++ b/src/java.compiler/share/classes/javax/annotation/processing/RoundEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,7 @@ package javax.annotation.processing; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; +import javax.lang.model.element.*; import java.util.LinkedHashSet; import java.util.Collections; import java.util.Set; @@ -58,8 +57,15 @@ public interface RoundEnvironment { boolean errorRaised(); /** - * Returns the {@linkplain Processor root elements} for annotation processing generated - * by the prior round. + * Returns the {@linkplain Processor root elements} for annotation + * processing {@linkplain Filer generated} by the prior round. + * + * @apiNote + * Root elements correspond to the top-level declarations in + * compilation units (JLS section {@jls 7.3}). Root elements are + * most commonly {@linkplain TypeElement types}, but can also be + * {@linkplain PackageElement packages} or {@linkplain + * ModuleElement modules}. * * @return the root elements for annotation processing generated * by the prior round, or an empty set if there were none diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaFileSystemModel.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaFileSystemModel.java index 29e947629e33cc20a4d6341a99f8856299a4944b..14aa78ec8498f2bd1e9dfeb02da50fb49d533e91 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaFileSystemModel.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaFileSystemModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,8 +47,8 @@ class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeLi private Vector files = null; JFileChooser filechooser = null; - Vector fileCache = null; - Object fileCacheLock; + ArrayList fileCache = null; + final Object fileCacheLock; Vector directories = null; int fetchID = 0; @@ -136,7 +136,7 @@ class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeLi synchronized(fileCacheLock) { for (int i = 0; i < fileCache.size(); i++) { - final SortableFile sf = fileCache.elementAt(i); + final SortableFile sf = fileCache.get(i); final File f = sf.fFile; if (filechooser.isTraversable(f)) { directories.addElement(f); @@ -180,7 +180,7 @@ class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeLi // PENDING(jeff) pick the size more sensibly invalidateFileCache(); synchronized(fileCacheLock) { - fileCache = new Vector(50); + fileCache = new ArrayList<>(50); } filesLoader = new FilesLoader(currentDirectory, fetchID); @@ -244,7 +244,7 @@ class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeLi synchronized(fileCacheLock) { if (fileCache != null) { if (!isAscending) row = fileCache.size() - row - 1; - return fileCache.elementAt(row).getValueAt(col); + return fileCache.get(row).getValueAt(col); } return null; } @@ -383,7 +383,7 @@ class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeLi } class FilesLoader implements Runnable { - Vector queuedTasks = new Vector<>(); + ArrayList queuedTasks = new ArrayList<>(); File currentDirectory = null; int fid; Thread loadThread; @@ -473,7 +473,7 @@ class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeLi synchronized(fileCacheLock) { if (fileCache != null) { for (int i = 0; i < contentFiles.size(); i++) { - fileCache.addElement(contentFiles.elementAt(i)); + fileCache.add(contentFiles.elementAt(i)); fireTableRowsInserted(i, i); } } diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java index ad069ca73b393c24b793b3871bb195840e4eb4c5..68ccd2180869a37b16fa06b57a3ef27d5c283163 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java @@ -48,6 +48,7 @@ import com.apple.laf.AquaUtils.RecyclableObject; import com.apple.laf.AquaUtils.RecyclableSingleton; import sun.awt.image.MultiResolutionCachedImage; import sun.lwawt.macosx.CImage; +import sun.swing.ImageIconUIResource; public class AquaImageFactory { public static IconUIResource getConfirmImageIcon() { @@ -231,14 +232,28 @@ public class AquaImageFactory { @SuppressWarnings("serial") // Superclass is not serializable across versions static class InvertableImageIcon extends ImageIcon implements InvertableIcon, UIResource { Icon invertedImage; + private Icon disabledIcon; public InvertableImageIcon(final Image image) { super(image); } + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + if (!c.isEnabled()) { + if (disabledIcon == null) { + disabledIcon = new ImageIcon(GrayFilter. + createDisabledImage(((ImageIcon)this).getImage())); + } + disabledIcon.paintIcon(c, g, x, y); + } else { + super.paintIcon(c, g, x, y); + } + } + @Override public Icon getInvertedIcon() { if (invertedImage != null) return invertedImage; - return invertedImage = new IconUIResource(new ImageIcon(AquaUtils.generateLightenedImage(getImage(), 100))); + return invertedImage = new InvertableImageIcon(AquaUtils.generateLightenedImage(getImage(), 100)); } } diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java index e8e84f4a7d813920a8d3e12f40176b0b8bdb0d18..63efa9508983eb3f0fb2c7960d13be037528df98 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java @@ -379,7 +379,8 @@ public class AquaKeyBindings { "ENTER", "selectNextRowCell", "shift ENTER", "selectPreviousRowCell", "alt TAB", "focusHeader", - "alt shift TAB", "focusHeader" + "alt shift TAB", "focusHeader", + "F8", "focusHeader" })); } diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java index 31047d2f5f5c827ade35edfc3900673714e32628..088fb01591e2b83031bc59e6463c2e7e78c26a3d 100644 --- a/src/java.desktop/macosx/classes/sun/font/CStrike.java +++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,8 +29,6 @@ import java.awt.Rectangle; import java.awt.geom.*; import java.util.*; -import sun.awt.SunHints; - public final class CStrike extends PhysicalStrike { // Creates the native strike @@ -444,9 +442,7 @@ public final class CStrike extends PhysicalStrike { // clean up everyone else if (generalCache != null) { - final Iterator i = generalCache.values().iterator(); - while (i.hasNext()) { - final long longValue = i.next().longValue(); + for (long longValue : generalCache.values()) { if (longValue != -1 && longValue != 0) { removeGlyphInfoFromCache(longValue); StrikeCache.freeLongPointer(longValue); diff --git a/src/java.desktop/macosx/classes/sun/font/PlatformFontInfo.java b/src/java.desktop/macosx/classes/sun/font/PlatformFontInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..b0ac181a88465bb809cb04cd5f94deb5c5ee66da --- /dev/null +++ b/src/java.desktop/macosx/classes/sun/font/PlatformFontInfo.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.font; + +final class PlatformFontInfo { + + /** + * The method is only to be called via the + * {@code FontManagerFactory.getInstance()} factory method. + */ + static FontManager createFontManager() { + return new CFontManager(); + } +} diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWLightweightFramePeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWLightweightFramePeer.java index 1644c6b6e7b12b384c36cba02023e17c8fa01ba6..41acd8ecab6d989d07b3ea401c4703b0153a3007 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/LWLightweightFramePeer.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/LWLightweightFramePeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -119,7 +119,7 @@ public class LWLightweightFramePeer extends LWWindowPeer implements OverrideNati } // SwingNode - private volatile long overriddenWindowHandle = 0L; + private volatile long overriddenWindowHandle; @Override public void overrideWindowHandle(final long handle) { diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java index 6356f6d21c7c6bbdfdda60297390699f201d8f8d..1796f5f3005b36d97f06f44b9fe8d823b73f0680 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -112,7 +112,7 @@ public class LWWindowPeer private volatile int windowState = Frame.NORMAL; // check that the mouse is over the window - private volatile boolean isMouseOver = false; + private volatile boolean isMouseOver; // A peer where the last mouse event came to. Used by cursor manager to // find the component under cursor diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index c4311bf0f5233d113dfc771769a9621e5ba7a9ae..2f23d8d0fdf258dd956d0508ac628ff73e34a78c 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -41,6 +41,7 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; import java.util.Arrays; +import java.util.function.Function; import javax.accessibility.Accessible; import javax.accessibility.AccessibleAction; @@ -662,21 +663,29 @@ class CAccessibility implements PropertyChangeListener { @Native static final int JAVA_AX_SELECTED_CHILDREN = -2; @Native static final int JAVA_AX_VISIBLE_CHILDREN = -3; + private static Object[] getTableRowChildrenAndRoles(Accessible a, Component c, int whichChildren, boolean allowIgnored, int tableRowIndex) { + return invokeGetChildrenAndRoles(a, c, whichChildren, allowIgnored, ChildrenOperations.createForTableRow(tableRowIndex)); + } + // Each child takes up two entries in the array: one for itself and one for its role - public static Object[] getChildrenAndRoles(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) { + private static Object[] getChildrenAndRoles(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) { + return invokeGetChildrenAndRoles(a, c, whichChildren, allowIgnored, ChildrenOperations.COMMON); + } + + private static Object[] invokeGetChildrenAndRoles(Accessible a, Component c, int whichChildren, boolean allowIgnored, ChildrenOperations ops) { if (a == null) return null; return invokeAndWait(new Callable() { public Object[] call() throws Exception { - return getChildrenAndRolesImpl(a, c, whichChildren, allowIgnored); + return getChildrenAndRolesImpl(a, c, whichChildren, allowIgnored, ops); } }, c); } - private static Object[] getChildrenAndRolesImpl(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) { + private static Object[] getChildrenAndRolesImpl(Accessible a, Component c, int whichChildren, boolean allowIgnored, ChildrenOperations ops) { if (a == null) return null; ArrayList childrenAndRoles = new ArrayList(); - _addChildren(a, whichChildren, allowIgnored, childrenAndRoles); + _addChildren(a, whichChildren, allowIgnored, childrenAndRoles, ops); /* In case of fetching a selection, we need to check if * the active descendant is at the beginning of the list, or @@ -747,7 +756,7 @@ class CAccessibility implements PropertyChangeListener { while (!parentStack.isEmpty()) { Accessible p = parentStack.get(parentStack.size() - 1); - currentLevelChildren.addAll(Arrays.asList(getChildrenAndRolesImpl(p, c, JAVA_AX_ALL_CHILDREN, allowIgnored))); + currentLevelChildren.addAll(Arrays.asList(getChildrenAndRolesImpl(p, c, JAVA_AX_ALL_CHILDREN, allowIgnored, ChildrenOperations.COMMON))); if ((currentLevelChildren.size() == 0) || (index >= currentLevelChildren.size())) { if (!parentStack.isEmpty()) parentStack.remove(parentStack.size() - 1); if (!indexses.isEmpty()) index = indexses.remove(indexses.size() - 1); @@ -859,20 +868,70 @@ class CAccessibility implements PropertyChangeListener { return role; } + private interface ChildrenOperations { + boolean isContextValid(AccessibleContext accessibleContext); + int getChildrenCount(AccessibleContext accessibleContext); + Accessible getAccessibleChild(AccessibleContext accessibleContext, int childIndex); + + static ChildrenOperations COMMON = createForCommon(); + + static ChildrenOperations createForCommon() { + return new ChildrenOperations() { + @Override + public boolean isContextValid(AccessibleContext accessibleContext) { + return accessibleContext != null; + } + + @Override + public int getChildrenCount(AccessibleContext accessibleContext) { + assert isContextValid(accessibleContext); + return accessibleContext.getAccessibleChildrenCount(); + } + + @Override + public Accessible getAccessibleChild(AccessibleContext accessibleContext, int childIndex) { + assert isContextValid(accessibleContext); + return accessibleContext.getAccessibleChild(childIndex); + } + }; + } + + static ChildrenOperations createForTableRow(int tableRowIndex) { + return new ChildrenOperations() { + @Override + public boolean isContextValid(AccessibleContext accessibleContext) { + return accessibleContext instanceof AccessibleTable; + } + + @Override + public int getChildrenCount(AccessibleContext accessibleContext) { + assert isContextValid(accessibleContext); + return ((AccessibleTable)accessibleContext).getAccessibleColumnCount(); + } + + @Override + public Accessible getAccessibleChild(AccessibleContext accessibleContext, int childIndex) { + assert isContextValid(accessibleContext); + return ((AccessibleTable)accessibleContext).getAccessibleAt(tableRowIndex, childIndex); + } + }; + } + } + // Either gets the immediate children of a, or recursively gets all unignored children of a - private static void _addChildren(final Accessible a, final int whichChildren, final boolean allowIgnored, final ArrayList childrenAndRoles) { + private static void _addChildren(Accessible a, int whichChildren, boolean allowIgnored, ArrayList childrenAndRoles, ChildrenOperations ops) { if (a == null) return; final AccessibleContext ac = a.getAccessibleContext(); - if (ac == null) return; + if (!ops.isContextValid(ac)) return; - final int numChildren = ac.getAccessibleChildrenCount(); + final int numChildren = ops.getChildrenCount(ac); // each child takes up two entries in the array: itself, and its role // so the array holds alternating Accessible and AccessibleRole objects for (int i = 0; i < numChildren; i++) { - final Accessible child = ac.getAccessibleChild(i); + final Accessible child = ops.getAccessibleChild(ac, i); if (child == null) continue; final AccessibleContext context = child.getAccessibleContext(); @@ -894,7 +953,7 @@ class CAccessibility implements PropertyChangeListener { final AccessibleRole role = context.getAccessibleRole(); if (role != null && ignoredRoles != null && ignoredRoles.contains(roleKey(role))) { // Get the child's unignored children. - _addChildren(child, whichChildren, false, childrenAndRoles); + _addChildren(child, whichChildren, false, childrenAndRoles, ChildrenOperations.COMMON); } else { childrenAndRoles.add(child); childrenAndRoles.add(getAccessibleRole(child)); diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java index 72bfa8d541e47673bcaa5618b218b63b5ff7fc82..b59c8cfe57435fff20f9505e5f8321a318f8580b 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,6 +73,8 @@ class CAccessible extends CFRetainedResource implements Accessible { private static native void menuItemSelected(long ptr); private static native void treeNodeExpanded(long ptr); private static native void treeNodeCollapsed(long ptr); + private static native void selectedCellsChanged(long ptr); + private static native void tableContentCacheClear(long ptr); private Accessible accessible; @@ -115,19 +117,39 @@ class CAccessible extends CFRetainedResource implements Accessible { if ( ptr != 0 ) { Object newValue = e.getNewValue(); Object oldValue = e.getOldValue(); - if (name.compareTo(ACCESSIBLE_CARET_PROPERTY) == 0) { + if (name.equals(ACCESSIBLE_CARET_PROPERTY)) { selectedTextChanged(ptr); - } else if (name.compareTo(ACCESSIBLE_TEXT_PROPERTY) == 0) { + } else if (name.equals(ACCESSIBLE_TEXT_PROPERTY)) { valueChanged(ptr); - } else if (name.compareTo(ACCESSIBLE_SELECTION_PROPERTY) == 0) { + } else if (name.equals(ACCESSIBLE_SELECTION_PROPERTY)) { selectionChanged(ptr); - } else if (name.compareTo(ACCESSIBLE_TABLE_MODEL_CHANGED) == 0) { + } else if (name.equals(ACCESSIBLE_TABLE_MODEL_CHANGED)) { valueChanged(ptr); - } else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) { + if (CAccessible.getSwingAccessible(CAccessible.this) != null) { + Accessible a = CAccessible.getSwingAccessible(CAccessible.this); + AccessibleContext ac = a.getAccessibleContext(); + if ((ac != null) && (ac.getAccessibleRole() == AccessibleRole.TABLE)) { + tableContentCacheClear(ptr); + } + } + } else if (name.equals(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY)) { if (newValue instanceof AccessibleContext) { activeDescendant = (AccessibleContext)newValue; + if (newValue instanceof Accessible) { + Accessible a = (Accessible)newValue; + AccessibleContext ac = a.getAccessibleContext(); + if (ac != null) { + Accessible p = ac.getAccessibleParent(); + if (p != null) { + AccessibleContext pac = p.getAccessibleContext(); + if ((pac != null) && (pac.getAccessibleRole() == AccessibleRole.TABLE)) { + selectedCellsChanged(ptr); + } + } + } + } } - } else if (name.compareTo(ACCESSIBLE_STATE_PROPERTY) == 0) { + } else if (name.equals(ACCESSIBLE_STATE_PROPERTY)) { AccessibleContext thisAC = accessible.getAccessibleContext(); AccessibleRole thisRole = thisAC.getAccessibleRole(); Accessible parentAccessible = thisAC.getAccessibleParent(); @@ -167,12 +189,12 @@ class CAccessible extends CFRetainedResource implements Accessible { if (thisRole == AccessibleRole.CHECK_BOX) { valueChanged(ptr); } - } else if (name.compareTo(ACCESSIBLE_NAME_PROPERTY) == 0) { + } else if (name.equals(ACCESSIBLE_NAME_PROPERTY)) { //for now trigger only for JTabbedPane. if (e.getSource() instanceof JTabbedPane) { titleChanged(ptr); } - } else if (name.compareTo(ACCESSIBLE_VALUE_PROPERTY) == 0) { + } else if (name.equals(ACCESSIBLE_VALUE_PROPERTY)) { AccessibleRole thisRole = accessible.getAccessibleContext() .getAccessibleRole(); if (thisRole == AccessibleRole.SLIDER || diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCheckboxMenuItem.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCheckboxMenuItem.java index d21de341fb4379245d5f243db589dbf8c1cda9de..5cc8fad2a4a64fc7942f869ae6bfca13aae194ae 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCheckboxMenuItem.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCheckboxMenuItem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ import sun.awt.SunToolkit; public class CCheckboxMenuItem extends CMenuItem implements CheckboxMenuItemPeer { volatile boolean fAutoToggle = true; - volatile boolean fIsIndeterminate = false; + volatile boolean fIsIndeterminate; private native void nativeSetState(long modelPtr, boolean state); private native void nativeSetIsCheckbox(long modelPtr); diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java index c29268e0e9f548d054aa219d1d2d55fb513e364b..c4b2640efb845c2e6e8aff2ff10f918c59237463 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,7 +55,7 @@ public class CMenuItem extends CMenuComponent implements MenuItemPeer { private boolean isSeparator() { String label = ((MenuItem)getTarget()).getLabel(); - return (label != null && label.equals("-")); + return "-".equals(label); } @Override diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java index 72fe894db465b29d56260dfda0c3b2bf3e8e421f..6a8076ee118130661daad5284cfadea07e76d7dc 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java @@ -50,8 +50,8 @@ public class CPlatformEmbeddedFrame implements PlatformWindow { private LWWindowPeer peer; private CEmbeddedFrame target; - private volatile int screenX = 0; - private volatile int screenY = 0; + private volatile int screenX; + private volatile int screenY; @Override // PlatformWindow public void initialize(Window target, final LWWindowPeer peer, PlatformWindow owner) { diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java index 35d726f8f7500b1c3d31ee916cfd9772713e94c3..52e85e338415aa74c434797558dd0e665865be83 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java @@ -56,7 +56,7 @@ public final class CWarningWindow extends CPlatformWindow /** * Animation stage. */ - private volatile int currentIcon = 0; + private volatile int currentIcon; /* -1 - uninitialized. * 0 - 16x16 diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m index 05aee212e6d84c4091e40775267701fa47340b07..150e82c6965d1acd77b4cedd940a2829315de88b 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m @@ -128,7 +128,7 @@ AWT_NS_WINDOW_IMPLEMENTATION // send up to the GestureHandler to recursively dispatch on the AWT event thread DECLARE_CLASS(jc_GestureHandler, "com/apple/eawt/event/GestureHandler"); - DECLARE_METHOD(sjm_handleGestureFromNative, jc_GestureHandler, + DECLARE_STATIC_METHOD(sjm_handleGestureFromNative, jc_GestureHandler, "handleGestureFromNative", "(Ljava/awt/Window;IDDDD)V"); (*env)->CallStaticVoidMethod(env, jc_GestureHandler, sjm_handleGestureFromNative, awtWindow, type, (jdouble)loc.x, (jdouble)loc.y, (jdouble)a, (jdouble)b); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m index 662c8b0287bc50e5fb483f11599ab220b58d115f..005d26954c2fbab4825ddf75a4a71462a919b333 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m @@ -51,6 +51,7 @@ static jclass sjc_CAccessibility = NULL; NSSize getAxComponentSize(JNIEnv *env, jobject axComponent, jobject component) { + GET_CACCESSIBILITY_CLASS_RETURN(NSZeroSize); DECLARE_CLASS_RETURN(jc_Dimension, "java/awt/Dimension", NSZeroSize); DECLARE_FIELD_RETURN(jf_width, jc_Dimension, "width", "I", NSZeroSize); DECLARE_FIELD_RETURN(jf_height, jc_Dimension, "height", "I", NSZeroSize); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h index bcaa0269576acb0cedf9c9372c3d539974f4e389..ef67c92a06d556f31253d4a4f47f6f48d7013a6c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.h @@ -22,7 +22,7 @@ * questions. */ -#import "CommonComponentAccessibility.h" +#import "ComponentWrapperAccessibility.h" -@interface CellAccessibility : CommonComponentAccessibility +@interface CellAccessibility : ComponentWrapperAccessibility @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m index 688d8eb3176b23e7f4150e27bf1ca4bb21234905..c44309a1e3cf49a6a36465df7f1976103f1c588f 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CellAccessibility.m @@ -35,22 +35,9 @@ return NSAccessibilityCellRole;; } -- (NSArray *)accessibilityChildren +- (NSInteger)accessibilityIndex { - NSArray *children = [super accessibilityChildren]; - if (children == NULL) { - NSString *javaRole = [self javaRole]; - CommonComponentAccessibility *newChild = [CommonComponentAccessibility createWithParent:self - accessible:self->fAccessible - role:javaRole - index:self->fIndex - withEnv:[ThreadUtilities getJNIEnv] - withView:self->fView - isWrapped:NO]; - return [NSArray arrayWithObject:newChild]; - } else { - return children; - } + return self->fIndex; } - (NSRect)accessibilityFrame diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m index 9d4582445a471cf3ac50e551053b6b3e32480f73..238b82f609e6ef188315cce627eba5527caa2b5a 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ColumnAccessibility.m @@ -49,61 +49,6 @@ static jmethodID jm_getChildrenAndRoles = NULL; return NSAccessibilityColumnRole; } -- (NSArray *)accessibilityChildren -{ - NSArray *children = [super accessibilityChildren]; - if (children == NULL) { - JNIEnv *env = [ThreadUtilities getJNIEnv]; - CommonComponentAccessibility *parent = [self accessibilityParent]; - if (parent->fAccessible == NULL) return nil; - GET_CHILDRENANDROLES_METHOD_RETURN(nil); - jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, - parent->fAccessible, parent->fComponent, sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN, NO); - CHECK_EXCEPTION(); - if (jchildrenAndRoles == NULL) return nil; - - jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); - NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; - - NSUInteger childIndex = fIndex; - - int inc = [(TableAccessibility *)[self accessibilityParent] accessibilityRowCount] * 2; - NSInteger i = childIndex * 2; - for(i; i < arrayLen; i += inc) - { - jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); - jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); - - NSString *childJavaRole = nil; - if (jchildJavaRole != NULL) { - DECLARE_CLASS_RETURN(sjc_AccessibleRole, "javax/accessibility/AccessibleRole", nil); - DECLARE_FIELD_RETURN(sjf_key, sjc_AccessibleRole, "key", "Ljava/lang/String;", nil); - jobject jkey = (*env)->GetObjectField(env, jchildJavaRole, sjf_key); - CHECK_EXCEPTION(); - childJavaRole = JavaStringToNSString(env, jkey); - (*env)->DeleteLocalRef(env, jkey); - } - - CellAccessibility *child = [[CellAccessibility alloc] initWithParent:self - withEnv:env - withAccessible:jchild - withIndex:childIndex - withView:self->fView - withJavaRole:childJavaRole]; - [childrenCells addObject:[[child retain] autorelease]]; - - (*env)->DeleteLocalRef(env, jchild); - (*env)->DeleteLocalRef(env, jchildJavaRole); - - childIndex += (inc / 2); - } - (*env)->DeleteLocalRef(env, jchildrenAndRoles); - return childrenCells; - } else { - return children; - } -} - - (NSInteger)accessibilityIndex { return fIndex; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m index 63c00414d66fc71f893734338522d22ff2cca566..0fd70eb81516502803a862e3676b1d6a666ab841 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m @@ -35,6 +35,12 @@ static jmethodID sjm_getAccessibleName = NULL; GET_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", \ "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", ret); +static jmethodID sjm_getAccessibleSelection = NULL; +#define GET_ACCESSIBLESELECTION_METHOD_RETURN(ret) \ + GET_CACCESSIBILITY_CLASS_RETURN(ret); \ + GET_STATIC_METHOD_RETURN(sjm_getAccessibleSelection, sjc_CAccessibility, "getAccessibleSelection", \ + "(Ljavax/accessibility/AccessibleContext;Ljava/awt/Component;)Ljavax/accessibility/AccessibleSelection;", ret); + @implementation ComboBoxAccessibility // NSAccessibilityElement protocol methods @@ -43,15 +49,21 @@ static jmethodID sjm_getAccessibleName = NULL; JNIEnv *env = [ThreadUtilities getJNIEnv]; jobject axContext = [self axContextWithEnv:env]; if (axContext == NULL) return nil; - jclass axContextClass = (*env)->GetObjectClass(env, axContext); - DECLARE_METHOD_RETURN(jm_getAccessibleSelection, axContextClass, "getAccessibleSelection", "(I)Ljavax/accessibility/Accessible;", nil); - jobject axSelectedChild = (*env)->CallObjectMethod(env, axContext, jm_getAccessibleSelection, 0); + GET_ACCESSIBLESELECTION_METHOD_RETURN(nil); + jobject axSelection = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleSelection, axContext, self->fComponent); + CHECK_EXCEPTION(); + if (axSelection == NULL) { + return nil; + } + jclass axSelectionClass = (*env)->GetObjectClass(env, axSelection); + DECLARE_METHOD_RETURN(jm_getAccessibleSelection, axSelectionClass, "getAccessibleSelection", "(I)Ljavax/accessibility/Accessible;", nil); + jobject axSelectedChild = (*env)->CallObjectMethod(env, axSelection, jm_getAccessibleSelection, 0); CHECK_EXCEPTION(); + (*env)->DeleteLocalRef(env, axSelection); (*env)->DeleteLocalRef(env, axContext); if (axSelectedChild == NULL) { return nil; } - GET_CACCESSIBILITY_CLASS_RETURN(nil); GET_ACCESSIBLENAME_METHOD_RETURN(nil); jobject childName = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, axSelectedChild, fComponent); CHECK_EXCEPTION(); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h index 0e946b4a67fc37915c0245706223711b99bdb5c4..33ab5c5fd2e5cb9623ba41eb7023ab96a427efcd 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h @@ -57,6 +57,7 @@ - (void)postTitleChanged; - (void)postTreeNodeExpanded; - (void)postTreeNodeCollapsed; +- (void)postSelectedCellsChanged; - (BOOL)isEqual:(nonnull id)anObject; - (BOOL)isAccessibleWithEnv:(JNIEnv _Nonnull * _Nonnull)env forAccessible:(nonnull jobject)accessible; @@ -64,20 +65,16 @@ + (void) initializeRolesMap; -+ (CommonComponentAccessibility* _Nullable) getComponentAccessibility:(NSString* _Nonnull)role; -+ (CommonComponentAccessibility * _Nullable) getComponentAccessibility:(NSString * _Nonnull)role andParent:(CommonComponentAccessibility * _Nonnull)parent; ++ (Class _Nonnull) getComponentAccessibilityClass:(NSString* _Nonnull)role; ++ (Class _Nonnull) getComponentAccessibilityClass:(NSString * _Nonnull)role andParent:(CommonComponentAccessibility * _Nonnull)parent; + (NSArray* _Nullable)childrenOfParent:(CommonComponentAccessibility* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored; + (NSArray* _Nullable)childrenOfParent:(CommonComponentAccessibility* _Nonnull)parent withEnv:(JNIEnv _Nonnull * _Nonnull)env withChildrenCode:(NSInteger)whichChildren allowIgnored:(BOOL)allowIgnored recursive:(BOOL)recursive; + (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; ++ (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent withClass:(Class _Nonnull)classType accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; + (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)role index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; + (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view; -// If the isWraped parameter is true, then the object passed as a parent was created based on the same java component, -// but performs a different NSAccessibilityRole of a table cell, or a list row, or tree row, -// and we need to create an element whose role corresponds to the role in Java. -+ (CommonComponentAccessibility* _Nullable) createWithParent:(CommonComponentAccessibility* _Nullable)parent accessible:(jobject _Nonnull)jaccessible role:(NSString* _Nonnull)javaRole index:(jint)index withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view isWrapped:(BOOL)wrapped; - // The current parameter is used to bypass the check for an item's index on the parent so that the item is created. This is necessary, // for example, for AccessibleJTreeNode, whose currentComponent has index -1 + (CommonComponentAccessibility* _Nullable) createWithAccessible:(jobject _Nonnull)jaccessible withEnv:(JNIEnv _Nonnull * _Nonnull)env withView:(NSView* _Nonnull)view isCurrent:(BOOL)current; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 9fe7ce4e520402144309ec7f2cb596b217dbabb5..aa7068180398a95ccc75f26118f92dc2bbaa8651 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -218,9 +218,9 @@ static jobject sAccessibilityClass = NULL; /* * If new implementation of the accessible component peer for the given role exists - * return the allocated class otherwise return nil to let old implementation being initialized + * return the component's class otherwise return CommonComponentAccessibility */ -+ (CommonComponentAccessibility *) getComponentAccessibility:(NSString *)role ++ (Class) getComponentAccessibilityClass:(NSString *)role { AWT_ASSERT_APPKIT_THREAD; if (rolesMap == nil) { @@ -229,12 +229,12 @@ static jobject sAccessibilityClass = NULL; NSString *className = [rolesMap objectForKey:role]; if (className != nil) { - return [NSClassFromString(className) alloc]; + return NSClassFromString(className); } - return [CommonComponentAccessibility alloc]; + return [CommonComponentAccessibility class]; } -+ (CommonComponentAccessibility *) getComponentAccessibility:(NSString *)role andParent:(CommonComponentAccessibility *)parent ++ (Class) getComponentAccessibilityClass:(NSString *)role andParent:(CommonComponentAccessibility *)parent { AWT_ASSERT_APPKIT_THREAD; if (rolesMap == nil) { @@ -242,9 +242,9 @@ static jobject sAccessibilityClass = NULL; } NSString *className = [rowRolesMapForParent objectForKey:[[parent class] className]]; if (className == nil) { - return [CommonComponentAccessibility getComponentAccessibility:role]; + return [CommonComponentAccessibility getComponentAccessibilityClass:role]; } - return [NSClassFromString(className) alloc]; + return NSClassFromString(className); } - (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole @@ -378,6 +378,12 @@ static jobject sAccessibilityClass = NULL; NSAccessibilityPostNotification([[self accessibilitySelectedRows] firstObject], NSAccessibilityRowCollapsedNotification); } +- (void)postSelectedCellsChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilitySelectedCellsChangedNotification); +} + - (BOOL)isEqual:(id)anObject { if (![anObject isKindOfClass:[self class]]) return NO; @@ -413,13 +419,20 @@ static jobject sAccessibilityClass = NULL; GET_CACCESSIBLE_CLASS_RETURN(NULL); DECLARE_STATIC_METHOD_RETURN(sjm_getCAccessible, sjc_CAccessible, "getCAccessible", "(Ljavax/accessibility/Accessible;)Lsun/lwawt/macosx/CAccessible;", NULL); - if ((*env)->IsInstanceOf(env, jaccessible, sjc_CAccessible)) { - return jaccessible; - } else if ((*env)->IsInstanceOf(env, jaccessible, sjc_Accessible)) { - jobject o = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getCAccessible, jaccessible); + + // jaccessible is a weak ref, check it's still alive + jobject jaccessibleLocal = (*env)->NewLocalRef(env, jaccessible); + if ((*env)->IsSameObject(env, jaccessibleLocal, NULL)) return NULL; + + if ((*env)->IsInstanceOf(env, jaccessibleLocal, sjc_CAccessible)) { + return jaccessibleLocal; // delete in the caller + } else if ((*env)->IsInstanceOf(env, jaccessibleLocal, sjc_Accessible)) { + jobject jCAX = (*env)->CallStaticObjectMethod(env, sjc_CAccessible, sjm_getCAccessible, jaccessibleLocal); CHECK_EXCEPTION(); - return o; + (*env)->DeleteLocalRef(env, jaccessibleLocal); + return jCAX; // delete in the caller } + (*env)->DeleteLocalRef(env, jaccessibleLocal); return NULL; } @@ -519,29 +532,25 @@ static jobject sAccessibilityClass = NULL; + (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view { - return [CommonComponentAccessibility createWithParent:parent accessible:jaccessible role:javaRole index:index withEnv:env withView:view isWrapped:NO]; + Class classType = [self getComponentAccessibilityClass:javaRole andParent:parent]; + return [CommonComponentAccessibility createWithParent:parent withClass:classType accessible:jaccessible role:javaRole index:index withEnv:env withView:view]; } -+ (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view isWrapped:(BOOL)wrapped ++ (CommonComponentAccessibility *) createWithParent:(CommonComponentAccessibility *)parent withClass:(Class)classType accessible:(jobject)jaccessible role:(NSString *)javaRole index:(jint)index withEnv:(JNIEnv *)env withView:(NSView *)view { GET_CACCESSIBLE_CLASS_RETURN(NULL); DECLARE_FIELD_RETURN(jf_ptr, sjc_CAccessible, "ptr", "J", NULL); // try to fetch the jCAX from Java, and return autoreleased jobject jCAX = [CommonComponentAccessibility getCAccessible:jaccessible withEnv:env]; if (jCAX == NULL) return nil; - if (!wrapped) { // If wrapped is true, then you don't need to get an existing instance, you need to create a new one - CommonComponentAccessibility *value = (CommonComponentAccessibility *) jlong_to_ptr((*env)->GetLongField(env, jCAX, jf_ptr)); - if (value != nil) { - (*env)->DeleteLocalRef(env, jCAX); - return [[value retain] autorelease]; - } + CommonComponentAccessibility *value = (CommonComponentAccessibility *) jlong_to_ptr((*env)->GetLongField(env, jCAX, jf_ptr)); + if (value != nil) { + (*env)->DeleteLocalRef(env, jCAX); + return [[value retain] autorelease]; } - // otherwise, create a new instance - CommonComponentAccessibility *newChild = [CommonComponentAccessibility getComponentAccessibility:javaRole andParent:parent]; - - // must init freshly -alloc'd object - [newChild initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // must init new instance + CommonComponentAccessibility *newChild = + [[classType alloc] initWithParent:parent withEnv:env withAccessible:jCAX withIndex:index withView:view withJavaRole:javaRole]; // If creating a JPopupMenu (not a combobox popup list) need to fire menuOpened. // This is the only way to know if the menu is opening; visible state change @@ -555,10 +564,7 @@ static jobject sAccessibilityClass = NULL; [newChild retain]; (*env)->SetLongField(env, jCAX, jf_ptr, ptr_to_jlong(newChild)); - // the link is removed in the wrapper - if (!wrapped) { - (*env)->DeleteLocalRef(env, jCAX); - } + (*env)->DeleteLocalRef(env, jCAX); // return autoreleased instance return [newChild autorelease]; @@ -1081,6 +1087,7 @@ static jobject sAccessibilityClass = NULL; { JNIEnv* env = [ThreadUtilities getJNIEnv]; + GET_CACCESSIBILITY_CLASS_RETURN(nil); DECLARE_CLASS_RETURN(jc_Container, "java/awt/Container", nil); DECLARE_STATIC_METHOD_RETURN(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest", "(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;", nil); @@ -1230,3 +1237,19 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeCollapsed waitUntilDone:NO]; JNI_COCOA_EXIT(env); } + +/* + * Class: sun_lwawt_macosx_CAccessible + * Method: selectedCellsChanged + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectedCellsChanged + (JNIEnv *env, jclass jklass, jlong element) +{ + JNI_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(postSelectedCellsChanged) + on:(CommonComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; + JNI_COCOA_EXIT(env); +} diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComponentWrapperAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComponentWrapperAccessibility.h new file mode 100644 index 0000000000000000000000000000000000000000..fd1e774c274cbdb5a766b2169d8a0457c1f5c452 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComponentWrapperAccessibility.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#import "CommonComponentAccessibility.h" + +/** + * Some native a11y elements do not have direct peers in Java, like list rows and cells. + * However, these elements are required by Cocoa in order for a11y to work properly. + * The ComponentWrapperAccessibility interface provides a concept of wrapping an element + * originated from java (like a list item, or a table element) with a component + * which has a11y role required Cocoa (like NSAccessibilityRowRole, or NSAccessibilityCellRole) + * but does not have peer in java. + * + * The wrapping component becomes a parent of the wrapped child in the a11y hierarchy. + * The child component is created automatically on demand with the same set of arguments, + * except that it has a11y role of its java peer. + * + * It is important that only the wrapping component is linked with sun.lwawt.macosx.CAccessible + * and thus its lifecycle depends on the java accessible. So when the same java accessible is passed + * to create a native peer, the wrapping component is retrieved in case it has already been + * created (see [CommonComponentAccessibility createWithParent]). When the wrapping component is + * deallocated (as triggered from the java side) it releases the wrapped child. + */ +@interface ComponentWrapperAccessibility : CommonComponentAccessibility + +@property (nonatomic, retain) CommonComponentAccessibility *wrappedChild; + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComponentWrapperAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComponentWrapperAccessibility.m new file mode 100644 index 0000000000000000000000000000000000000000..2101d7042fe62fa757022b09fcc8f2d6ebee1ee7 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComponentWrapperAccessibility.m @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#import "ComponentWrapperAccessibility.h" +#import "ThreadUtilities.h" + +@implementation ComponentWrapperAccessibility + +@synthesize wrappedChild; + +- (NSAccessibilityRole)accessibilityRole { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override -(NSAccessibilityRole)accessibilityRole in a subclass"] + userInfo:nil]; +} + +- (NSArray *)accessibilityChildren { + if (!wrappedChild) { + wrappedChild = + [[CommonComponentAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:fAccessible + withIndex:0 + withView:fView + withJavaRole:fJavaRole]; + } + return [NSArray arrayWithObject:wrappedChild]; +} + +- (void)dealloc { + if (wrappedChild) { + [wrappedChild release]; + } + [super dealloc]; +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h index 2453f92e58fe052a0cbafb179de14294311c380d..130d12721e6de0e54a793204aaa5991885182cee 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.h @@ -22,7 +22,7 @@ * questions. */ -#import "CommonComponentAccessibility.h" +#import "ComponentWrapperAccessibility.h" -@interface ListRowAccessibility : CommonComponentAccessibility +@interface ListRowAccessibility : ComponentWrapperAccessibility @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m index 2c4d51e10ffab51c6849db66712dd2814bf8a69d..8b30080c85b78eb772eda242b61dbc6c9e4f9172 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ListRowAccessibility.m @@ -38,25 +38,6 @@ return NSAccessibilityRowRole; } -- (NSArray *)accessibilityChildren -{ - NSArray *children = [super accessibilityChildren]; - if (children == NULL) { - - // Since the row element has already been created, we should no create it again, but just retrieve it by a pointer, that's why isWrapped is set to YES. - CommonComponentAccessibility *newChild = [CommonComponentAccessibility createWithParent:self - accessible:self->fAccessible - role:self->fJavaRole - index:self->fIndex - withEnv:[ThreadUtilities getJNIEnv] - withView:self->fView - isWrapped:YES]; - return [NSArray arrayWithObject:newChild]; - } else { - return children; - } -} - - (NSInteger)accessibilityIndex { return [[self accessibilityParent] accessibilityIndexOfChild:self]; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m index a2bce0bf70152f365d92b1989eb455f49d4a5370..e4d7e66027d9609dce2ec94c2e5854167d103542 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineRowAccessibility.m @@ -72,14 +72,7 @@ static jclass sjc_CAccessible = NULL; return children; } } - - return [NSArray arrayWithObject:[CommonComponentAccessibility createWithParent:self - accessible:self->fAccessible - role:self->fJavaRole - index:self->fIndex - withEnv:env - withView:self->fView - isWrapped:YES]]; + return [super accessibilityChildren]; } - (NSInteger)accessibilityDisclosureLevel diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h index 4f746706f5c53c35928165b40896b97bb4628572..5fe04aa257f61376c355d53f57ccb09052a3af29 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.h @@ -25,6 +25,9 @@ #import "CommonComponentAccessibility.h" @interface TableAccessibility : CommonComponentAccessibility +{ + NSMutableDictionary *rowCache; +} - (BOOL)isAccessibleChildSelectedFromIndex:(int)index; - (int) accessibleRowAtIndex:(int)index; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m index cd534c1ad3ccb7dea74741994b72d56872d22df5..d6382822e1e52a95a6a43691724e35a8175449a3 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableAccessibility.m @@ -33,6 +33,7 @@ #import "JNIUtilities.h" #import "CellAccessibility.h" #import "sun_lwawt_macosx_CAccessibility.h" +#import "sun_lwawt_macosx_CAccessible.h" static jclass sjc_CAccessibility = NULL; @@ -122,6 +123,27 @@ static jmethodID sjm_getAccessibleName = NULL; return isAccessibleChildSelected; } +- (TableRowAccessibility *)createRowWithIndex:(NSUInteger)index +{ + if (rowCache == nil) { + int rowCount = [self accessibilityRowCount]; + rowCache = [[NSMutableDictionary dictionaryWithCapacity:rowCount] retain]; + } + + id row = [rowCache objectForKey:[NSNumber numberWithUnsignedInteger:index]]; + if (row == nil) { + row = [[TableRowAccessibility alloc] initWithParent:self + withEnv:[ThreadUtilities getJNIEnv] + withAccessible:NULL + withIndex:index + withView:[self view] + withJavaRole:JavaAccessibilityIgnore]; + [rowCache setObject:row forKey:[NSNumber numberWithUnsignedInteger:index]]; + } + + return row; +} + // NSAccessibilityElement protocol methods - (NSArray *)accessibilityChildren @@ -139,12 +161,7 @@ static jmethodID sjm_getAccessibleName = NULL; int rowCount = [self accessibilityRowCount]; NSMutableArray *children = [NSMutableArray arrayWithCapacity:rowCount]; for (int i = 0; i < rowCount; i++) { - [children addObject:[[TableRowAccessibility alloc] initWithParent:self - withEnv:[ThreadUtilities getJNIEnv] - withAccessible:NULL - withIndex:i - withView:[self view] - withJavaRole:JavaAccessibilityIgnore]]; + [children addObject:[self createRowWithIndex:i]]; } return [NSArray arrayWithArray:children]; } @@ -154,12 +171,7 @@ static jmethodID sjm_getAccessibleName = NULL; NSArray *selectedRowIndexses = [self getTableSelectedInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_ROWS]; NSMutableArray *children = [NSMutableArray arrayWithCapacity:[selectedRowIndexses count]]; for (NSNumber *index in selectedRowIndexses) { - [children addObject:[[TableRowAccessibility alloc] initWithParent:self - withEnv:[ThreadUtilities getJNIEnv] - withAccessible:NULL - withIndex:index.unsignedIntValue - withView:[self view] - withJavaRole:JavaAccessibilityIgnore]]; + [children addObject:[self createRowWithIndex:index.unsignedIntValue]]; } return [NSArray arrayWithArray:children]; } @@ -179,36 +191,6 @@ static jmethodID sjm_getAccessibleName = NULL; return [super accessibilityParent]; } -- (nullable NSArray *)accessibilityColumns -{ - int colCount = [self accessibilityColumnCount]; - NSMutableArray *columns = [NSMutableArray arrayWithCapacity:colCount]; - for (int i = 0; i < colCount; i++) { - [columns addObject:[[ColumnAccessibility alloc] initWithParent:self - withEnv:[ThreadUtilities getJNIEnv] - withAccessible:NULL - withIndex:i - withView:self->fView - withJavaRole:JavaAccessibilityIgnore]]; - } - return [NSArray arrayWithArray:columns]; -} - -- (nullable NSArray *)accessibilitySelectedColumns -{ - NSArray *indexes = [self getTableSelectedInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_COLS]; - NSMutableArray *columns = [NSMutableArray arrayWithCapacity:[indexes count]]; - for (NSNumber *i in indexes) { - [columns addObject:[[ColumnAccessibility alloc] initWithParent:self - withEnv:[ThreadUtilities getJNIEnv] - withAccessible:NULL - withIndex:i.unsignedIntValue - withView:self->fView - withJavaRole:JavaAccessibilityIgnore]]; - } - return [NSArray arrayWithArray:columns]; -} - - (NSInteger)accessibilityRowCount { return [[self getTableInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_ROWS] integerValue]; @@ -219,23 +201,47 @@ static jmethodID sjm_getAccessibleName = NULL; return [[self getTableInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_COLS] integerValue]; } -- (nullable NSArray *)accessibilitySelectedCells +- (id)accessibilityCellForColumn:(NSInteger)column row:(NSInteger)row +{ + return [[[self createRowWithIndex:row] accessibilityChildren] objectAtIndex:column]; +} + +- (NSArray *)accessibilitySelectedCells { - NSArray *children = [super accessibilitySelectedChildren]; - NSMutableArray *cells = [NSMutableArray arrayWithCapacity:[children count]]; - for (CommonComponentAccessibility *child in children) { - [cells addObject:[[CellAccessibility alloc] initWithParent:self - withEnv:[ThreadUtilities getJNIEnv] - withAccessible:child->fAccessible - withIndex:child->fIndex - withView:fView - withJavaRole:child->fJavaRole]]; + NSArray *selectedRows = [self getTableSelectedInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_ROWS]; + NSArray *selectedColumns = [self getTableSelectedInfo:sun_lwawt_macosx_CAccessibility_JAVA_AX_COLS]; + NSMutableArray *selectedCells = [NSMutableArray arrayWithCapacity:[selectedRows count] * [selectedColumns count]]; + for (NSNumber *row in selectedRows) { + for (NSNumber *col in selectedColumns) { + CellAccessibility *cell = [self accessibilityCellForColumn:[col integerValue] row:[row integerValue]]; + [selectedCells addObject:cell]; + } } - return [NSArray arrayWithArray:cells]; + return [NSArray arrayWithArray:selectedCells]; } -- (id)accessibilityCellForColumn:(NSInteger)column row:(NSInteger)row { - return [[(TableRowAccessibility *)[[self accessibilityRows] objectAtIndex:row] accessibilityChildren] objectAtIndex:column]; +- (void)clearCache { + for (NSNumber *key in [rowCache allKeys]) { + [[rowCache objectForKey:key] release]; + } + [rowCache release]; + rowCache = nil; } @end + +/* + * Class: sun_lwawt_macosx_CAccessible + * Method: tableContentIndexDestroy + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_tableContentCacheClear + (JNIEnv *env, jclass class, jlong element) +{ + JNI_COCOA_ENTER(env); + [ThreadUtilities performOnMainThread:@selector(clearCache) + on:(CommonComponentAccessibility *)jlong_to_ptr(element) + withObject:nil + waitUntilDone:NO]; + JNI_COCOA_EXIT(env); +} diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m index 60985e203f9a681c28bcc35cd428fa6552aa50b9..31636dd99bb4acdfd08aaeda34aee3db6e1d6276 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TableRowAccessibility.m @@ -33,12 +33,6 @@ static jclass sjc_CAccessibility = NULL; -static jmethodID jm_getChildrenAndRoles = NULL; -#define GET_CHILDRENANDROLES_METHOD_RETURN(ret) \ - GET_CACCESSIBILITY_CLASS_RETURN(ret); \ - GET_STATIC_METHOD_RETURN(jm_getChildrenAndRoles, sjc_CAccessibility, "getChildrenAndRoles",\ - "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret); - @implementation TableRowAccessibility // NSAccessibilityElement protocol methods @@ -55,25 +49,28 @@ static jmethodID jm_getChildrenAndRoles = NULL; - (NSArray *)accessibilityChildren { - NSArray *children = [super accessibilityChildren]; + NSMutableArray *children = [super accessibilityChildren]; if (children == nil) { JNIEnv *env = [ThreadUtilities getJNIEnv]; CommonComponentAccessibility *parent = [self accessibilityParent]; if (parent->fAccessible == NULL) return nil; - GET_CHILDRENANDROLES_METHOD_RETURN(nil); - jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRoles, - parent->fAccessible, parent->fComponent, sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN, NO); + + GET_CACCESSIBILITY_CLASS_RETURN(nil); + DECLARE_STATIC_METHOD_RETURN(jm_getTableRowChildrenAndRoles, sjc_CAccessibility, "getTableRowChildrenAndRoles",\ + "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZI)[Ljava/lang/Object;", nil); + + jobjectArray jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod( + env, sjc_CAccessibility, jm_getTableRowChildrenAndRoles, parent->fAccessible, parent->fComponent, + sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN, NO, [self rowNumberInTable]); CHECK_EXCEPTION(); + if (jchildrenAndRoles == NULL) return nil; jsize arrayLen = (*env)->GetArrayLength(env, jchildrenAndRoles); - NSMutableArray *childrenCells = [NSMutableArray arrayWithCapacity:arrayLen/2]; + children = [NSMutableArray arrayWithCapacity:arrayLen / 2]; + int childIndex = [self rowNumberInTable] * [(TableAccessibility *)parent accessibilityColumnCount]; - NSUInteger childIndex = fIndex * [(TableAccessibility *)parent accessibilityColumnCount]; - NSInteger i = childIndex * 2; - NSInteger n = (fIndex + 1) * [(TableAccessibility *)parent accessibilityColumnCount] * 2; - for(i; i < n; i+=2) - { + for (NSInteger i = 0; i < arrayLen; i += 2) { jobject /* Accessible */ jchild = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i); jobject /* String */ jchildJavaRole = (*env)->GetObjectArrayElement(env, jchildrenAndRoles, i+1); @@ -87,13 +84,15 @@ static jmethodID jm_getChildrenAndRoles = NULL; (*env)->DeleteLocalRef(env, jkey); } - CellAccessibility *child = [[CellAccessibility alloc] initWithParent:self - withEnv:env - withAccessible:jchild - withIndex:childIndex - withView:self->fView - withJavaRole:childJavaRole]; - [childrenCells addObject:[[child retain] autorelease]]; + CellAccessibility *child = (CellAccessibility *) + [CommonComponentAccessibility createWithParent:self + withClass:[CellAccessibility class] + accessible:jchild + role:childJavaRole + index:childIndex + withEnv:env + withView:self->fView]; + [children addObject:child]; (*env)->DeleteLocalRef(env, jchild); (*env)->DeleteLocalRef(env, jchildJavaRole); @@ -101,10 +100,12 @@ static jmethodID jm_getChildrenAndRoles = NULL; childIndex++; } (*env)->DeleteLocalRef(env, jchildrenAndRoles); - return childrenCells; - } else { - return children; } + return children; +} + +- (NSUInteger)rowNumberInTable { + return self->fIndex; } - (NSInteger)accessibilityIndex @@ -142,4 +143,9 @@ static jmethodID jm_getChildrenAndRoles = NULL; return NSMakeRect(point.x, point.y, width, height); } +- (BOOL)isAccessibilityOrderedByRow +{ + return YES; +} + @end diff --git a/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m b/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m index 700f435d4826c5e3f70298200f0f2b1105d369ca..f7a65db5a69e00480f420b09623a9046ec9d72ab 100644 --- a/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m +++ b/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m @@ -180,26 +180,11 @@ AWT_ASSERT_APPKIT_THREAD; } // If it wasn't specified as an argument, see if it was specified as a system property. + // The launcher code sets this if it is not already set on the command line. if (fApplicationName == nil) { fApplicationName = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.application.name" withEnv:env]; } - // If we STILL don't have it, the app name is retrieved from an environment variable (set in java.c) It should be UTF8. - if (fApplicationName == nil) { - char mainClassEnvVar[80]; - snprintf(mainClassEnvVar, sizeof(mainClassEnvVar), "JAVA_MAIN_CLASS_%d", getpid()); - char *mainClass = getenv(mainClassEnvVar); - if (mainClass != NULL) { - fApplicationName = [NSString stringWithUTF8String:mainClass]; - unsetenv(mainClassEnvVar); - - NSRange lastPeriod = [fApplicationName rangeOfString:@"." options:NSBackwardsSearch]; - if (lastPeriod.location != NSNotFound) { - fApplicationName = [fApplicationName substringFromIndex:lastPeriod.location + 1]; - } - } - } - // The dock name is nil for double-clickable Java apps (bundled and Web Start apps) // When that happens get the display name, and if that's not available fall back to // CFBundleName. diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java index ae711c0103a9398fb09be45a024accdbcc358519..cf2fdd26307292a7e09d5803e3c8c7fb1f7eec32 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java @@ -220,6 +220,7 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { } private Font font; + @SuppressWarnings("serial") // Type of field is not Serializable private Toolkit toolkit; private String sampleText = "Abcde..."; diff --git a/src/java.desktop/share/classes/com/sun/beans/introspect/EventSetInfo.java b/src/java.desktop/share/classes/com/sun/beans/introspect/EventSetInfo.java index 23b4f1c3f245d4f49361e06b35f0a869217be63b..188fcbdef3c14f91aef392612bb9c2c7ab1ef417 100644 --- a/src/java.desktop/share/classes/com/sun/beans/introspect/EventSetInfo.java +++ b/src/java.desktop/share/classes/com/sun/beans/introspect/EventSetInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -132,12 +132,7 @@ public final class EventSetInfo { } } } - Iterator iterator = map.values().iterator(); - while (iterator.hasNext()) { - if (!iterator.next().initialize()) { - iterator.remove(); - } - } + map.values().removeIf(eventSetInfo -> !eventSetInfo.initialize()); return !map.isEmpty() ? Collections.unmodifiableMap(map) : Collections.emptyMap(); diff --git a/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java b/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java index 934398ae7bd2b9fef3e0661ce843068337d0c8fd..833aeb7265ce647c3ba553b518f1c803953e6f55 100644 --- a/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java +++ b/src/java.desktop/share/classes/com/sun/beans/introspect/PropertyInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -303,12 +302,7 @@ public final class PropertyInfo { } } } - Iterator iterator = map.values().iterator(); - while (iterator.hasNext()) { - if (!iterator.next().initialize()) { - iterator.remove(); - } - } + map.values().removeIf(propertyInfo -> !propertyInfo.initialize()); return !map.isEmpty() ? Collections.unmodifiableMap(map) : Collections.emptyMap(); diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java index 37d9a9b03844882adb92cf2f4e752b40ce2a46d4..df283e8102cd9ab099fbbe759718ac2669df6d0a 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java @@ -591,6 +591,13 @@ public class BMPImageReader extends ImageReader implements BMPConstants { height = Math.abs(height); } + if (metadata.compression == BI_RGB) { + long imageDataSize = (width * height * (bitsPerPixel / 8)); + if (imageDataSize > (bitmapFileSize - bitmapOffset)) { + throw new IIOException(I18N.getString("BMPImageReader9")); + } + } + // Reset Image Layout so there's only one tile. //Define the color space ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java index 997d01e3973aa56bd47d69048102a8e626eff1e8..cca9129376d1abc81a47e6f6e18ad67ab9a024ae 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java @@ -34,7 +34,6 @@ import java.awt.image.WritableRaster; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; -import java.util.Iterator; import java.util.Vector; public abstract class SimpleRenderedImage implements RenderedImage { @@ -282,13 +281,7 @@ public abstract class SimpleRenderedImage implements RenderedImage { return null; } - // Copy the strings from the Vector over to a String array. - String[] prefixNames = new String[names.size()]; - int count = 0; - for (Iterator it = names.iterator(); it.hasNext(); ) { - prefixNames[count++] = it.next(); - } - + String[] prefixNames = names.toArray(new String[0]); return prefixNames; } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties b/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties index 0705b99eee8c2f099254dfed3384d585c4b892d8..5b9e47b921530b1e43d4ae3bf1ef12b657a98fc1 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties @@ -24,6 +24,7 @@ BMPImageReader5=Input has not been set. BMPImageReader6=Unable to read the image header. BMPImageReader7=Invalid bitmap offset. BMPImageReader8=Invalid bits per pixel in image header. +BMPImageReader9=Invalid width/height for BI_RGB image data. BMPImageWriter0=Output is not an ImageOutputStream. BMPImageWriter1=The image region to be encoded is empty. BMPImageWriter2=Only 1 or 3 band image is encoded. diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/DHTMarkerSegment.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/DHTMarkerSegment.java index 80405bac20c2380325416450d6bc407efa125653..34eca627d97dd4276ff5c03cb1f38d9743c6ffa7 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/DHTMarkerSegment.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/DHTMarkerSegment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ import javax.imageio.plugins.jpeg.JPEGHuffmanTable; import java.io.IOException; import java.util.List; import java.util.ArrayList; -import java.util.Iterator; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -119,8 +118,7 @@ class DHTMarkerSegment extends MarkerSegment { void print() { printTag("DHT"); - System.out.println("Num tables: " - + Integer.toString(tables.size())); + System.out.println("Num tables: " + tables.size()); for (int i= 0; i(); - for (Iterator iter = - extSegments.iterator(); iter.hasNext();) { - JFIFExtensionMarkerSegment jfxx = iter.next(); + for (JFIFExtensionMarkerSegment jfxx : extSegments) { newGuy.extSegments.add((JFIFExtensionMarkerSegment) jfxx.clone()); } } @@ -231,9 +228,7 @@ class JFIFMarkerSegment extends MarkerSegment { if (!extSegments.isEmpty()) { IIOMetadataNode JFXXnode = new IIOMetadataNode("JFXX"); node.appendChild(JFXXnode); - for (Iterator iter = - extSegments.iterator(); iter.hasNext();) { - JFIFExtensionMarkerSegment seg = iter.next(); + for (JFIFExtensionMarkerSegment seg : extSegments) { JFXXnode.appendChild(seg.getNativeNode()); } } @@ -622,8 +617,7 @@ class JFIFMarkerSegment extends MarkerSegment { printTag("JFIF"); System.out.print("Version "); System.out.print(majorVersion); - System.out.println(".0" - + Integer.toString(minorVersion)); + System.out.println(".0" + minorVersion); System.out.print("Resolution units: "); System.out.println(resUnits); System.out.print("X density: "); @@ -635,9 +629,7 @@ class JFIFMarkerSegment extends MarkerSegment { System.out.print("Thumbnail Height: "); System.out.println(thumbHeight); if (!extSegments.isEmpty()) { - for (Iterator iter = - extSegments.iterator(); iter.hasNext();) { - JFIFExtensionMarkerSegment extSegment = iter.next(); + for (JFIFExtensionMarkerSegment extSegment : extSegments) { extSegment.print(); } } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageReader.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageReader.java index 11eed2e94e049dcdfa97bd535bdaa7473b476abb..ab15bdfc6b810e9b40779164d2dcdfb351d623ef 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageReader.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageReader.java @@ -1275,9 +1275,8 @@ public class JPEGImageReader extends ImageReader { // and set knownPassCount if (imageIndex == imageMetadataIndex) { // We have metadata knownPassCount = 0; - for (Iterator iter = - imageMetadata.markerSequence.iterator(); iter.hasNext();) { - if (iter.next() instanceof SOSMarkerSegment) { + for (MarkerSegment markerSegment : imageMetadata.markerSequence) { + if (markerSegment instanceof SOSMarkerSegment) { knownPassCount++; } } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java index 720e970903f695357ffd78bb3deeb95243d2a1a9..31acae628a14e895a17c426e471a0c1a0783b725 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java @@ -41,7 +41,6 @@ import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import javax.imageio.IIOException; @@ -1382,9 +1381,7 @@ public class JPEGImageWriter extends ImageWriter { List segments = new ArrayList<>(); int SCAN_SIZE = 9; int MAX_COMPS_PER_SCAN = 4; - for (Iterator iter = metadata.markerSequence.iterator(); - iter.hasNext();) { - MarkerSegment seg = iter.next(); + for (MarkerSegment seg : metadata.markerSequence) { if (seg instanceof SOSMarkerSegment) { segments.add((SOSMarkerSegment) seg); } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java index 477897ec2ba4439c81d93675ee9ee5903b519c4b..ece1df86fcf50984241eab5c36666f7b67d498ae 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java @@ -1711,9 +1711,7 @@ public class JPEGMetadata extends IIOMetadata implements Cloneable { } if (idsDiffer) { // update the ids in each SOS marker segment - for (Iterator iter = markerSequence.iterator(); - iter.hasNext();) { - MarkerSegment seg = iter.next(); + for (MarkerSegment seg : markerSequence) { if (seg instanceof SOSMarkerSegment) { SOSMarkerSegment target = (SOSMarkerSegment) seg; for (int i = 0; i < target.componentSpecs.length; i++) { @@ -1766,9 +1764,7 @@ public class JPEGMetadata extends IIOMetadata implements Cloneable { if (updateQtables) { List tableSegments = new ArrayList<>(); - for (Iterator iter = markerSequence.iterator(); - iter.hasNext();) { - MarkerSegment seg = iter.next(); + for (MarkerSegment seg : markerSequence) { if (seg instanceof DQTMarkerSegment) { tableSegments.add((DQTMarkerSegment) seg); } @@ -1784,12 +1780,8 @@ public class JPEGMetadata extends IIOMetadata implements Cloneable { // Find the table with selector 1. boolean found = false; - for (Iterator iter = tableSegments.iterator(); - iter.hasNext();) { - DQTMarkerSegment testdqt = iter.next(); - for (Iterator tabiter = - testdqt.tables.iterator(); tabiter.hasNext();) { - DQTMarkerSegment.Qtable tab = tabiter.next(); + for (DQTMarkerSegment testdqt : tableSegments) { + for (DQTMarkerSegment.Qtable tab : testdqt.tables) { if (tab.tableID == 1) { found = true; } @@ -1798,12 +1790,8 @@ public class JPEGMetadata extends IIOMetadata implements Cloneable { if (!found) { // find the table with selector 0. There should be one. DQTMarkerSegment.Qtable table0 = null; - for (Iterator iter = - tableSegments.iterator(); iter.hasNext();) { - DQTMarkerSegment testdqt = iter.next(); - for (Iterator tabiter = - testdqt.tables.iterator(); tabiter.hasNext();) { - DQTMarkerSegment.Qtable tab = tabiter.next(); + for (DQTMarkerSegment testdqt : tableSegments) { + for (DQTMarkerSegment.Qtable tab : testdqt.tables) { if (tab.tableID == 0) { table0 = tab; } @@ -1821,9 +1809,7 @@ public class JPEGMetadata extends IIOMetadata implements Cloneable { if (updateHtables) { List tableSegments = new ArrayList<>(); - for (Iterator iter = markerSequence.iterator(); - iter.hasNext();) { - MarkerSegment seg = iter.next(); + for (MarkerSegment seg : markerSequence) { if (seg instanceof DHTMarkerSegment) { tableSegments.add((DHTMarkerSegment) seg); } @@ -1838,12 +1824,8 @@ public class JPEGMetadata extends IIOMetadata implements Cloneable { // find a table with selector 1. AC/DC is irrelevant boolean found = false; - for (Iterator iter = tableSegments.iterator(); - iter.hasNext();) { - DHTMarkerSegment testdht = iter.next(); - for (Iterator tabiter = - testdht.tables.iterator(); tabiter.hasNext();) { - DHTMarkerSegment.Htable tab = tabiter.next(); + for (DHTMarkerSegment testdht : tableSegments) { + for (DHTMarkerSegment.Htable tab : testdht.tables) { if (tab.tableID == 1) { found = true; } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/MarkerSegment.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/MarkerSegment.java index 6f51fee8b8a0a2180d6ef6a959174fdadd3d95cc..a0b4b871a04d1359839e6d8476492654dd2b6b8f 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/MarkerSegment.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/MarkerSegment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -98,11 +98,8 @@ class MarkerSegment implements Cloneable { try { data = (byte []) iioNode.getUserObject(); } catch (Exception e) { - IIOInvalidTreeException newGuy = - new IIOInvalidTreeException - ("Can't get User Object", node); - newGuy.initCause(e); - throw newGuy; + throw new IIOInvalidTreeException + ("Can't get User Object", e, node); } } else { throw new IIOInvalidTreeException diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java index b0a5d72a59e35bd1e1c4ece2bb4f71adedad8eae..3a80c07572f2fbc136a5d9b21662e9a81b8d128c 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java @@ -979,14 +979,14 @@ public class PNGMetadata extends IIOMetadata implements Cloneable { sbits = Integer.toString(sBIT_grayBits); } else { // sBIT_colorType == PNGImageReader.PNG_COLOR_RGB || // sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA - sbits = Integer.toString(sBIT_redBits) + " " + - Integer.toString(sBIT_greenBits) + " " + - Integer.toString(sBIT_blueBits); + sbits = sBIT_redBits + " " + + sBIT_greenBits + " " + + sBIT_blueBits; } if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA || sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) { - sbits += " " + Integer.toString(sBIT_alphaBits); + sbits += " " + sBIT_alphaBits; } node.setAttribute("value", sbits); @@ -1134,9 +1134,9 @@ public class PNGMetadata extends IIOMetadata implements Cloneable { node = new IIOMetadataNode("TransparentColor"); if (tRNS_colorType == PNGImageReader.PNG_COLOR_RGB) { node.setAttribute("value", - Integer.toString(tRNS_red) + " " + - Integer.toString(tRNS_green) + " " + - Integer.toString(tRNS_blue)); + tRNS_red + " " + + tRNS_green + " " + + tRNS_blue); } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_GRAY) { node.setAttribute("value", Integer.toString(tRNS_gray)); } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java index 3d307e9ad1b8484c3d3ac5f3830f4ece2799f6ef..6f4475b670a93b15c6567b882ba629502427183f 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java @@ -56,12 +56,12 @@ public class TIFFIFD extends TIFFDirectory { // A set of tag numbers corresponding to tags essential to decoding // the image and metadata required to interpret its samples. // - private static volatile Set essentialTags = null; + private static volatile Set essentialTags; private static void initializeEssentialTags() { Set tags = essentialTags; if (tags == null) { - essentialTags = tags = Set.of( + essentialTags = Set.of( BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE, BaselineTIFFTagSet.TAG_COLOR_MAP, BaselineTIFFTagSet.TAG_COMPRESSION, diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java index 141184087d0fe8bca96ba091aa7828cb70eaba28..134cdd9a2dc6cadaa8194f5ecd671c0e3422e725 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java @@ -1708,7 +1708,7 @@ class Metacity implements SynthConstants { protected boolean getBooleanAttr(Node node, String name, boolean fallback) { String str = getStringAttr(node, name); if (str != null) { - return Boolean.valueOf(str).booleanValue(); + return Boolean.parseBoolean(str); } return fallback; } diff --git a/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java b/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java index c699b3e87a0f64fabef5500a0395aab8af384dcc..3d62b3ae5a63074180f5dda2828a69978dc668e9 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java @@ -105,7 +105,7 @@ public final class DLSSoundbank implements Soundbank { @Override public int hashCode() { - return (int)i1; + return Long.hashCode(i1); } @Override diff --git a/src/java.desktop/share/classes/com/sun/media/sound/DirectAudioDevice.java b/src/java.desktop/share/classes/com/sun/media/sound/DirectAudioDevice.java index e7aca8d3682fe88d984ed31d9c4100c83d880d76..b261b6799ef4a4f012240a14edb7b1f2eb47865f 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/DirectAudioDevice.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/DirectAudioDevice.java @@ -361,13 +361,13 @@ final class DirectAudioDevice extends AbstractMixer { protected final int deviceID; protected long id; protected int waitTime; - protected volatile boolean flushing = false; + protected volatile boolean flushing; protected final boolean isSource; // true for SourceDataLine, false for TargetDataLine protected volatile long bytePosition; - protected volatile boolean doIO = false; // true in between start() and stop() calls - protected volatile boolean stoppedWritten = false; // true if a write occurred in stopped state - protected volatile boolean drained = false; // set to true when drain function returns, set to false in write() - protected boolean monitoring = false; + protected volatile boolean doIO; // true in between start() and stop() calls + protected volatile boolean stoppedWritten; // true if a write occurred in stopped state + protected volatile boolean drained; // set to true when drain function returns, set to false in write() + protected boolean monitoring; // if native needs to manually swap samples/convert sign, this // is set to the framesize @@ -379,7 +379,7 @@ final class DirectAudioDevice extends AbstractMixer { private final Balance balanceControl = new Balance(); private final Pan panControl = new Pan(); private float leftGain, rightGain; - protected volatile boolean noService = false; // do not run the nService method + protected volatile boolean noService; // do not run the nService method // Guards all native calls. protected final Object lockNative = new Object(); @@ -975,7 +975,7 @@ final class DirectAudioDevice extends AbstractMixer { implements Clip, Runnable, AutoClosingClip { private volatile Thread thread; - private volatile byte[] audioData = null; + private volatile byte[] audioData; private volatile int frameSize; // size of one frame in bytes private volatile int m_lengthInFrames; private volatile int loopCount; diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftAudioPusher.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftAudioPusher.java index 4f9c9f21c5c439139973fd97754ea712c9cf3b95..94f2a04466e78179a37f193ee97994901f93d756 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftAudioPusher.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftAudioPusher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,8 +37,8 @@ import javax.sound.sampled.SourceDataLine; */ public final class SoftAudioPusher implements Runnable { - private volatile boolean active = false; - private SourceDataLine sourceDataLine = null; + private volatile boolean active; + private SourceDataLine sourceDataLine; private Thread audiothread; private final AudioInputStream ais; private final byte[] buffer; diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java index 70b8b622df9c32e30eede8b91ac7e6942c7daa44..9d73c68e6ade115101cbc63175f1f93d9cf3ad60 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java @@ -76,7 +76,7 @@ public final class SoftSynthesizer implements AudioSynthesizer, public SoftAudioPusher pusher = null; public AudioInputStream jitter_stream = null; public SourceDataLine sourceDataLine = null; - public volatile long silent_samples = 0; + public volatile long silent_samples; private int framesize = 0; private final WeakReference weak_stream_link; private final AudioFloatConverter converter; @@ -289,12 +289,11 @@ public final class SoftSynthesizer implements AudioSynthesizer, c.current_instrument = null; c.current_director = null; } - for (Instrument instrument : instruments) { + for (ModelInstrument instrument : instruments) { String pat = patchToString(instrument.getPatch()); - SoftInstrument softins - = new SoftInstrument((ModelInstrument) instrument); + SoftInstrument softins = new SoftInstrument(instrument); inslist.put(pat, softins); - loadedlist.put(pat, (ModelInstrument) instrument); + loadedlist.put(pat, instrument); } } diff --git a/src/java.desktop/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java b/src/java.desktop/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java index 179fd8d9b7675b8aff25ac951f73de3e9a0ea277..d56d4d6242d36a3224d31cfb0ac9d3b4374ecbd3 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -91,7 +91,7 @@ public final class WaveExtensibleFileReader extends SunFileReader { @Override public int hashCode() { - return (int) i1; + return Long.hashCode(i1); } @Override diff --git a/src/java.desktop/share/classes/java/awt/Component.java b/src/java.desktop/share/classes/java/awt/Component.java index e5209c49ce2c32a7cbfdb23c2f0304d016bb6096..ff522f5141a5ca64e5a36d7af6ee586ccfba9e9e 100644 --- a/src/java.desktop/share/classes/java/awt/Component.java +++ b/src/java.desktop/share/classes/java/awt/Component.java @@ -395,7 +395,7 @@ public abstract class Component implements ImageObserver, MenuContainer, * @see #validate * @see #invalidate */ - private volatile boolean valid = false; + private volatile boolean valid; /** * The {@code DropTarget} associated with this component. @@ -3177,17 +3177,6 @@ public abstract class Component implements ImageObserver, MenuContainer, * @since 1.0 */ public FontMetrics getFontMetrics(Font font) { - // This is an unsupported hack, but left in for a customer. - // Do not remove. - FontManager fm = FontManagerFactory.getInstance(); - if (fm instanceof SunFontManager - && ((SunFontManager) fm).usePlatformFontMetrics()) { - - if (peer != null && - !(peer instanceof LightweightPeer)) { - return peer.getFontMetrics(font); - } - } return sun.font.FontDesignMetrics.getMetrics(font); } @@ -9340,7 +9329,7 @@ public abstract class Component implements ImageObserver, MenuContainer, * to add/remove ComponentListener and FocusListener to track * target Component's state. */ - private transient volatile int propertyListenersCount = 0; + private transient volatile int propertyListenersCount; /** * A component listener to track show/hide/resize events diff --git a/src/java.desktop/share/classes/java/awt/Container.java b/src/java.desktop/share/classes/java/awt/Container.java index 1327b2fded9ab95c87ef34a5b32840b7b1aca607..68270d35adae69e00ca9043e2d5fa06a86abdc38 100644 --- a/src/java.desktop/share/classes/java/awt/Container.java +++ b/src/java.desktop/share/classes/java/awt/Container.java @@ -3861,7 +3861,7 @@ public class Container extends Component { * Number of PropertyChangeListener objects registered. It's used * to add/remove ContainerListener to track target Container's state. */ - private transient volatile int propertyListenersCount = 0; + private transient volatile int propertyListenersCount; /** * The handler to fire {@code PropertyChange} diff --git a/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java b/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java index 16218b866fa0bc47ece8e1161dfdad85d4e9c869..00e6a9a4a68dfd7207fedd2615897532ac212c99 100644 --- a/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java +++ b/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java @@ -887,11 +887,11 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager { boolean stopPostProcessing = false; java.util.List processors = getKeyEventPostProcessors(); if (processors != null) { - for (java.util.Iterator iter = processors.iterator(); - !stopPostProcessing && iter.hasNext(); ) - { - stopPostProcessing = iter.next(). - postProcessKeyEvent(e); + for (KeyEventPostProcessor processor : processors) { + stopPostProcessing = processor.postProcessKeyEvent(e); + if (stopPostProcessing) { + break; + } } } if (!stopPostProcessing) { @@ -1078,8 +1078,8 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager { * @since 1.5 */ private boolean hasMarker(Component comp) { - for (Iterator iter = typeAheadMarkers.iterator(); iter.hasNext(); ) { - if (iter.next().untilFocused == comp) { + for (TypeAheadMarker typeAheadMarker : typeAheadMarkers) { + if (typeAheadMarker.untilFocused == comp) { return true; } } @@ -1137,15 +1137,11 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager { java.util.List dispatchers = getKeyEventDispatchers(); if (dispatchers != null) { - for (java.util.Iterator iter = dispatchers.iterator(); - iter.hasNext(); ) - { - if (iter.next(). - dispatchKeyEvent(ke)) - { - return true; - } - } + for (KeyEventDispatcher dispatcher : dispatchers) { + if (dispatcher.dispatchKeyEvent(ke)) { + return true; + } + } } return dispatchKeyEvent(ke); } diff --git a/src/java.desktop/share/classes/java/awt/Dialog.java b/src/java.desktop/share/classes/java/awt/Dialog.java index 4132d7b050f8a83345f459df0a7c8cb42fdba879..1a54d70730697ca0aeff76ff75ea0f8c37ad9c1c 100644 --- a/src/java.desktop/share/classes/java/awt/Dialog.java +++ b/src/java.desktop/share/classes/java/awt/Dialog.java @@ -301,7 +301,7 @@ public class Dialog extends Window { * @see #hideAndDisposeHandler() * @see #shouldBlock() */ - transient volatile boolean isInHide = false; + transient volatile boolean isInHide; /* * Indicates that this dialog is being disposed. This flag is set to true at @@ -312,7 +312,7 @@ public class Dialog extends Window { * @see #hideAndDisposeHandler() * @see #doDispose() */ - transient volatile boolean isInDispose = false; + transient volatile boolean isInDispose; private static final String base = "dialog"; private static int nameCounter = 0; diff --git a/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java b/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java index d29d5d9e408f4b19d04d229f7aaf49b2563c184f..62a86ced4b41722046be741fc3148c97a6328b66 100644 --- a/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java +++ b/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java @@ -2977,13 +2977,9 @@ public abstract class KeyboardFocusManager if (hwFocusRequest != null) { heavyweightRequests.removeFirst(); if (hwFocusRequest.lightweightRequests != null) { - for (Iterator lwIter = hwFocusRequest.lightweightRequests. - iterator(); - lwIter.hasNext(); ) - { + for (LightweightFocusRequest lwFocusRequest : hwFocusRequest.lightweightRequests) { manager.dequeueKeyEvents - (-1, lwIter.next(). - component); + (-1, lwFocusRequest.component); } } } diff --git a/src/java.desktop/share/classes/java/awt/Window.java b/src/java.desktop/share/classes/java/awt/Window.java index a14591d66704bbdd748d9ed4312689ec7f7832d0..59e63abbb3ca4d958027e0931ba4f9348858ec84 100644 --- a/src/java.desktop/share/classes/java/awt/Window.java +++ b/src/java.desktop/share/classes/java/awt/Window.java @@ -404,8 +404,8 @@ public class Window extends Container implements Accessible { * These fields are initialized in the native peer code * or via AWTAccessor's WindowAccessor. */ - private transient volatile int securityWarningWidth = 0; - private transient volatile int securityWarningHeight = 0; + private transient volatile int securityWarningWidth; + private transient volatile int securityWarningHeight; static { /* ensure that the necessary native libraries are loaded */ @@ -417,11 +417,11 @@ public class Window extends Container implements Accessible { @SuppressWarnings("removal") String s = java.security.AccessController.doPrivileged( new GetPropertyAction("java.awt.syncLWRequests")); - systemSyncLWRequests = (s != null && s.equals("true")); + systemSyncLWRequests = "true".equals(s); @SuppressWarnings("removal") String s2 = java.security.AccessController.doPrivileged( new GetPropertyAction("java.awt.Window.locationByPlatform")); - locationByPlatformProp = (s2 != null && s2.equals("true")); + locationByPlatformProp = "true".equals(s2); } /** diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java index da14d24892680563f7b693cd24bc5492ca7b7da4..ff8e0745e19d77653bf0551261c300cc743e0703 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java @@ -767,24 +767,6 @@ public class ICC_Profile implements Serializable { deferralInfo = pdi; } - /** - * Frees the resources associated with an {@code ICC_Profile} object. - * - * @deprecated The {@code finalize} method has been deprecated. Subclasses - * that override {@code finalize} in order to perform cleanup should - * be modified to use alternative cleanup mechanisms and to remove - * the overriding {@code finalize} method. When overriding the - * {@code finalize} method, its implementation must explicitly - * ensure that {@code super.finalize()} is invoked as described in - * {@link Object#finalize}. See the specification for {@link - * Object#finalize()} for further information about migration - * options. - */ - @Deprecated(since = "9", forRemoval = true) - @SuppressWarnings("removal") - protected void finalize() { - } - /** * Constructs an {@code ICC_Profile} object corresponding to the data in a * byte array. diff --git a/src/java.desktop/share/classes/java/awt/event/InvocationEvent.java b/src/java.desktop/share/classes/java/awt/event/InvocationEvent.java index b2b8eb5b289cdce7cb16b30f8b9846c85871e2b9..c73b77282d539f8fc0e5900e0b8ee766b45c11c1 100644 --- a/src/java.desktop/share/classes/java/awt/event/InvocationEvent.java +++ b/src/java.desktop/share/classes/java/awt/event/InvocationEvent.java @@ -116,7 +116,7 @@ public class InvocationEvent extends AWTEvent implements ActiveEvent { * @see #isDispatched * @since 1.7 */ - private volatile boolean dispatched = false; + private volatile boolean dispatched; /** * Set to true if dispatch() catches Throwable and stores it in the diff --git a/src/java.desktop/share/classes/java/awt/font/NumericShaper.java b/src/java.desktop/share/classes/java/awt/font/NumericShaper.java index 5b2ebfe7784f5b28a31ff831a116697c7e836884..da21e8fce236c2a959f97e9f172c573445ff85ba 100644 --- a/src/java.desktop/share/classes/java/awt/font/NumericShaper.java +++ b/src/java.desktop/share/classes/java/awt/font/NumericShaper.java @@ -1364,7 +1364,7 @@ public final class NumericShaper implements java.io.Serializable { // use a binary search with a cache - private transient volatile int stCache = 0; + private transient volatile int stCache; private boolean isStrongDirectional(char c) { int cachedIndex = stCache; diff --git a/src/java.desktop/share/classes/java/awt/im/InputContext.java b/src/java.desktop/share/classes/java/awt/im/InputContext.java index 27ddd28655be95474ae57be8c37f47c928ae6486..2a73793df2b7c79af656e10d538a80f6278ab94a 100644 --- a/src/java.desktop/share/classes/java/awt/im/InputContext.java +++ b/src/java.desktop/share/classes/java/awt/im/InputContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ import java.util.Locale; import java.awt.AWTEvent; import java.beans.Transient; import java.lang.Character.Subset; -import sun.awt.im.InputMethodContext; +import java.util.Objects; /** * Provides methods to control text input facilities such as input @@ -144,6 +144,7 @@ public class InputContext { */ public boolean selectInputMethod(Locale locale) { // real implementation is in sun.awt.im.InputContext + Objects.requireNonNull(locale); return false; } @@ -215,6 +216,7 @@ public class InputContext { */ public void setCompositionEnabled(boolean enable) { // real implementation is in sun.awt.im.InputContext + throw new UnsupportedOperationException(); } /** @@ -234,7 +236,7 @@ public class InputContext { @Transient public boolean isCompositionEnabled() { // real implementation is in sun.awt.im.InputContext - return false; + throw new UnsupportedOperationException(); } /** @@ -258,6 +260,7 @@ public class InputContext { */ public void reconvert() { // real implementation is in sun.awt.im.InputContext + throw new UnsupportedOperationException(); } /** @@ -269,6 +272,7 @@ public class InputContext { */ public void dispatchEvent(AWTEvent event) { // real implementation is in sun.awt.im.InputContext + Objects.requireNonNull(event); } /** @@ -286,6 +290,7 @@ public class InputContext { */ public void removeNotify(Component client) { // real implementation is in sun.awt.im.InputContext + Objects.requireNonNull(client); } /** diff --git a/src/java.desktop/share/classes/java/awt/image/ColorModel.java b/src/java.desktop/share/classes/java/awt/image/ColorModel.java index 37c6a5d66d79e48281cf02b2ef89e8247ae17748..98d870c5abf13cfe3de82650431e39e98139f9cb 100644 --- a/src/java.desktop/share/classes/java/awt/image/ColorModel.java +++ b/src/java.desktop/share/classes/java/awt/image/ColorModel.java @@ -1617,26 +1617,6 @@ public abstract class ColorModel implements Transparency{ ("This method is not supported by this color model"); } - /** - * Disposes of system resources associated with this - * {@code ColorModel} once this {@code ColorModel} is no - * longer referenced. - * - * @deprecated The {@code finalize} method has been deprecated. - * Subclasses that override {@code finalize} in order to perform cleanup - * should be modified to use alternative cleanup mechanisms and - * to remove the overriding {@code finalize} method. - * When overriding the {@code finalize} method, its implementation must explicitly - * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}. - * See the specification for {@link Object#finalize()} for further - * information about migration options. - */ - @Deprecated(since = "9", forRemoval = true) - @SuppressWarnings("removal") - public void finalize() { - } - - /** * Returns a {@code Raster} representing the alpha channel of an * image, extracted from the input {@code Raster}, provided that diff --git a/src/java.desktop/share/classes/java/awt/image/IndexColorModel.java b/src/java.desktop/share/classes/java/awt/image/IndexColorModel.java index 1d9e6e59b7596ace436429d062e013939e9521a9..49ab60bce89b0c48b92fe8521a30ae394a44dc95 100644 --- a/src/java.desktop/share/classes/java/awt/image/IndexColorModel.java +++ b/src/java.desktop/share/classes/java/awt/image/IndexColorModel.java @@ -1510,25 +1510,6 @@ public class IndexColorModel extends ColorModel { } } - /** - * Disposes of system resources associated with this - * {@code ColorModel} once this {@code ColorModel} is no - * longer referenced. - * - * @deprecated The {@code finalize} method has been deprecated. - * Subclasses that override {@code finalize} in order to perform cleanup - * should be modified to use alternative cleanup mechanisms and - * to remove the overriding {@code finalize} method. - * When overriding the {@code finalize} method, its implementation must explicitly - * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}. - * See the specification for {@link Object#finalize()} for further - * information about migration options. - */ - @Deprecated(since = "9", forRemoval = true) - @SuppressWarnings("removal") - public void finalize() { - } - /** * Returns the {@code String} representation of the contents of * this {@code ColorModel} object. diff --git a/src/java.desktop/share/classes/java/beans/MetaData.java b/src/java.desktop/share/classes/java/beans/MetaData.java index e2dbb6ff66fed23678917eeca711cdb026241016..52237b15792adb55f36a2faf47b008bb0c050f3e 100644 --- a/src/java.desktop/share/classes/java/beans/MetaData.java +++ b/src/java.desktop/share/classes/java/beans/MetaData.java @@ -28,13 +28,9 @@ import com.sun.beans.finder.PrimitiveWrapperMap; import java.awt.AWTKeyStroke; import java.awt.BorderLayout; -import java.awt.Dimension; import java.awt.Color; import java.awt.Font; -import java.awt.GridBagConstraints; import java.awt.Insets; -import java.awt.Point; -import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.awt.font.TextAttribute; @@ -521,8 +517,8 @@ static class java_util_Collection_PersistenceDelegate extends DefaultPersistence if (newO.size() != 0) { invokeStatement(oldInstance, "clear", new Object[]{}, out); } - for (Iterator i = oldO.iterator(); i.hasNext();) { - invokeStatement(oldInstance, "add", new Object[]{i.next()}, out); + for (Object o : oldO) { + invokeStatement(oldInstance, "add", new Object[]{o}, out); } } } diff --git a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java index f5c027b912fcff692922cb1b7f890da1778075ae..878c489e0f6fe0d84fdc55bccd4af7653466c491 100644 --- a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java +++ b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java @@ -1163,23 +1163,23 @@ public class BeanContextServicesSupport extends BeanContextSupport int count = 0; - Iterator> i = services.entrySet().iterator(); + for (Map.Entry entry : services.entrySet()) { + BCSSServiceProvider bcsp; - while (i.hasNext() && count < serializable) { - Map.Entry entry = i.next(); - BCSSServiceProvider bcsp = null; - - try { + try { bcsp = entry.getValue(); - } catch (ClassCastException cce) { + } catch (ClassCastException cce) { continue; - } + } - if (bcsp.getServiceProvider() instanceof Serializable) { + if (bcsp.getServiceProvider() instanceof Serializable) { oos.writeObject(entry.getKey()); oos.writeObject(bcsp); count++; - } + } + if (count >= serializable) { + break; + } } if (count != serializable) diff --git a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java index 9989bec6381d37698899acf92233d7bedb19177b..58e05f7708410bd013ad9b83b6ee017065da87c9 100644 --- a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java +++ b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java @@ -771,17 +771,15 @@ public class BeanContextSupport extends BeanContextChildSupport } synchronized(children) { - for (Iterator i = children.keySet().iterator(); i.hasNext();) { - Object c = i.next(); - + for (Object c : children.keySet()) { try { - return ((Visibility)c).needsGui(); - } catch (ClassCastException cce) { - // do nothing ... - } + return ((Visibility)c).needsGui(); + } catch (ClassCastException cce) { + // do nothing ... + } - if (c instanceof Container || c instanceof Component) - return true; + if (c instanceof Container || c instanceof Component) + return true; } } @@ -798,11 +796,11 @@ public class BeanContextSupport extends BeanContextChildSupport // lets also tell the Children that can that they may not use their GUI's synchronized(children) { - for (Iterator i = children.keySet().iterator(); i.hasNext();) { - Visibility v = getChildVisibility(i.next()); + for (Object c : children.keySet()) { + Visibility v = getChildVisibility(c); if (v != null) v.dontUseGui(); - } + } } } } @@ -817,8 +815,8 @@ public class BeanContextSupport extends BeanContextChildSupport // lets also tell the Children that can that they may use their GUI's synchronized(children) { - for (Iterator i = children.keySet().iterator(); i.hasNext();) { - Visibility v = getChildVisibility(i.next()); + for (Object c : children.keySet()) { + Visibility v = getChildVisibility(c); if (v != null) v.okToUseGui(); } diff --git a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadata.java b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadata.java index 90b0bfd8766d4cc3b3d75d7b161ec8dcc2f0e273..9952544b48745c3dd87de2d630f25fe3efa72247 100644 --- a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadata.java +++ b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadata.java @@ -407,10 +407,7 @@ public abstract class IIOMetadata { Method meth = cls.getMethod("getInstance"); return (IIOMetadataFormat) meth.invoke(null); } catch (Exception e) { - RuntimeException ex = - new IllegalStateException ("Can't obtain format"); - ex.initCause(e); - throw ex; + throw new IllegalStateException("Can't obtain format", e); } } diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFField.java b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFField.java index 3ed11d20753885f35f253f11f7cc224d03f8392b..f3965fb4b70987a57aabd0ab89d296db5d5f155c 100644 --- a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFField.java +++ b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFField.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ import java.util.StringTokenizer; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import com.sun.imageio.plugins.tiff.TIFFFieldNode; -import com.sun.imageio.plugins.tiff.TIFFIFD; /** * A class representing a field in a TIFF 6.0 Image File Directory. @@ -1304,14 +1303,10 @@ public final class TIFFField implements Cloneable { // If the denominator is a non-zero integral divisor // of the numerator then convert the fraction to be // with respect to a unity denominator. - srationalString = - Integer.toString(ivalue[0] / ivalue[1]) + "/1"; + srationalString = (ivalue[0] / ivalue[1]) + "/1"; } else { // Use the values directly. - srationalString = - Integer.toString(ivalue[0]) + - "/" + - Integer.toString(ivalue[1]); + srationalString = ivalue[0] + "/" + ivalue[1]; } return srationalString; case TIFFTag.TIFF_RATIONAL: @@ -1321,14 +1316,10 @@ public final class TIFFField implements Cloneable { // If the denominator is a non-zero integral divisor // of the numerator then convert the fraction to be // with respect to a unity denominator. - rationalString = - Long.toString(lvalue[0] / lvalue[1]) + "/1"; + rationalString = (lvalue[0] / lvalue[1]) + "/1"; } else { // Use the values directly. - rationalString = - Long.toString(lvalue[0]) + - "/" + - Long.toString(lvalue[1]); + rationalString = lvalue[0] + "/" + lvalue[1]; } return rationalString; default: diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFTag.java b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFTag.java index 33f88f9e34cf4e83dd96c5e908ac01578bebbcbd..ced327455c11830922e73a14b78a89c6a846b1ea 100644 --- a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFTag.java +++ b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ */ package javax.imageio.plugins.tiff; -import java.util.Iterator; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -401,11 +400,10 @@ public class TIFFTag { int[] intValues = null; if (valueNames != null) { Set values = valueNames.keySet(); - Iterator iter = values.iterator(); intValues = new int[values.size()]; int i = 0; - while (iter.hasNext()) { - intValues[i++] = iter.next(); + for (int value : values) { + intValues[i++] = value; } } return intValues; diff --git a/src/java.desktop/share/classes/javax/imageio/spi/ImageReaderWriterSpi.java b/src/java.desktop/share/classes/javax/imageio/spi/ImageReaderWriterSpi.java index 74912019c4f60842f1a06b122a5457c7350aa545..29ed6de0d50dea580db71cedcd86b88bdaa95db0 100644 --- a/src/java.desktop/share/classes/javax/imageio/spi/ImageReaderWriterSpi.java +++ b/src/java.desktop/share/classes/javax/imageio/spi/ImageReaderWriterSpi.java @@ -25,18 +25,12 @@ package javax.imageio.spi; -import java.io.IOException; -import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.Arrays; -import java.util.Iterator; -import javax.imageio.ImageReader; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataFormat; import javax.imageio.metadata.IIOMetadataFormatImpl; -import javax.imageio.stream.ImageInputStream; /** * A superclass containing instance variables and methods common to @@ -597,10 +591,7 @@ public abstract class ImageReaderWriterSpi extends IIOServiceProvider { Method meth = cls.getMethod("getInstance"); return (IIOMetadataFormat) meth.invoke(null); } catch (Exception e) { - RuntimeException ex = - new IllegalStateException ("Can't obtain format"); - ex.initCause(e); - throw ex; + throw new IllegalStateException("Can't obtain format", e); } } diff --git a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java index bd2d0f66fac538617fd8048c0a0b889a1f7cf5bd..ecc4d3c44d7cfd0faf799bcbf2a46d49d64629b8 100644 --- a/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java +++ b/src/java.desktop/share/classes/javax/print/attribute/standard/PrinterStateReasons.java @@ -242,9 +242,7 @@ public final class PrinterStateReasons public int size() { int result = 0; - Iterator iter = iterator(); - while (iter.hasNext()) { - iter.next(); + for (PrinterStateReason ignored : this) { ++ result; } return result; diff --git a/src/java.desktop/share/classes/javax/swing/DefaultDesktopManager.java b/src/java.desktop/share/classes/javax/swing/DefaultDesktopManager.java index a0ac9e9996ab022e872614be4431d0c5df39a2ae..0e6460abf205533bfa9392f5dfcbdf370ce80344 100644 --- a/src/java.desktop/share/classes/javax/swing/DefaultDesktopManager.java +++ b/src/java.desktop/share/classes/javax/swing/DefaultDesktopManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -328,9 +328,9 @@ public class DefaultDesktopManager implements DesktopManager, java.io.Serializab Window window = SwingUtilities.getWindowAncestor(f); if (window != null && !window.isOpaque()) { dragMode = DEFAULT_DRAG_MODE; - } else if (mode != null && mode.equals("outline")) { + } else if ("outline".equals(mode)) { dragMode = OUTLINE_DRAG_MODE; - } else if (mode != null && mode.equals("faster") + } else if ("faster".equals(mode) && f instanceof JInternalFrame && ((JInternalFrame)f).isOpaque() && (parent == null || parent.isOpaque())) { diff --git a/src/java.desktop/share/classes/javax/swing/DefaultListSelectionModel.java b/src/java.desktop/share/classes/javax/swing/DefaultListSelectionModel.java index 505f3c804d567eeb77fe19da71f2da73c4ae879d..25e47c8b90b29fd23c8a41ed91534c97885a87ab 100644 --- a/src/java.desktop/share/classes/javax/swing/DefaultListSelectionModel.java +++ b/src/java.desktop/share/classes/javax/swing/DefaultListSelectionModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -230,7 +230,7 @@ public class DefaultListSelectionModel implements ListSelectionModel, Cloneable, if (lastAdjustedIndex == MIN) { return; } - /* If getValueAdjusting() is true, (eg. during a drag opereration) + /* If getValueAdjusting() is true, (eg. during a drag operation) * record the bounds of the changes so that, when the drag finishes (and * setValueAdjusting(false) is called) we can post a single event * with bounds covering all of these individual adjustments. @@ -738,7 +738,7 @@ public class DefaultListSelectionModel implements ListSelectionModel, Cloneable, */ public String toString() { String s = ((getValueIsAdjusting()) ? "~" : "=") + value.toString(); - return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s; + return getClass().getName() + " " + hashCode() + " " + s; } /** @@ -825,7 +825,7 @@ public class DefaultListSelectionModel implements ListSelectionModel, Cloneable, * anchor and the new lead are either all selected or all deselected. * If the value at the anchor index is selected, first clear all the * values in the range [anchor, oldLeadIndex], then select all the values - * values in the range [anchor, newLeadIndex], where oldLeadIndex is the old + * in the range [anchor, newLeadIndex], where oldLeadIndex is the old * leadIndex and newLeadIndex is the new one. *

      * If the value at the anchor index is not selected, do the same thing in diff --git a/src/java.desktop/share/classes/javax/swing/GroupLayout.java b/src/java.desktop/share/classes/javax/swing/GroupLayout.java index ebcf2f1b5f262a4777bb9f749ee7ff49a65f0f26..61ed76bd5362a44df48cdfddc4471ea02d2b9864 100644 --- a/src/java.desktop/share/classes/javax/swing/GroupLayout.java +++ b/src/java.desktop/share/classes/javax/swing/GroupLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1230,7 +1230,7 @@ public class GroupLayout implements LayoutManager2 { String padding = ""; if (spring instanceof ComponentSpring) { ComponentSpring cSpring = (ComponentSpring)spring; - origin = Integer.toString(cSpring.getOrigin()) + " "; + origin = cSpring.getOrigin() + " "; String name = cSpring.getComponent().getName(); if (name != null) { origin = "name=" + name + ", "; @@ -1885,7 +1885,7 @@ public class GroupLayout implements LayoutManager2 { * @param comp1 the first component * @param comp2 the second component * @param type the type of gap - * @param pref the preferred size of the grap; one of + * @param pref the preferred size of the gap; one of * {@code DEFAULT_SIZE} or a value >= 0 * @param max the maximum size of the gap; one of * {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE} @@ -1944,7 +1944,7 @@ public class GroupLayout implements LayoutManager2 { * @param type the type of gap; one of * {@code LayoutStyle.ComponentPlacement.RELATED} or * {@code LayoutStyle.ComponentPlacement.UNRELATED} - * @param pref the preferred size of the grap; one of + * @param pref the preferred size of the gap; one of * {@code DEFAULT_SIZE} or a value >= 0 * @param max the maximum size of the gap; one of * {@code DEFAULT_SIZE}, {@code PREFERRED_SIZE} @@ -3506,10 +3506,10 @@ public class GroupLayout implements LayoutManager2 { String getMatchDescription() { if (targets != null) { - return "leading: " + targets.toString(); + return "leading: " + targets; } if (sources != null) { - return "trailing: " + sources.toString(); + return "trailing: " + sources; } return "--"; } diff --git a/src/java.desktop/share/classes/javax/swing/JComponent.java b/src/java.desktop/share/classes/javax/swing/JComponent.java index 5aa1a6a79bc360f21300f236dab3049c0281a1e6..d028c00b98b005f48f60481ae513375bace083e6 100644 --- a/src/java.desktop/share/classes/javax/swing/JComponent.java +++ b/src/java.desktop/share/classes/javax/swing/JComponent.java @@ -3752,7 +3752,7 @@ public abstract class JComponent extends Container implements Serializable, * to add/remove ContainerListener and FocusListener to track * target JComponent's state */ - private transient volatile int propertyListenersCount = 0; + private transient volatile int propertyListenersCount; /** * This field duplicates the function of the accessibleAWTFocusHandler field diff --git a/src/java.desktop/share/classes/javax/swing/JDesktopPane.java b/src/java.desktop/share/classes/javax/swing/JDesktopPane.java index 069a24eaad50cf4cf8075850968c80542b307577..c5ad9399caab3682a8ffeed771b16504ef5420c9 100644 --- a/src/java.desktop/share/classes/javax/swing/JDesktopPane.java +++ b/src/java.desktop/share/classes/javax/swing/JDesktopPane.java @@ -35,7 +35,6 @@ import java.io.ObjectOutputStream; import java.io.Serial; import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -328,12 +327,7 @@ public class JDesktopPane extends JLayeredPane implements Accessible */ public JInternalFrame[] getAllFramesInLayer(int layer) { Collection allFrames = getAllFrames(this); - Iterator iterator = allFrames.iterator(); - while (iterator.hasNext()) { - if (iterator.next().getLayer() != layer) { - iterator.remove(); - } - } + allFrames.removeIf(frame -> frame.getLayer() != layer); return allFrames.toArray(new JInternalFrame[0]); } diff --git a/src/java.desktop/share/classes/javax/swing/JFormattedTextField.java b/src/java.desktop/share/classes/javax/swing/JFormattedTextField.java index 9ee465e3744638cbf41c4aa56ef5745cb25eaf23..51516f79c99caa4a3f0171b3a4225af4edf897ce 100644 --- a/src/java.desktop/share/classes/javax/swing/JFormattedTextField.java +++ b/src/java.desktop/share/classes/javax/swing/JFormattedTextField.java @@ -868,11 +868,11 @@ public class JFormattedTextField extends JTextField { return new DefaultFormatterFactory(new DateFormatter()); } if (type instanceof Number) { - AbstractFormatter displayFormatter = new NumberFormatter(); - ((NumberFormatter)displayFormatter).setValueClass(type.getClass()); - AbstractFormatter editFormatter = new NumberFormatter( + NumberFormatter displayFormatter = new NumberFormatter(); + displayFormatter.setValueClass(type.getClass()); + NumberFormatter editFormatter = new NumberFormatter( new DecimalFormat("#.#")); - ((NumberFormatter)editFormatter).setValueClass(type.getClass()); + editFormatter.setValueClass(type.getClass()); return new DefaultFormatterFactory(displayFormatter, displayFormatter,editFormatter); diff --git a/src/java.desktop/share/classes/javax/swing/JLayeredPane.java b/src/java.desktop/share/classes/javax/swing/JLayeredPane.java index 4e4f0215f3e0d4471d9a69790f5b04a2fbdf4369..3ff321dfcf213f333f1ddce1a058cf03bd24fb9b 100644 --- a/src/java.desktop/share/classes/javax/swing/JLayeredPane.java +++ b/src/java.desktop/share/classes/javax/swing/JLayeredPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -205,7 +205,7 @@ public class JLayeredPane extends JComponent implements Accessible { (layer = (Integer)((JComponent)c). getClientProperty(LAYER_PROPERTY)) != null)) { - if(layer != null && layer.equals(FRAME_CONTENT_LAYER)) + if (FRAME_CONTENT_LAYER.equals(layer)) continue; layeredComponentFound = true; break; diff --git a/src/java.desktop/share/classes/javax/swing/JList.java b/src/java.desktop/share/classes/javax/swing/JList.java index 83b6cbed5530f37a739027a0073e2fcb6ac3fcc7..7932209974bcc4a32c3cd02fd56ccf24a96f36d8 100644 --- a/src/java.desktop/share/classes/javax/swing/JList.java +++ b/src/java.desktop/share/classes/javax/swing/JList.java @@ -2947,7 +2947,7 @@ public class JList extends JComponent implements Scrollable, Accessible Object newValue = e.getNewValue(); // re-set listData listeners - if (name.compareTo("model") == 0) { + if (name.equals("model")) { if (oldValue != null && oldValue instanceof ListModel) { ((ListModel) oldValue).removeListDataListener(this); @@ -2957,7 +2957,7 @@ public class JList extends JComponent implements Scrollable, Accessible } // re-set listSelectionModel listeners - } else if (name.compareTo("selectionModel") == 0) { + } else if (name.equals("selectionModel")) { if (oldValue != null && oldValue instanceof ListSelectionModel) { ((ListSelectionModel) oldValue).removeListSelectionListener(this); diff --git a/src/java.desktop/share/classes/javax/swing/JRootPane.java b/src/java.desktop/share/classes/javax/swing/JRootPane.java index e3eb7ac75a4d3b21c8ae40e69a5d0f774d6c981f..dcc6d6bb4efae6915c99cebb001881617a3fb9f8 100644 --- a/src/java.desktop/share/classes/javax/swing/JRootPane.java +++ b/src/java.desktop/share/classes/javax/swing/JRootPane.java @@ -24,18 +24,13 @@ */ package javax.swing; -import java.applet.Applet; import java.awt.*; -import java.awt.event.*; import java.beans.*; import java.security.AccessController; import javax.accessibility.*; import javax.swing.plaf.RootPaneUI; -import java.util.Vector; import java.io.Serializable; -import javax.swing.border.*; -import sun.awt.AWTAccessor; import sun.security.action.GetBooleanAction; @@ -511,10 +506,10 @@ public class JRootPane extends JComponent implements Accessible { * @return the default glassPane */ protected Component createGlassPane() { - JComponent c = new JPanel(); + JPanel c = new JPanel(); c.setName(this.getName()+".glassPane"); c.setVisible(false); - ((JPanel)c).setOpaque(false); + c.setOpaque(false); return c; } diff --git a/src/java.desktop/share/classes/javax/swing/JTable.java b/src/java.desktop/share/classes/javax/swing/JTable.java index b4d3037e5726ae7d1e775d7a32a28cc951c863e2..4b3e02f860f0cd91e49279f4a3ee8e946a9a6ab1 100644 --- a/src/java.desktop/share/classes/javax/swing/JTable.java +++ b/src/java.desktop/share/classes/javax/swing/JTable.java @@ -6756,7 +6756,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable Object newValue = e.getNewValue(); // re-set tableModel listeners - if (name.compareTo("model") == 0) { + if (name.equals("model")) { if (oldValue != null && oldValue instanceof TableModel) { ((TableModel) oldValue).removeTableModelListener(this); @@ -6766,7 +6766,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable } // re-set selectionModel listeners - } else if (name.compareTo("selectionModel") == 0) { + } else if (name.equals("selectionModel")) { Object source = e.getSource(); if (source == JTable.this) { // row selection model @@ -6797,7 +6797,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable // re-set columnModel listeners // and column's selection property listener as well - } else if (name.compareTo("columnModel") == 0) { + } else if (name.equals("columnModel")) { if (oldValue != null && oldValue instanceof TableColumnModel) { TableColumnModel tcm = (TableColumnModel) oldValue; @@ -6811,7 +6811,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable } // re-se cellEditor listeners - } else if (name.compareTo("tableCellEditor") == 0) { + } else if (name.equals("tableCellEditor")) { if (oldValue != null && oldValue instanceof TableCellEditor) { ((TableCellEditor) oldValue).removeCellEditorListener(this); diff --git a/src/java.desktop/share/classes/javax/swing/JTree.java b/src/java.desktop/share/classes/javax/swing/JTree.java index f1ba5b9d3d31bba048a8b2eae4b38c384196a66b..e48c1b2933372b361acbd2f0ad2d560c08a3d4ac 100644 --- a/src/java.desktop/share/classes/javax/swing/JTree.java +++ b/src/java.desktop/share/classes/javax/swing/JTree.java @@ -2033,7 +2033,7 @@ public class JTree extends JComponent implements Scrollable, Accessible Enumeration toggledPaths = expandedState.keys(); Vector elements = null; TreePath path; - Object value; + Boolean value; if(toggledPaths != null) { while(toggledPaths.hasMoreElements()) { @@ -2042,8 +2042,7 @@ public class JTree extends JComponent implements Scrollable, Accessible // Add the path if it is expanded, a descendant of parent, // and it is visible (all parents expanded). This is rather // expensive! - if(path != parent && value != null && - ((Boolean)value).booleanValue() && + if (path != parent && value != null && value && parent.isDescendant(path) && isVisible(path)) { if (elements == null) { elements = new Vector(); @@ -2081,11 +2080,11 @@ public class JTree extends JComponent implements Scrollable, Accessible if(path == null) return false; - Object value; + Boolean value; do{ value = expandedState.get(path); - if(value == null || !((Boolean)value).booleanValue()) + if (value == null || !value) return false; } while( (path=path.getParentPath())!=null ); @@ -2109,7 +2108,7 @@ public class JTree extends JComponent implements Scrollable, Accessible if(path != null) { Boolean value = expandedState.get(path); - return (value != null && value.booleanValue()); + return (value != null && value); } } return false; @@ -3729,9 +3728,9 @@ public class JTree extends JComponent implements Scrollable, Accessible } if(!state) { // collapse last path. - Object cValue = expandedState.get(path); + Boolean cValue = expandedState.get(path); - if(cValue != null && ((Boolean)cValue).booleanValue()) { + if (cValue != null && cValue) { try { fireTreeWillCollapse(path); } @@ -3753,9 +3752,9 @@ public class JTree extends JComponent implements Scrollable, Accessible } else { // Expand last path. - Object cValue = expandedState.get(path); + Boolean cValue = expandedState.get(path); - if(cValue == null || !((Boolean)cValue).booleanValue()) { + if (cValue == null || !cValue) { try { fireTreeWillExpand(path); } diff --git a/src/java.desktop/share/classes/javax/swing/RepaintManager.java b/src/java.desktop/share/classes/javax/swing/RepaintManager.java index f4d16c4e10235c9791074fe37dd65ef8f536fc3e..88684226d95dbcb128d81206777f3af35e4f9dfe 100644 --- a/src/java.desktop/share/classes/javax/swing/RepaintManager.java +++ b/src/java.desktop/share/classes/javax/swing/RepaintManager.java @@ -799,8 +799,7 @@ public class RepaintManager Set windows = new HashSet(); Set dirtyComps = dirtyComponents.keySet(); - for (Iterator it = dirtyComps.iterator(); it.hasNext();) { - Component dirty = it.next(); + for (Component dirty : dirtyComps) { Window window = dirty instanceof Window ? (Window)dirty : SwingUtilities.getWindowAncestor(dirty); diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index 7a8aa2e316e6e61a9f593f117cfed6836e178845..35260056d7ee62ee682c19750ce97cf5e2186b36 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -78,7 +78,7 @@ public class SwingUtilities implements SwingConstants @SuppressWarnings("removal") private static boolean getSuppressDropTarget() { if (!checkedSuppressDropSupport) { - suppressDropSupport = Boolean.valueOf( + suppressDropSupport = Boolean.parseBoolean( AccessController.doPrivileged( new GetPropertyAction("suppressSwingDropSupport"))); checkedSuppressDropSupport = true; diff --git a/src/java.desktop/share/classes/javax/swing/event/TreeModelEvent.java b/src/java.desktop/share/classes/javax/swing/event/TreeModelEvent.java index 1d377bf2a65dc6da1e310cedcbb7f024caf22203..6661cb3775e8ec6d01250c99e90817cdb8d30593 100644 --- a/src/java.desktop/share/classes/javax/swing/event/TreeModelEvent.java +++ b/src/java.desktop/share/classes/javax/swing/event/TreeModelEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -294,14 +294,13 @@ public class TreeModelEvent extends EventObject { public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(getClass().getName() + " " + - Integer.toString(hashCode())); + sb.append(getClass().getName() + " " + hashCode()); if(path != null) sb.append(" path " + path); if(childIndices != null) { sb.append(" indices [ "); for(int counter = 0; counter < childIndices.length; counter++) - sb.append(Integer.toString(childIndices[counter])+ " "); + sb.append(childIndices[counter] + " "); sb.append("]"); } if(children != null) { diff --git a/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java b/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java index b1cbc21d60f57597448d8663d59f0590aa6ee607..17a3b196b07e40ce61a8a25e617fa22d062b0f86 100644 --- a/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java +++ b/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java @@ -824,7 +824,7 @@ class UnixFileSystemView extends FileSystemView { public boolean isComputerNode(File dir) { if (dir != null) { String parent = dir.getParent(); - if (parent != null && parent.equals("/net")) { + if ("/net".equals(parent)) { return true; } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneDivider.java index f03671bcc561992c694547aee2b2463a5322b241..d6ffc2f3789b9348a78e8fb1ddfc3b9858a408ea 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneDivider.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneDivider.java @@ -376,9 +376,10 @@ public class BasicSplitPaneDivider extends Container /** * Messaged when the oneTouchExpandable value of the JSplitPane the - * receiver is contained in changes. Will create the - * leftButton and rightButton if they - * are null. invalidates the receiver as well. + * divider is contained in changes. Will create the + * leftButton and rightButton if they are null + * and corresponding JSplitPane supports oneTouchExpandable property. + * Invalidates the corresponding JSplitPane as well. */ protected void oneTouchExpandableChanged() { if (!DefaultLookup.getBoolean(splitPane, splitPaneUI, diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java index 4899ae0736b16cf68f0755ae19a7e46d38aeaaea..4142a92eb96296384e6de4059172e9a82e0959ba 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -125,8 +125,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel @SuppressWarnings("removal") String systemFonts = AccessController.doPrivileged( new GetPropertyAction("swing.useSystemFontSettings")); - useSystemFonts = (systemFonts != null && - (Boolean.valueOf(systemFonts).booleanValue())); + useSystemFonts = Boolean.parseBoolean(systemFonts); } checkedWindows = true; } @@ -1398,8 +1397,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Tree.openIcon",(LazyValue) t -> MetalIconFactory.getTreeFolderIcon(), "Tree.closedIcon",(LazyValue) t -> MetalIconFactory.getTreeFolderIcon(), "Tree.leafIcon",(LazyValue) t -> MetalIconFactory.getTreeLeafIcon(), - "Tree.expandedIcon",(LazyValue) t -> MetalIconFactory.getTreeControlIcon(Boolean.valueOf(MetalIconFactory.DARK)), - "Tree.collapsedIcon",(LazyValue) t -> MetalIconFactory.getTreeControlIcon(Boolean.valueOf( MetalIconFactory.LIGHT )), + "Tree.expandedIcon",(LazyValue) t -> MetalIconFactory.getTreeControlIcon(MetalIconFactory.DARK), + "Tree.collapsedIcon",(LazyValue) t -> MetalIconFactory.getTreeControlIcon(MetalIconFactory.LIGHT), "Tree.line", primaryControl, // horiz lines "Tree.hash", primaryControl, // legs diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthParser.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthParser.java index e053fe3d54cfccffd96142f402f5713a1d488135..4c212bc1ad8117c17b7518b8831a85c7e992a910 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthParser.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1024,10 +1024,7 @@ class SynthParser extends DefaultHandler { painter, direction); - for (Object infoObject: painters) { - ParsedSynthStyle.PainterInfo info; - info = (ParsedSynthStyle.PainterInfo) infoObject; - + for (ParsedSynthStyle.PainterInfo info: painters) { if (painterInfo.equalsPainter(info)) { info.addPainter(painter); return; diff --git a/src/java.desktop/share/classes/javax/swing/text/TabStop.java b/src/java.desktop/share/classes/javax/swing/text/TabStop.java index 4d2d60d448b2e9251635de6ccff510b1c4f972ea..12b0ea4591383a6a2e3f81529ba71bf206e5ecb2 100644 --- a/src/java.desktop/share/classes/javax/swing/text/TabStop.java +++ b/src/java.desktop/share/classes/javax/swing/text/TabStop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,7 +181,7 @@ public class TabStop implements Serializable { buf = "bar "; break; } - buf = buf + "tab @" + String.valueOf(position); + buf = buf + "tab @" + position; if (leader != LEAD_NONE) buf = buf + " (w/leaders)"; return buf; diff --git a/src/java.desktop/share/classes/javax/swing/text/html/AccessibleHTML.java b/src/java.desktop/share/classes/javax/swing/text/html/AccessibleHTML.java index 3e090982cec71819af88f5af7c85eb653241f790..a756cb2a02fbfcf60e5a2d8f45d66ae9ae00be13 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/AccessibleHTML.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/AccessibleHTML.java @@ -317,14 +317,12 @@ class AccessibleHTML implements Accessible { */ public AccessibleStateSet getAccessibleStateSet() { AccessibleStateSet states = new AccessibleStateSet(); - Component comp = getTextComponent(); + JTextComponent comp = getTextComponent(); if (comp.isEnabled()) { states.add(AccessibleState.ENABLED); } - if (comp instanceof JTextComponent && - ((JTextComponent)comp).isEditable()) { - + if (comp.isEditable()) { states.add(AccessibleState.EDITABLE); states.add(AccessibleState.FOCUSABLE); } @@ -742,11 +740,9 @@ class AccessibleHTML implements Accessible { * @see AccessibleStateSet */ public boolean isFocusTraversable() { - Component comp = getTextComponent(); - if (comp instanceof JTextComponent) { - if (((JTextComponent)comp).isEditable()) { - return true; - } + JTextComponent comp = getTextComponent(); + if (comp != null && comp.isEditable()) { + return true; } return false; } @@ -763,8 +759,8 @@ class AccessibleHTML implements Accessible { return; } - Component comp = getTextComponent(); - if (comp instanceof JTextComponent) { + JTextComponent comp = getTextComponent(); + if (comp != null) { comp.requestFocusInWindow(); @@ -772,7 +768,7 @@ class AccessibleHTML implements Accessible { if (elementInfo.validateIfNecessary()) { // set the caret position to the start of this component Element elem = elementInfo.getElement(); - ((JTextComponent)comp).setCaretPosition(elem.getStartOffset()); + comp.setCaretPosition(elem.getStartOffset()); // fire a AccessibleState.FOCUSED property change event AccessibleContext ac = editor.getAccessibleContext(); diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index 150e24ca26326287498693c952a990a84f642379..76ce4ac815f270e5e62ac7668734d6f868e0fd9c 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -38,7 +38,6 @@ import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; -import java.util.Vector; import javax.swing.ImageIcon; import javax.swing.SizeRequirements; @@ -891,8 +890,8 @@ public class CSS implements Serializable { (CSS.Attribute.VERTICAL_ALIGN); if ((vAlignV != null)) { String vAlign = vAlignV.toString(); - if ((vAlign.indexOf("sup") >= 0) || - (vAlign.indexOf("sub") >= 0)) { + if ((vAlign.contains("sup")) || + (vAlign.contains("sub"))) { size -= 2; } } @@ -908,7 +907,7 @@ public class CSS implements Serializable { style |= Font.BOLD; } Object fs = a.getAttribute(CSS.Attribute.FONT_STYLE); - if ((fs != null) && (fs.toString().indexOf("italic") >= 0)) { + if ((fs != null) && (fs.toString().contains("italic"))) { style |= Font.ITALIC; } if (family.equalsIgnoreCase("monospace")) { @@ -1965,12 +1964,12 @@ public class CSS implements Serializable { */ Object toStyleConstants(StyleConstants key, View v) { if (key == StyleConstants.Italic) { - if (svalue.indexOf("italic") >= 0) { + if (svalue.contains("italic")) { return Boolean.TRUE; } return Boolean.FALSE; } else if (key == StyleConstants.Underline) { - if (svalue.indexOf("underline") >= 0) { + if (svalue.contains("underline")) { return Boolean.TRUE; } return Boolean.FALSE; @@ -1984,17 +1983,17 @@ public class CSS implements Serializable { } return StyleConstants.ALIGN_LEFT; } else if (key == StyleConstants.StrikeThrough) { - if (svalue.indexOf("line-through") >= 0) { + if (svalue.contains("line-through")) { return Boolean.TRUE; } return Boolean.FALSE; } else if (key == StyleConstants.Superscript) { - if (svalue.indexOf("super") >= 0) { + if (svalue.contains("super")) { return Boolean.TRUE; } return Boolean.FALSE; } else if (key == StyleConstants.Subscript) { - if (svalue.indexOf("sub") >= 0) { + if (svalue.contains("sub")) { return Boolean.TRUE; } return Boolean.FALSE; @@ -2004,23 +2003,23 @@ public class CSS implements Serializable { // Used by ViewAttributeSet boolean isItalic() { - return (svalue.indexOf("italic") != -1); + return (svalue.contains("italic")); } boolean isStrike() { - return (svalue.indexOf("line-through") != -1); + return (svalue.contains("line-through")); } boolean isUnderline() { - return (svalue.indexOf("underline") != -1); + return (svalue.contains("underline")); } boolean isSub() { - return (svalue.indexOf("sub") != -1); + return (svalue.contains("sub")); } boolean isSup() { - return (svalue.indexOf("sup") != -1); + return (svalue.contains("sup")); } } diff --git a/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java b/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java index 4063d2d7a75e4615aff7a5c40b8b305bd6cc1cde..00272bc1eaae2cf94b3f3eac527dc2ccb4bad9ce 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java @@ -3252,8 +3252,8 @@ public class HTMLDocument extends DefaultStyledDocument { } if (rel != null) { rel = rel.toLowerCase(); - if ((media.indexOf("all") != -1 || - media.indexOf("screen") != -1) && + if ((media.contains("all") || + media.contains("screen")) && (rel.equals("stylesheet") || (rel.equals("alternate stylesheet") && title.equals(defaultStyle)))) { @@ -4195,7 +4195,7 @@ public class HTMLDocument extends DefaultStyledDocument { try { if (offset == 0 || !getText(offset - 1, 1).equals("\n")) { // Need to insert a newline. - AttributeSet newAttrs = null; + SimpleAttributeSet newAttrs = null; boolean joinP = true; if (offset != 0) { @@ -4229,9 +4229,8 @@ public class HTMLDocument extends DefaultStyledDocument { // sure and set the name (otherwise it will be // inherited). newAttrs = new SimpleAttributeSet(); - ((SimpleAttributeSet)newAttrs).addAttribute - (StyleConstants.NameAttribute, - HTML.Tag.CONTENT); + newAttrs.addAttribute(StyleConstants.NameAttribute, + HTML.Tag.CONTENT); } ElementSpec es = new ElementSpec(newAttrs, ElementSpec.ContentType, NEWLINE, 0, diff --git a/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java b/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java index 53e0aedf3a5d7434d47b2fadae43d3317f23e573..b6b0b789eefc387f92c982e84406c2fe14fa1395 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java @@ -61,7 +61,6 @@ import javax.accessibility.Accessible; import javax.accessibility.AccessibleAction; import javax.accessibility.AccessibleContext; import javax.swing.Action; -import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JViewport; import javax.swing.SizeRequirements; @@ -848,13 +847,12 @@ public class HTMLEditorKit extends StyledEditorKit implements Accessible { Rectangle bounds; TextUI ui = html.getUI(); try { - Shape lBounds = ui.modelToView(html, offset, + Rectangle lBounds = ui.modelToView(html, offset, Position.Bias.Forward); - Shape rBounds = ui.modelToView(html, offset + 1, + Rectangle rBounds = ui.modelToView(html, offset + 1, Position.Bias.Backward); - bounds = lBounds.getBounds(); - bounds.add((rBounds instanceof Rectangle) ? - (Rectangle)rBounds : rBounds.getBounds()); + bounds = lBounds; + bounds.add(rBounds); } catch (BadLocationException ble) { bounds = null; } @@ -885,18 +883,14 @@ public class HTMLEditorKit extends StyledEditorKit implements Accessible { if (e != null && offset > 0 && e.getStartOffset() == offset) { try { TextUI ui = editor.getUI(); - Shape s1 = ui.modelToView(editor, offset, - Position.Bias.Forward); - if (s1 == null) { + Rectangle r1 = ui.modelToView(editor, offset, + Position.Bias.Forward); + if (r1 == null) { return false; } - Rectangle r1 = (s1 instanceof Rectangle) ? (Rectangle)s1 : - s1.getBounds(); - Shape s2 = ui.modelToView(editor, e.getEndOffset(), - Position.Bias.Backward); - if (s2 != null) { - Rectangle r2 = (s2 instanceof Rectangle) ? (Rectangle)s2 : - s2.getBounds(); + Rectangle r2 = ui.modelToView(editor, e.getEndOffset(), + Position.Bias.Backward); + if (r2 != null) { r1.add(r2); } return r1.contains(x, y); @@ -1332,7 +1326,7 @@ public class HTMLEditorKit extends StyledEditorKit implements Accessible { } else if (kind == HTML.Tag.IMPLIED) { String ws = (String) elem.getAttributes().getAttribute( CSS.Attribute.WHITE_SPACE); - if ((ws != null) && ws.equals("pre")) { + if ("pre".equals(ws)) { return new LineView(elem); } return new javax.swing.text.html.ParagraphView(elem); @@ -1517,9 +1511,9 @@ public class HTMLEditorKit extends StyledEditorKit implements Accessible { //if parent == null unregister component listener if (parent == null) { if (cachedViewPort != null) { - Object cachedObject; + JViewport cachedObject; if ((cachedObject = cachedViewPort.get()) != null) { - ((JComponent)cachedObject).removeComponentListener(this); + cachedObject.removeComponentListener(this); } cachedViewPort = null; } diff --git a/src/java.desktop/share/classes/javax/swing/text/html/HTMLWriter.java b/src/java.desktop/share/classes/javax/swing/text/html/HTMLWriter.java index f8a9558505ca68b998b67f64ab69acea2adbd92f..a166235d4c205faff1cff053b67d5c352a6a8744 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/HTMLWriter.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/HTMLWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1074,23 +1074,23 @@ public class HTMLWriter extends AbstractWriter { } } else if (key == CSS.Attribute.FONT_STYLE) { String s = from.getAttribute(key).toString(); - if (s.indexOf("italic") >= 0) { + if (s.contains("italic")) { addAttribute(to, HTML.Tag.I, SimpleAttributeSet.EMPTY); } } else if (key == CSS.Attribute.TEXT_DECORATION) { String decor = from.getAttribute(key).toString(); - if (decor.indexOf("underline") >= 0) { + if (decor.contains("underline")) { addAttribute(to, HTML.Tag.U, SimpleAttributeSet.EMPTY); } - if (decor.indexOf("line-through") >= 0) { + if (decor.contains("line-through")) { addAttribute(to, HTML.Tag.STRIKE, SimpleAttributeSet.EMPTY); } } else if (key == CSS.Attribute.VERTICAL_ALIGN) { String vAlign = from.getAttribute(key).toString(); - if (vAlign.indexOf("sup") >= 0) { + if (vAlign.contains("sup")) { addAttribute(to, HTML.Tag.SUP, SimpleAttributeSet.EMPTY); } - if (vAlign.indexOf("sub") >= 0) { + if (vAlign.contains("sub")) { addAttribute(to, HTML.Tag.SUB, SimpleAttributeSet.EMPTY); } } else if (key == CSS.Attribute.TEXT_ALIGN) { diff --git a/src/java.desktop/share/classes/javax/swing/text/html/InlineView.java b/src/java.desktop/share/classes/javax/swing/text/html/InlineView.java index c6fe5fe01fca35172a95d6b7fb2b974f3b4e75e3..38975b3c686ed21d1856e5ea2ef51e2c3db9a8bc 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/InlineView.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/InlineView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -188,15 +188,15 @@ public class InlineView extends LabelView { AttributeSet a = getAttributes(); Object decor = a.getAttribute(CSS.Attribute.TEXT_DECORATION); boolean u = (decor != null) ? - (decor.toString().indexOf("underline") >= 0) : false; + (decor.toString().contains("underline")) : false; setUnderline(u); boolean s = (decor != null) ? - (decor.toString().indexOf("line-through") >= 0) : false; + (decor.toString().contains("line-through")) : false; setStrikeThrough(s); Object vAlign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN); - s = (vAlign != null) ? (vAlign.toString().indexOf("sup") >= 0) : false; + s = (vAlign != null) ? (vAlign.toString().contains("sup")) : false; setSuperscript(s); - s = (vAlign != null) ? (vAlign.toString().indexOf("sub") >= 0) : false; + s = (vAlign != null) ? (vAlign.toString().contains("sub")) : false; setSubscript(s); Object whitespace = a.getAttribute(CSS.Attribute.WHITE_SPACE); diff --git a/src/java.desktop/share/classes/javax/swing/text/html/OptionListModel.java b/src/java.desktop/share/classes/javax/swing/text/html/OptionListModel.java index 70603d0cc636a589bc09cde29a2c6bc176b3aa64..2b84b54b3e162ddb924c985c7d2d63f5fb7b53d8 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/OptionListModel.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/OptionListModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -456,7 +456,7 @@ class OptionListModel extends DefaultListModel implements ListSelectionMod public String toString() { String s = ((getValueIsAdjusting()) ? "~" : "=") + value.toString(); - return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s; + return getClass().getName() + " " + hashCode() + " " + s; } /** @@ -499,7 +499,7 @@ class OptionListModel extends DefaultListModel implements ListSelectionMod * anchor and the new lead are either all selected or all deselected. * If the value at the anchor index is selected, first clear all the * values in the range [anchor, oldLeadIndex], then select all the values - * values in the range [anchor, newLeadIndex], where oldLeadIndex is the old + * in the range [anchor, newLeadIndex], where oldLeadIndex is the old * leadIndex and newLeadIndex is the new one. *

      * If the value at the anchor index is not selected, do the same thing in reverse, diff --git a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFGenerator.java b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFGenerator.java index 25f39ae58f8ffb0dedc4c1e8ffb9ac594182ad6a..46a7ce7efb98603f1e83db806e9f91a15ca60b1e 100644 --- a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFGenerator.java +++ b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFGenerator.java @@ -27,7 +27,6 @@ package javax.swing.text.rtf; import java.lang.*; import java.util.*; import java.awt.Color; -import java.awt.Font; import java.io.OutputStream; import java.io.IOException; @@ -515,13 +514,13 @@ void updateSectionAttributes(MutableAttributeSet current, { if (emitStyleChanges) { Object oldStyle = current.getAttribute("sectionStyle"); - Object newStyle = findStyleNumber(newAttributes, Constants.STSection); + Integer newStyle = findStyleNumber(newAttributes, Constants.STSection); if (oldStyle != newStyle) { if (oldStyle != null) { resetSectionAttributes(current); } if (newStyle != null) { - writeControlWord("ds", ((Integer)newStyle).intValue()); + writeControlWord("ds", newStyle); current.addAttribute("sectionStyle", newStyle); } else { current.removeAttribute("sectionStyle"); @@ -554,8 +553,8 @@ void updateParagraphAttributes(MutableAttributeSet current, boolean emitStyleChanges) throws IOException { - Object parm; - Object oldStyle, newStyle; + Object oldStyle; + Integer newStyle; /* The only way to get rid of tabs or styles is with the \pard keyword, emitted by resetParagraphAttributes(). Ideally we should avoid @@ -587,7 +586,7 @@ void updateParagraphAttributes(MutableAttributeSet current, } if (oldStyle != newStyle && newStyle != null) { - writeControlWord("s", ((Integer)newStyle).intValue()); + writeControlWord("s", newStyle); current.addAttribute("paragraphStyle", newStyle); } @@ -706,14 +705,14 @@ void updateCharacterAttributes(MutableAttributeSet current, if (updateStyleChanges) { Object oldStyle = current.getAttribute("characterStyle"); - Object newStyle = findStyleNumber(newAttributes, + Integer newStyle = findStyleNumber(newAttributes, Constants.STCharacter); if (oldStyle != newStyle) { if (oldStyle != null) { resetCharacterAttributes(current); } if (newStyle != null) { - writeControlWord("cs", ((Integer)newStyle).intValue()); + writeControlWord("cs", newStyle.intValue()); current.addAttribute("characterStyle", newStyle); } else { current.removeAttribute("characterStyle"); diff --git a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFParser.java b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFParser.java index 88e2990942c5ed2002ca48843579890cd0a5be0a..259b63ed8eebc6fb40a7d2288ab9027a8aeed28b 100644 --- a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFParser.java +++ b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFParser.java @@ -233,25 +233,52 @@ abstract class RTFParser extends AbstractFilter currentCharacters.append(ch); } else { /* TODO: Test correct behavior of \bin keyword */ + if (pendingKeyword.equals("bin")) { /* magic layer-breaking kwd */ - long parameter = Long.parseLong(currentCharacters.toString()); + long parameter = 0L; + try { + parameter = Long.parseLong(currentCharacters.toString()); + } catch (NumberFormatException e) { + warning("Illegal number format " + currentCharacters.toString() + + " in \bin tag"); + pendingKeyword = null; + currentCharacters = new StringBuffer(); + state = S_text; + // Delimiters here are interpreted as text too + if (!Character.isWhitespace(ch)) + write(ch); + break; + } pendingKeyword = null; state = S_inblob; + int maxBytes = 4 * 1024 * 1024; binaryBytesLeft = parameter; - if (binaryBytesLeft > Integer.MAX_VALUE) - binaryBuf = new ByteArrayOutputStream(Integer.MAX_VALUE); - else - binaryBuf = new ByteArrayOutputStream((int)binaryBytesLeft); + + if (binaryBytesLeft > maxBytes) { + binaryBuf = new ByteArrayOutputStream(maxBytes); + } else if (binaryBytesLeft < 0) { + binaryBytesLeft = 0; + binaryBuf = new ByteArrayOutputStream((int)binaryBytesLeft); + } else { + binaryBuf = new ByteArrayOutputStream((int) binaryBytesLeft); + } savedSpecials = specialsTable; specialsTable = allSpecialsTable; break; } - int parameter = Integer.parseInt(currentCharacters.toString()); - ok = handleKeyword(pendingKeyword, parameter); - if (!ok) - warning("Unknown keyword: " + pendingKeyword + - " (param " + currentCharacters + ")"); + int parameter = 0; + try { + parameter = Integer.parseInt(currentCharacters.toString()); + ok = handleKeyword(pendingKeyword, parameter); + if (!ok) { + warning("Unknown keyword: " + pendingKeyword + + " (param " + currentCharacters + ")"); + } + } catch (NumberFormatException e) { + warning("Illegal number format " + currentCharacters.toString() + + " in " + pendingKeyword + " tag"); + } pendingKeyword = null; currentCharacters = new StringBuffer(); state = S_text; @@ -280,14 +307,16 @@ abstract class RTFParser extends AbstractFilter } break; case S_inblob: - binaryBuf.write(ch); - binaryBytesLeft --; + if (binaryBytesLeft > 0) { + binaryBuf.write(ch); + binaryBytesLeft--; + } if (binaryBytesLeft == 0) { - state = S_text; - specialsTable = savedSpecials; - savedSpecials = null; - handleBinaryBlob(binaryBuf.toByteArray()); - binaryBuf = null; + state = S_text; + specialsTable = savedSpecials; + savedSpecials = null; + handleBinaryBlob(binaryBuf.toByteArray()); + binaryBuf = null; } } } diff --git a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFReader.java b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFReader.java index 0867e40defe3f28d9615fa0ef7c90b29370f0747..add3a86a8918312ab2e455e8a478a071add07468 100644 --- a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFReader.java +++ b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFReader.java @@ -36,7 +36,11 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Dictionary; import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; import java.util.Hashtable; +import java.util.Map; +import java.util.Set; import java.util.Vector; import javax.swing.text.AttributeSet; @@ -85,12 +89,12 @@ class RTFReader extends RTFParser Dictionary fontTable; /** This array maps color indices to Color objects. */ Color[] colorTable; - /** This array maps character style numbers to Style objects. */ - Style[] characterStyles; - /** This array maps paragraph style numbers to Style objects. */ - Style[] paragraphStyles; - /** This array maps section style numbers to Style objects. */ - Style[] sectionStyles; + /** This Map maps character style numbers to Style objects. */ + Map characterStyles; + /** This Map maps paragraph style numbers to Style objects. */ + Map paragraphStyles; + /** This Map maps section style numbers to Style objects. */ + Map sectionStyles; /** This is the RTF version number, extracted from the \rtf keyword. * The version information is currently not used. */ @@ -861,9 +865,9 @@ class StylesheetDestination public void close() { - Vector