From 038ff0920f3193c344f6b8e965211781ba53e528 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 24 Apr 2024 21:06:29 +0200 Subject: [PATCH] Backport 1d06170758bd76a0ea32e5bb7d4a017e829ae710 --- src/hotspot/share/opto/c2_globals.hpp | 4 + src/hotspot/share/opto/escape.cpp | 4 +- src/hotspot/share/opto/memnode.cpp | 2 +- src/hotspot/share/opto/parse1.cpp | 3 +- src/hotspot/share/opto/stringopts.cpp | 2 +- .../c2/irTests/ConstructorBarriers.java | 309 ++++++++++++++++++ .../compiler/lib/ir_framework/IRNode.java | 4 + .../vm/compiler/ConstructorBarriers.java | 269 +++++++++++++++ 8 files changed, 593 insertions(+), 4 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java create mode 100644 test/micro/org/openjdk/bench/vm/compiler/ConstructorBarriers.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index a8ef7ecd044..53446b62af2 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -770,6 +770,10 @@ \ product(bool, VerifyReceiverTypes, trueInDebug, DIAGNOSTIC, \ "Verify receiver types at runtime") \ + \ + product(bool, UseStoreStoreForCtor, true, DIAGNOSTIC, \ + "Use StoreStore barrier instead of Release barrier at the end " \ + "of constructors") \ // end of C2_FLAGS diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 0c84d8bd97e..c0e48d366d9 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -173,7 +173,9 @@ bool ConnectionGraph::compute_escape() { // Collect all MemBarStoreStore nodes so that depending on the // escape status of the associated Allocate node some of them // may be eliminated. - storestore_worklist.append(n); + if (!UseStoreStoreForCtor || n->req() > MemBarNode::Precedent) { + storestore_worklist.append(n); + } } else if (n->is_MemBar() && (n->Opcode() == Op_MemBarRelease) && (n->req() > MemBarNode::Precedent)) { record_for_optimizer(n); diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 074b129b059..a55205e72e8 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -3369,7 +3369,7 @@ Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) { eliminate = true; } } - } else if (opc == Op_MemBarRelease) { + } else if (opc == Op_MemBarRelease || (UseStoreStoreForCtor && opc == Op_MemBarStoreStore)) { // Final field stores. Node* alloc = AllocateNode::Ideal_allocation(in(MemBarNode::Precedent), phase); if ((alloc != nullptr) && alloc->is_Allocate() && diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 8ce2bdeb71a..db6fa26c119 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1001,7 +1001,8 @@ void Parse::do_exits() { (wrote_final() || (AlwaysSafeConstructors && wrote_fields()) || (support_IRIW_for_not_multiple_copy_atomic_cpu && wrote_volatile()))) { - _exits.insert_mem_bar(Op_MemBarRelease, alloc_with_final()); + _exits.insert_mem_bar(UseStoreStoreForCtor ? Op_MemBarStoreStore : Op_MemBarRelease, + alloc_with_final()); // If Memory barrier is created for final fields write // and allocation node does not escape the initialize method, diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index 200e071db92..2d392636f80 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -2067,7 +2067,7 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { // of the initialization is committed to memory before any code publishes // a reference to the newly constructed object (see Parse::do_exits()). assert(AllocateNode::Ideal_allocation(result, _gvn) != nullptr, "should be newly allocated"); - kit.insert_mem_bar(Op_MemBarRelease, result); + kit.insert_mem_bar(UseStoreStoreForCtor ? Op_MemBarStoreStore : Op_MemBarRelease, result); } else { result = C->top(); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java new file mode 100644 index 00000000000..42d3ff3cdb3 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java @@ -0,0 +1,309 @@ +/* + * Copyright Amazon.com Inc. 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. + */ + +package compiler.c2.irTests; + +import compiler.lib.ir_framework.*; + +/* + * @test + * @bug 8300148 + * @summary Test barriers emitted in constructors + * @library /test/lib / + * @requires os.arch=="aarch64" | os.arch=="riscv64" | os.arch=="x86_64" | os.arch=="amd64" + * @run main compiler.c2.irTests.ConstructorBarriers + */ +public class ConstructorBarriers { + public static void main(String[] args) { + TestFramework.run(); + } + + // Checks the barrier coalescing/optimization around field initializations. + // Uses long fields to avoid store merging. + + public static class PlainPlain { + long f1; + long f2; + public PlainPlain(long i) { + f1 = i; + f2 = i; + } + } + + private static class FinalPlain { + final long f1; + long f2; + public FinalPlain(long i) { + f1 = i; + f2 = i; + } + } + + private static class PlainFinal { + long f1; + final long f2; + public PlainFinal(long i) { + f1 = i; + f2 = i; + } + } + + private static class FinalFinal { + final long f1; + final long f2; + public FinalFinal(long i) { + f1 = i; + f2 = i; + } + } + + private static class PlainVolatile { + long f1; + volatile long f2; + public PlainVolatile(long i) { + f1 = i; + f2 = i; + } + } + + private static class VolatilePlain { + volatile long f1; + long f2; + public VolatilePlain(long i) { + f1 = i; + f2 = i; + } + } + + private static class FinalVolatile { + final long f1; + volatile long f2; + public FinalVolatile(long i) { + f1 = i; + f2 = i; + } + } + + private static class VolatileFinal { + volatile long f1; + final long f2; + public VolatileFinal(long i) { + f1 = i; + f2 = i; + } + } + + private static class VolatileVolatile { + volatile long f1; + volatile long f2; + public VolatileVolatile(long i) { + f1 = i; + f2 = i; + } + } + + long l = 42; + + @DontInline + public void consume(Object o) {} + + @Test + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + public long escaping_plainPlain() { + PlainPlain c = new PlainPlain(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + public long escaping_plainFinal() { + PlainFinal c = new PlainFinal(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + public long escaping_finalPlain() { + FinalPlain c = new FinalPlain(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + public long escaping_finalFinal() { + FinalFinal c = new FinalFinal(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(counts = {IRNode.MEMBAR_RELEASE, "1"}) + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(counts = {IRNode.MEMBAR_VOLATILE, "1"}) + public long escaping_plainVolatile() { + PlainVolatile c = new PlainVolatile(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(counts = {IRNode.MEMBAR_RELEASE, "1"}) + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(counts = {IRNode.MEMBAR_VOLATILE, "1"}) + public long escaping_volatilePlain() { + VolatilePlain c = new VolatilePlain(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(counts = {IRNode.MEMBAR_RELEASE, "2"}) + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(counts = {IRNode.MEMBAR_VOLATILE, "2"}) + public long escaping_volatileVolatile() { + VolatileVolatile c = new VolatileVolatile(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(counts = {IRNode.MEMBAR_RELEASE, "1"}) + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(counts = {IRNode.MEMBAR_VOLATILE, "1"}) + public long escaping_finalVolatile() { + FinalVolatile c = new FinalVolatile(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(counts = {IRNode.MEMBAR_RELEASE, "1"}) + @IR(counts = {IRNode.MEMBAR_STORESTORE, "1"}) + @IR(counts = {IRNode.MEMBAR_VOLATILE, "1"}) + public long escaping_volatileFinal() { + VolatileFinal c = new VolatileFinal(l); + consume(c); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR) + public long non_escaping_plainPlain() { + PlainPlain c = new PlainPlain(l); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR) + public long non_escaping_plainFinal() { + PlainFinal c = new PlainFinal(l); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR) + public long non_escaping_finalPlain() { + FinalPlain c = new FinalPlain(l); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR) + public long non_escaping_finalFinal() { + FinalFinal c = new FinalFinal(l); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_STORESTORE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + @IR(counts = {IRNode.MEMBAR_ACQUIRE, "1"}) + public long non_escaping_plainVolatile() { + PlainVolatile c = new PlainVolatile(l); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_STORESTORE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + @IR(counts = {IRNode.MEMBAR_ACQUIRE, "1"}) + public long non_escaping_volatilePlain() { + VolatilePlain c = new VolatilePlain(l); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_STORESTORE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + @IR(counts = {IRNode.MEMBAR_ACQUIRE, "2"}) + public long non_escaping_volatileVolatile() { + VolatileVolatile c = new VolatileVolatile(l); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_STORESTORE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + @IR(counts = {IRNode.MEMBAR_ACQUIRE, "1"}) + public long non_escaping_finalVolatile() { + FinalVolatile c = new FinalVolatile(l); + return c.f1 + c.f2; + } + + @Test + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_STORESTORE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + @IR(counts = {IRNode.MEMBAR_ACQUIRE, "1"}) + public long non_escaping_volatileFinal() { + VolatileFinal c = new VolatileFinal(l); + return c.f1 + c.f2; + } + + String s1 = "foo"; + String s2 = "bar"; + String s3 = "baz"; + + @Test + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + @IR(counts = {IRNode.MEMBAR_STORESTORE, "3"}) + public String stringBuilder() { + return new StringBuilder().append(s1).append(s2).append(s3).toString(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 2f2395b77c6..8bbb8a3305e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -133,7 +133,11 @@ public class IRNode { public static final String SCOPE_OBJECT = "(.*# ScObj.*" + END; public static final String MEMBAR = START + "MemBar" + MID + END; + public static final String MEMBAR_ACQUIRE = START + "MemBarAcquire" + MID + END; + public static final String MEMBAR_RELEASE = START + "MemBarRelease" + MID + END; public static final String MEMBAR_STORESTORE = START + "MemBarStoreStore" + MID + END; + public static final String MEMBAR_VOLATILE = START + "MemBarVolatile" + MID + END; + public static final String SAFEPOINT = START + "SafePoint" + MID + END; public static final String CMP_U = START + "CmpU" + MID + END; diff --git a/test/micro/org/openjdk/bench/vm/compiler/ConstructorBarriers.java b/test/micro/org/openjdk/bench/vm/compiler/ConstructorBarriers.java new file mode 100644 index 00000000000..7adbbe0e1a7 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/ConstructorBarriers.java @@ -0,0 +1,269 @@ +/* + * Copyright Amazon.com Inc. 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. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(value = 3, jvmArgsAppend = {"-Xms512m", "-Xmx512m", "-XX:+AlwaysPreTouch", "-XX:+UseParallelGC"}) +public class ConstructorBarriers { + + // Checks the barrier coalescing/optimization around field initializations. + // Uses long fields to avoid store merging. + + public static class PlainPlain { + long f1; + long f2; + public PlainPlain(long i) { + f1 = i; + f2 = i; + } + } + + private static class FinalPlain { + final long f1; + long f2; + public FinalPlain(long i) { + f1 = i; + f2 = i; + } + } + + private static class PlainFinal { + long f1; + final long f2; + public PlainFinal(long i) { + f1 = i; + f2 = i; + } + } + + private static class FinalFinal { + final long f1; + final long f2; + public FinalFinal(long i) { + f1 = i; + f2 = i; + } + } + + private static class PlainVolatile { + long f1; + volatile long f2; + public PlainVolatile(long i) { + f1 = i; + f2 = i; + } + } + + private static class VolatilePlain { + volatile long f1; + long f2; + public VolatilePlain(long i) { + f1 = i; + f2 = i; + } + } + + private static class FinalVolatile { + final long f1; + volatile long f2; + public FinalVolatile(long i) { + f1 = i; + f2 = i; + } + } + + private static class VolatileFinal { + volatile long f1; + final long f2; + public VolatileFinal(long i) { + f1 = i; + f2 = i; + } + } + + private static class VolatileVolatile { + volatile long f1; + volatile long f2; + public VolatileVolatile(long i) { + f1 = i; + f2 = i; + } + } + + long l = 42; + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_plainPlain(Blackhole bh) { + PlainPlain c = new PlainPlain(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_plainFinal(Blackhole bh) { + PlainFinal c = new PlainFinal(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_finalPlain(Blackhole bh) { + FinalPlain c = new FinalPlain(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_finalFinal(Blackhole bh) { + FinalFinal c = new FinalFinal(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_plainVolatile(Blackhole bh) { + PlainVolatile c = new PlainVolatile(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_volatilePlain(Blackhole bh) { + VolatilePlain c = new VolatilePlain(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_volatileVolatile(Blackhole bh) { + VolatileVolatile c = new VolatileVolatile(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_finalVolatile(Blackhole bh) { + FinalVolatile c = new FinalVolatile(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long escaping_volatileFinal(Blackhole bh) { + VolatileFinal c = new VolatileFinal(l); + bh.consume(c); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_plainPlain() { + PlainPlain c = new PlainPlain(l); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_plainFinal() { + PlainFinal c = new PlainFinal(l); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_finalPlain() { + FinalPlain c = new FinalPlain(l); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_finalFinal() { + FinalFinal c = new FinalFinal(l); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_plainVolatile() { + PlainVolatile c = new PlainVolatile(l); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_volatilePlain() { + VolatilePlain c = new VolatilePlain(l); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_volatileVolatile() { + VolatileVolatile c = new VolatileVolatile(l); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_finalVolatile() { + FinalVolatile c = new FinalVolatile(l); + return c.f1 + c.f2; + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long non_escaping_volatileFinal() { + VolatileFinal c = new VolatileFinal(l); + return c.f1 + c.f2; + } + +} +