diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 785b76849c1..baf2256005e 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -797,6 +797,10 @@ \ product(bool, InlineSecondarySupersTest, true, DIAGNOSTIC, \ "Inline the secondary supers hash lookup.") \ + \ + 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 2fb5e6b13b3..55d54d44545 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -199,7 +199,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->as_MemBarStoreStore()); + if (!UseStoreStoreForCtor || n->req() > MemBarNode::Precedent) { + storestore_worklist.append(n->as_MemBarStoreStore()); + } break; case Op_MemBarRelease: if (n->req() > MemBarNode::Precedent) { diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 23928b554e4..d4ecadf0cfc 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -633,7 +633,10 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode } else if (!reduce_merge_precheck) { safepoints->append_if_missing(sfpt); } - } else if (reduce_merge_precheck && (use->is_Phi() || use->is_EncodeP() || use->Opcode() == Op_MemBarRelease)) { + } else if (reduce_merge_precheck && + (use->is_Phi() || use->is_EncodeP() || + use->Opcode() == Op_MemBarRelease || + (UseStoreStoreForCtor && use->Opcode() == Op_MemBarStoreStore))) { // Nothing to do } else if (use->Opcode() != Op_CastP2X) { // CastP2X is used by card mark if (use->is_Phi()) { diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 8347321fe90..dea4640b9d1 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -4125,7 +4125,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)); if ((alloc != nullptr) && alloc->is_Allocate() && diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 5f868474422..80cb6721fce 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1016,7 +1016,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 fca532bd9a5..2e227be765c 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -2010,7 +2010,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) != 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..7252427ffcd --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java @@ -0,0 +1,311 @@ +/* + * 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; + } + + @Setup + Object[] stringBuilderSetup() { + return new Object[] { "foo", "bar", "baz" }; + } + + @Test + @Arguments(setup = "stringBuilderSetup") + @IR(failOn = IRNode.MEMBAR_RELEASE) + @IR(failOn = IRNode.MEMBAR_VOLATILE) + @IR(counts = {IRNode.MEMBAR_STORESTORE, "3"}) + public String stringBuilder(String s1, String s2, String s3) { + 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 96698540019..449252b6bad 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -887,11 +887,26 @@ public class IRNode { beforeMatchingNameRegex(MEMBAR, "MemBar"); } + public static final String MEMBAR_ACQUIRE = PREFIX + "MEMBAR_ACQUIRE" + POSTFIX; + static { + beforeMatchingNameRegex(MEMBAR_ACQUIRE, "MemBarAcquire"); + } + + public static final String MEMBAR_RELEASE = PREFIX + "MEMBAR_RELEASE" + POSTFIX; + static { + beforeMatchingNameRegex(MEMBAR_RELEASE, "MemBarRelease"); + } + public static final String MEMBAR_STORESTORE = PREFIX + "MEMBAR_STORESTORE" + POSTFIX; static { beforeMatchingNameRegex(MEMBAR_STORESTORE, "MemBarStoreStore"); } + public static final String MEMBAR_VOLATILE = PREFIX + "MEMBAR_VOLATILE" + POSTFIX; + static { + beforeMatchingNameRegex(MEMBAR_VOLATILE, "MemBarVolatile"); + } + public static final String MIN = PREFIX + "MIN" + POSTFIX; static { beforeMatchingNameRegex(MIN, "Min(I|L)"); 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; + } + +} +