8007898: Incorrect optimization of Memory Barriers in Matcher::post_store_load_barrier()
Generate one "fat" membar instead of set of barriers for volitile store Reviewed-by: roland
This commit is contained in:
parent
2d3b117249
commit
1338c067aa
@ -2305,26 +2305,26 @@ void Matcher::validate_null_checks( ) {
|
||||
// atomic instruction acting as a store_load barrier without any
|
||||
// intervening volatile load, and thus we don't need a barrier here.
|
||||
// We retain the Node to act as a compiler ordering barrier.
|
||||
bool Matcher::post_store_load_barrier(const Node *vmb) {
|
||||
Compile *C = Compile::current();
|
||||
assert( vmb->is_MemBar(), "" );
|
||||
assert( vmb->Opcode() != Op_MemBarAcquire, "" );
|
||||
const MemBarNode *mem = (const MemBarNode*)vmb;
|
||||
bool Matcher::post_store_load_barrier(const Node* vmb) {
|
||||
Compile* C = Compile::current();
|
||||
assert(vmb->is_MemBar(), "");
|
||||
assert(vmb->Opcode() != Op_MemBarAcquire, "");
|
||||
const MemBarNode* membar = vmb->as_MemBar();
|
||||
|
||||
// Get the Proj node, ctrl, that can be used to iterate forward
|
||||
Node *ctrl = NULL;
|
||||
DUIterator_Fast imax, i = mem->fast_outs(imax);
|
||||
while( true ) {
|
||||
ctrl = mem->fast_out(i); // Throw out-of-bounds if proj not found
|
||||
assert( ctrl->is_Proj(), "only projections here" );
|
||||
ProjNode *proj = (ProjNode*)ctrl;
|
||||
if( proj->_con == TypeFunc::Control &&
|
||||
!C->node_arena()->contains(ctrl) ) // Unmatched old-space only
|
||||
// Get the Ideal Proj node, ctrl, that can be used to iterate forward
|
||||
Node* ctrl = NULL;
|
||||
for (DUIterator_Fast imax, i = membar->fast_outs(imax); i < imax; i++) {
|
||||
Node* p = membar->fast_out(i);
|
||||
assert(p->is_Proj(), "only projections here");
|
||||
if ((p->as_Proj()->_con == TypeFunc::Control) &&
|
||||
!C->node_arena()->contains(p)) { // Unmatched old-space only
|
||||
ctrl = p;
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
assert((ctrl != NULL), "missing control projection");
|
||||
|
||||
for( DUIterator_Fast jmax, j = ctrl->fast_outs(jmax); j < jmax; j++ ) {
|
||||
for (DUIterator_Fast jmax, j = ctrl->fast_outs(jmax); j < jmax; j++) {
|
||||
Node *x = ctrl->fast_out(j);
|
||||
int xop = x->Opcode();
|
||||
|
||||
@ -2336,37 +2336,36 @@ bool Matcher::post_store_load_barrier(const Node *vmb) {
|
||||
// that a monitor exit operation contains a serializing instruction.
|
||||
|
||||
if (xop == Op_MemBarVolatile ||
|
||||
xop == Op_FastLock ||
|
||||
xop == Op_CompareAndSwapL ||
|
||||
xop == Op_CompareAndSwapP ||
|
||||
xop == Op_CompareAndSwapN ||
|
||||
xop == Op_CompareAndSwapI)
|
||||
xop == Op_CompareAndSwapI) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Op_FastLock previously appeared in the Op_* list above.
|
||||
// With biased locking we're no longer guaranteed that a monitor
|
||||
// enter operation contains a serializing instruction.
|
||||
if ((xop == Op_FastLock) && !UseBiasedLocking) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x->is_MemBar()) {
|
||||
// We must retain this membar if there is an upcoming volatile
|
||||
// load, which will be preceded by acquire membar.
|
||||
if (xop == Op_MemBarAcquire)
|
||||
// load, which will be followed by acquire membar.
|
||||
if (xop == Op_MemBarAcquire) {
|
||||
return false;
|
||||
// For other kinds of barriers, check by pretending we
|
||||
// are them, and seeing if we can be removed.
|
||||
else
|
||||
return post_store_load_barrier((const MemBarNode*)x);
|
||||
} else {
|
||||
// For other kinds of barriers, check by pretending we
|
||||
// are them, and seeing if we can be removed.
|
||||
return post_store_load_barrier(x->as_MemBar());
|
||||
}
|
||||
}
|
||||
|
||||
// Delicate code to detect case of an upcoming fastlock block
|
||||
if( x->is_If() && x->req() > 1 &&
|
||||
!C->node_arena()->contains(x) ) { // Unmatched old-space only
|
||||
Node *iff = x;
|
||||
Node *bol = iff->in(1);
|
||||
// The iff might be some random subclass of If or bol might be Con-Top
|
||||
if (!bol->is_Bool()) return false;
|
||||
assert( bol->req() > 1, "" );
|
||||
return (bol->in(1)->Opcode() == Op_FastUnlock);
|
||||
}
|
||||
// probably not necessary to check for these
|
||||
if (x->is_Call() || x->is_SafePoint() || x->is_block_proj())
|
||||
if (x->is_Call() || x->is_SafePoint() || x->is_block_proj()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -294,25 +294,7 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
|
||||
// If reference is volatile, prevent following volatiles ops from
|
||||
// floating up before the volatile write.
|
||||
if (is_vol) {
|
||||
// First place the specific membar for THIS volatile index. This first
|
||||
// membar is dependent on the store, keeping any other membars generated
|
||||
// below from floating up past the store.
|
||||
int adr_idx = C->get_alias_index(adr_type);
|
||||
insert_mem_bar_volatile(Op_MemBarVolatile, adr_idx, store);
|
||||
|
||||
// Now place a membar for AliasIdxBot for the unknown yet-to-be-parsed
|
||||
// volatile alias indices. Skip this if the membar is redundant.
|
||||
if (adr_idx != Compile::AliasIdxBot) {
|
||||
insert_mem_bar_volatile(Op_MemBarVolatile, Compile::AliasIdxBot, store);
|
||||
}
|
||||
|
||||
// Finally, place alias-index-specific membars for each volatile index
|
||||
// that isn't the adr_idx membar. Typically there's only 1 or 2.
|
||||
for( int i = Compile::AliasIdxRaw; i < C->num_alias_types(); i++ ) {
|
||||
if (i != adr_idx && C->alias_type(i)->is_volatile()) {
|
||||
insert_mem_bar_volatile(Op_MemBarVolatile, i, store);
|
||||
}
|
||||
}
|
||||
insert_mem_bar(Op_MemBarVolatile); // Use fat membar
|
||||
}
|
||||
|
||||
// If the field is final, the rules of Java say we are in <init> or <clinit>.
|
||||
|
163
hotspot/test/compiler/membars/DekkerTest.java
Normal file
163
hotspot/test/compiler/membars/DekkerTest.java
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2013 SAP AG. 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8007898
|
||||
* @summary Incorrect optimization of Memory Barriers in Matcher::post_store_load_barrier().
|
||||
* @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:CICompilerCount=1 -XX:+StressGCM -XX:+StressLCM DekkerTest
|
||||
* @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:CICompilerCount=1 -XX:+StressGCM -XX:+StressLCM DekkerTest
|
||||
* @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:CICompilerCount=1 -XX:+StressGCM -XX:+StressLCM DekkerTest
|
||||
* @author Martin Doerr martin DOT doerr AT sap DOT com
|
||||
*
|
||||
* Run 3 times since the failure is intermittent.
|
||||
*/
|
||||
|
||||
public class DekkerTest {
|
||||
|
||||
/*
|
||||
Read After Write Test (basically a simple Dekker test with volatile variables)
|
||||
Derived from the original jcstress test, available at:
|
||||
http://hg.openjdk.java.net/code-tools/jcstress/file/6c339a5aa00d/
|
||||
tests-custom/src/main/java/org/openjdk/jcstress/tests/volatiles/DekkerTest.java
|
||||
*/
|
||||
|
||||
static final int ITERATIONS = 1000000;
|
||||
|
||||
static class TestData {
|
||||
public volatile int a;
|
||||
public volatile int b;
|
||||
}
|
||||
|
||||
static class ResultData {
|
||||
public int a;
|
||||
public int b;
|
||||
}
|
||||
|
||||
TestData[] testDataArray;
|
||||
ResultData[] results;
|
||||
|
||||
volatile boolean start;
|
||||
|
||||
public DekkerTest() {
|
||||
testDataArray = new TestData[ITERATIONS];
|
||||
results = new ResultData[ITERATIONS];
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
testDataArray[i] = new TestData();
|
||||
results[i] = new ResultData();
|
||||
}
|
||||
start = false;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
testDataArray[i].a = 0;
|
||||
testDataArray[i].b = 0;
|
||||
results[i].a = 0;
|
||||
results[i].b = 0;
|
||||
}
|
||||
start = false;
|
||||
}
|
||||
|
||||
int actor1(TestData t) {
|
||||
t.a = 1;
|
||||
return t.b;
|
||||
}
|
||||
|
||||
int actor2(TestData t) {
|
||||
t.b = 1;
|
||||
return t.a;
|
||||
}
|
||||
|
||||
class Runner1 extends Thread {
|
||||
public void run() {
|
||||
do {} while (!start);
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
results[i].a = actor1(testDataArray[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Runner2 extends Thread {
|
||||
public void run() {
|
||||
do {} while (!start);
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
results[i].b = actor2(testDataArray[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testRunner() {
|
||||
Thread thread1 = new Runner1();
|
||||
Thread thread2 = new Runner2();
|
||||
thread1.start();
|
||||
thread2.start();
|
||||
do {} while (!thread1.isAlive());
|
||||
do {} while (!thread2.isAlive());
|
||||
start = true;
|
||||
Thread.yield();
|
||||
try {
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("interrupted!");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
boolean printResult() {
|
||||
int[] count = new int[4];
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
int event_kind = (results[i].a << 1) + results[i].b;
|
||||
++count[event_kind];
|
||||
}
|
||||
if (count[0] == 0 && count[3] == 0) {
|
||||
System.out.println("[not interesting]");
|
||||
return false; // not interesting
|
||||
}
|
||||
String error = (count[0] == 0) ? " ok" : " disallowed!";
|
||||
System.out.println("[0,0] " + count[0] + error);
|
||||
System.out.println("[0,1] " + count[1]);
|
||||
System.out.println("[1,0] " + count[2]);
|
||||
System.out.println("[1,1] " + count[3]);
|
||||
return (count[0] != 0);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
DekkerTest test = new DekkerTest();
|
||||
final int runs = 30;
|
||||
int failed = 0;
|
||||
for (int c = 0; c < runs; ++c) {
|
||||
test.testRunner();
|
||||
if (test.printResult()) {
|
||||
failed++;
|
||||
}
|
||||
test.reset();
|
||||
}
|
||||
if (failed > 0) {
|
||||
throw new InternalError("FAILED. Got " + failed + " failed ITERATIONS");
|
||||
}
|
||||
System.out.println("PASSED.");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user