changeset: 9066:35c8d8696835 tag: tip user: thartmann date: Mon Oct 07 17:06:35 2019 +0200 summary: 8229496: SIGFPE (division by zero) in C2 OSR compiled method diff -r 506ba6f67cb2 -r 35c8d8696835 src/cpu/ppc/vm/ppc.ad --- a/src/cpu/ppc/vm/ppc.ad Fri Aug 30 09:24:54 2019 +0200 +++ b/src/cpu/ppc/vm/ppc.ad Mon Oct 07 17:06:35 2019 +0200 @@ -9532,6 +9532,14 @@ ins_pipe(pipe_class_default); %} +instruct castLL(iRegLdst dst) %{ + match(Set dst (CastLL dst)); + format %{ " -- \t// castLL of $dst" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + instruct checkCastPP(iRegPdst dst) %{ match(Set dst (CheckCastPP dst)); format %{ " -- \t// checkcastPP of $dst" %} diff -r 506ba6f67cb2 -r 35c8d8696835 src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Fri Aug 30 09:24:54 2019 +0200 +++ b/src/cpu/sparc/vm/sparc.ad Mon Oct 07 17:06:35 2019 +0200 @@ -7241,6 +7241,14 @@ ins_pipe(empty); %} +instruct castLL( iRegL dst ) %{ + match(Set dst (CastLL dst)); + format %{ "# castLL of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_cost(0); + ins_pipe(empty); +%} + //----------Arithmetic Instructions-------------------------------------------- // Addition Instructions // Register Addition diff -r 506ba6f67cb2 -r 35c8d8696835 src/cpu/x86/vm/x86_32.ad --- a/src/cpu/x86/vm/x86_32.ad Fri Aug 30 09:24:54 2019 +0200 +++ b/src/cpu/x86/vm/x86_32.ad Mon Oct 07 17:06:35 2019 +0200 @@ -7225,6 +7225,14 @@ ins_pipe( empty ); %} +instruct castLL( rRegL dst ) %{ + match(Set dst (CastLL dst)); + format %{ "#castLL of $dst" %} + ins_encode( /*empty encoding*/ ); + ins_cost(0); + ins_pipe( empty ); +%} + // Load-locked - same as a regular pointer load when used with compare-swap instruct loadPLocked(eRegP dst, memory mem) %{ diff -r 506ba6f67cb2 -r 35c8d8696835 src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Fri Aug 30 09:24:54 2019 +0200 +++ b/src/cpu/x86/vm/x86_64.ad Mon Oct 07 17:06:35 2019 +0200 @@ -7199,6 +7199,17 @@ ins_pipe(empty); %} +instruct castLL(rRegL dst) +%{ + match(Set dst (CastLL dst)); + + size(0); + format %{ "# castLL of $dst" %} + ins_encode(/* empty encoding */); + ins_cost(0); + ins_pipe(empty); +%} + // LoadP-locked same as a regular LoadP when used with compare-swap instruct loadPLocked(rRegP dst, memory mem) %{ diff -r 506ba6f67cb2 -r 35c8d8696835 src/share/vm/opto/classes.hpp --- a/src/share/vm/opto/classes.hpp Fri Aug 30 09:24:54 2019 +0200 +++ b/src/share/vm/opto/classes.hpp Mon Oct 07 17:06:35 2019 +0200 @@ -54,6 +54,7 @@ macro(CallRuntime) macro(CallStaticJava) macro(CastII) +macro(CastLL) macro(CastX2P) macro(CastP2X) macro(CastPP) diff -r 506ba6f67cb2 -r 35c8d8696835 src/share/vm/opto/connode.cpp --- a/src/share/vm/opto/connode.cpp Fri Aug 30 09:24:54 2019 +0200 +++ b/src/share/vm/opto/connode.cpp Mon Oct 07 17:06:35 2019 +0200 @@ -392,6 +392,9 @@ //============================================================================= // If input is already higher or equal to cast type, then this is an identity. Node *ConstraintCastNode::Identity( PhaseTransform *phase ) { + if (_carry_dependency) { + return this; + } return phase->type(in(1))->higher_equal_speculative(_type) ? in(1) : this; } @@ -434,6 +437,9 @@ //------------------------------Ideal_DU_postCCP------------------------------- // Throw away cast after constant propagation Node *ConstraintCastNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { + if (_carry_dependency) { + return NULL; + } const Type *t = ccp->type(in(1)); ccp->hash_delete(this); set_type(t); // Turn into ID function @@ -441,23 +447,55 @@ return this; } +uint ConstraintCastNode::cmp(const Node &n) const { + return TypeNode::cmp(n) && ((ConstraintCastNode&)n)._carry_dependency == _carry_dependency; +} + +uint ConstraintCastNode::size_of() const { + return sizeof(*this); +} + +Node* ConstraintCastNode::make_cast(int opcode, Node* c, Node *n, const Type *t, bool carry_dependency) { + switch(opcode) { + case Op_CastII: { + Node* cast = new (Compile::current()) CastIINode(n, t, carry_dependency); + cast->set_req(0, c); + return cast; + } + case Op_CastLL: { + Node* cast = new (Compile::current()) CastLLNode(n, t, carry_dependency); + cast->set_req(0, c); + return cast; + } + case Op_CastPP: { + Node* cast = new (Compile::current()) CastPPNode(n, t, carry_dependency); + cast->set_req(0, c); + return cast; + } + default: + fatal(err_msg_res("Bad opcode %d", opcode)); + } + return NULL; +} + +#ifndef PRODUCT +void ConstraintCastNode::dump_spec(outputStream *st) const { + TypeNode::dump_spec(st); + if (_carry_dependency) { + st->print(" carry dependency"); + } +} +#endif + uint CastIINode::size_of() const { return sizeof(*this); } uint CastIINode::cmp(const Node &n) const { - return TypeNode::cmp(n) && - ((CastIINode&)n)._carry_dependency == _carry_dependency && + return ConstraintCastNode::cmp(n) && ((CastIINode&)n)._range_check_dependency == _range_check_dependency; } -Node *CastIINode::Identity(PhaseTransform *phase) { - if (_carry_dependency) { - return this; - } - return ConstraintCastNode::Identity(phase); -} - const Type *CastIINode::Value(PhaseTransform *phase) const { const Type *res = ConstraintCastNode::Value(phase); @@ -525,7 +563,7 @@ } Node *CastIINode::Ideal_DU_postCCP(PhaseCCP *ccp) { - if (_carry_dependency || _range_check_dependency) { + if (_range_check_dependency) { return NULL; } return ConstraintCastNode::Ideal_DU_postCCP(ccp); @@ -533,10 +571,7 @@ #ifndef PRODUCT void CastIINode::dump_spec(outputStream *st) const { - TypeNode::dump_spec(st); - if (_carry_dependency) { - st->print(" carry dependency"); - } + ConstraintCastNode::dump_spec(st); if (_range_check_dependency) { st->print(" range check dependency"); } diff -r 506ba6f67cb2 -r 35c8d8696835 src/share/vm/opto/connode.hpp --- a/src/share/vm/opto/connode.hpp Fri Aug 30 09:24:54 2019 +0200 +++ b/src/share/vm/opto/connode.hpp Mon Oct 07 17:06:35 2019 +0200 @@ -225,8 +225,15 @@ //------------------------------ConstraintCastNode----------------------------- // cast to a different range class ConstraintCastNode: public TypeNode { +protected: + // Can this node be removed post CCP or does it carry a required dependency? + const bool _carry_dependency; + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; + public: - ConstraintCastNode (Node *n, const Type *t ): TypeNode(t,2) { + ConstraintCastNode (Node *n, const Type *t, bool carry_dependency): + TypeNode(t,2), _carry_dependency(carry_dependency) { init_class_id(Class_ConstraintCast); init_req(1, n); } @@ -236,14 +243,18 @@ virtual int Opcode() const; virtual uint ideal_reg() const = 0; virtual Node *Ideal_DU_postCCP( PhaseCCP * ); + virtual bool depends_only_on_test() const { return !_carry_dependency; } + static Node* make_cast(int opcode, Node* c, Node *n, const Type *t, bool carry_dependency); + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif }; //------------------------------CastIINode------------------------------------- // cast integer to integer (different range) class CastIINode: public ConstraintCastNode { private: - // Can this node be removed post CCP or does it carry a required dependency? - const bool _carry_dependency; // Is this node dependent on a range check? const bool _range_check_dependency; @@ -253,7 +264,7 @@ public: CastIINode(Node *n, const Type *t, bool carry_dependency = false, bool range_check_dependency = false) - : ConstraintCastNode(n,t), _carry_dependency(carry_dependency), _range_check_dependency(range_check_dependency) { + : ConstraintCastNode(n,t, carry_dependency), _range_check_dependency(range_check_dependency) { init_class_id(Class_CastII); } virtual int Opcode() const; @@ -274,11 +285,26 @@ #endif }; +//------------------------------CastLLNode------------------------------------- +// cast long to long (different range) +class CastLLNode: public ConstraintCastNode { + public: + CastLLNode(Node* n, const Type* t, bool carry_dependency = false) + : ConstraintCastNode(n, t, carry_dependency) { + init_class_id(Class_CastLL); + } + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegL; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); +}; + + //------------------------------CastPPNode------------------------------------- // cast pointer to pointer (different type) class CastPPNode: public ConstraintCastNode { public: - CastPPNode (Node *n, const Type *t ): ConstraintCastNode(n, t) {} + CastPPNode (Node *n, const Type *t, bool carry_dependency = false) + : ConstraintCastNode(n, t, carry_dependency) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } virtual Node *Ideal_DU_postCCP( PhaseCCP * ); diff -r 506ba6f67cb2 -r 35c8d8696835 src/share/vm/opto/graphKit.cpp --- a/src/share/vm/opto/graphKit.cpp Fri Aug 30 09:24:54 2019 +0200 +++ b/src/share/vm/opto/graphKit.cpp Mon Oct 07 17:06:35 2019 +0200 @@ -1353,33 +1353,36 @@ // Cast obj to not-null on this path, if there is no null_control. // (If there is a null_control, a non-null value may come back to haunt us.) - if (type == T_OBJECT) { - Node* cast = cast_not_null(value, false); - if (null_control == NULL || (*null_control) == top()) - replace_in_map(value, cast); - value = cast; - } - - return value; + return cast_not_null(value, (null_control == NULL || (*null_control) == top())); } //------------------------------cast_not_null---------------------------------- // Cast obj to not-null on this path Node* GraphKit::cast_not_null(Node* obj, bool do_replace_in_map) { - const Type *t = _gvn.type(obj); - const Type *t_not_null = t->join_speculative(TypePtr::NOTNULL); - // Object is already not-null? - if( t == t_not_null ) return obj; - - Node *cast = new (C) CastPPNode(obj,t_not_null); - cast->init_req(0, control()); - cast = _gvn.transform( cast ); + Node* cast = NULL; + const Type* t = _gvn.type(obj); + if (t->make_ptr() != NULL) { + const Type* t_not_null = t->join_speculative(TypePtr::NOTNULL); + // Object is already not-null? + if (t == t_not_null) { + return obj; + } + cast = ConstraintCastNode::make_cast(Op_CastPP, control(), obj, t_not_null, false); + } else if (t->isa_int() != NULL) { + cast = ConstraintCastNode::make_cast(Op_CastII, control(), obj, TypeInt::INT, true); + } else if (t->isa_long() != NULL) { + cast = ConstraintCastNode::make_cast(Op_CastLL, control(), obj, TypeLong::LONG, true); + } else { + fatal(err_msg_res("unexpected type: %s", type2name(t->basic_type()))); + } + cast = _gvn.transform(cast); // Scan for instances of 'obj' in the current JVM mapping. // These instances are known to be not-null after the test. - if (do_replace_in_map) + if (do_replace_in_map) { replace_in_map(obj, cast); + } return cast; // Return casted value } diff -r 506ba6f67cb2 -r 35c8d8696835 src/share/vm/opto/node.hpp --- a/src/share/vm/opto/node.hpp Fri Aug 30 09:24:54 2019 +0200 +++ b/src/share/vm/opto/node.hpp Mon Oct 07 17:06:35 2019 +0200 @@ -55,6 +55,7 @@ class CatchProjNode; class CheckCastPPNode; class CastIINode; +class CastLLNode; class ClearArrayNode; class CmpNode; class CodeBuffer; @@ -605,6 +606,7 @@ DEFINE_CLASS_ID(Phi, Type, 0) DEFINE_CLASS_ID(ConstraintCast, Type, 1) DEFINE_CLASS_ID(CastII, ConstraintCast, 0) + DEFINE_CLASS_ID(CastLL, ConstraintCast, 1) DEFINE_CLASS_ID(CheckCastPP, Type, 2) DEFINE_CLASS_ID(CMove, Type, 3) DEFINE_CLASS_ID(SafePointScalarObject, Type, 4) @@ -730,6 +732,7 @@ DEFINE_CLASS_QUERY(CatchProj) DEFINE_CLASS_QUERY(CheckCastPP) DEFINE_CLASS_QUERY(CastII) + DEFINE_CLASS_QUERY(CastLL) DEFINE_CLASS_QUERY(ConstraintCast) DEFINE_CLASS_QUERY(ClearArray) DEFINE_CLASS_QUERY(CMove) diff -r 506ba6f67cb2 -r 35c8d8696835 src/share/vm/runtime/vmStructs.cpp --- a/src/share/vm/runtime/vmStructs.cpp Fri Aug 30 09:24:54 2019 +0200 +++ b/src/share/vm/runtime/vmStructs.cpp Mon Oct 07 17:06:35 2019 +0200 @@ -1801,6 +1801,7 @@ declare_c2_type(DecodeNKlassNode, TypeNode) \ declare_c2_type(ConstraintCastNode, TypeNode) \ declare_c2_type(CastIINode, ConstraintCastNode) \ + declare_c2_type(CastLLNode, ConstraintCastNode) \ declare_c2_type(CastPPNode, ConstraintCastNode) \ declare_c2_type(CheckCastPPNode, TypeNode) \ declare_c2_type(Conv2BNode, Node) \ diff -r 506ba6f67cb2 -r 35c8d8696835 test/compiler/loopopts/TestDivZeroCheckControl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/loopopts/TestDivZeroCheckControl.java Mon Oct 07 17:06:35 2019 +0200 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 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. + * + */ + +/* + * @test + * @bug 8229496 + * @summary Verify that zero check is executed before division/modulo operation. + * @run main/othervm -Xbatch -XX:LoopUnrollLimit=0 + * -XX:CompileCommand=dontinline,compiler.loopopts.TestDivZeroCheckControl::test* + * compiler.loopopts.TestDivZeroCheckControl + */ + +package compiler.loopopts; + +public class TestDivZeroCheckControl { + + public static int test1(int div, int array[]) { + int res = 0; + for (int i = 0; i < 256; i++) { + int j = 0; + do { + array[i] = i; + try { + res = 1 % div; + } catch (ArithmeticException ex) { } + } while (++j < 9); + } + return res; + } + + // Same as test1 but with division instead of modulo + public static int test2(int div, int array[]) { + int res = 0; + for (int i = 0; i < 256; i++) { + int j = 0; + do { + array[i] = i; + try { + res = 1 / div; + } catch (ArithmeticException ex) { } + } while (++j < 9); + } + return res; + } + + // Same as test1 but with long + public static long test3(long div, int array[]) { + long res = 0; + for (int i = 0; i < 256; i++) { + int j = 0; + do { + array[i] = i; + try { + res = 1L % div; + } catch (ArithmeticException ex) { } + } while (++j < 9); + } + return res; + } + + // Same as test2 but with long + public static long test4(long div, int array[]) { + long res = 0; + for (int i = 0; i < 256; i++) { + int j = 0; + do { + array[i] = i; + try { + res = 1L / div; + } catch (ArithmeticException ex) { } + } while (++j < 9); + } + return res; + } + + public static void main(String[] args) { + int array[] = new int[256]; + for (int i = 0; i < 50_000; ++i) { + test1(0, array); + test2(0, array); + test3(0, array); + test4(0, array); + } + } +}