# HG changeset patch # User roland # Date 1614959004 0 # Fri Mar 05 15:43:24 2021 +0000 # Node ID ef79db99660853442931bdac3b221aa9138c2959 # Parent bdc05c8355d19eb4aa3e0baeef6641bdac4946e4 8261812: C2 compilation fails with assert(!had_error) failed: bad dominance Reviewed-by: kvn, thartmann diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -4810,6 +4810,30 @@ return (os::random() & RANDOMIZED_DOMAIN_MASK) < (RANDOMIZED_DOMAIN / count); } +Node* Compile::narrow_value(BasicType bt, Node* value, const Type* type, PhaseGVN* phase, bool transform_res) { + if (type != NULL && phase->type(value)->higher_equal(type)) { + return value; + } + Node* result = NULL; + if (bt == T_BYTE) { + result = phase->transform(new LShiftINode(value, phase->intcon(24))); + result = new RShiftINode(result, phase->intcon(24)); + } else if (bt == T_BOOLEAN) { + result = new AndINode(value, phase->intcon(0xFF)); + } else if (bt == T_CHAR) { + result = new AndINode(value,phase->intcon(0xFFFF)); + } else { + assert(bt == T_SHORT, "unexpected narrow type"); + result = phase->transform(new LShiftINode(value, phase->intcon(16))); + result = new RShiftINode(result, phase->intcon(16)); + } + if (transform_res) { + result = phase->transform(result); + } + return result; +} + + CloneMap& Compile::clone_map() { return _clone_map; } void Compile::set_clone_map(Dict* d) { _clone_map._dict = d; } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -1382,6 +1382,8 @@ void set_exception_backedge() { _exception_backedge = true; } bool has_exception_backedge() const { return _exception_backedge; } #endif + + static Node* narrow_value(BasicType bt, Node* value, const Type* type, PhaseGVN* phase, bool transform_res); }; #endif // SHARE_VM_OPTO_COMPILE_HPP diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -468,16 +468,17 @@ if (val == mem) { values.at_put(j, mem); } else if (val->is_Store()) { + Node* n = val->in(MemNode::ValueIn); #if INCLUDE_SHENANDOAHGC - Node* n = val->in(MemNode::ValueIn); if (UseShenandoahGC) { BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); n = bs->step_over_gc_barrier(n); } +#endif + if (is_subword_type(ft)) { + n = Compile::narrow_value(ft, n, phi_type, &_igvn, true); + } values.at_put(j, n); -#else - values.at_put(j, val->in(MemNode::ValueIn)); -#endif } else if(val->is_Proj() && val->in(0) == alloc) { values.at_put(j, _igvn.zerocon(ft)); } else if (val->is_Phi()) { diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1995,12 +1995,14 @@ // with the value stored truncated to a byte. If no truncation is // needed, the replacement is done in LoadNode::Identity(). // -Node *LoadBNode::Ideal(PhaseGVN *phase, bool can_reshape) { +Node* LoadBNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* mem = in(MemNode::Memory); Node* value = can_see_stored_value(mem,phase); - if( value && !phase->type(value)->higher_equal( _type ) ) { - Node *result = phase->transform( new LShiftINode(value, phase->intcon(24)) ); - return new RShiftINode(result, phase->intcon(24)); + if (value != NULL) { + Node* narrow = Compile::narrow_value(T_BYTE, value, _type, phase, false); + if (narrow != value) { + return narrow; + } } // Identity call will handle the case where truncation is not needed. return LoadNode::Ideal(phase, can_reshape); @@ -2030,8 +2032,12 @@ Node* LoadUBNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* mem = in(MemNode::Memory); Node* value = can_see_stored_value(mem, phase); - if (value && !phase->type(value)->higher_equal(_type)) - return new AndINode(value, phase->intcon(0xFF)); + if (value != NULL) { + Node* narrow = Compile::narrow_value(T_BOOLEAN, value, _type, phase, false); + if (narrow != value) { + return narrow; + } + } // Identity call will handle the case where truncation is not needed. return LoadNode::Ideal(phase, can_reshape); } @@ -2057,11 +2063,15 @@ // with the value stored truncated to a char. If no truncation is // needed, the replacement is done in LoadNode::Identity(). // -Node *LoadUSNode::Ideal(PhaseGVN *phase, bool can_reshape) { +Node* LoadUSNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* mem = in(MemNode::Memory); Node* value = can_see_stored_value(mem,phase); - if( value && !phase->type(value)->higher_equal( _type ) ) - return new AndINode(value,phase->intcon(0xFFFF)); + if (value != NULL) { + Node* narrow = Compile::narrow_value(T_CHAR, value, _type, phase, false); + if (narrow != value) { + return narrow; + } + } // Identity call will handle the case where truncation is not needed. return LoadNode::Ideal(phase, can_reshape); } @@ -2087,12 +2097,14 @@ // with the value stored truncated to a short. If no truncation is // needed, the replacement is done in LoadNode::Identity(). // -Node *LoadSNode::Ideal(PhaseGVN *phase, bool can_reshape) { +Node* LoadSNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* mem = in(MemNode::Memory); Node* value = can_see_stored_value(mem,phase); - if( value && !phase->type(value)->higher_equal( _type ) ) { - Node *result = phase->transform( new LShiftINode(value, phase->intcon(16)) ); - return new RShiftINode(result, phase->intcon(16)); + if (value != NULL) { + Node* narrow = Compile::narrow_value(T_SHORT, value, _type, phase, false); + if (narrow != value) { + return narrow; + } } // Identity call will handle the case where truncation is not needed. return LoadNode::Ideal(phase, can_reshape); diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -2628,19 +2628,18 @@ case Bytecodes::_i2b: // Sign extend a = pop(); - a = _gvn.transform( new LShiftINode(a,_gvn.intcon(24)) ); - a = _gvn.transform( new RShiftINode(a,_gvn.intcon(24)) ); - push( a ); + a = Compile::narrow_value(T_BYTE, a, NULL, &_gvn, true); + push(a); break; case Bytecodes::_i2s: a = pop(); - a = _gvn.transform( new LShiftINode(a,_gvn.intcon(16)) ); - a = _gvn.transform( new RShiftINode(a,_gvn.intcon(16)) ); - push( a ); + a = Compile::narrow_value(T_SHORT, a, NULL, &_gvn, true); + push(a); break; case Bytecodes::_i2c: a = pop(); - push( _gvn.transform( new AndINode(a,_gvn.intcon(0xFFFF)) ) ); + a = Compile::narrow_value(T_CHAR, a, NULL, &_gvn, true); + push(a); break; case Bytecodes::_i2f: diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestValAtSafepointOverflowsInt.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestValAtSafepointOverflowsInt.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestValAtSafepointOverflowsInt.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021, Red Hat, Inc. 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 8261812 + * @summary C2 compilation fails with assert(!had_error) failed: bad dominance + * + * @run main/othervm -XX:-BackgroundCompilation TestValAtSafepointOverflowsInt + * + */ + +public class TestValAtSafepointOverflowsInt { + private static volatile int volatileField; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + testByte(true, false); + testByte(false, false); + testShort(true, false); + testShort(false, false); + testChar(true, false); + testChar(false, false); + } + testByte(true, true); + testShort(true, true); + testChar(true, true); + } + + private static Object testByte(boolean flag, boolean flag2) { + int i; + // loop to delay constant folding + for (i = 0; i < 9; i++) { + } + C obj = new C(); + if (flag) { + obj.byteField = (byte)(1 << i); + } else { + obj.byteField = (byte)(1 << (i+1)); + } + // Phi for byte here for uncommon trap in never taken path below + // Phi inputs don't fit in a byte. Phi transfomed to top. + if (flag2) { + return obj; + } + return null; + } + + private static Object testShort(boolean flag, boolean flag2) { + int i; + for (i = 0; i < 17; i++) { + } + C obj = new C(); + if (flag) { + obj.shortField = (short)(1 << i); + } else { + obj.shortField = (short)(1 << (i+1)); + } + if (flag2) { + return obj; + } + return null; + } + + private static Object testChar(boolean flag, boolean flag2) { + int i; + for (i = 0; i < 17; i++) { + } + C obj = new C(); + if (flag) { + obj.charField = (char)(1 << i); + } else { + obj.charField = (char)(1 << (i+1)); + } + if (flag2) { + return obj; + } + return null; + } + + + static class C { + byte byteField; + short shortField; + char charField; + } + +}