From 3a87d66d85da7cbf8c195586c4e5bd53600b21de Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sat, 20 Jan 2024 20:01:35 +0800 Subject: [PATCH 01/45] implement unsigned bounds and known bits --- src/hotspot/share/opto/addnode.cpp | 8 +- src/hotspot/share/opto/castnode.cpp | 10 +- src/hotspot/share/opto/compile.cpp | 8 +- src/hotspot/share/opto/convertnode.cpp | 8 +- src/hotspot/share/opto/graphKit.cpp | 8 +- src/hotspot/share/opto/ifnode.cpp | 11 +- src/hotspot/share/opto/loopPredicate.cpp | 2 +- src/hotspot/share/opto/macroArrayCopy.cpp | 4 +- src/hotspot/share/opto/mulnode.cpp | 8 +- src/hotspot/share/opto/subnode.cpp | 10 +- src/hotspot/share/opto/type.cpp | 1109 ++++++++++++++------- src/hotspot/share/opto/type.hpp | 137 ++- 12 files changed, 852 insertions(+), 471 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 1b92193300449..be33d592bc966 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -988,8 +988,8 @@ const Type* XorINode::Value(PhaseGVN* phase) const { (t2i->_lo >= 0) && (t2i->_hi > 0)) { // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(t1i->_hi) + (round_down_power_of_2(t1i->_hi) - 1), t1i->_widen); - const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(t2i->_hi) + (round_down_power_of_2(t2i->_hi) - 1), t2i->_widen); + const Type* t1x = TypeInt::make(0, round_down_power_of_2(t1i->_hi) + (round_down_power_of_2(t1i->_hi) - 1), t1i->_widen); + const Type* t2x = TypeInt::make(0, round_down_power_of_2(t2i->_hi) + (round_down_power_of_2(t2i->_hi) - 1), t2i->_widen); return t1x->meet(t2x); } return AddNode::Value(phase); @@ -1073,8 +1073,8 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { (t2l->_lo >= 0) && (t2l->_hi > 0)) { // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(t1l->_hi) + (round_down_power_of_2(t1l->_hi) - 1), t1l->_widen); - const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(t2l->_hi) + (round_down_power_of_2(t2l->_hi) - 1), t2l->_widen); + const Type* t1x = TypeLong::make(0, round_down_power_of_2(t1l->_hi) + (round_down_power_of_2(t1l->_hi) - 1), t1l->_widen); + const Type* t2x = TypeLong::make(0, round_down_power_of_2(t2l->_hi) + (round_down_power_of_2(t2l->_hi) - 1), t2l->_widen); return t1x->meet(t2x); } return AddNode::Value(phase); diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index a93c9b382f923..a5d1baf72b325 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -321,7 +321,7 @@ Node* CastLLNode::Ideal(PhaseGVN* phase, bool can_reshape) { assert(tl->_lo >= t_in_l->_lo && tl->_hi <= t_in_l->_hi, "CastLL type should be narrower than or equal to the type of its input"); assert((tl != t_in_l) == (tl->_lo > t_in_l->_lo || tl->_hi < t_in_l->_hi), "if type differs then this nodes's type must be narrower"); if (tl != t_in_l) { - const TypeInt* ti = TypeInt::make(checked_cast(tl->_lo), checked_cast(tl->_hi), tl->_widen); + const TypeInt* ti = TypeInt::make(checked_cast(tl->_lo), checked_cast(tl->_hi), tl->_widen)->is_int(); Node* castii = phase->transform(new CastIINode(in(0), in1->in(1), ti)); Node* convi2l = in1->clone(); convi2l->set_req(1, castii); @@ -473,7 +473,11 @@ Node* ConstraintCastNode::make_cast_for_type(Node* c, Node* in, const Type* type Node* ConstraintCastNode::optimize_integer_cast(PhaseGVN* phase, BasicType bt) { PhaseIterGVN *igvn = phase->is_IterGVN(); - const TypeInteger* this_type = this->type()->is_integer(bt); + const TypeInteger* this_type = this->type()->isa_integer(bt); + if (this_type == nullptr) { + return nullptr; + } + Node* z = in(1); const TypeInteger* rx = nullptr; const TypeInteger* ry = nullptr; diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index acce455b7265d..42e5080430bc1 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -4497,8 +4497,10 @@ Node* Compile::conv_I2X_index(PhaseGVN* phase, Node* idx, const TypeInt* sizetyp // number. (The prior range check has ensured this.) // This assertion is used by ConvI2LNode::Ideal. int index_max = max_jint - 1; // array size is max_jint, index is one less - if (sizetype != nullptr) index_max = sizetype->_hi - 1; - const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax); + if (sizetype != nullptr && sizetype->_hi > 0) { + index_max = sizetype->_hi - 1; + } + const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax)->is_int(); idx = constrained_convI2L(phase, idx, iidxtype, ctrl); #endif return idx; @@ -4515,7 +4517,7 @@ Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* value = new CastIINode(ctrl, value, itype, carry_dependency ? ConstraintCastNode::StrongDependency : ConstraintCastNode::RegularDependency, true /* range check dependency */); value = phase->transform(value); } - const TypeLong* ltype = TypeLong::make(itype->_lo, itype->_hi, itype->_widen); + const TypeLong* ltype = TypeLong::make(itype->_lo, itype->_hi, itype->_widen)->is_long(); return phase->transform(new ConvI2LNode(value, ltype)); } diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index 0a2131782a237..b7422ac3cdb65 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -648,8 +648,8 @@ static bool compute_updates_ranges(const TypeInteger* tx, const TypeInteger* ty, } int widen = MAX2(tx->widen_limit(), ty->widen_limit()); - rx = TypeInteger::make(rxlo, rxhi, widen, out_bt); - ry = TypeInteger::make(rylo, ryhi, widen, out_bt); + rx = TypeInteger::make(rxlo, rxhi, widen, out_bt)->is_integer(out_bt); + ry = TypeInteger::make(rylo, ryhi, widen, out_bt)->is_integer(out_bt); return true; } @@ -791,7 +791,7 @@ const Type* ConvL2INode::Value(PhaseGVN* phase) const { // Easy case. ti = TypeInt::make((jint)tl->get_con()); } else if (tl->_lo >= min_jint && tl->_hi <= max_jint) { - ti = TypeInt::make((jint)tl->_lo, (jint)tl->_hi, tl->_widen); + ti = TypeInt::make((jint)tl->_lo, (jint)tl->_hi, tl->_widen)->is_int(); } return ti->filter(_type); } diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 2389121528105..0899b84f10da6 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, 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 @@ -3866,8 +3866,10 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) if (tilen != nullptr && tilen->_lo < 0) { // Add a manual constraint to a positive range. Cf. array_element_address. jint size_max = fast_size_limit; - if (size_max > tilen->_hi) size_max = tilen->_hi; - const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin); + if (size_max > tilen->_hi && tilen->_hi >= 0) { + size_max = tilen->_hi; + } + const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin)->is_int(); // Only do a narrow I2L conversion if the range check passed. IfNode* iff = new IfNode(control(), initial_slow_test, PROB_MIN, COUNT_UNKNOWN); diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 6ae62b24b3c8d..2d92bb0174a82 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, 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 @@ -642,9 +642,9 @@ const TypeInt* IfNode::filtered_int_type(PhaseGVN* gvn, Node* val, Node* if_proj const TypeInt* val_t = gvn->type(val)->isa_int(); if (val_t != nullptr && !val_t->singleton() && cmp2_t->is_con()) { if (val_t->_lo == lo) { - return TypeInt::make(val_t->_lo + 1, val_t->_hi, val_t->_widen); + return TypeInt::make(val_t->_lo + 1, val_t->_hi, val_t->_widen)->is_int(); } else if (val_t->_hi == hi) { - return TypeInt::make(val_t->_lo, val_t->_hi - 1, val_t->_widen); + return TypeInt::make(val_t->_lo, val_t->_hi - 1, val_t->_widen)->is_int(); } } // Can't refine type @@ -674,7 +674,7 @@ const TypeInt* IfNode::filtered_int_type(PhaseGVN* gvn, Node* val, Node* if_proj default: break; } - const TypeInt* rtn_t = TypeInt::make(lo, hi, cmp2_t->_widen); + const TypeInt* rtn_t = TypeInt::make(lo, hi, cmp2_t->_widen)->is_int(); return rtn_t; } } @@ -1014,8 +1014,7 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f if (failtype != nullptr) { const TypeInt* type2 = filtered_int_type(igvn, n, fail); if (type2 != nullptr) { - failtype = failtype->join(type2)->is_int(); - if (failtype->_lo > failtype->_hi) { + if (failtype->filter(type2) == Type::TOP) { // previous if determines the result of this if so // replace Bool with constant igvn->replace_input_of(this, 1, igvn->intcon(success->_con)); diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index fd0576832cf70..90cb7661bab92 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -818,7 +818,7 @@ BoolNode* PhaseIdealLoop::rc_predicate(IdealLoopTree* loop, Node* ctrl, int scal ConINode* con_stride = _igvn.intcon(stride); set_ctrl(con_stride, C->root()); max_idx_expr = new SubINode(limit, con_stride); - idx_type = TypeInt::make(limit_lo - stride, limit_hi - stride, limit_type->_widen); + idx_type = TypeInt::make(limit_lo - stride, limit_hi - stride, limit_type->_widen)->is_int(); } else { // May overflow overflow = true; diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp index 1c658f20715bb..ad7b870654ba3 100644 --- a/src/hotspot/share/opto/macroArrayCopy.cpp +++ b/src/hotspot/share/opto/macroArrayCopy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -56,7 +56,7 @@ Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType el #ifdef _LP64 // see comment in GraphKit::array_element_address int index_max = max_jint - 1; // array size is max_jint, index is one less - const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax); + const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax)->is_long(); idx = transform_later( new ConvI2LNode(idx, lidxtype) ); #endif Node* scale = new LShiftXNode(idx, intcon(shift)); diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 1f22c60832388..9c54a56e9e982 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -1397,7 +1397,7 @@ const Type* RShiftINode::Value(PhaseGVN* phase) const { jint lo = (jint)r1->_lo >> (jint)shift; jint hi = (jint)r1->_hi >> (jint)shift; assert(lo <= hi, "must have valid bounds"); - const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen)); + const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen))->is_int(); #ifdef ASSERT // Make sure we get the sign-capture idiom correct. if (shift == BitsPerJavaInteger-1) { @@ -1467,7 +1467,7 @@ const Type* RShiftLNode::Value(PhaseGVN* phase) const { jlong lo = (jlong)r1->_lo >> (jlong)shift; jlong hi = (jlong)r1->_hi >> (jlong)shift; assert(lo <= hi, "must have valid bounds"); - const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen)); + const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen))->is_long(); #ifdef ASSERT // Make sure we get the sign-capture idiom correct. if (shift == (2*BitsPerJavaInteger)-1) { @@ -1653,7 +1653,7 @@ const Type* URShiftINode::Value(PhaseGVN* phase) const { hi = MAX2(neg_hi, pos_hi); // == -1 >>> shift; } assert(lo <= hi, "must have valid bounds"); - const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen)); + const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen))->is_int(); #ifdef ASSERT // Make sure we get the sign-capture idiom correct. if (shift == BitsPerJavaInteger-1) { @@ -1800,7 +1800,7 @@ const Type* URShiftLNode::Value(PhaseGVN* phase) const { hi = neg_hi > pos_hi ? neg_hi : pos_hi; } assert(lo <= hi, "must have valid bounds"); - const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen)); + const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen))->is_long(); #ifdef ASSERT // Make sure we get the sign-capture idiom correct. if (shift == BitsPerJavaLong - 1) { diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index f679017235129..9eaf975433a37 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -840,8 +840,8 @@ const Type* CmpUNode::Value(PhaseGVN* phase) const { if ((underflow != overflow) && (hi_tr1 < lo_tr2)) { // Overflow only on one boundary, compare 2 separate type ranges. int w = MAX2(r0->_widen, r1->_widen); // _widen does not matter here - const TypeInt* tr1 = TypeInt::make(lo_tr1, hi_tr1, w); - const TypeInt* tr2 = TypeInt::make(lo_tr2, hi_tr2, w); + const TypeInt* tr1 = TypeInt::make(lo_tr1, hi_tr1, w)->is_int(); + const TypeInt* tr2 = TypeInt::make(lo_tr2, hi_tr2, w)->is_int(); const TypeInt* cmp1 = sub(tr1, t2)->is_int(); const TypeInt* cmp2 = sub(tr2, t2)->is_int(); // Compute union, so that cmp handles all possible results from the two cases @@ -1452,8 +1452,8 @@ Node* BoolNode::fold_cmpI(PhaseGVN* phase, SubNode* cmp, Node* cmp1, int cmp_op, // Overflow on one boundary, compute resulting type ranges: // tr1 [MIN_INT, hi_int] and tr2 [lo_int, MAX_INT] int w = MAX2(r0->_widen, r1->_widen); // _widen does not matter here - const TypeInt* tr1 = TypeInt::make(min_jint, hi_int, w); - const TypeInt* tr2 = TypeInt::make(lo_int, max_jint, w); + const TypeInt* tr1 = TypeInt::make(min_jint, hi_int, w)->is_int(); + const TypeInt* tr2 = TypeInt::make(lo_int, max_jint, w)->is_int(); // Compare second input of cmp to both type ranges const Type* sub_tr1 = cmp->sub(tr1, cmp2_type); const Type* sub_tr2 = cmp->sub(tr2, cmp2_type); diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 84d092f2ffd59..648217257a592 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -418,7 +418,7 @@ int Type::uhash( const Type *const t ) { return (int)t->hash(); } -#define SMALLINT ((juint)3) // a value too insignificant to consider widening +constexpr juint SMALLINT = 3; // a value too insignificant to consider widening #define POSITIVE_INFINITE_F 0x7f800000 // hex representation for IEEE 754 single precision positive infinite #define POSITIVE_INFINITE_D 0x7ff0000000000000 // hex representation for IEEE 754 double precision positive infinite @@ -468,22 +468,24 @@ void Type::Initialize_shared(Compile* current) { TypeInt::MINUS_1 = TypeInt::make(-1); // -1 TypeInt::ZERO = TypeInt::make( 0); // 0 TypeInt::ONE = TypeInt::make( 1); // 1 - TypeInt::BOOL = TypeInt::make(0,1, WidenMin); // 0 or 1, FALSE or TRUE. - TypeInt::CC = TypeInt::make(-1, 1, WidenMin); // -1, 0 or 1, condition codes - TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin); // == TypeInt::MINUS_1 - TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin); // == TypeInt::ONE - TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin); // == TypeInt::ZERO - TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin); - TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin); // == TypeInt::BOOL - TypeInt::BYTE = TypeInt::make(-128,127, WidenMin); // Bytes - TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin); // Unsigned Bytes - TypeInt::CHAR = TypeInt::make(0,65535, WidenMin); // Java chars - TypeInt::SHORT = TypeInt::make(-32768,32767, WidenMin); // Java shorts - TypeInt::POS = TypeInt::make(0,max_jint, WidenMin); // Non-neg values - TypeInt::POS1 = TypeInt::make(1,max_jint, WidenMin); // Positive values - TypeInt::INT = TypeInt::make(min_jint,max_jint, WidenMax); // 32-bit integers - TypeInt::SYMINT = TypeInt::make(-max_jint,max_jint,WidenMin); // symmetric range - TypeInt::TYPE_DOMAIN = TypeInt::INT; + TypeInt::BOOL = TypeInt::make( 0, 1, WidenMin)->is_int(); // 0 or 1, FALSE or TRUE. + TypeInt::CC = TypeInt::make(-1, 1, WidenMin)->is_int(); // -1, 0 or 1, condition codes + TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin)->is_int(); // == TypeInt::MINUS_1 + TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin)->is_int(); // == TypeInt::ONE + TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin)->is_int(); // == TypeInt::ZERO + TypeInt::CC_NE = TypeInt::make(-1, 1, 1, -1, 0, 1, WidenMin)->is_int(); + TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin)->is_int(); + TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin)->is_int(); // == TypeInt::BOOL + TypeInt::BYTE = TypeInt::make(-128, 127, WidenMin)->is_int(); // Bytes + TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin)->is_int(); // Unsigned Bytes + TypeInt::CHAR = TypeInt::make(0,65535, WidenMin)->is_int(); // Java chars + TypeInt::SHORT = TypeInt::make(-32768,32767, WidenMin)->is_int(); // Java shorts + TypeInt::NON_ZERO= TypeInt::make(min_jint, max_jint, 1, -1, 0, 0, WidenMin)->is_int(); + TypeInt::POS = TypeInt::make(0,max_jint, WidenMin)->is_int(); // Non-neg values + TypeInt::POS1 = TypeInt::make(1,max_jint, WidenMin)->is_int(); // Positive values + TypeInt::INT = TypeInt::make(min_jint, max_jint, WidenMax)->is_int(); // 32-bit integers + TypeInt::SYMINT = TypeInt::make(-max_jint, max_jint, WidenMin)->is_int(); // symmetric range + TypeInt::TYPE_DOMAIN = TypeInt::INT; // CmpL is overloaded both as the bytecode computation returning // a trinary (-1,0,+1) integer result AND as an efficient long // compare returning optimizer ideal-type flags. @@ -498,10 +500,12 @@ void Type::Initialize_shared(Compile* current) { TypeLong::MINUS_1 = TypeLong::make(-1); // -1 TypeLong::ZERO = TypeLong::make( 0); // 0 TypeLong::ONE = TypeLong::make( 1); // 1 - TypeLong::POS = TypeLong::make(0,max_jlong, WidenMin); // Non-neg values - TypeLong::LONG = TypeLong::make(min_jlong,max_jlong,WidenMax); // 64-bit integers - TypeLong::INT = TypeLong::make((jlong)min_jint,(jlong)max_jint,WidenMin); - TypeLong::UINT = TypeLong::make(0,(jlong)max_juint,WidenMin); + TypeLong::NON_ZERO= TypeLong::make(min_jlong, max_jlong, 1, -1, 0, 0, WidenMin)->is_long(); + TypeLong::POS = TypeLong::make(0,max_jlong, WidenMin)->is_long(); // Non-neg values + TypeLong::NEG = TypeLong::make(min_jlong, -1, WidenMin)->is_long(); + TypeLong::LONG = TypeLong::make(min_jlong,max_jlong,WidenMax)->is_long(); // 64-bit integers + TypeLong::INT = TypeLong::make((jlong)min_jint,(jlong)max_jint,WidenMin)->is_long(); + TypeLong::UINT = TypeLong::make(0,(jlong)max_juint,WidenMin)->is_long(); TypeLong::TYPE_DOMAIN = TypeLong::LONG; const Type **fboth =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); @@ -1509,7 +1513,7 @@ bool TypeD::empty(void) const { return false; // always exactly a singleton } -const TypeInteger* TypeInteger::make(jlong lo, jlong hi, int w, BasicType bt) { +const Type* TypeInteger::make(jlong lo, jlong hi, int w, BasicType bt) { if (bt == T_INT) { return TypeInt::make(checked_cast(lo), checked_cast(hi), w); } @@ -1557,64 +1561,431 @@ const TypeInteger* TypeInteger::minus_1(BasicType bt) { return TypeLong::MINUS_1; } -//============================================================================= -// Convenience common pre-built types. -const TypeInt *TypeInt::MAX; // INT_MAX -const TypeInt *TypeInt::MIN; // INT_MIN -const TypeInt *TypeInt::MINUS_1;// -1 -const TypeInt *TypeInt::ZERO; // 0 -const TypeInt *TypeInt::ONE; // 1 -const TypeInt *TypeInt::BOOL; // 0 or 1, FALSE or TRUE. -const TypeInt *TypeInt::CC; // -1,0 or 1, condition codes -const TypeInt *TypeInt::CC_LT; // [-1] == MINUS_1 -const TypeInt *TypeInt::CC_GT; // [1] == ONE -const TypeInt *TypeInt::CC_EQ; // [0] == ZERO -const TypeInt *TypeInt::CC_LE; // [-1,0] -const TypeInt *TypeInt::CC_GE; // [0,1] == BOOL (!) -const TypeInt *TypeInt::BYTE; // Bytes, -128 to 127 -const TypeInt *TypeInt::UBYTE; // Unsigned Bytes, 0 to 255 -const TypeInt *TypeInt::CHAR; // Java chars, 0-65535 -const TypeInt *TypeInt::SHORT; // Java shorts, -32768-32767 -const TypeInt *TypeInt::POS; // Positive 32-bit integers or zero -const TypeInt *TypeInt::POS1; // Positive 32-bit integers -const TypeInt *TypeInt::INT; // 32-bit integers -const TypeInt *TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] -const TypeInt *TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT - -//------------------------------TypeInt---------------------------------------- -TypeInt::TypeInt( jint lo, jint hi, int w ) : TypeInteger(Int, w), _lo(lo), _hi(hi) { +template +static bool adjust_bounds_from_bits(bool& empty, T& lo, T& hi, T zeros, T ones) { + static_assert(std::is_unsigned::value, ""); + + auto adjust_lo = [](T lo, T zeros, T ones) { + constexpr size_t W = sizeof(T) * 8; + T zero_violation = lo & zeros; + T one_violation = ~lo & ones; + if (zero_violation == 0 && one_violation == 0) { + return lo; + } + + if (zero_violation < one_violation) { + // Align the last violation of ones unset all the lower bits + // so we don't care about violations of zeros + juint last_violation = W - 1 - count_leading_zeros(one_violation); + T alignment = T(1) << last_violation; + lo = (lo & -alignment) + alignment; + return lo | ones; + } + + // Suppose lo = 00110010, zeros = 01010010, ones = 10001000 + // Since the 4-th bit must be 0, we need to align up the lower bound. + // This results in lo = 01000000, but then the 6-th bit does not match, + // align up again gives us 10000000. + // We can align up directly to 10000000 by finding the first place after + // the highest mismatch such that both the corresponding bits are unset. + // Since all bits lower than the alignment are unset we don't need to + // align for the violations of ones anymore. + juint last_violation = W - 1 - count_leading_zeros(zero_violation); + T find_mask = std::numeric_limits::max() << last_violation; + T either = lo | zeros; + T tmp = ~either & find_mask; + T alignment = tmp & (-tmp); + lo = (lo & -alignment) + alignment; + return lo | ones; + }; + + T new_lo = adjust_lo(lo, zeros, ones); + if (new_lo < lo) { + empty = true; + return true; + } + + T new_hi = ~adjust_lo(~hi, ones, zeros); + if (new_hi > hi) { + empty = true; + return true; + } + bool progress = (new_lo != lo) || (new_hi != hi); + lo = new_lo; + hi = new_hi; + empty = lo > hi; + return progress; +} + +template +static bool adjust_bits_from_bounds(bool& empty, T& zeros, T& ones, T lo, T hi) { + static_assert(std::is_unsigned::value, ""); + T mismatch = lo ^ hi; + T match_mask = mismatch == 0 ? std::numeric_limits::max() + : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); + T new_zeros = zeros | (match_mask &~ lo); + T new_ones = ones | (match_mask & lo); + bool progress = (new_zeros != zeros) || (new_ones != ones); + zeros = new_zeros; + ones = new_ones; + empty = ((zeros & ones) != 0); + return progress; +} + +template +static void normalize_constraints_simple(bool& empty, T& lo, T& hi, T& zeros, T& ones) { + adjust_bits_from_bounds(empty, zeros, ones, lo, hi); + if (empty) { + return; + } + while (true) { + bool progress = adjust_bounds_from_bits(empty, lo, hi, zeros, ones); + if (!progress || empty) { + return; + } + progress = adjust_bits_from_bounds(empty, zeros, ones, lo, hi); + if (!progress || empty) { + return; + } + } } -//------------------------------make------------------------------------------- -const TypeInt *TypeInt::make( jint lo ) { - return (TypeInt*)(new TypeInt(lo,lo,WidenMin))->hashcons(); +template +static void normalize_constraints(bool& empty, T& lo, T& hi, U& ulo, U& uhi, U& zeros, U& ones) { + static_assert(std::is_signed::value, ""); + static_assert(std::is_unsigned::value, ""); + static_assert(sizeof(T) == sizeof(U), ""); + + if (lo > hi || ulo > uhi || (zeros & ones) != 0) { + empty = true; + return; + } + + if (T(ulo) > T(uhi)) { + if (T(uhi) < lo) { + uhi = std::numeric_limits::max(); + } else if (T(ulo) > hi) { + ulo = std::numeric_limits::min(); + } + } + + if (T(ulo) <= T(uhi)) { + ulo = MAX2(ulo, lo); + uhi = MIN2(uhi, hi); + if (ulo > uhi) { + empty = true; + return; + } + + normalize_constraints_simple(empty, ulo, uhi, zeros, ones); + lo = ulo; + hi = uhi; + return; + } + + bool empty1 = false; + U lo1 = lo; + U hi1 = uhi; + U zeros1 = zeros; + U ones1 = ones; + normalize_constraints_simple(empty1, lo1, hi1, zeros1, ones1); + + bool empty2 = false; + U lo2 = ulo; + U hi2 = hi; + U zeros2 = zeros; + U ones2 = ones; + normalize_constraints_simple(empty2, lo2, hi2, zeros2, ones2); + + if (empty1 & empty2) { + empty = true; + } else if (empty1) { + lo = lo2; + hi = hi2; + ulo = lo2; + uhi = hi2; + zeros = zeros2; + ones = ones2; + } else if (empty2) { + lo = lo1; + hi = hi1; + ulo = lo1; + uhi = hi1; + zeros = zeros1; + ones = ones1; + } else { + lo = lo1; + hi = hi2; + ulo = lo2; + uhi = hi1; + zeros = zeros1 & zeros2; + ones = ones1 & ones2; + } +} + +#ifdef ASSERT +template +static void verify_constraints(T lo, T hi, U ulo, U uhi, U zeros, U ones) { + static_assert(std::is_signed::value, ""); + static_assert(std::is_unsigned::value, ""); + static_assert(sizeof(T) == sizeof(U), ""); + + // Assert that the bounds cannot be further tightened + assert(lo <= hi && U(lo) >= ulo && U(lo) <= uhi && (lo & zeros) == 0 && (~lo & ones) == 0, ""); + assert(hi >= lo && U(hi) >= ulo && U(hi) <= uhi && (hi & zeros) == 0 && (~hi & ones) == 0, ""); + assert(T(ulo) >= lo && T(ulo) <= hi && ulo <= uhi && (ulo & zeros) == 0 && (~ulo & ones) == 0, ""); + assert(T(uhi) >= lo && T(uhi) <= hi && uhi >= ulo && (uhi & zeros) == 0 && (~uhi & ones) == 0, ""); + + // Assert that the bits cannot be further tightened + if (U(lo) == ulo) { + bool empty = false; + assert(!adjust_bits_from_bounds(empty, zeros, ones, ulo, uhi), ""); + } else { + bool empty1 = false; + U lo1 = lo; + U hi1 = uhi; + U zeros1 = zeros; + U ones1 = ones; + adjust_bits_from_bounds(empty1, zeros1, ones1, lo1, hi1); + assert(!empty1, ""); + assert(!adjust_bounds_from_bits(empty1, lo1, hi1, zeros1, ones1), ""); + + bool empty2 = false; + U lo2 = ulo; + U hi2 = hi; + U zeros2 = zeros; + U ones2 = ones; + adjust_bits_from_bounds(empty2, zeros2, ones2, lo2, hi2); + assert(!empty2, ""); + assert(!adjust_bounds_from_bits(empty2, lo2, hi2, zeros2, ones2), ""); + + assert((zeros1 & zeros2) == zeros && (ones1 & ones2) == ones, ""); + } +} +#endif + +// The result is tuned down by one since we do not have empty type +// and this is not required to be accurate +template +static U cardinality_from_bounds(T lo, T hi, U ulo, U uhi) { + if (U(lo) == ulo) { + return uhi - ulo; + } + + return uhi - U(lo) + U(hi) - ulo + 1; } -static int normalize_int_widen( jint lo, jint hi, int w ) { +template +static int normalize_widen(T lo, T hi, U ulo, U uhi, U zeros, U ones, int w) { // Certain normalizations keep us sane when comparing types. // The 'SMALLINT' covers constants and also CC and its relatives. - if (lo <= hi) { - if (((juint)hi - lo) <= SMALLINT) w = Type::WidenMin; - if (((juint)hi - lo) >= max_juint) w = Type::WidenMax; // TypeInt::INT - } else { - if (((juint)lo - hi) <= SMALLINT) w = Type::WidenMin; - if (((juint)lo - hi) >= max_juint) w = Type::WidenMin; // dual TypeInt::INT + if (cardinality_from_bounds(lo, hi, ulo, uhi) <= SMALLINT) { + return Type::WidenMin; + } + if (lo == std::numeric_limits::min() && hi == std::numeric_limits::max() && + ulo == std::numeric_limits::min() && uhi == std::numeric_limits::max() && + zeros == 0 && ones == 0) { + // bottom type + return Type::WidenMax; } return w; } -const TypeInt *TypeInt::make( jint lo, jint hi, int w ) { - w = normalize_int_widen(lo, hi, w); - return (TypeInt*)(new TypeInt(lo,hi,w))->hashcons(); +template +static bool int_type_equal(const CT* t1, const CT* t2) { + return t1->_lo == t2->_lo && t1->_hi == t2->_hi && t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && + t1->_zeros == t2->_zeros && t1->_ones == t2->_ones; +} + +template +static bool int_type_subset(const CT* super, const CT* sub) { + return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && + (super->_zeros &~ sub->_zeros) == 0 && (super->_ones &~ sub->_ones) == 0; +} + +// Called in PhiNode::Value during CCP, monotically widen the value set, do so rigorously +// first, after WidenMax attempts, if the type has still not converged we speed up the +// convergence by abandoning the bounds +template +static const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt, const CT* bot) { + using T = std::remove_const_t; + using U = std::remove_const_t; + + if (ot == nullptr) { + return nt; + } + + // If new guy is equal to old guy, no widening + if (int_type_equal(nt, ot)) { + return ot; + } + + // If old guy contains new, then we probably widened too far & dropped to + // bottom. Return the wider fellow. + if (int_type_subset(ot, nt)) { + return ot; + } + + // Neither contains each other, weird? + // fatal("Integer value range is not subset"); + // return this; + if (!int_type_subset(nt, ot)) { + return bot; + } + + // If old guy was a constant, do not bother + if (ot->singleton()) { + return nt; + } + + // If new guy contains old, then we widened + // If new guy is already wider than old, no widening + if (nt->_widen > ot->_widen) { + return nt; + } + + if (nt->_widen < Type::WidenMax) { + // Returned widened new guy + return CT::make(nt->_lo, nt->_hi, nt->_ulo, nt->_uhi, nt->_zeros, nt->_ones, nt->_widen + 1); + } + + // Speed up the convergence by abandoning the bounds, there are only a couple of bits so + // they converge fast + T min = std::numeric_limits::min(); + T max = std::numeric_limits::max(); + U umin = std::numeric_limits::min(); + U umax = std::numeric_limits::max(); + U zeros = nt->_zeros; + U ones = nt->_ones; + if (lt != nullptr) { + min = lt->_lo; + max = lt->_hi; + umin = lt->_ulo; + umax = lt->_uhi; + zeros |= lt->_zeros; + ones |= lt->_ones; + } + return CT::make(min, max, umin, umax, zeros, ones, Type::WidenMax); +} + +// Called by PhiNode::Value during GVN, monotonically narrow the value set, only +// narrow if the bits change or if the bounds are tightened enough to avoid +// slow convergence +template +static const Type* int_type_narrow(const CT* nt, const CT* ot, const CT* bot) { + using T = decltype(CT::_lo); + using U = decltype(CT::_ulo); + + if (nt->singleton() || ot == nullptr) { + return nt; + } + + // If new guy is equal to old guy, no narrowing + if (int_type_equal(nt, ot)) { + return ot; + } + + // If old guy was maximum range, allow the narrowing + if (int_type_equal(ot, bot)) { + return nt; + } + + // Doesn't narrow; pretty weird + if (!int_type_subset(ot, nt)) { + return nt; + } + + // Bits change + if (ot->_zeros != nt->_zeros || ot->_ones != nt->_ones) { + return nt; + } + + // Only narrow if the range shrinks a lot + U oc = cardinality_from_bounds(ot->_lo, ot->_hi, ot->_ulo, ot->_uhi); + U nc = cardinality_from_bounds(nt->_lo, nt->_hi, nt->_ulo, nt->_uhi); + return (nc > (oc >> 1) + (SMALLINT * 2)) ? ot : nt; +} + +//============================================================================= +// Convenience common pre-built types. +const TypeInt* TypeInt::MAX; // INT_MAX +const TypeInt* TypeInt::MIN; // INT_MIN +const TypeInt* TypeInt::MINUS_1;// -1 +const TypeInt* TypeInt::ZERO; // 0 +const TypeInt* TypeInt::ONE; // 1 +const TypeInt* TypeInt::BOOL; // 0 or 1, FALSE or TRUE. +const TypeInt* TypeInt::CC; // -1,0 or 1, condition codes +const TypeInt* TypeInt::CC_LT; // [-1] == MINUS_1 +const TypeInt* TypeInt::CC_GT; // [1] == ONE +const TypeInt* TypeInt::CC_EQ; // [0] == ZERO +const TypeInt* TypeInt::CC_NE; +const TypeInt* TypeInt::CC_LE; // [-1,0] +const TypeInt* TypeInt::CC_GE; // [0,1] == BOOL (!) +const TypeInt* TypeInt::BYTE; // Bytes, -128 to 127 +const TypeInt* TypeInt::UBYTE; // Unsigned Bytes, 0 to 255 +const TypeInt* TypeInt::CHAR; // Java chars, 0-65535 +const TypeInt* TypeInt::SHORT; // Java shorts, -32768-32767 +const TypeInt* TypeInt::NON_ZERO; +const TypeInt* TypeInt::POS; // Positive 32-bit integers or zero +const TypeInt* TypeInt::POS1; // Positive 32-bit integers +const TypeInt* TypeInt::INT; // 32-bit integers +const TypeInt* TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] +const TypeInt* TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT + +TypeInt::TypeInt(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w, bool dual) + : TypeInteger(Int, normalize_widen(lo, hi, ulo, uhi, zeros, ones, w), dual), + _lo(lo), _hi(hi), _ulo(ulo), _uhi(uhi), _zeros(zeros), _ones(ones) { + DEBUG_ONLY(verify_constraints(lo, hi, ulo, uhi, zeros, ones)); +} + +const Type* TypeInt::make(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w, bool dual) { + bool empty = false; + normalize_constraints(empty, lo, hi, ulo, uhi, zeros, ones); + if (empty) { + return dual ? Type::BOTTOM : Type::TOP; + } + return (new TypeInt(lo, hi, ulo, uhi, zeros, ones, w, dual))->hashcons()->is_int(); +} + +const TypeInt* TypeInt::make(jint lo) { + return (new TypeInt(lo, lo, lo, lo, ~lo, lo, WidenMin, false))->hashcons()->is_int(); +} + +const Type* TypeInt::make(jint lo, jint hi, int w) { + return make(lo, hi, 0, max_juint, 0, 0, w); +} + +const Type* TypeInt::make_bits(juint zeros, juint ones, int w) { + return make(min_jint, max_jint, 0, max_juint, zeros, ones, w); +} + +const Type* TypeInt::make(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w) { + return make(lo, hi, ulo, uhi, zeros, ones, w, false); +} + +bool TypeInt::contains(jint i) const { + juint u = i; + return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && + (u & _zeros) == 0 && (~u & _ones) == 0; +} + +bool TypeInt::contains(const TypeInt* t) const { + return int_type_subset(this, t); +} + +bool TypeInt::properly_contains(const TypeInt* t) const { + return int_type_subset(this, t) && !int_type_equal(this, t); } //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type representation object // with reference count equal to the number of Types pointing at it. // Caller should wrap a Types around it. -const Type *TypeInt::xmeet( const Type *t ) const { +const Type* TypeInt::xmeet(const Type* t) const { // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type? + if (this == t) { + return this; + } // Currently "this->_base" is a TypeInt switch (t->base()) { // Switch on original type @@ -1647,127 +2018,65 @@ const Type *TypeInt::xmeet( const Type *t ) const { } // Expand covered set - const TypeInt *r = t->is_int(); - return make( MIN2(_lo,r->_lo), MAX2(_hi,r->_hi), MAX2(_widen,r->_widen) ); + const TypeInt* i = t->is_int(); + assert(_dual == i->_dual, ""); + if (!_dual) { + // meet + return make(MIN2(_lo, i->_lo), MAX2(_hi, i->_hi), MIN2(_ulo, i->_ulo), MAX2(_uhi, i->_uhi), + _zeros & i->_zeros, _ones & i->_ones, MAX2(_widen, i->_widen), false); + } + // join + return make(MAX2(_lo, i->_lo), MIN2(_hi, i->_hi), MAX2(_ulo, i->_ulo), MIN2(_uhi, i->_uhi), + _zeros | i->_zeros, _ones | i->_ones, MIN2(_widen, i->_widen), true); } -//------------------------------xdual------------------------------------------ -// Dual: reverse hi & lo; flip widen -const Type *TypeInt::xdual() const { - int w = normalize_int_widen(_hi,_lo, WidenMax-_widen); - return new TypeInt(_hi,_lo,w); +const Type* TypeInt::xdual() const { + return new TypeInt(_lo, _hi, _ulo, _uhi, _zeros, _ones, _widen, !_dual); } -//------------------------------widen------------------------------------------ -// Only happens for optimistic top-down optimizations. -const Type *TypeInt::widen( const Type *old, const Type* limit ) const { - // Coming from TOP or such; no widening - if( old->base() != Int ) return this; - const TypeInt *ot = old->is_int(); - - // If new guy is equal to old guy, no widening - if( _lo == ot->_lo && _hi == ot->_hi ) - return old; - - // If new guy contains old, then we widened - if( _lo <= ot->_lo && _hi >= ot->_hi ) { - // New contains old - // If new guy is already wider than old, no widening - if( _widen > ot->_widen ) return this; - // If old guy was a constant, do not bother - if (ot->_lo == ot->_hi) return this; - // Now widen new guy. - // Check for widening too far - if (_widen == WidenMax) { - int max = max_jint; - int min = min_jint; - if (limit->isa_int()) { - max = limit->is_int()->_hi; - min = limit->is_int()->_lo; - } - if (min < _lo && _hi < max) { - // If neither endpoint is extremal yet, push out the endpoint - // which is closer to its respective limit. - if (_lo >= 0 || // easy common case - ((juint)_lo - min) >= ((juint)max - _hi)) { - // Try to widen to an unsigned range type of 31 bits: - return make(_lo, max, WidenMax); - } else { - return make(min, _hi, WidenMax); - } - } - return TypeInt::INT; - } - // Returned widened new guy - return make(_lo,_hi,_widen+1); - } - - // If old guy contains new, then we probably widened too far & dropped to - // bottom. Return the wider fellow. - if ( ot->_lo <= _lo && ot->_hi >= _hi ) - return old; - - //fatal("Integer value range is not subset"); - //return this; - return TypeInt::INT; +const Type* TypeInt::widen(const Type* old, const Type* limit) const { + assert(!_dual, ""); + return int_type_widen(this, old->isa_int(), limit->isa_int(), TypeInt::INT); } -//------------------------------narrow--------------------------------------- -// Only happens for pessimistic optimizations. -const Type *TypeInt::narrow( const Type *old ) const { - if (_lo >= _hi) return this; // already narrow enough - if (old == nullptr) return this; - const TypeInt* ot = old->isa_int(); - if (ot == nullptr) return this; - jint olo = ot->_lo; - jint ohi = ot->_hi; - - // If new guy is equal to old guy, no narrowing - if (_lo == olo && _hi == ohi) return old; - - // If old guy was maximum range, allow the narrowing - if (olo == min_jint && ohi == max_jint) return this; - - if (_lo < olo || _hi > ohi) - return this; // doesn't narrow; pretty weird - - // The new type narrows the old type, so look for a "death march". - // See comments on PhaseTransform::saturate. - juint nrange = (juint)_hi - _lo; - juint orange = (juint)ohi - olo; - if (nrange < max_juint - 1 && nrange > (orange >> 1) + (SMALLINT*2)) { - // Use the new type only if the range shrinks a lot. - // We do not want the optimizer computing 2^31 point by point. - return old; +const Type* TypeInt::narrow(const Type* old) const { + assert(!_dual, ""); + if (old == nullptr) { + return this; } - return this; + return int_type_narrow(this, old->isa_int(), TypeInt::INT); } //-----------------------------filter------------------------------------------ -const Type *TypeInt::filter_helper(const Type *kills, bool include_speculative) const { +const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) const { + assert(!_dual, ""); const TypeInt* ft = join_helper(kills, include_speculative)->isa_int(); - if (ft == nullptr || ft->empty()) + if (ft == nullptr) { return Type::TOP; // Canonical empty value + } + assert(!ft->_dual, ""); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. - ft = TypeInt::make(ft->_lo, ft->_hi, this->_widen); + return (new TypeInt(ft->_lo, ft->_hi, ft->_ulo, ft->_uhi, + ft->_zeros, ft->_ones, this->_widen, false))->hashcons(); } return ft; } //------------------------------eq--------------------------------------------- // Structural equality check for Type representations -bool TypeInt::eq( const Type *t ) const { - const TypeInt *r = t->is_int(); // Handy access - return r->_lo == _lo && r->_hi == _hi && r->_widen == _widen; +bool TypeInt::eq(const Type* t) const { + const TypeInt* r = t->is_int(); + return int_type_equal(this, r) && _widen == r->_widen && _dual == r->_dual; } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeInt::hash(void) const { - return (uint)_lo + (uint)_hi + (uint)_widen + (uint)Type::Int; + return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + + (uint)_zeros + (uint)_ones + (uint)_widen + (uint)_dual + (uint)Type::Int; } //------------------------------is_finite-------------------------------------- @@ -1776,100 +2085,76 @@ bool TypeInt::is_finite() const { return true; } -//------------------------------dump2------------------------------------------ -// Dump TypeInt -#ifndef PRODUCT -static const char* intname(char* buf, size_t buf_size, jint n) { - if (n == min_jint) - return "min"; - else if (n < min_jint + 10000) - os::snprintf_checked(buf, buf_size, "min+" INT32_FORMAT, n - min_jint); - else if (n == max_jint) - return "max"; - else if (n > max_jint - 10000) - os::snprintf_checked(buf, buf_size, "max-" INT32_FORMAT, max_jint - n); - else - os::snprintf_checked(buf, buf_size, INT32_FORMAT, n); - return buf; -} - -void TypeInt::dump2( Dict &d, uint depth, outputStream *st ) const { - char buf[40], buf2[40]; - if (_lo == min_jint && _hi == max_jint) - st->print("int"); - else if (is_con()) - st->print("int:%s", intname(buf, sizeof(buf), get_con())); - else if (_lo == BOOL->_lo && _hi == BOOL->_hi) - st->print("bool"); - else if (_lo == BYTE->_lo && _hi == BYTE->_hi) - st->print("byte"); - else if (_lo == CHAR->_lo && _hi == CHAR->_hi) - st->print("char"); - else if (_lo == SHORT->_lo && _hi == SHORT->_hi) - st->print("short"); - else if (_hi == max_jint) - st->print("int:>=%s", intname(buf, sizeof(buf), _lo)); - else if (_lo == min_jint) - st->print("int:<=%s", intname(buf, sizeof(buf), _hi)); - else - st->print("int:%s..%s", intname(buf, sizeof(buf), _lo), intname(buf2, sizeof(buf2), _hi)); - - if (_widen != 0 && this != TypeInt::INT) - st->print(":%.*s", _widen, "wwww"); -} -#endif - //------------------------------singleton-------------------------------------- // TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple // constants. bool TypeInt::singleton(void) const { - return _lo >= _hi; + return _lo == _hi; } bool TypeInt::empty(void) const { - return _lo > _hi; + return false; } //============================================================================= // Convenience common pre-built types. -const TypeLong *TypeLong::MAX; -const TypeLong *TypeLong::MIN; -const TypeLong *TypeLong::MINUS_1;// -1 -const TypeLong *TypeLong::ZERO; // 0 -const TypeLong *TypeLong::ONE; // 1 -const TypeLong *TypeLong::POS; // >=0 -const TypeLong *TypeLong::LONG; // 64-bit integers -const TypeLong *TypeLong::INT; // 32-bit subrange -const TypeLong *TypeLong::UINT; // 32-bit unsigned subrange -const TypeLong *TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG +const TypeLong* TypeLong::MAX; +const TypeLong* TypeLong::MIN; +const TypeLong* TypeLong::MINUS_1;// -1 +const TypeLong* TypeLong::ZERO; // 0 +const TypeLong* TypeLong::ONE; // 1 +const TypeLong* TypeLong::NON_ZERO; +const TypeLong* TypeLong::POS; // >=0 +const TypeLong* TypeLong::NEG; +const TypeLong* TypeLong::LONG; // 64-bit integers +const TypeLong* TypeLong::INT; // 32-bit subrange +const TypeLong* TypeLong::UINT; // 32-bit unsigned subrange +const TypeLong* TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG -//------------------------------TypeLong--------------------------------------- -TypeLong::TypeLong(jlong lo, jlong hi, int w) : TypeInteger(Long, w), _lo(lo), _hi(hi) { +TypeLong::TypeLong(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w, bool dual) + : TypeInteger(Long, normalize_widen(lo, hi, ulo, uhi, zeros, ones, w), dual), + _lo(lo), _hi(hi), _ulo(ulo), _uhi(uhi), _zeros(zeros), _ones(ones) { + DEBUG_ONLY(verify_constraints(lo, hi, ulo, uhi, zeros, ones)); } -//------------------------------make------------------------------------------- -const TypeLong *TypeLong::make( jlong lo ) { - return (TypeLong*)(new TypeLong(lo,lo,WidenMin))->hashcons(); +const Type* TypeLong::make(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w, bool dual) { + bool empty = false; + normalize_constraints(empty, lo, hi, ulo, uhi, zeros, ones); + if (empty) { + return dual ? Type::BOTTOM : Type::TOP; + } + return (new TypeLong(lo, hi, ulo, uhi, zeros, ones, w, dual))->hashcons()->is_long(); } -static int normalize_long_widen( jlong lo, jlong hi, int w ) { - // Certain normalizations keep us sane when comparing types. - // The 'SMALLINT' covers constants. - if (lo <= hi) { - if (((julong)hi - lo) <= SMALLINT) w = Type::WidenMin; - if (((julong)hi - lo) >= max_julong) w = Type::WidenMax; // TypeLong::LONG - } else { - if (((julong)lo - hi) <= SMALLINT) w = Type::WidenMin; - if (((julong)lo - hi) >= max_julong) w = Type::WidenMin; // dual TypeLong::LONG - } - return w; +const TypeLong* TypeLong::make(jlong lo ) { + return (new TypeLong(lo, lo, lo, lo, ~lo, lo, WidenMin, false))->hashcons()->is_long(); } -const TypeLong *TypeLong::make( jlong lo, jlong hi, int w ) { - w = normalize_long_widen(lo, hi, w); - return (TypeLong*)(new TypeLong(lo,hi,w))->hashcons(); +const Type* TypeLong::make(jlong lo, jlong hi, int w) { + return make(lo, hi, 0, max_julong, 0, 0, w); } +const Type* TypeLong::make_bits(julong zeros, julong ones, int w) { + return make(min_jlong, max_jlong, 0, max_julong, zeros, ones, w); +} + +const Type* TypeLong::make(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w) { + return make(lo, hi, ulo, uhi, zeros, ones, w, false); +} + +bool TypeLong::contains(jlong i) const { + julong u = i; + return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && + (u & _zeros) == 0 && (~u & _ones) == 0; +} + +bool TypeLong::contains(const TypeLong* t) const { + return int_type_subset(this, t); +} + +bool TypeLong::properly_contains(const TypeLong* t) const { + return int_type_subset(this, t) && !int_type_equal(this, t); +} //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type representation object @@ -1910,130 +2195,65 @@ const Type *TypeLong::xmeet( const Type *t ) const { } // Expand covered set - const TypeLong *r = t->is_long(); // Turn into a TypeLong - return make( MIN2(_lo,r->_lo), MAX2(_hi,r->_hi), MAX2(_widen,r->_widen) ); + const TypeLong* i = t->is_long(); + assert(_dual == i->_dual, ""); + if (!_dual) { + // meet + return make(MIN2(_lo, i->_lo), MAX2(_hi, i->_hi), MIN2(_ulo, i->_ulo), MAX2(_uhi, i->_uhi), + _zeros & i->_zeros, _ones & i->_ones, MAX2(_widen, i->_widen), false); + } + // join + return make(MAX2(_lo, i->_lo), MIN2(_hi, i->_hi), MAX2(_ulo, i->_ulo), MIN2(_uhi, i->_uhi), + _zeros | i->_zeros, _ones | i->_ones, MIN2(_widen, i->_widen), true); } -//------------------------------xdual------------------------------------------ -// Dual: reverse hi & lo; flip widen -const Type *TypeLong::xdual() const { - int w = normalize_long_widen(_hi,_lo, WidenMax-_widen); - return new TypeLong(_hi,_lo,w); +const Type* TypeLong::xdual() const { + return new TypeLong(_lo, _hi, _ulo, _uhi, _zeros, _ones, _widen, !_dual); } -//------------------------------widen------------------------------------------ -// Only happens for optimistic top-down optimizations. -const Type *TypeLong::widen( const Type *old, const Type* limit ) const { - // Coming from TOP or such; no widening - if( old->base() != Long ) return this; - const TypeLong *ot = old->is_long(); - - // If new guy is equal to old guy, no widening - if( _lo == ot->_lo && _hi == ot->_hi ) - return old; - - // If new guy contains old, then we widened - if( _lo <= ot->_lo && _hi >= ot->_hi ) { - // New contains old - // If new guy is already wider than old, no widening - if( _widen > ot->_widen ) return this; - // If old guy was a constant, do not bother - if (ot->_lo == ot->_hi) return this; - // Now widen new guy. - // Check for widening too far - if (_widen == WidenMax) { - jlong max = max_jlong; - jlong min = min_jlong; - if (limit->isa_long()) { - max = limit->is_long()->_hi; - min = limit->is_long()->_lo; - } - if (min < _lo && _hi < max) { - // If neither endpoint is extremal yet, push out the endpoint - // which is closer to its respective limit. - if (_lo >= 0 || // easy common case - ((julong)_lo - min) >= ((julong)max - _hi)) { - // Try to widen to an unsigned range type of 32/63 bits: - if (max >= max_juint && _hi < max_juint) - return make(_lo, max_juint, WidenMax); - else - return make(_lo, max, WidenMax); - } else { - return make(min, _hi, WidenMax); - } - } - return TypeLong::LONG; - } - // Returned widened new guy - return make(_lo,_hi,_widen+1); - } - - // If old guy contains new, then we probably widened too far & dropped to - // bottom. Return the wider fellow. - if ( ot->_lo <= _lo && ot->_hi >= _hi ) - return old; - - // fatal("Long value range is not subset"); - // return this; - return TypeLong::LONG; +const Type* TypeLong::widen(const Type* old, const Type* limit) const { + assert(!_dual, ""); + return int_type_widen(this, old->isa_long(), limit->isa_long(), TypeLong::LONG); } -//------------------------------narrow---------------------------------------- -// Only happens for pessimistic optimizations. -const Type *TypeLong::narrow( const Type *old ) const { - if (_lo >= _hi) return this; // already narrow enough - if (old == nullptr) return this; - const TypeLong* ot = old->isa_long(); - if (ot == nullptr) return this; - jlong olo = ot->_lo; - jlong ohi = ot->_hi; - - // If new guy is equal to old guy, no narrowing - if (_lo == olo && _hi == ohi) return old; - - // If old guy was maximum range, allow the narrowing - if (olo == min_jlong && ohi == max_jlong) return this; - - if (_lo < olo || _hi > ohi) - return this; // doesn't narrow; pretty weird - - // The new type narrows the old type, so look for a "death march". - // See comments on PhaseTransform::saturate. - julong nrange = (julong)_hi - _lo; - julong orange = (julong)ohi - olo; - if (nrange < max_julong - 1 && nrange > (orange >> 1) + (SMALLINT*2)) { - // Use the new type only if the range shrinks a lot. - // We do not want the optimizer computing 2^31 point by point. - return old; +const Type* TypeLong::narrow(const Type* old) const { + assert(!_dual, ""); + if (old == nullptr) { + return this; } - return this; + return int_type_narrow(this, old->isa_long(), TypeLong::LONG); } //-----------------------------filter------------------------------------------ -const Type *TypeLong::filter_helper(const Type *kills, bool include_speculative) const { +const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) const { + assert(!_dual, ""); const TypeLong* ft = join_helper(kills, include_speculative)->isa_long(); - if (ft == nullptr || ft->empty()) + if (ft == nullptr) { return Type::TOP; // Canonical empty value + } + assert(!ft->_dual, ""); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. - ft = TypeLong::make(ft->_lo, ft->_hi, this->_widen); + return (new TypeLong(ft->_lo, ft->_hi, ft->_ulo, ft->_uhi, + ft->_zeros, ft->_ones, this->_widen, false))->hashcons(); } return ft; } //------------------------------eq--------------------------------------------- // Structural equality check for Type representations -bool TypeLong::eq( const Type *t ) const { - const TypeLong *r = t->is_long(); // Handy access - return r->_lo == _lo && r->_hi == _hi && r->_widen == _widen; +bool TypeLong::eq(const Type* t) const { + const TypeLong* r = t->is_long(); + return int_type_equal(this, r) && _widen == r->_widen && _dual == r->_dual; } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeLong::hash(void) const { - return (uint)_lo + (uint)_hi + (uint)_widen + (uint)Type::Long; + return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + + (uint)_zeros + (uint)_ones + (uint)_widen + (uint)_dual + (uint)Type::Long; } //------------------------------is_finite-------------------------------------- @@ -2042,71 +2262,188 @@ bool TypeLong::is_finite() const { return true; } +//------------------------------singleton-------------------------------------- +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// constants +bool TypeLong::singleton(void) const { + return _lo == _hi; +} + +bool TypeLong::empty(void) const { + return false; +} + //------------------------------dump2------------------------------------------ -// Dump TypeLong #ifndef PRODUCT -static const char* longnamenear(jlong x, const char* xname, char* buf, size_t buf_size, jlong n) { - if (n > x) { - if (n >= x + 10000) return nullptr; - os::snprintf_checked(buf, buf_size, "%s+" JLONG_FORMAT, xname, n - x); - } else if (n < x) { - if (n <= x - 10000) return nullptr; - os::snprintf_checked(buf, buf_size, "%s-" JLONG_FORMAT, xname, x - n); +template +static const char* intnamenear(T origin, const char* xname, char* buf, size_t buf_size, T n) { + if (n < origin) { + if (n <= origin - 10000) { + return nullptr; + } + os::snprintf_checked(buf, buf_size, "%s-" INT32_FORMAT, xname, jint(origin - n)); + } else if (n > origin) { + if (n >= origin + 10000) { + return nullptr; + } + os::snprintf_checked(buf, buf_size, "%s+" INT32_FORMAT, xname, jint(n - origin)); } else { return xname; } return buf; } +static const char* intname(char* buf, size_t buf_size, jint n) { + const char* str = intnamenear(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(min_jint, "minint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, INT32_FORMAT, n); + return buf; +} + +static const char* uintname(char* buf, size_t buf_size, juint n) { + const char* str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, UINT32_FORMAT"u", n); + return buf; +} + static const char* longname(char* buf, size_t buf_size, jlong n) { - const char* str; - if (n == min_jlong) - return "min"; - else if (n < min_jlong + 10000) - os::snprintf_checked(buf, buf_size, "min+" JLONG_FORMAT, n - min_jlong); - else if (n == max_jlong) - return "max"; - else if (n > max_jlong - 10000) - os::snprintf_checked(buf, buf_size, "max-" JLONG_FORMAT, max_jlong - n); - else if ((str = longnamenear(max_juint, "maxuint", buf, buf_size, n)) != nullptr) + const char* str = intnamenear(max_jlong, "maxlong", buf, buf_size, n); + if (str != nullptr) { return str; - else if ((str = longnamenear(max_jint, "maxint", buf, buf_size, n)) != nullptr) + } + + str = intnamenear(min_jlong, "minlong", buf, buf_size, n); + if (str != nullptr) { return str; - else if ((str = longnamenear(min_jint, "minint", buf, buf_size, n)) != nullptr) + } + + str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { return str; - else - os::snprintf_checked(buf, buf_size, JLONG_FORMAT, n); + } + + str = intnamenear(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(min_jint, "minint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, JLONG_FORMAT, n); return buf; } -void TypeLong::dump2( Dict &d, uint depth, outputStream *st ) const { - char buf[80], buf2[80]; - if (_lo == min_jlong && _hi == max_jlong) - st->print("long"); - else if (is_con()) - st->print("long:%s", longname(buf, sizeof(buf), get_con())); - else if (_hi == max_jlong) - st->print("long:>=%s", longname(buf, sizeof(buf), _lo)); - else if (_lo == min_jlong) - st->print("long:<=%s", longname(buf, sizeof(buf), _hi)); - else - st->print("long:%s..%s", longname(buf, sizeof(buf), _lo), longname(buf2,sizeof(buf2), _hi)); +static const char* ulongname(char* buf, size_t buf_size, julong n) { + const char* str = intnamenear(max_julong, "maxulong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_jlong, "maxlong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } - if (_widen != 0 && this != TypeLong::LONG) - st->print(":%.*s", _widen, "wwww"); + os::snprintf_checked(buf, buf_size, JULONG_FORMAT"u", n); + return buf; } -#endif -//------------------------------singleton-------------------------------------- -// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple -// constants -bool TypeLong::singleton(void) const { - return _lo >= _hi; +template +static const char* bitname(char* buf, size_t buf_size, U zeros, U ones) { + constexpr juint W = sizeof(U) * 8; + + if (buf_size < W + 1) { + return "#####"; + } + + for (juint i = 0; i < W; i++) { + U mask = U(1) << (W - 1 - i); + if ((zeros & mask) != 0) { + buf[i] = '0'; + } else if ((ones & mask) != 0) { + buf[i] = '1'; + } else { + buf[i] = '*'; + } + } + buf[W] = 0; + return buf; } -bool TypeLong::empty(void) const { - return _lo > _hi; +void TypeInt::dump2( Dict &d, uint depth, outputStream *st ) const { + char buf1[40], buf2[40], buf3[40], buf4[40], buf5[40]; + if (int_type_equal(this, TypeInt::INT)) { + st->print("int"); + } else if (is_con()) { + st->print("int:%s", intname(buf1, sizeof(buf1), get_con())); + } else if (int_type_equal(this, TypeInt::BOOL)) { + st->print("bool"); + } else if (int_type_equal(this, TypeInt::BYTE)) { + st->print("byte"); + } else if (int_type_equal(this, TypeInt::CHAR)) { + st->print("char"); + } else if (int_type_equal(this, TypeInt::SHORT)) { + st->print("short"); + } else { + st->print("int:%s..%s ^ %s..%s, bits:%s", + intname(buf1, sizeof(buf1), _lo), intname(buf2, sizeof(buf2), _hi), + uintname(buf3, sizeof(buf3), _ulo), uintname(buf4, sizeof(buf4), _uhi), + bitname(buf5, sizeof(buf5), _zeros, _ones)); + } + + if (_widen > 0 && this != TypeInt::INT) { + st->print(", widen: %d", _widen); + } +} + +void TypeLong::dump2( Dict &d, uint depth, outputStream *st ) const { + char buf1[80], buf2[80], buf3[80], buf4[80], buf5[80]; + if (int_type_equal(this, TypeLong::LONG)) { + st->print("long"); + } else if (is_con()) { + st->print("long:%s", longname(buf1, sizeof(buf1), get_con())); + } else { + st->print("long:%s..%s ^ %s..%s, bits:%s", + longname(buf1, sizeof(buf1), _lo), longname(buf2,sizeof(buf2), _hi), + ulongname(buf3, sizeof(buf3), _ulo), ulongname(buf4, sizeof(buf4), _uhi), + bitname(buf5, sizeof(buf5), _zeros, _ones)); + } + + if (_widen > 0 && this != TypeLong::LONG) { + st->print(", widen: %d", _widen); + } } +#endif //============================================================================= // Convenience common pre-built types. @@ -2329,7 +2666,7 @@ inline const TypeInt* normalize_array_size(const TypeInt* size) { // of their index types. Pick minimum wideness, since that is the // forced wideness of small ranges anyway. if (size->_widen != Type::WidenMin) - return TypeInt::make(size->_lo, size->_hi, Type::WidenMin); + return TypeInt::make(size->_lo, size->_hi, Type::WidenMin)->is_int(); else return size; } @@ -2359,10 +2696,14 @@ const Type *TypeAry::xmeet( const Type *t ) const { typerr(t); case Array: { // Meeting 2 arrays? - const TypeAry *a = t->is_ary(); + const TypeAry* a = t->is_ary(); + const Type* size = _size->xmeet(a->_size); + const TypeInt* isize = size->isa_int(); + if (isize == nullptr) { + return size; + } return TypeAry::make(_elem->meet_speculative(a->_elem), - _size->xmeet(a->_size)->is_int(), - _stable && a->_stable); + isize, _stable && a->_stable); } case Top: break; @@ -4786,11 +5127,13 @@ const TypeInt* TypeAryPtr::narrow_size_type(const TypeInt* size) const { chg = true; } // Negative length arrays will produce weird intermediate dead fast-path code - if (lo > hi) + if (lo > hi) { return TypeInt::ZERO; - if (!chg) + } + if (!chg) { return size; - return TypeInt::make(lo, hi, Type::WidenMin); + } + return TypeInt::make(lo, hi, Type::WidenMin)->is_int(); } //-------------------------------cast_to_size---------------------------------- @@ -4951,7 +5294,11 @@ const Type *TypeAryPtr::xmeet_helper(const Type *t) const { case AryPtr: { // Meeting 2 references? const TypeAryPtr *tap = t->is_aryptr(); int off = meet_offset(tap->offset()); - const TypeAry *tary = _ary->meet_speculative(tap->_ary)->is_ary(); + const Type* tm = _ary->meet_speculative(tap->_ary); + const TypeAry* tary = tm->isa_ary(); + if (tary == nullptr) { + return tm; + } PTR ptr = meet_ptr(tap->ptr()); int instance_id = meet_instance_id(tap->instance_id()); const TypePtr* speculative = xmeet_speculative(tap); diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index de9b52b4cc4bd..5268cb30ca7f3 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -551,7 +551,10 @@ class TypeD : public Type { class TypeInteger : public Type { protected: - TypeInteger(TYPES t, int w) : Type(t), _widen(w) {} + TypeInteger(TYPES t, int w, bool dual) : Type(t), _dual(dual), _widen(w) {} + + // Use to compute join of 2 sets + const bool _dual; public: const short _widen; // Limit on times we widen this sucker @@ -562,7 +565,7 @@ class TypeInteger : public Type { bool is_con() const { return lo_as_long() == hi_as_long(); } virtual short widen_limit() const { return _widen; } - static const TypeInteger* make(jlong lo, jlong hi, int w, BasicType bt); + static const Type* make(jlong lo, jlong hi, int w, BasicType bt); static const TypeInteger* bottom(BasicType type); static const TypeInteger* zero(BasicType type); @@ -576,60 +579,72 @@ class TypeInteger : public Type { // Class of integer ranges, the set of integers between a lower bound and an // upper bound, inclusive. class TypeInt : public TypeInteger { - TypeInt( jint lo, jint hi, int w ); + TypeInt(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w, bool dual); + static const Type* make(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w, bool dual); protected: - virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; + virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; public: typedef jint NativeType; - virtual bool eq( const Type *t ) const; + virtual bool eq(const Type* t) const; virtual uint hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous const jint _lo, _hi; // Lower bound, upper bound + const juint _ulo, _uhi; + const juint _zeros, _ones; - static const TypeInt *make(jint lo); + static const TypeInt* cast(const Type* t) { return t->is_int(); } + static const TypeInt* try_cast(const Type* t) { return t->isa_int(); } + static const TypeInt* make(jint lo); // must always specify w - static const TypeInt *make(jint lo, jint hi, int w); + static const Type* make(jint lo, jint hi, int w); + static const Type* make_bits(juint zeros, juint ones, int w); + static const Type* make(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w); // Check for single integer - bool is_con() const { return _lo==_hi; } + bool is_con() const { return _lo == _hi; } bool is_con(jint i) const { return is_con() && _lo == i; } - jint get_con() const { assert(is_con(), "" ); return _lo; } + jint get_con() const { assert(is_con(), ""); return _lo; } + bool contains(jint i) const; + bool contains(const TypeInt* t) const; + bool properly_contains(const TypeInt* t) const; - virtual bool is_finite() const; // Has a finite value + virtual bool is_finite() const; // Has a finite value - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. - virtual const Type *widen( const Type *t, const Type* limit_type ) const; - virtual const Type *narrow( const Type *t ) const; + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xdual() const; // Compute dual right now. + virtual const Type* widen(const Type* t, const Type* limit_type) const; + virtual const Type* narrow(const Type* t) const; virtual jlong hi_as_long() const { return _hi; } virtual jlong lo_as_long() const { return _lo; } // Do not kill _widen bits. // Convenience common pre-built types. - static const TypeInt *MAX; - static const TypeInt *MIN; - static const TypeInt *MINUS_1; - static const TypeInt *ZERO; - static const TypeInt *ONE; - static const TypeInt *BOOL; - static const TypeInt *CC; - static const TypeInt *CC_LT; // [-1] == MINUS_1 - static const TypeInt *CC_GT; // [1] == ONE - static const TypeInt *CC_EQ; // [0] == ZERO - static const TypeInt *CC_LE; // [-1,0] - static const TypeInt *CC_GE; // [0,1] == BOOL (!) - static const TypeInt *BYTE; - static const TypeInt *UBYTE; - static const TypeInt *CHAR; - static const TypeInt *SHORT; - static const TypeInt *POS; - static const TypeInt *POS1; - static const TypeInt *INT; - static const TypeInt *SYMINT; // symmetric range [-max_jint..max_jint] - static const TypeInt *TYPE_DOMAIN; // alias for TypeInt::INT + static const TypeInt* MAX; + static const TypeInt* MIN; + static const TypeInt* MINUS_1; + static const TypeInt* ZERO; + static const TypeInt* ONE; + static const TypeInt* BOOL; + static const TypeInt* CC; + static const TypeInt* CC_LT; // [-1] == MINUS_1 + static const TypeInt* CC_GT; // [1] == ONE + static const TypeInt* CC_EQ; // [0] == ZERO + static const TypeInt* CC_NE; // [-1, 1] + static const TypeInt* CC_LE; // [-1,0] + static const TypeInt* CC_GE; // [0,1] == BOOL (!) + static const TypeInt* BYTE; + static const TypeInt* UBYTE; + static const TypeInt* CHAR; + static const TypeInt* SHORT; + static const TypeInt* NON_ZERO; + static const TypeInt* POS; + static const TypeInt* POS1; + static const TypeInt* INT; + static const TypeInt* SYMINT; // symmetric range [-max_jint..max_jint] + static const TypeInt* TYPE_DOMAIN; // alias for TypeInt::INT static const TypeInt *as_self(const Type *t) { return t->is_int(); } #ifndef PRODUCT @@ -642,10 +657,11 @@ class TypeInt : public TypeInteger { // Class of long integer ranges, the set of integers between a lower bound and // an upper bound, inclusive. class TypeLong : public TypeInteger { - TypeLong( jlong lo, jlong hi, int w ); + TypeLong(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w, bool dual); + static const Type* make(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w, bool dual); protected: // Do not kill _widen bits. - virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; + virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; public: typedef jlong NativeType; virtual bool eq( const Type *t ) const; @@ -654,15 +670,24 @@ class TypeLong : public TypeInteger { virtual bool empty(void) const; // TRUE if type is vacuous public: const jlong _lo, _hi; // Lower bound, upper bound + const julong _ulo, _uhi; + const julong _zeros, _ones; - static const TypeLong *make(jlong lo); + static const TypeLong* cast(const Type* t) { return t->is_long(); } + static const TypeLong* try_cast(const Type* t) { return t->isa_long(); } + static const TypeLong* make(jlong lo); // must always specify w - static const TypeLong *make(jlong lo, jlong hi, int w); + static const Type* make(jlong lo, jlong hi, int w); + static const Type* make_bits(julong zeros, julong ones, int w); + static const Type* make(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w); // Check for single integer - bool is_con() const { return _lo==_hi; } + bool is_con() const { return _lo == _hi; } bool is_con(jlong i) const { return is_con() && _lo == i; } jlong get_con() const { assert(is_con(), "" ); return _lo; } + bool contains(jlong i) const; + bool contains(const TypeLong* t) const; + bool properly_contains(const TypeLong* t) const; // Check for positive 32-bit value. int is_positive_int() const { return _lo >= 0 && _hi <= (jlong)max_jint; } @@ -672,21 +697,23 @@ class TypeLong : public TypeInteger { virtual jlong hi_as_long() const { return _hi; } virtual jlong lo_as_long() const { return _lo; } - virtual const Type *xmeet( const Type *t ) const; - virtual const Type *xdual() const; // Compute dual right now. - virtual const Type *widen( const Type *t, const Type* limit_type ) const; - virtual const Type *narrow( const Type *t ) const; + virtual const Type* xmeet(const Type* t) const; + virtual const Type* xdual() const; // Compute dual right now. + virtual const Type* widen(const Type* t, const Type* limit_type) const; + virtual const Type* narrow(const Type* t) const; // Convenience common pre-built types. - static const TypeLong *MAX; - static const TypeLong *MIN; - static const TypeLong *MINUS_1; - static const TypeLong *ZERO; - static const TypeLong *ONE; - static const TypeLong *POS; - static const TypeLong *LONG; - static const TypeLong *INT; // 32-bit subrange [min_jint..max_jint] - static const TypeLong *UINT; // 32-bit unsigned [0..max_juint] - static const TypeLong *TYPE_DOMAIN; // alias for TypeLong::LONG + static const TypeLong* MAX; + static const TypeLong* MIN; + static const TypeLong* MINUS_1; + static const TypeLong* ZERO; + static const TypeLong* ONE; + static const TypeLong* NON_ZERO; + static const TypeLong* POS; + static const TypeLong* NEG; + static const TypeLong* LONG; + static const TypeLong* INT; // 32-bit subrange [min_jint..max_jint] + static const TypeLong* UINT; // 32-bit unsigned [0..max_juint] + static const TypeLong* TYPE_DOMAIN; // alias for TypeLong::LONG // static convenience methods. static const TypeLong *as_self(const Type *t) { return t->is_long(); } From 61f4d216ce48e6b951e6dc82f159ce06fe49b8dc Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sat, 20 Jan 2024 23:33:55 +0800 Subject: [PATCH 02/45] refactor --- src/hotspot/share/opto/compile.hpp | 4 +- src/hotspot/share/opto/rangeinference.cpp | 536 ++++++++++++++++++++ src/hotspot/share/opto/rangeinference.hpp | 84 ++++ src/hotspot/share/opto/type.cpp | 583 +--------------------- src/hotspot/share/opto/type.hpp | 1 + 5 files changed, 631 insertions(+), 577 deletions(-) create mode 100644 src/hotspot/share/opto/rangeinference.cpp create mode 100644 src/hotspot/share/opto/rangeinference.hpp diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 42f866df781c2..16d1b5eef1e34 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -907,8 +907,8 @@ class Compile : public Phase { bool copy_node_notes_to(Node* dest, Node* source); // Workhorse function to sort out the blocked Node_Notes array: - inline Node_Notes* locate_node_notes(GrowableArray* arr, - int idx, bool can_grow = false); + Node_Notes* locate_node_notes(GrowableArray* arr, + int idx, bool can_grow = false); void grow_node_notes(GrowableArray* arr, int grow_by); diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp new file mode 100644 index 0000000000000..eead31cfd79f2 --- /dev/null +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2024, 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. + * + */ + +#include "precompiled.hpp" +#include "opto/rangeinference.hpp" + +constexpr juint SMALLINT = 3; // a value too insignificant to consider widening + +template +static bool adjust_bounds_from_bits(bool& empty, T& lo, T& hi, T zeros, T ones) { + static_assert(std::is_unsigned::value, ""); + + auto adjust_lo = [](T lo, T zeros, T ones) { + constexpr size_t W = sizeof(T) * 8; + T zero_violation = lo & zeros; + T one_violation = ~lo & ones; + if (zero_violation == 0 && one_violation == 0) { + return lo; + } + + if (zero_violation < one_violation) { + // Align the last violation of ones unset all the lower bits + // so we don't care about violations of zeros + juint last_violation = W - 1 - count_leading_zeros(one_violation); + T alignment = T(1) << last_violation; + lo = (lo & -alignment) + alignment; + return lo | ones; + } + + // Suppose lo = 00110010, zeros = 01010010, ones = 10001000 + // Since the 4-th bit must be 0, we need to align up the lower bound. + // This results in lo = 01000000, but then the 6-th bit does not match, + // align up again gives us 10000000. + // We can align up directly to 10000000 by finding the first place after + // the highest mismatch such that both the corresponding bits are unset. + // Since all bits lower than the alignment are unset we don't need to + // align for the violations of ones anymore. + juint last_violation = W - 1 - count_leading_zeros(zero_violation); + T find_mask = std::numeric_limits::max() << last_violation; + T either = lo | zeros; + T tmp = ~either & find_mask; + T alignment = tmp & (-tmp); + lo = (lo & -alignment) + alignment; + return lo | ones; + }; + + T new_lo = adjust_lo(lo, zeros, ones); + if (new_lo < lo) { + empty = true; + return true; + } + + T new_hi = ~adjust_lo(~hi, ones, zeros); + if (new_hi > hi) { + empty = true; + return true; + } + bool progress = (new_lo != lo) || (new_hi != hi); + lo = new_lo; + hi = new_hi; + empty = lo > hi; + return progress; +} + +template +static bool adjust_bits_from_bounds(bool& empty, T& zeros, T& ones, T lo, T hi) { + static_assert(std::is_unsigned::value, ""); + T mismatch = lo ^ hi; + T match_mask = mismatch == 0 ? std::numeric_limits::max() + : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); + T new_zeros = zeros | (match_mask &~ lo); + T new_ones = ones | (match_mask & lo); + bool progress = (new_zeros != zeros) || (new_ones != ones); + zeros = new_zeros; + ones = new_ones; + empty = ((zeros & ones) != 0); + return progress; +} + +template +static void normalize_constraints_simple(bool& empty, T& lo, T& hi, T& zeros, T& ones) { + adjust_bits_from_bounds(empty, zeros, ones, lo, hi); + if (empty) { + return; + } + while (true) { + bool progress = adjust_bounds_from_bits(empty, lo, hi, zeros, ones); + if (!progress || empty) { + return; + } + progress = adjust_bits_from_bounds(empty, zeros, ones, lo, hi); + if (!progress || empty) { + return; + } + } +} + +template +void normalize_constraints(bool& empty, T& lo, T& hi, U& ulo, U& uhi, U& zeros, U& ones) { + static_assert(std::is_signed::value, ""); + static_assert(std::is_unsigned::value, ""); + static_assert(sizeof(T) == sizeof(U), ""); + + if (lo > hi || ulo > uhi || (zeros & ones) != 0) { + empty = true; + return; + } + + if (T(ulo) > T(uhi)) { + if (T(uhi) < lo) { + uhi = std::numeric_limits::max(); + } else if (T(ulo) > hi) { + ulo = std::numeric_limits::min(); + } + } + + if (T(ulo) <= T(uhi)) { + ulo = MAX2(ulo, lo); + uhi = MIN2(uhi, hi); + if (ulo > uhi) { + empty = true; + return; + } + + normalize_constraints_simple(empty, ulo, uhi, zeros, ones); + lo = ulo; + hi = uhi; + return; + } + + bool empty1 = false; + U lo1 = lo; + U hi1 = uhi; + U zeros1 = zeros; + U ones1 = ones; + normalize_constraints_simple(empty1, lo1, hi1, zeros1, ones1); + + bool empty2 = false; + U lo2 = ulo; + U hi2 = hi; + U zeros2 = zeros; + U ones2 = ones; + normalize_constraints_simple(empty2, lo2, hi2, zeros2, ones2); + + if (empty1 & empty2) { + empty = true; + } else if (empty1) { + lo = lo2; + hi = hi2; + ulo = lo2; + uhi = hi2; + zeros = zeros2; + ones = ones2; + } else if (empty2) { + lo = lo1; + hi = hi1; + ulo = lo1; + uhi = hi1; + zeros = zeros1; + ones = ones1; + } else { + lo = lo1; + hi = hi2; + ulo = lo2; + uhi = hi1; + zeros = zeros1 & zeros2; + ones = ones1 & ones2; + } +} +template void normalize_constraints(bool& empty, jint& lo, jint& hi, juint& ulo, juint& uhi, juint& zeros, juint& ones); +template void normalize_constraints(bool& empty, jlong& lo, jlong& hi, julong& ulo, julong& uhi, julong& zeros, julong& ones); + +template +void verify_constraints(T lo, T hi, U ulo, U uhi, U zeros, U ones) { + static_assert(std::is_signed::value, ""); + static_assert(std::is_unsigned::value, ""); + static_assert(sizeof(T) == sizeof(U), ""); + + // Assert that the bounds cannot be further tightened + assert(lo <= hi && U(lo) >= ulo && U(lo) <= uhi && (lo & zeros) == 0 && (~lo & ones) == 0, ""); + assert(hi >= lo && U(hi) >= ulo && U(hi) <= uhi && (hi & zeros) == 0 && (~hi & ones) == 0, ""); + assert(T(ulo) >= lo && T(ulo) <= hi && ulo <= uhi && (ulo & zeros) == 0 && (~ulo & ones) == 0, ""); + assert(T(uhi) >= lo && T(uhi) <= hi && uhi >= ulo && (uhi & zeros) == 0 && (~uhi & ones) == 0, ""); + + // Assert that the bits cannot be further tightened + if (U(lo) == ulo) { + bool empty = false; + assert(!adjust_bits_from_bounds(empty, zeros, ones, ulo, uhi), ""); + } else { + bool empty1 = false; + U lo1 = lo; + U hi1 = uhi; + U zeros1 = zeros; + U ones1 = ones; + adjust_bits_from_bounds(empty1, zeros1, ones1, lo1, hi1); + assert(!empty1, ""); + assert(!adjust_bounds_from_bits(empty1, lo1, hi1, zeros1, ones1), ""); + + bool empty2 = false; + U lo2 = ulo; + U hi2 = hi; + U zeros2 = zeros; + U ones2 = ones; + adjust_bits_from_bounds(empty2, zeros2, ones2, lo2, hi2); + assert(!empty2, ""); + assert(!adjust_bounds_from_bits(empty2, lo2, hi2, zeros2, ones2), ""); + + assert((zeros1 & zeros2) == zeros && (ones1 & ones2) == ones, ""); + } +} +template void verify_constraints(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones); +template void verify_constraints(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones); + +template +int normalize_widen(T lo, T hi, U ulo, U uhi, U zeros, U ones, int w) { + // Certain normalizations keep us sane when comparing types. + // The 'SMALLINT' covers constants and also CC and its relatives. + if (cardinality_from_bounds(lo, hi, ulo, uhi) <= SMALLINT) { + return Type::WidenMin; + } + if (lo == std::numeric_limits::min() && hi == std::numeric_limits::max() && + ulo == std::numeric_limits::min() && uhi == std::numeric_limits::max() && + zeros == 0 && ones == 0) { + // bottom type + return Type::WidenMax; + } + return w; +} +template int normalize_widen(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w); +template int normalize_widen(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w); + +template +const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(T, T, UT, UT, UT, UT, int, bool), bool dual) { + // Perform a fast test for common case; meeting the same types together. + if (i1 == t2 || t2 == Type::TOP) { + return i1; + } + const CT* i2 = CT::try_cast(t2); + if (i2 != nullptr) { + if (!dual) { + // meet + return make(MIN2(i1->_lo, i2->_lo), MAX2(i1->_hi, i2->_hi), MIN2(i1->_ulo, i2->_ulo), MAX2(i1->_uhi, i2->_uhi), + i1->_zeros & i2->_zeros, i1->_ones & i2->_ones, MAX2(i1->_widen, i2->_widen), false); + } + // join + return make(MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi), MAX2(i1->_ulo, i2->_ulo), MIN2(i1->_uhi, i2->_uhi), + i1->_zeros | i2->_zeros, i1->_ones | i2->_ones, MIN2(i1->_widen, i2->_widen), true); + } + + assert(t2->base() != i1->base(), ""); + switch (t2->base()) { // Switch on original type + case Type::AnyPtr: // Mixing with oops happens when javac + case Type::RawPtr: // reuses local variables + case Type::OopPtr: + case Type::InstPtr: + case Type::AryPtr: + case Type::MetadataPtr: + case Type::KlassPtr: + case Type::InstKlassPtr: + case Type::AryKlassPtr: + case Type::NarrowOop: + case Type::NarrowKlass: + case Type::Int: + case Type::Long: + case Type::FloatTop: + case Type::FloatCon: + case Type::FloatBot: + case Type::DoubleTop: + case Type::DoubleCon: + case Type::DoubleBot: + case Type::Bottom: // Ye Olde Default + return Type::BOTTOM; + default: // All else is a mistake + i1->typerr(t2); + return nullptr; + } +} +template const Type* int_type_xmeet(const TypeInt* i1, const Type* t2, + const Type* (*make)(jint, jint, juint, juint, juint, juint, int, bool), bool dual); +template const Type* int_type_xmeet(const TypeLong* i1, const Type* t2, + const Type* (*make)(jlong, jlong, julong, julong, julong, julong, int, bool), bool dual); + +// Called in PhiNode::Value during CCP, monotically widen the value set, do so rigorously +// first, after WidenMax attempts, if the type has still not converged we speed up the +// convergence by abandoning the bounds +template +const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { + using T = std::remove_const_t; + using U = std::remove_const_t; + + if (ot == nullptr) { + return nt; + } + + // If new guy is equal to old guy, no widening + if (int_type_equal(nt, ot)) { + return ot; + } + + // If old guy contains new, then we probably widened too far & dropped to + // bottom. Return the wider fellow. + if (int_type_subset(ot, nt)) { + return ot; + } + + // Neither contains each other, weird? + // fatal("Integer value range is not subset"); + // return this; + if (!int_type_subset(nt, ot)) { + return CT::TYPE_DOMAIN; + } + + // If old guy was a constant, do not bother + if (ot->singleton()) { + return nt; + } + + // If new guy contains old, then we widened + // If new guy is already wider than old, no widening + if (nt->_widen > ot->_widen) { + return nt; + } + + if (nt->_widen < Type::WidenMax) { + // Returned widened new guy + return CT::make(nt->_lo, nt->_hi, nt->_ulo, nt->_uhi, nt->_zeros, nt->_ones, nt->_widen + 1); + } + + // Speed up the convergence by abandoning the bounds, there are only a couple of bits so + // they converge fast + T min = std::numeric_limits::min(); + T max = std::numeric_limits::max(); + U umin = std::numeric_limits::min(); + U umax = std::numeric_limits::max(); + U zeros = nt->_zeros; + U ones = nt->_ones; + if (lt != nullptr) { + min = lt->_lo; + max = lt->_hi; + umin = lt->_ulo; + umax = lt->_uhi; + zeros |= lt->_zeros; + ones |= lt->_ones; + } + return CT::make(min, max, umin, umax, zeros, ones, Type::WidenMax); +} +template const Type* int_type_widen(const TypeInt* nt, const TypeInt* ot, const TypeInt* lt); +template const Type* int_type_widen(const TypeLong* nt, const TypeLong* ot, const TypeLong* lt); + +// Called by PhiNode::Value during GVN, monotonically narrow the value set, only +// narrow if the bits change or if the bounds are tightened enough to avoid +// slow convergence +template +const Type* int_type_narrow(const CT* nt, const CT* ot) { + using T = decltype(CT::_lo); + using U = decltype(CT::_ulo); + + if (nt->singleton() || ot == nullptr) { + return nt; + } + + // If new guy is equal to old guy, no narrowing + if (int_type_equal(nt, ot)) { + return ot; + } + + // If old guy was maximum range, allow the narrowing + if (int_type_equal(ot, CT::TYPE_DOMAIN)) { + return nt; + } + + // Doesn't narrow; pretty weird + if (!int_type_subset(ot, nt)) { + return nt; + } + + // Bits change + if (ot->_zeros != nt->_zeros || ot->_ones != nt->_ones) { + return nt; + } + + // Only narrow if the range shrinks a lot + U oc = cardinality_from_bounds(ot->_lo, ot->_hi, ot->_ulo, ot->_uhi); + U nc = cardinality_from_bounds(nt->_lo, nt->_hi, nt->_ulo, nt->_uhi); + return (nc > (oc >> 1) + (SMALLINT * 2)) ? ot : nt; +} +template const Type* int_type_narrow(const TypeInt* nt, const TypeInt* ot); +template const Type* int_type_narrow(const TypeLong* nt, const TypeLong* ot); + +template +static const char* intnamenear(T origin, const char* xname, char* buf, size_t buf_size, T n) { + if (n < origin) { + if (n <= origin - 10000) { + return nullptr; + } + os::snprintf_checked(buf, buf_size, "%s-" INT32_FORMAT, xname, jint(origin - n)); + } else if (n > origin) { + if (n >= origin + 10000) { + return nullptr; + } + os::snprintf_checked(buf, buf_size, "%s+" INT32_FORMAT, xname, jint(n - origin)); + } else { + return xname; + } + return buf; +} + +const char* intname(char* buf, size_t buf_size, jint n) { + const char* str = intnamenear(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(min_jint, "minint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, INT32_FORMAT, n); + return buf; +} + +const char* uintname(char* buf, size_t buf_size, juint n) { + const char* str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, UINT32_FORMAT"u", n); + return buf; +} + +const char* longname(char* buf, size_t buf_size, jlong n) { + const char* str = intnamenear(max_jlong, "maxlong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(min_jlong, "minlong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(min_jint, "minint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, JLONG_FORMAT, n); + return buf; +} + +const char* ulongname(char* buf, size_t buf_size, julong n) { + const char* str = intnamenear(max_julong, "maxulong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_jlong, "maxlong", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + str = intnamenear(max_jint, "maxint", buf, buf_size, n); + if (str != nullptr) { + return str; + } + + os::snprintf_checked(buf, buf_size, JULONG_FORMAT"u", n); + return buf; +} + +template +const char* bitname(char* buf, size_t buf_size, U zeros, U ones) { + constexpr juint W = sizeof(U) * 8; + + if (buf_size < W + 1) { + return "#####"; + } + + for (juint i = 0; i < W; i++) { + U mask = U(1) << (W - 1 - i); + if ((zeros & mask) != 0) { + buf[i] = '0'; + } else if ((ones & mask) != 0) { + buf[i] = '1'; + } else { + buf[i] = '*'; + } + } + buf[W] = 0; + return buf; +} +template const char* bitname(char* buf, size_t buf_size, juint zeros, juint ones); +template const char* bitname(char* buf, size_t buf_size, julong zeros, julong ones); diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp new file mode 100644 index 0000000000000..46e748e319cea --- /dev/null +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, 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. + * + */ + +#ifndef SHARE_OPTO_RANGEINFERENCE_HPP +#define SHARE_OPTO_RANGEINFERENCE_HPP + +#include "opto/type.hpp" +#include "runtime/os.hpp" + +template +void normalize_constraints(bool& empty, T& lo, T& hi, U& ulo, U& uhi, U& zeros, U& ones); + +#ifdef ASSERT +template +void verify_constraints(T lo, T hi, U ulo, U uhi, U zeros, U ones); +#endif + +// The result is tuned down by one since we do not have empty type +// and this is not required to be accurate +template +U cardinality_from_bounds(T lo, T hi, U ulo, U uhi) { + if (U(lo) == ulo) { + return uhi - ulo; + } + + return uhi - U(lo) + U(hi) - ulo + 1; +} + +template +int normalize_widen(T lo, T hi, U ulo, U uhi, U zeros, U ones, int w); + +template +const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(T, T, UT, UT, UT, UT, int, bool), bool dual); + +template +bool int_type_equal(const CT* t1, const CT* t2) { + return t1->_lo == t2->_lo && t1->_hi == t2->_hi && t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && + t1->_zeros == t2->_zeros && t1->_ones == t2->_ones; +} + +template +bool int_type_subset(const CT* super, const CT* sub) { + return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && + (super->_zeros &~ sub->_zeros) == 0 && (super->_ones &~ sub->_ones) == 0; +} + +template +const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt); + +template +const Type* int_type_narrow(const CT* nt, const CT* ot); + +#ifndef PRODUCT +const char* intname(char* buf, size_t buf_size, jint n); +const char* uintname(char* buf, size_t buf_size, juint n); +const char* longname(char* buf, size_t buf_size, jlong n); +const char* ulongname(char* buf, size_t buf_size, julong n); + +template +const char* bitname(char* buf, size_t buf_size, U zeros, U ones); +#endif // PRODUCT + +#endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 648217257a592..03bc52acb3ce4 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -38,6 +38,7 @@ #include "opto/matcher.hpp" #include "opto/node.hpp" #include "opto/opcodes.hpp" +#include "opto/rangeinference.hpp" #include "opto/type.hpp" #include "utilities/checkedCast.hpp" #include "utilities/powerOfTwo.hpp" @@ -418,7 +419,6 @@ int Type::uhash( const Type *const t ) { return (int)t->hash(); } -constexpr juint SMALLINT = 3; // a value too insignificant to consider widening #define POSITIVE_INFINITE_F 0x7f800000 // hex representation for IEEE 754 single precision positive infinite #define POSITIVE_INFINITE_D 0x7ff0000000000000 // hex representation for IEEE 754 double precision positive infinite @@ -493,7 +493,6 @@ void Type::Initialize_shared(Compile* current) { assert( TypeInt::CC_GT == TypeInt::ONE, "types must match for CmpL to work" ); assert( TypeInt::CC_EQ == TypeInt::ZERO, "types must match for CmpL to work" ); assert( TypeInt::CC_GE == TypeInt::BOOL, "types must match for CmpL to work" ); - assert( (juint)(TypeInt::CC->_hi - TypeInt::CC->_lo) <= SMALLINT, "CC is truly small"); TypeLong::MAX = TypeLong::make(max_jlong); // Long MAX TypeLong::MIN = TypeLong::make(min_jlong); // Long MIN @@ -1561,351 +1560,6 @@ const TypeInteger* TypeInteger::minus_1(BasicType bt) { return TypeLong::MINUS_1; } -template -static bool adjust_bounds_from_bits(bool& empty, T& lo, T& hi, T zeros, T ones) { - static_assert(std::is_unsigned::value, ""); - - auto adjust_lo = [](T lo, T zeros, T ones) { - constexpr size_t W = sizeof(T) * 8; - T zero_violation = lo & zeros; - T one_violation = ~lo & ones; - if (zero_violation == 0 && one_violation == 0) { - return lo; - } - - if (zero_violation < one_violation) { - // Align the last violation of ones unset all the lower bits - // so we don't care about violations of zeros - juint last_violation = W - 1 - count_leading_zeros(one_violation); - T alignment = T(1) << last_violation; - lo = (lo & -alignment) + alignment; - return lo | ones; - } - - // Suppose lo = 00110010, zeros = 01010010, ones = 10001000 - // Since the 4-th bit must be 0, we need to align up the lower bound. - // This results in lo = 01000000, but then the 6-th bit does not match, - // align up again gives us 10000000. - // We can align up directly to 10000000 by finding the first place after - // the highest mismatch such that both the corresponding bits are unset. - // Since all bits lower than the alignment are unset we don't need to - // align for the violations of ones anymore. - juint last_violation = W - 1 - count_leading_zeros(zero_violation); - T find_mask = std::numeric_limits::max() << last_violation; - T either = lo | zeros; - T tmp = ~either & find_mask; - T alignment = tmp & (-tmp); - lo = (lo & -alignment) + alignment; - return lo | ones; - }; - - T new_lo = adjust_lo(lo, zeros, ones); - if (new_lo < lo) { - empty = true; - return true; - } - - T new_hi = ~adjust_lo(~hi, ones, zeros); - if (new_hi > hi) { - empty = true; - return true; - } - bool progress = (new_lo != lo) || (new_hi != hi); - lo = new_lo; - hi = new_hi; - empty = lo > hi; - return progress; -} - -template -static bool adjust_bits_from_bounds(bool& empty, T& zeros, T& ones, T lo, T hi) { - static_assert(std::is_unsigned::value, ""); - T mismatch = lo ^ hi; - T match_mask = mismatch == 0 ? std::numeric_limits::max() - : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); - T new_zeros = zeros | (match_mask &~ lo); - T new_ones = ones | (match_mask & lo); - bool progress = (new_zeros != zeros) || (new_ones != ones); - zeros = new_zeros; - ones = new_ones; - empty = ((zeros & ones) != 0); - return progress; -} - -template -static void normalize_constraints_simple(bool& empty, T& lo, T& hi, T& zeros, T& ones) { - adjust_bits_from_bounds(empty, zeros, ones, lo, hi); - if (empty) { - return; - } - while (true) { - bool progress = adjust_bounds_from_bits(empty, lo, hi, zeros, ones); - if (!progress || empty) { - return; - } - progress = adjust_bits_from_bounds(empty, zeros, ones, lo, hi); - if (!progress || empty) { - return; - } - } -} - -template -static void normalize_constraints(bool& empty, T& lo, T& hi, U& ulo, U& uhi, U& zeros, U& ones) { - static_assert(std::is_signed::value, ""); - static_assert(std::is_unsigned::value, ""); - static_assert(sizeof(T) == sizeof(U), ""); - - if (lo > hi || ulo > uhi || (zeros & ones) != 0) { - empty = true; - return; - } - - if (T(ulo) > T(uhi)) { - if (T(uhi) < lo) { - uhi = std::numeric_limits::max(); - } else if (T(ulo) > hi) { - ulo = std::numeric_limits::min(); - } - } - - if (T(ulo) <= T(uhi)) { - ulo = MAX2(ulo, lo); - uhi = MIN2(uhi, hi); - if (ulo > uhi) { - empty = true; - return; - } - - normalize_constraints_simple(empty, ulo, uhi, zeros, ones); - lo = ulo; - hi = uhi; - return; - } - - bool empty1 = false; - U lo1 = lo; - U hi1 = uhi; - U zeros1 = zeros; - U ones1 = ones; - normalize_constraints_simple(empty1, lo1, hi1, zeros1, ones1); - - bool empty2 = false; - U lo2 = ulo; - U hi2 = hi; - U zeros2 = zeros; - U ones2 = ones; - normalize_constraints_simple(empty2, lo2, hi2, zeros2, ones2); - - if (empty1 & empty2) { - empty = true; - } else if (empty1) { - lo = lo2; - hi = hi2; - ulo = lo2; - uhi = hi2; - zeros = zeros2; - ones = ones2; - } else if (empty2) { - lo = lo1; - hi = hi1; - ulo = lo1; - uhi = hi1; - zeros = zeros1; - ones = ones1; - } else { - lo = lo1; - hi = hi2; - ulo = lo2; - uhi = hi1; - zeros = zeros1 & zeros2; - ones = ones1 & ones2; - } -} - -#ifdef ASSERT -template -static void verify_constraints(T lo, T hi, U ulo, U uhi, U zeros, U ones) { - static_assert(std::is_signed::value, ""); - static_assert(std::is_unsigned::value, ""); - static_assert(sizeof(T) == sizeof(U), ""); - - // Assert that the bounds cannot be further tightened - assert(lo <= hi && U(lo) >= ulo && U(lo) <= uhi && (lo & zeros) == 0 && (~lo & ones) == 0, ""); - assert(hi >= lo && U(hi) >= ulo && U(hi) <= uhi && (hi & zeros) == 0 && (~hi & ones) == 0, ""); - assert(T(ulo) >= lo && T(ulo) <= hi && ulo <= uhi && (ulo & zeros) == 0 && (~ulo & ones) == 0, ""); - assert(T(uhi) >= lo && T(uhi) <= hi && uhi >= ulo && (uhi & zeros) == 0 && (~uhi & ones) == 0, ""); - - // Assert that the bits cannot be further tightened - if (U(lo) == ulo) { - bool empty = false; - assert(!adjust_bits_from_bounds(empty, zeros, ones, ulo, uhi), ""); - } else { - bool empty1 = false; - U lo1 = lo; - U hi1 = uhi; - U zeros1 = zeros; - U ones1 = ones; - adjust_bits_from_bounds(empty1, zeros1, ones1, lo1, hi1); - assert(!empty1, ""); - assert(!adjust_bounds_from_bits(empty1, lo1, hi1, zeros1, ones1), ""); - - bool empty2 = false; - U lo2 = ulo; - U hi2 = hi; - U zeros2 = zeros; - U ones2 = ones; - adjust_bits_from_bounds(empty2, zeros2, ones2, lo2, hi2); - assert(!empty2, ""); - assert(!adjust_bounds_from_bits(empty2, lo2, hi2, zeros2, ones2), ""); - - assert((zeros1 & zeros2) == zeros && (ones1 & ones2) == ones, ""); - } -} -#endif - -// The result is tuned down by one since we do not have empty type -// and this is not required to be accurate -template -static U cardinality_from_bounds(T lo, T hi, U ulo, U uhi) { - if (U(lo) == ulo) { - return uhi - ulo; - } - - return uhi - U(lo) + U(hi) - ulo + 1; -} - -template -static int normalize_widen(T lo, T hi, U ulo, U uhi, U zeros, U ones, int w) { - // Certain normalizations keep us sane when comparing types. - // The 'SMALLINT' covers constants and also CC and its relatives. - if (cardinality_from_bounds(lo, hi, ulo, uhi) <= SMALLINT) { - return Type::WidenMin; - } - if (lo == std::numeric_limits::min() && hi == std::numeric_limits::max() && - ulo == std::numeric_limits::min() && uhi == std::numeric_limits::max() && - zeros == 0 && ones == 0) { - // bottom type - return Type::WidenMax; - } - return w; -} - -template -static bool int_type_equal(const CT* t1, const CT* t2) { - return t1->_lo == t2->_lo && t1->_hi == t2->_hi && t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && - t1->_zeros == t2->_zeros && t1->_ones == t2->_ones; -} - -template -static bool int_type_subset(const CT* super, const CT* sub) { - return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && - (super->_zeros &~ sub->_zeros) == 0 && (super->_ones &~ sub->_ones) == 0; -} - -// Called in PhiNode::Value during CCP, monotically widen the value set, do so rigorously -// first, after WidenMax attempts, if the type has still not converged we speed up the -// convergence by abandoning the bounds -template -static const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt, const CT* bot) { - using T = std::remove_const_t; - using U = std::remove_const_t; - - if (ot == nullptr) { - return nt; - } - - // If new guy is equal to old guy, no widening - if (int_type_equal(nt, ot)) { - return ot; - } - - // If old guy contains new, then we probably widened too far & dropped to - // bottom. Return the wider fellow. - if (int_type_subset(ot, nt)) { - return ot; - } - - // Neither contains each other, weird? - // fatal("Integer value range is not subset"); - // return this; - if (!int_type_subset(nt, ot)) { - return bot; - } - - // If old guy was a constant, do not bother - if (ot->singleton()) { - return nt; - } - - // If new guy contains old, then we widened - // If new guy is already wider than old, no widening - if (nt->_widen > ot->_widen) { - return nt; - } - - if (nt->_widen < Type::WidenMax) { - // Returned widened new guy - return CT::make(nt->_lo, nt->_hi, nt->_ulo, nt->_uhi, nt->_zeros, nt->_ones, nt->_widen + 1); - } - - // Speed up the convergence by abandoning the bounds, there are only a couple of bits so - // they converge fast - T min = std::numeric_limits::min(); - T max = std::numeric_limits::max(); - U umin = std::numeric_limits::min(); - U umax = std::numeric_limits::max(); - U zeros = nt->_zeros; - U ones = nt->_ones; - if (lt != nullptr) { - min = lt->_lo; - max = lt->_hi; - umin = lt->_ulo; - umax = lt->_uhi; - zeros |= lt->_zeros; - ones |= lt->_ones; - } - return CT::make(min, max, umin, umax, zeros, ones, Type::WidenMax); -} - -// Called by PhiNode::Value during GVN, monotonically narrow the value set, only -// narrow if the bits change or if the bounds are tightened enough to avoid -// slow convergence -template -static const Type* int_type_narrow(const CT* nt, const CT* ot, const CT* bot) { - using T = decltype(CT::_lo); - using U = decltype(CT::_ulo); - - if (nt->singleton() || ot == nullptr) { - return nt; - } - - // If new guy is equal to old guy, no narrowing - if (int_type_equal(nt, ot)) { - return ot; - } - - // If old guy was maximum range, allow the narrowing - if (int_type_equal(ot, bot)) { - return nt; - } - - // Doesn't narrow; pretty weird - if (!int_type_subset(ot, nt)) { - return nt; - } - - // Bits change - if (ot->_zeros != nt->_zeros || ot->_ones != nt->_ones) { - return nt; - } - - // Only narrow if the range shrinks a lot - U oc = cardinality_from_bounds(ot->_lo, ot->_hi, ot->_ulo, ot->_uhi); - U nc = cardinality_from_bounds(nt->_lo, nt->_hi, nt->_ulo, nt->_uhi); - return (nc > (oc >> 1) + (SMALLINT * 2)) ? ot : nt; -} - //============================================================================= // Convenience common pre-built types. const TypeInt* TypeInt::MAX; // INT_MAX @@ -1977,57 +1631,8 @@ bool TypeInt::properly_contains(const TypeInt* t) const { return int_type_subset(this, t) && !int_type_equal(this, t); } -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type representation object -// with reference count equal to the number of Types pointing at it. -// Caller should wrap a Types around it. const Type* TypeInt::xmeet(const Type* t) const { - // Perform a fast test for common case; meeting the same types together. - if (this == t) { - return this; - } - - // Currently "this->_base" is a TypeInt - switch (t->base()) { // Switch on original type - case AnyPtr: // Mixing with oops happens when javac - case RawPtr: // reuses local variables - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case NarrowOop: - case NarrowKlass: - case Long: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - default: // All else is a mistake - typerr(t); - case Top: // No change - return this; - case Int: // Int vs Int? - break; - } - - // Expand covered set - const TypeInt* i = t->is_int(); - assert(_dual == i->_dual, ""); - if (!_dual) { - // meet - return make(MIN2(_lo, i->_lo), MAX2(_hi, i->_hi), MIN2(_ulo, i->_ulo), MAX2(_uhi, i->_uhi), - _zeros & i->_zeros, _ones & i->_ones, MAX2(_widen, i->_widen), false); - } - // join - return make(MAX2(_lo, i->_lo), MIN2(_hi, i->_hi), MAX2(_ulo, i->_ulo), MIN2(_uhi, i->_uhi), - _zeros | i->_zeros, _ones | i->_ones, MIN2(_widen, i->_widen), true); + return int_type_xmeet(this, t, TypeInt::make, _dual); } const Type* TypeInt::xdual() const { @@ -2036,7 +1641,7 @@ const Type* TypeInt::xdual() const { const Type* TypeInt::widen(const Type* old, const Type* limit) const { assert(!_dual, ""); - return int_type_widen(this, old->isa_int(), limit->isa_int(), TypeInt::INT); + return int_type_widen(this, old->isa_int(), limit->isa_int()); } const Type* TypeInt::narrow(const Type* old) const { @@ -2045,7 +1650,7 @@ const Type* TypeInt::narrow(const Type* old) const { return this; } - return int_type_narrow(this, old->isa_int(), TypeInt::INT); + return int_type_narrow(this, old->isa_int()); } //-----------------------------filter------------------------------------------ @@ -2156,55 +1761,8 @@ bool TypeLong::properly_contains(const TypeLong* t) const { return int_type_subset(this, t) && !int_type_equal(this, t); } -//------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type representation object -// with reference count equal to the number of Types pointing at it. -// Caller should wrap a Types around it. -const Type *TypeLong::xmeet( const Type *t ) const { - // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type? - - // Currently "this->_base" is a TypeLong - switch (t->base()) { // Switch on original type - case AnyPtr: // Mixing with oops happens when javac - case RawPtr: // reuses local variables - case OopPtr: - case InstPtr: - case AryPtr: - case MetadataPtr: - case KlassPtr: - case InstKlassPtr: - case AryKlassPtr: - case NarrowOop: - case NarrowKlass: - case Int: - case FloatTop: - case FloatCon: - case FloatBot: - case DoubleTop: - case DoubleCon: - case DoubleBot: - case Bottom: // Ye Olde Default - return Type::BOTTOM; - default: // All else is a mistake - typerr(t); - case Top: // No change - return this; - case Long: // Long vs Long? - break; - } - - // Expand covered set - const TypeLong* i = t->is_long(); - assert(_dual == i->_dual, ""); - if (!_dual) { - // meet - return make(MIN2(_lo, i->_lo), MAX2(_hi, i->_hi), MIN2(_ulo, i->_ulo), MAX2(_uhi, i->_uhi), - _zeros & i->_zeros, _ones & i->_ones, MAX2(_widen, i->_widen), false); - } - // join - return make(MAX2(_lo, i->_lo), MIN2(_hi, i->_hi), MAX2(_ulo, i->_ulo), MIN2(_uhi, i->_uhi), - _zeros | i->_zeros, _ones | i->_ones, MIN2(_widen, i->_widen), true); +const Type *TypeLong::xmeet(const Type* t) const { + return int_type_xmeet(this, t, TypeLong::make, _dual); } const Type* TypeLong::xdual() const { @@ -2213,7 +1771,7 @@ const Type* TypeLong::xdual() const { const Type* TypeLong::widen(const Type* old, const Type* limit) const { assert(!_dual, ""); - return int_type_widen(this, old->isa_long(), limit->isa_long(), TypeLong::LONG); + return int_type_widen(this, old->isa_long(), limit->isa_long()); } const Type* TypeLong::narrow(const Type* old) const { @@ -2222,7 +1780,7 @@ const Type* TypeLong::narrow(const Type* old) const { return this; } - return int_type_narrow(this, old->isa_long(), TypeLong::LONG); + return int_type_narrow(this, old->isa_long()); } //-----------------------------filter------------------------------------------ @@ -2275,131 +1833,6 @@ bool TypeLong::empty(void) const { //------------------------------dump2------------------------------------------ #ifndef PRODUCT -template -static const char* intnamenear(T origin, const char* xname, char* buf, size_t buf_size, T n) { - if (n < origin) { - if (n <= origin - 10000) { - return nullptr; - } - os::snprintf_checked(buf, buf_size, "%s-" INT32_FORMAT, xname, jint(origin - n)); - } else if (n > origin) { - if (n >= origin + 10000) { - return nullptr; - } - os::snprintf_checked(buf, buf_size, "%s+" INT32_FORMAT, xname, jint(n - origin)); - } else { - return xname; - } - return buf; -} - -static const char* intname(char* buf, size_t buf_size, jint n) { - const char* str = intnamenear(max_jint, "maxint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(min_jint, "minint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - os::snprintf_checked(buf, buf_size, INT32_FORMAT, n); - return buf; -} - -static const char* uintname(char* buf, size_t buf_size, juint n) { - const char* str = intnamenear(max_juint, "maxuint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(max_jint, "maxint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - os::snprintf_checked(buf, buf_size, UINT32_FORMAT"u", n); - return buf; -} - -static const char* longname(char* buf, size_t buf_size, jlong n) { - const char* str = intnamenear(max_jlong, "maxlong", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(min_jlong, "minlong", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(max_juint, "maxuint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(max_jint, "maxint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(min_jint, "minint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - os::snprintf_checked(buf, buf_size, JLONG_FORMAT, n); - return buf; -} - -static const char* ulongname(char* buf, size_t buf_size, julong n) { - const char* str = intnamenear(max_julong, "maxulong", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(max_jlong, "maxlong", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(max_juint, "maxuint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - str = intnamenear(max_jint, "maxint", buf, buf_size, n); - if (str != nullptr) { - return str; - } - - os::snprintf_checked(buf, buf_size, JULONG_FORMAT"u", n); - return buf; -} - -template -static const char* bitname(char* buf, size_t buf_size, U zeros, U ones) { - constexpr juint W = sizeof(U) * 8; - - if (buf_size < W + 1) { - return "#####"; - } - - for (juint i = 0; i < W; i++) { - U mask = U(1) << (W - 1 - i); - if ((zeros & mask) != 0) { - buf[i] = '0'; - } else if ((ones & mask) != 0) { - buf[i] = '1'; - } else { - buf[i] = '*'; - } - } - buf[W] = 0; - return buf; -} - void TypeInt::dump2( Dict &d, uint depth, outputStream *st ) const { char buf1[40], buf2[40], buf3[40], buf4[40], buf5[40]; if (int_type_equal(this, TypeInt::INT)) { diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 5268cb30ca7f3..083ee7727daf4 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -26,6 +26,7 @@ #define SHARE_OPTO_TYPE_HPP #include "opto/adlcVMDeps.hpp" +#include "opto/compile.hpp" #include "runtime/handles.hpp" // Portions of code courtesy of Clifford Click From d11497eb4ee2bf887f5229ddd992fdc1f957e059 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sun, 21 Jan 2024 00:43:13 +0800 Subject: [PATCH 03/45] fix template parameter --- src/hotspot/share/opto/rangeinference.cpp | 2 +- src/hotspot/share/opto/rangeinference.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index eead31cfd79f2..00a3d113787c5 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -250,7 +250,7 @@ int normalize_widen(T lo, T hi, U ulo, U uhi, U zeros, U ones, int w) { template int normalize_widen(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w); template int normalize_widen(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w); -template +template const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(T, T, UT, UT, UT, UT, int, bool), bool dual) { // Perform a fast test for common case; meeting the same types together. if (i1 == t2 || t2 == Type::TOP) { diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 46e748e319cea..4eb44c40ccab3 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -50,7 +50,7 @@ U cardinality_from_bounds(T lo, T hi, U ulo, U uhi) { template int normalize_widen(T lo, T hi, U ulo, U uhi, U zeros, U ones, int w); -template +template const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(T, T, UT, UT, UT, UT, int, bool), bool dual); template From 12f268a1fd16f09208ef2189876ef2440fe02366 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sun, 21 Jan 2024 03:14:12 +0800 Subject: [PATCH 04/45] add unit tests --- .../gtest/opto/test_rangeinference.cpp | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 test/hotspot/gtest/opto/test_rangeinference.cpp diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp new file mode 100644 index 0000000000000..17005bddae657 --- /dev/null +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2024, 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. + * + */ + +#include "precompiled.hpp" +#include "opto/rangeinference.hpp" +#include "runtime/os.hpp" +#include "unittest.hpp" + +#include + +template +static U uniform_random(); + +template <> +juint uniform_random() { + return os::random(); +} + +template <> +julong uniform_random() { + return (julong(os::random()) << 32) | julong(juint(os::random())); +} + +template +static void test_normalize_constraints_simple() { + constexpr int parameters = 10; + for (int i = 0; i < parameters; i++) { + T a = uniform_random(); + T b = uniform_random(); + + { + T lo = MIN2(a, b); + T hi = MAX2(a, b); + T nlo = lo; + T nhi = hi; + U nulo = std::numeric_limits::min(); + U nuhi = std::numeric_limits::max(); + U nzeros = 0; + U nones = 0; + bool empty = false; + normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); + ASSERT_FALSE(empty); + ASSERT_EQ(lo, nlo); + ASSERT_EQ(hi, nhi); + if (U(lo) <= U(hi)) { + ASSERT_EQ(U(lo), nulo); + ASSERT_EQ(U(hi), nuhi); + } else { + ASSERT_EQ(std::numeric_limits::min(), nulo); + ASSERT_EQ(std::numeric_limits::max(), nuhi); + } + } + + { + U ulo = MIN2(a, b); + U uhi = MAX2(a, b); + T nlo = std::numeric_limits::min(); + T nhi = std::numeric_limits::max(); + U nulo = ulo; + U nuhi = uhi; + U nzeros = 0; + U nones = 0; + bool empty = false; + normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); + ASSERT_FALSE(empty); + ASSERT_EQ(ulo, nulo); + ASSERT_EQ(uhi, nuhi); + if (T(ulo) <= T(uhi)) { + ASSERT_EQ(T(ulo), nlo); + ASSERT_EQ(T(uhi), nhi); + } else { + ASSERT_EQ(std::numeric_limits::min(), nlo); + ASSERT_EQ(std::numeric_limits::max(), nhi); + } + } + + { + U intersection = a & b; + U zeros = a ^ intersection; + U ones = b ^ intersection; + T nlo = std::numeric_limits::min(); + T nhi = std::numeric_limits::max(); + U nulo = std::numeric_limits::min(); + U nuhi = std::numeric_limits::max(); + U nzeros = zeros; + U nones = ones; + bool empty = false; + normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); + ASSERT_FALSE(empty); + ASSERT_EQ(zeros, nzeros); + ASSERT_EQ(ones, nones); + ASSERT_EQ(ones, nulo); + ASSERT_EQ(~zeros, nuhi); + } + } +} + +template +static void test_normalize_constraints_random() { + constexpr int samples = 1000; + constexpr int parameters = 1000; + int non_empty = 0; + for (int i = 0; i < parameters; i++) { + T s1 = uniform_random(); + T s2 = uniform_random(); + T lo = MIN2(s1, s2); + T hi = MAX2(s1, s2); + U u1 = uniform_random(); + U u2 = uniform_random(); + U ulo = MIN2(u1, u2); + U uhi = MAX2(u1, u2); + U b1 = uniform_random(); + U b2 = uniform_random(); + U intersection = b1 & b2; + U zeros = b1 ^ intersection; + U ones = b2 ^ intersection; + T ns1 = s1; + T ns2 = s2; + U nu1 = u1; + U nu2 = u2; + U nzeros = zeros; + U nones = ones; + bool empty = false; + normalize_constraints(empty, ns1, ns2, nu1, nu2, nzeros, nones); + auto contains = [](T lo, T hi, U ulo, U uhi, U zeros, U ones, T value) { + U u = value; + return value >= lo && value <= hi && u >= ulo && u <= uhi && + (u & zeros) == 0 && (~u & ones) == 0; + }; + if (!empty) { + non_empty++; + } + for (int j = 0; j < samples; j++) { + T v = uniform_random(); + if (empty) { + ASSERT_FALSE(contains(s1, s2, u1, u2, zeros, ones, v)); + } else { + ASSERT_EQ(contains(s1, s2, u1, u2, zeros, ones, v), contains(ns1, ns2, nu1, nu2, nzeros, nones, v)); + } + } + } +} + +TEST_VM(opto, normalize_constraints) { + test_normalize_constraints_simple(); + test_normalize_constraints_simple(); + test_normalize_constraints_random(); + test_normalize_constraints_random(); +} From 6b417f94eda967f9a4eaf7d127bb41ff580e8be6 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sun, 21 Jan 2024 03:37:45 +0800 Subject: [PATCH 05/45] fix tests, add verify --- src/hotspot/share/opto/rangeinference.cpp | 2 +- .../gtest/opto/test_rangeinference.cpp | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 00a3d113787c5..f6473205881c8 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -163,7 +163,7 @@ void normalize_constraints(bool& empty, T& lo, T& hi, U& ulo, U& uhi, U& zeros, U ones2 = ones; normalize_constraints_simple(empty2, lo2, hi2, zeros2, ones2); - if (empty1 & empty2) { + if (empty1 && empty2) { empty = true; } else if (empty1) { lo = lo2; diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 17005bddae657..2b45fd133a125 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -60,6 +60,7 @@ static void test_normalize_constraints_simple() { U nones = 0; bool empty = false; normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); + DEBUG_ONLY(verify_constraints(nlo, nhi, nulo, nuhi, nzeros, nones)); ASSERT_FALSE(empty); ASSERT_EQ(lo, nlo); ASSERT_EQ(hi, nhi); @@ -83,6 +84,7 @@ static void test_normalize_constraints_simple() { U nones = 0; bool empty = false; normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); + DEBUG_ONLY(verify_constraints(nlo, nhi, nulo, nuhi, nzeros, nones)); ASSERT_FALSE(empty); ASSERT_EQ(ulo, nulo); ASSERT_EQ(uhi, nuhi); @@ -107,6 +109,7 @@ static void test_normalize_constraints_simple() { U nones = ones; bool empty = false; normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); + DEBUG_ONLY(verify_constraints(nlo, nhi, nulo, nuhi, nzeros, nones)); ASSERT_FALSE(empty); ASSERT_EQ(zeros, nzeros); ASSERT_EQ(ones, nones); @@ -135,28 +138,30 @@ static void test_normalize_constraints_random() { U intersection = b1 & b2; U zeros = b1 ^ intersection; U ones = b2 ^ intersection; - T ns1 = s1; - T ns2 = s2; - U nu1 = u1; - U nu2 = u2; + T nlo = lo; + T nhi = hi; + U nulo = ulo; + U nuhi = uhi; U nzeros = zeros; U nones = ones; bool empty = false; - normalize_constraints(empty, ns1, ns2, nu1, nu2, nzeros, nones); + normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); auto contains = [](T lo, T hi, U ulo, U uhi, U zeros, U ones, T value) { U u = value; return value >= lo && value <= hi && u >= ulo && u <= uhi && (u & zeros) == 0 && (~u & ones) == 0; }; +#ifdef ASSERT if (!empty) { - non_empty++; + verify_constraints(nlo, nhi, nulo, nuhi, nzeros, nones); } +#endif // ASSERT for (int j = 0; j < samples; j++) { T v = uniform_random(); if (empty) { - ASSERT_FALSE(contains(s1, s2, u1, u2, zeros, ones, v)); + ASSERT_FALSE(contains(lo, hi, ulo, uhi, zeros, ones, v)); } else { - ASSERT_EQ(contains(s1, s2, u1, u2, zeros, ones, v), contains(ns1, ns2, nu1, nu2, nzeros, nones, v)); + ASSERT_EQ(contains(lo, hi, ulo, uhi, zeros, ones, v), contains(nlo, nhi, nulo, nuhi, nzeros, nones, v)); } } } From 756d615920b1cfcf9eedd80dc1c5dca7b48080f3 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Tue, 23 Jan 2024 02:27:10 +0800 Subject: [PATCH 06/45] add comments, group arguments to reduce C-style reference passing arguments --- src/hotspot/share/opto/rangeinference.cpp | 339 ++++++++++-------- src/hotspot/share/opto/rangeinference.hpp | 47 ++- src/hotspot/share/opto/type.cpp | 88 ++--- src/hotspot/share/opto/type.hpp | 36 +- .../gtest/opto/test_rangeinference.cpp | 104 ++---- 5 files changed, 318 insertions(+), 296 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index f6473205881c8..aa7795898a18b 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -24,18 +24,40 @@ #include "precompiled.hpp" #include "opto/rangeinference.hpp" +#include "opto/type.hpp" +#include "utilities/tuple.hpp" -constexpr juint SMALLINT = 3; // a value too insignificant to consider widening +constexpr juint SMALLINT = 3; // a value too insignificant to consider widening template -static bool adjust_bounds_from_bits(bool& empty, T& lo, T& hi, T zeros, T ones) { +class AdjustResult { +public: + bool _progress; + bool _present; + T _data; +}; + +template +class NormalizeSimpleResult { +public: + bool _present; + RangeInt _bounds; + KnownBits _bits; +}; + +// Try to tighten the bound constraints from the known bit information +// E.g: if lo = 0 but the lowest bit is always 1 then we can tighten +// lo = 1 +template +static AdjustResult> +adjust_bounds_from_bits(const RangeInt& bounds, const KnownBits& bits) { static_assert(std::is_unsigned::value, ""); - auto adjust_lo = [](T lo, T zeros, T ones) { + auto adjust_lo = [](T lo, const KnownBits& bits) { constexpr size_t W = sizeof(T) * 8; - T zero_violation = lo & zeros; - T one_violation = ~lo & ones; - if (zero_violation == 0 && one_violation == 0) { + T zero_violation = lo & bits._zeros; + T one_violation = ~lo & bits._ones; + if (zero_violation == one_violation) { return lo; } @@ -45,7 +67,7 @@ static bool adjust_bounds_from_bits(bool& empty, T& lo, T& hi, T zeros, T ones) juint last_violation = W - 1 - count_leading_zeros(one_violation); T alignment = T(1) << last_violation; lo = (lo & -alignment) + alignment; - return lo | ones; + return lo | bits._ones; } // Suppose lo = 00110010, zeros = 01010010, ones = 10001000 @@ -58,200 +80,190 @@ static bool adjust_bounds_from_bits(bool& empty, T& lo, T& hi, T zeros, T ones) // align for the violations of ones anymore. juint last_violation = W - 1 - count_leading_zeros(zero_violation); T find_mask = std::numeric_limits::max() << last_violation; - T either = lo | zeros; + T either = lo | bits._zeros; T tmp = ~either & find_mask; T alignment = tmp & (-tmp); lo = (lo & -alignment) + alignment; - return lo | ones; + return lo | bits._ones; }; - T new_lo = adjust_lo(lo, zeros, ones); - if (new_lo < lo) { - empty = true; - return true; + T new_lo = adjust_lo(bounds._lo, bits); + if (new_lo < bounds._lo) { + return {true, false, {}}; } - - T new_hi = ~adjust_lo(~hi, ones, zeros); - if (new_hi > hi) { - empty = true; - return true; + T new_hi = ~adjust_lo(~bounds._hi, {bits._ones, bits._zeros}); + if (new_hi > bounds._hi) { + return {true, false, {}}; } - bool progress = (new_lo != lo) || (new_hi != hi); - lo = new_lo; - hi = new_hi; - empty = lo > hi; - return progress; + + bool progress = (new_lo != bounds._lo) || (new_hi != bounds._hi); + bool present = new_lo <= new_hi; + return {progress, present, {new_lo, new_hi}}; } +// Try to tighten the known bit constraints from the bound information +// E.g: if lo = 0 and hi = 10, then all but the lowest 4 bits must be 0 template -static bool adjust_bits_from_bounds(bool& empty, T& zeros, T& ones, T lo, T hi) { +static AdjustResult> +adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { static_assert(std::is_unsigned::value, ""); - T mismatch = lo ^ hi; + T mismatch = bounds._lo ^ bounds._hi; T match_mask = mismatch == 0 ? std::numeric_limits::max() : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); - T new_zeros = zeros | (match_mask &~ lo); - T new_ones = ones | (match_mask & lo); - bool progress = (new_zeros != zeros) || (new_ones != ones); - zeros = new_zeros; - ones = new_ones; - empty = ((zeros & ones) != 0); - return progress; + T new_zeros = bits._zeros | (match_mask &~ bounds._lo); + T new_ones = bits._ones | (match_mask & bounds._lo); + bool progress = (new_zeros != bits._zeros) || (new_ones != bits._ones); + bool present = ((new_zeros & new_ones) == 0); + return {progress, present, {new_zeros, new_ones}}; } +// Try to tighten both the bounds and the bits at the same time +// Iteratively tighten 1 using the other until no progress is made. +// This function converges because bit constraints converge fast. template -static void normalize_constraints_simple(bool& empty, T& lo, T& hi, T& zeros, T& ones) { - adjust_bits_from_bounds(empty, zeros, ones, lo, hi); - if (empty) { - return; +static NormalizeSimpleResult +normalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { + AdjustResult> nbits = adjust_bits_from_bounds(bits, bounds); + if (!nbits._present) { + return {false, {}, {}}; } + AdjustResult> nbounds{true, true, bounds}; while (true) { - bool progress = adjust_bounds_from_bits(empty, lo, hi, zeros, ones); - if (!progress || empty) { - return; + nbounds = adjust_bounds_from_bits(nbounds._data, nbits._data); + if (!nbounds._progress || !nbounds._present) { + return {nbounds._present, nbounds._data, nbits._data}; } - progress = adjust_bits_from_bounds(empty, zeros, ones, lo, hi); - if (!progress || empty) { - return; + nbits = adjust_bits_from_bounds(nbits._data, nbounds._data); + if (!nbits._progress || !nbits._present) { + return {nbits._present, nbounds._data, nbits._data}; } } } +// Tighten all constraints of a TypeIntPrototype to its canonical form. +// i.e the result represents the same set as the input, each bound belongs to +// the set and for each bit position that is not constrained, there exists 2 +// values with the bit value at that position being set and unset, respectively, +// such that both belong to the set represented by the constraints. template -void normalize_constraints(bool& empty, T& lo, T& hi, U& ulo, U& uhi, U& zeros, U& ones) { +Pair> +TypeIntPrototype::normalize_constraints() const { static_assert(std::is_signed::value, ""); static_assert(std::is_unsigned::value, ""); static_assert(sizeof(T) == sizeof(U), ""); - if (lo > hi || ulo > uhi || (zeros & ones) != 0) { - empty = true; - return; + RangeInt srange = _srange; + RangeInt urange = _urange; + if (srange._lo > srange._hi || + urange._lo > urange._hi || + (_bits._zeros & _bits._ones) != 0) { + return {false, {}}; } - if (T(ulo) > T(uhi)) { - if (T(uhi) < lo) { - uhi = std::numeric_limits::max(); - } else if (T(ulo) > hi) { - ulo = std::numeric_limits::min(); + if (T(urange._lo) > T(urange._hi)) { + if (T(urange._hi) < srange._lo) { + urange._hi = std::numeric_limits::max(); + } else if (T(urange._lo) > srange._hi) { + urange._lo = std::numeric_limits::min(); } } - if (T(ulo) <= T(uhi)) { - ulo = MAX2(ulo, lo); - uhi = MIN2(uhi, hi); - if (ulo > uhi) { - empty = true; - return; + if (T(urange._lo) <= T(urange._hi)) { + // [lo, hi] and [ulo, uhi] represent the same range + urange._lo = MAX2(urange._lo, srange._lo); + urange._hi = MIN2(urange._hi, srange._hi); + if (urange._lo > urange._hi) { + return {false, {}}; } - normalize_constraints_simple(empty, ulo, uhi, zeros, ones); - lo = ulo; - hi = uhi; - return; - } - - bool empty1 = false; - U lo1 = lo; - U hi1 = uhi; - U zeros1 = zeros; - U ones1 = ones; - normalize_constraints_simple(empty1, lo1, hi1, zeros1, ones1); - - bool empty2 = false; - U lo2 = ulo; - U hi2 = hi; - U zeros2 = zeros; - U ones2 = ones; - normalize_constraints_simple(empty2, lo2, hi2, zeros2, ones2); - - if (empty1 && empty2) { - empty = true; - } else if (empty1) { - lo = lo2; - hi = hi2; - ulo = lo2; - uhi = hi2; - zeros = zeros2; - ones = ones2; - } else if (empty2) { - lo = lo1; - hi = hi1; - ulo = lo1; - uhi = hi1; - zeros = zeros1; - ones = ones1; + auto type = normalize_constraints_simple(urange, _bits); + return {type._present, {{T(type._bounds._lo), T(type._bounds._hi)}, + type._bounds, type._bits}}; + } + + // [lo, hi] intersects with [ulo, uhi] in 2 ranges: + // [lo, uhi], which consists of negative values + // [ulo, hi] which consists of non-negative values + // We process these 2 separately and combine the results + auto neg_type = normalize_constraints_simple({U(srange._lo), urange._hi}, _bits); + auto pos_type = normalize_constraints_simple({urange._lo, U(srange._hi)}, _bits); + + if (!neg_type._present && !pos_type._present) { + return {false, {}}; + } else if (!neg_type._present) { + return {true, {{T(pos_type._bounds._lo), T(pos_type._bounds._hi)}, + pos_type._bounds, pos_type._bits}}; + } else if (!pos_type._present) { + return {true, {{T(neg_type._bounds._lo), T(neg_type._bounds._hi)}, + neg_type._bounds, neg_type._bits}}; } else { - lo = lo1; - hi = hi2; - ulo = lo2; - uhi = hi1; - zeros = zeros1 & zeros2; - ones = ones1 & ones2; + return {true, {{T(neg_type._bounds._lo), T(pos_type._bounds._hi)}, + {pos_type._bounds._lo, neg_type._bounds._hi}, + {neg_type._bits._zeros & pos_type._bits._zeros, neg_type._bits._ones & pos_type._bits._ones}}}; + } +} + +template +int TypeIntPrototype::normalize_widen(int w) const { + // Certain normalizations keep us sane when comparing types. + // The 'SMALLINT' covers constants and also CC and its relatives. + if (cardinality_from_bounds(_srange, _urange) <= SMALLINT) { + return Type::WidenMin; } + if (_srange._lo == std::numeric_limits::min() && _srange._hi == std::numeric_limits::max() && + _urange._lo == std::numeric_limits::min() && _urange._hi == std::numeric_limits::max() && + _bits._zeros == 0 && _bits._ones == 0) { + // bottom type + return Type::WidenMax; + } + return w; } -template void normalize_constraints(bool& empty, jint& lo, jint& hi, juint& ulo, juint& uhi, juint& zeros, juint& ones); -template void normalize_constraints(bool& empty, jlong& lo, jlong& hi, julong& ulo, julong& uhi, julong& zeros, julong& ones); +#ifdef ASSERT template -void verify_constraints(T lo, T hi, U ulo, U uhi, U zeros, U ones) { +bool TypeIntPrototype::contains(T v) const { + return v >= _srange._lo && v <= _srange._hi && U(v) >= _urange._lo && U(v) <= _urange._hi && + (v & _bits._zeros) == 0 && (~v & _bits._ones) == 0; +} + +// Verify that this set representation is canonical +template +void TypeIntPrototype::verify_constraints() const { static_assert(std::is_signed::value, ""); static_assert(std::is_unsigned::value, ""); static_assert(sizeof(T) == sizeof(U), ""); // Assert that the bounds cannot be further tightened - assert(lo <= hi && U(lo) >= ulo && U(lo) <= uhi && (lo & zeros) == 0 && (~lo & ones) == 0, ""); - assert(hi >= lo && U(hi) >= ulo && U(hi) <= uhi && (hi & zeros) == 0 && (~hi & ones) == 0, ""); - assert(T(ulo) >= lo && T(ulo) <= hi && ulo <= uhi && (ulo & zeros) == 0 && (~ulo & ones) == 0, ""); - assert(T(uhi) >= lo && T(uhi) <= hi && uhi >= ulo && (uhi & zeros) == 0 && (~uhi & ones) == 0, ""); + assert(contains(_srange._lo) && contains(_srange._hi) && + contains(_urange._lo) && contains(_urange._hi), ""); // Assert that the bits cannot be further tightened - if (U(lo) == ulo) { - bool empty = false; - assert(!adjust_bits_from_bounds(empty, zeros, ones, ulo, uhi), ""); + if (U(_srange._lo) == _urange._lo) { + assert(!adjust_bits_from_bounds(_bits, _urange)._progress, ""); } else { - bool empty1 = false; - U lo1 = lo; - U hi1 = uhi; - U zeros1 = zeros; - U ones1 = ones; - adjust_bits_from_bounds(empty1, zeros1, ones1, lo1, hi1); - assert(!empty1, ""); - assert(!adjust_bounds_from_bits(empty1, lo1, hi1, zeros1, ones1), ""); - - bool empty2 = false; - U lo2 = ulo; - U hi2 = hi; - U zeros2 = zeros; - U ones2 = ones; - adjust_bits_from_bounds(empty2, zeros2, ones2, lo2, hi2); - assert(!empty2, ""); - assert(!adjust_bounds_from_bits(empty2, lo2, hi2, zeros2, ones2), ""); - - assert((zeros1 & zeros2) == zeros && (ones1 & ones2) == ones, ""); - } -} -template void verify_constraints(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones); -template void verify_constraints(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones); + RangeInt neg_range{U(_srange._lo), _urange._hi}; + auto neg_bits = adjust_bits_from_bounds(_bits, neg_range); + assert(neg_bits._present, ""); + assert(!adjust_bounds_from_bits(neg_range, neg_bits._data)._progress, ""); -template -int normalize_widen(T lo, T hi, U ulo, U uhi, U zeros, U ones, int w) { - // Certain normalizations keep us sane when comparing types. - // The 'SMALLINT' covers constants and also CC and its relatives. - if (cardinality_from_bounds(lo, hi, ulo, uhi) <= SMALLINT) { - return Type::WidenMin; - } - if (lo == std::numeric_limits::min() && hi == std::numeric_limits::max() && - ulo == std::numeric_limits::min() && uhi == std::numeric_limits::max() && - zeros == 0 && ones == 0) { - // bottom type - return Type::WidenMax; + RangeInt pos_range{_urange._lo, U(_srange._hi)}; + auto pos_bits = adjust_bits_from_bounds(_bits, pos_range); + assert(pos_bits._present, ""); + assert(!adjust_bounds_from_bits(pos_range, pos_bits._data)._progress, ""); + + assert((neg_bits._data._zeros & pos_bits._data._zeros) == _bits._zeros && + (neg_bits._data._ones & pos_bits._data._ones) == _bits._ones, ""); } - return w; } -template int normalize_widen(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w); -template int normalize_widen(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w); +#endif // ASSERT +template class TypeIntPrototype; +template class TypeIntPrototype; + +// Compute the meet of 2 types, when dual is true, we are actually computing the +// join. template -const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(T, T, UT, UT, UT, UT, int, bool), bool dual) { +const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual) { // Perform a fast test for common case; meeting the same types together. if (i1 == t2 || t2 == Type::TOP) { return i1; @@ -260,12 +272,16 @@ const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(T, if (i2 != nullptr) { if (!dual) { // meet - return make(MIN2(i1->_lo, i2->_lo), MAX2(i1->_hi, i2->_hi), MIN2(i1->_ulo, i2->_ulo), MAX2(i1->_uhi, i2->_uhi), - i1->_zeros & i2->_zeros, i1->_ones & i2->_ones, MAX2(i1->_widen, i2->_widen), false); + return make(TypeIntPrototype{{MIN2(i1->_lo, i2->_lo), MAX2(i1->_hi, i2->_hi)}, + {MIN2(i1->_ulo, i2->_ulo), MAX2(i1->_uhi, i2->_uhi)}, + {i1->_zeros & i2->_zeros, i1->_ones & i2->_ones}}, + MAX2(i1->_widen, i2->_widen), false); } // join - return make(MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi), MAX2(i1->_ulo, i2->_ulo), MIN2(i1->_uhi, i2->_uhi), - i1->_zeros | i2->_zeros, i1->_ones | i2->_ones, MIN2(i1->_widen, i2->_widen), true); + return make(TypeIntPrototype{{MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi)}, + {MAX2(i1->_ulo, i2->_ulo), MIN2(i1->_uhi, i2->_uhi)}, + {i1->_zeros | i2->_zeros, i1->_ones | i2->_ones}}, + MIN2(i1->_widen, i2->_widen), true); } assert(t2->base() != i1->base(), ""); @@ -297,9 +313,9 @@ const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(T, } } template const Type* int_type_xmeet(const TypeInt* i1, const Type* t2, - const Type* (*make)(jint, jint, juint, juint, juint, juint, int, bool), bool dual); + const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); template const Type* int_type_xmeet(const TypeLong* i1, const Type* t2, - const Type* (*make)(jlong, jlong, julong, julong, julong, julong, int, bool), bool dual); + const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); // Called in PhiNode::Value during CCP, monotically widen the value set, do so rigorously // first, after WidenMax attempts, if the type has still not converged we speed up the @@ -344,7 +360,8 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { if (nt->_widen < Type::WidenMax) { // Returned widened new guy - return CT::make(nt->_lo, nt->_hi, nt->_ulo, nt->_uhi, nt->_zeros, nt->_ones, nt->_widen + 1); + TypeIntPrototype prototype{{nt->_lo, nt->_hi}, {nt->_ulo, nt->_uhi}, {nt->_zeros, nt->_ones}}; + return CT::make(prototype, nt->_widen + 1); } // Speed up the convergence by abandoning the bounds, there are only a couple of bits so @@ -363,7 +380,8 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { zeros |= lt->_zeros; ones |= lt->_ones; } - return CT::make(min, max, umin, umax, zeros, ones, Type::WidenMax); + TypeIntPrototype prototype{{min, max}, {umin, umax}, {zeros, ones}}; + return CT::make(prototype, Type::WidenMax); } template const Type* int_type_widen(const TypeInt* nt, const TypeInt* ot, const TypeInt* lt); template const Type* int_type_widen(const TypeLong* nt, const TypeLong* ot, const TypeLong* lt); @@ -401,13 +419,17 @@ const Type* int_type_narrow(const CT* nt, const CT* ot) { } // Only narrow if the range shrinks a lot - U oc = cardinality_from_bounds(ot->_lo, ot->_hi, ot->_ulo, ot->_uhi); - U nc = cardinality_from_bounds(nt->_lo, nt->_hi, nt->_ulo, nt->_uhi); + U oc = cardinality_from_bounds(RangeInt{ot->_lo, ot->_hi}, + RangeInt{ot->_ulo, ot->_uhi}); + U nc = cardinality_from_bounds(RangeInt{nt->_lo, nt->_hi}, + RangeInt{nt->_ulo, nt->_uhi}); return (nc > (oc >> 1) + (SMALLINT * 2)) ? ot : nt; } template const Type* int_type_narrow(const TypeInt* nt, const TypeInt* ot); template const Type* int_type_narrow(const TypeLong* nt, const TypeLong* ot); + +#ifndef PRODUCT template static const char* intnamenear(T origin, const char* xname, char* buf, size_t buf_size, T n) { if (n < origin) { @@ -534,3 +556,4 @@ const char* bitname(char* buf, size_t buf_size, U zeros, U ones) { } template const char* bitname(char* buf, size_t buf_size, juint zeros, juint ones); template const char* bitname(char* buf, size_t buf_size, julong zeros, julong ones); +#endif // PRODUCT diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 4eb44c40ccab3..be999776b9118 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -25,33 +25,52 @@ #ifndef SHARE_OPTO_RANGEINFERENCE_HPP #define SHARE_OPTO_RANGEINFERENCE_HPP -#include "opto/type.hpp" -#include "runtime/os.hpp" +#include "utilities/pair.hpp" + +class Type; + +template +class RangeInt { +public: + T _lo; + T _hi; +}; + +template +class KnownBits { +public: + T _zeros; + T _ones; +}; template -void normalize_constraints(bool& empty, T& lo, T& hi, U& ulo, U& uhi, U& zeros, U& ones); +class TypeIntPrototype { +public: + RangeInt _srange; + RangeInt _urange; + KnownBits _bits; + Pair> normalize_constraints() const; + int normalize_widen(int w) const; #ifdef ASSERT -template -void verify_constraints(T lo, T hi, U ulo, U uhi, U zeros, U ones); -#endif + bool contains(T v) const; + void verify_constraints() const; +#endif // ASSERT +}; // The result is tuned down by one since we do not have empty type // and this is not required to be accurate template -U cardinality_from_bounds(T lo, T hi, U ulo, U uhi) { - if (U(lo) == ulo) { - return uhi - ulo; +U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { + if (U(srange._lo) == urange._lo) { + return urange._hi - urange._lo; } - return uhi - U(lo) + U(hi) - ulo + 1; + return urange._hi - U(srange._lo) + U(srange._hi) - urange._lo + 1; } -template -int normalize_widen(T lo, T hi, U ulo, U uhi, U zeros, U ones, int w); - template -const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(T, T, UT, UT, UT, UT, int, bool), bool dual); +const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); template bool int_type_equal(const CT* t1, const CT* t2) { diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 03bc52acb3ce4..e9daac79eab64 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -473,14 +473,14 @@ void Type::Initialize_shared(Compile* current) { TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin)->is_int(); // == TypeInt::MINUS_1 TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin)->is_int(); // == TypeInt::ONE TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin)->is_int(); // == TypeInt::ZERO - TypeInt::CC_NE = TypeInt::make(-1, 1, 1, -1, 0, 1, WidenMin)->is_int(); + TypeInt::CC_NE = TypeInt::make(TypeIntPrototype{{-1, 1}, {1, max_juint}, {0, 1}}, WidenMin)->is_int(); TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin)->is_int(); TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin)->is_int(); // == TypeInt::BOOL TypeInt::BYTE = TypeInt::make(-128, 127, WidenMin)->is_int(); // Bytes TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin)->is_int(); // Unsigned Bytes TypeInt::CHAR = TypeInt::make(0,65535, WidenMin)->is_int(); // Java chars TypeInt::SHORT = TypeInt::make(-32768,32767, WidenMin)->is_int(); // Java shorts - TypeInt::NON_ZERO= TypeInt::make(min_jint, max_jint, 1, -1, 0, 0, WidenMin)->is_int(); + TypeInt::NON_ZERO= TypeInt::make(TypeIntPrototype{{min_jint, max_jint}, {1, max_juint}, {0, 0}}, WidenMin)->is_int(); TypeInt::POS = TypeInt::make(0,max_jint, WidenMin)->is_int(); // Non-neg values TypeInt::POS1 = TypeInt::make(1,max_jint, WidenMin)->is_int(); // Positive values TypeInt::INT = TypeInt::make(min_jint, max_jint, WidenMax)->is_int(); // 32-bit integers @@ -499,7 +499,7 @@ void Type::Initialize_shared(Compile* current) { TypeLong::MINUS_1 = TypeLong::make(-1); // -1 TypeLong::ZERO = TypeLong::make( 0); // 0 TypeLong::ONE = TypeLong::make( 1); // 1 - TypeLong::NON_ZERO= TypeLong::make(min_jlong, max_jlong, 1, -1, 0, 0, WidenMin)->is_long(); + TypeLong::NON_ZERO= TypeLong::make(TypeIntPrototype{{min_jlong, max_jlong}, {1, max_julong}, {0, 0}}, WidenMin)->is_long(); TypeLong::POS = TypeLong::make(0,max_jlong, WidenMin)->is_long(); // Non-neg values TypeLong::NEG = TypeLong::make(min_jlong, -1, WidenMin)->is_long(); TypeLong::LONG = TypeLong::make(min_jlong,max_jlong,WidenMax)->is_long(); // 64-bit integers @@ -1586,35 +1586,33 @@ const TypeInt* TypeInt::INT; // 32-bit integers const TypeInt* TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] const TypeInt* TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT -TypeInt::TypeInt(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w, bool dual) - : TypeInteger(Int, normalize_widen(lo, hi, ulo, uhi, zeros, ones, w), dual), - _lo(lo), _hi(hi), _ulo(ulo), _uhi(uhi), _zeros(zeros), _ones(ones) { - DEBUG_ONLY(verify_constraints(lo, hi, ulo, uhi, zeros, ones)); +TypeInt::TypeInt(const TypeIntPrototype& t, int w, bool dual) + : TypeInteger(Int, t.normalize_widen(w), dual), + _lo(t._srange._lo), _hi(t._srange._hi), _ulo(t._urange._lo), _uhi(t._urange._hi), + _zeros(t._bits._zeros), _ones(t._bits._ones) { + DEBUG_ONLY(t.verify_constraints()); } -const Type* TypeInt::make(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w, bool dual) { - bool empty = false; - normalize_constraints(empty, lo, hi, ulo, uhi, zeros, ones); - if (empty) { +const Type* TypeInt::make(const TypeIntPrototype& t, int w, bool dual) { + auto new_t = t.normalize_constraints(); + if (!new_t.first) { return dual ? Type::BOTTOM : Type::TOP; } - return (new TypeInt(lo, hi, ulo, uhi, zeros, ones, w, dual))->hashcons()->is_int(); + return (new TypeInt(new_t.second, w, dual))->hashcons()->is_int(); } const TypeInt* TypeInt::make(jint lo) { - return (new TypeInt(lo, lo, lo, lo, ~lo, lo, WidenMin, false))->hashcons()->is_int(); + juint ulo = lo; + return (new TypeInt(TypeIntPrototype{{lo, lo}, {ulo, ulo}, {~ulo, ulo}}, + WidenMin, false))->hashcons()->is_int(); } const Type* TypeInt::make(jint lo, jint hi, int w) { - return make(lo, hi, 0, max_juint, 0, 0, w); + return make(TypeIntPrototype{{lo, hi}, {0, max_juint}, {0, 0}}, w); } -const Type* TypeInt::make_bits(juint zeros, juint ones, int w) { - return make(min_jint, max_jint, 0, max_juint, zeros, ones, w); -} - -const Type* TypeInt::make(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w) { - return make(lo, hi, ulo, uhi, zeros, ones, w, false); +const Type* TypeInt::make(const TypeIntPrototype& t, int w) { + return make(t, w, false); } bool TypeInt::contains(jint i) const { @@ -1636,7 +1634,8 @@ const Type* TypeInt::xmeet(const Type* t) const { } const Type* TypeInt::xdual() const { - return new TypeInt(_lo, _hi, _ulo, _uhi, _zeros, _ones, _widen, !_dual); + return new TypeInt(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, {_zeros, _ones}}, + _widen, !_dual); } const Type* TypeInt::widen(const Type* old, const Type* limit) const { @@ -1664,8 +1663,9 @@ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. - return (new TypeInt(ft->_lo, ft->_hi, ft->_ulo, ft->_uhi, - ft->_zeros, ft->_ones, this->_widen, false))->hashcons(); + return (new TypeInt(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, + {ft->_zeros, ft->_ones}}, + this->_widen, false))->hashcons(); } return ft; } @@ -1716,35 +1716,33 @@ const TypeLong* TypeLong::INT; // 32-bit subrange const TypeLong* TypeLong::UINT; // 32-bit unsigned subrange const TypeLong* TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG -TypeLong::TypeLong(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w, bool dual) - : TypeInteger(Long, normalize_widen(lo, hi, ulo, uhi, zeros, ones, w), dual), - _lo(lo), _hi(hi), _ulo(ulo), _uhi(uhi), _zeros(zeros), _ones(ones) { - DEBUG_ONLY(verify_constraints(lo, hi, ulo, uhi, zeros, ones)); +TypeLong::TypeLong(const TypeIntPrototype& t, int w, bool dual) + : TypeInteger(Long, t.normalize_widen(w), dual), + _lo(t._srange._lo), _hi(t._srange._hi), _ulo(t._urange._lo), _uhi(t._urange._hi), + _zeros(t._bits._zeros), _ones(t._bits._ones) { + DEBUG_ONLY(t.verify_constraints()); } -const Type* TypeLong::make(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w, bool dual) { - bool empty = false; - normalize_constraints(empty, lo, hi, ulo, uhi, zeros, ones); - if (empty) { +const Type* TypeLong::make(const TypeIntPrototype& t, int w, bool dual) { + auto new_t = t.normalize_constraints(); + if (!new_t.first) { return dual ? Type::BOTTOM : Type::TOP; } - return (new TypeLong(lo, hi, ulo, uhi, zeros, ones, w, dual))->hashcons()->is_long(); + return (new TypeLong(new_t.second, w, dual))->hashcons()->is_long(); } -const TypeLong* TypeLong::make(jlong lo ) { - return (new TypeLong(lo, lo, lo, lo, ~lo, lo, WidenMin, false))->hashcons()->is_long(); +const TypeLong* TypeLong::make(jlong lo) { + julong ulo = lo; + return (new TypeLong(TypeIntPrototype{{lo, lo}, {ulo, ulo}, {~ulo, ulo}}, + WidenMin, false))->hashcons()->is_long(); } const Type* TypeLong::make(jlong lo, jlong hi, int w) { - return make(lo, hi, 0, max_julong, 0, 0, w); -} - -const Type* TypeLong::make_bits(julong zeros, julong ones, int w) { - return make(min_jlong, max_jlong, 0, max_julong, zeros, ones, w); + return make(TypeIntPrototype{{lo, hi}, {0, max_julong}, {0, 0}}, w); } -const Type* TypeLong::make(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w) { - return make(lo, hi, ulo, uhi, zeros, ones, w, false); +const Type* TypeLong::make(const TypeIntPrototype& t, int w) { + return make(t, w, false); } bool TypeLong::contains(jlong i) const { @@ -1766,7 +1764,8 @@ const Type *TypeLong::xmeet(const Type* t) const { } const Type* TypeLong::xdual() const { - return new TypeLong(_lo, _hi, _ulo, _uhi, _zeros, _ones, _widen, !_dual); + return new TypeLong(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, {_zeros, _ones}}, + _widen, !_dual); } const Type* TypeLong::widen(const Type* old, const Type* limit) const { @@ -1794,8 +1793,9 @@ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. - return (new TypeLong(ft->_lo, ft->_hi, ft->_ulo, ft->_uhi, - ft->_zeros, ft->_ones, this->_widen, false))->hashcons(); + return (new TypeLong(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, + {ft->_zeros, ft->_ones}}, + this->_widen, false))->hashcons(); } return ft; } diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 083ee7727daf4..5f53c0f7ff1a1 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -73,6 +73,9 @@ class TypeAryKlassPtr; class TypeMetadataPtr; class VerifyMeet; +template +class TypeIntPrototype; + //------------------------------Type------------------------------------------- // Basic Type object, represents a set of primitive Values. // Types are hash-cons'd into a private class dictionary, so only one of each @@ -554,7 +557,12 @@ class TypeInteger : public Type { protected: TypeInteger(TYPES t, int w, bool dual) : Type(t), _dual(dual), _widen(w) {} - // Use to compute join of 2 sets + // Previously, we signify that a set is in the dual space if _lo > _hi. + // However, with the addition of unsigned range and known bits, this becomes + // ambiguous whether the set is empty or a dual of a non-empty set. + // As a result, we use this field to denote that a set is a dual set. + // Dual sets are only used to compute the join of 2 sets, and not used + // outside. const bool _dual; public: @@ -580,8 +588,8 @@ class TypeInteger : public Type { // Class of integer ranges, the set of integers between a lower bound and an // upper bound, inclusive. class TypeInt : public TypeInteger { - TypeInt(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w, bool dual); - static const Type* make(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w, bool dual); + TypeInt(const TypeIntPrototype& t, int w, bool dual); + static const Type* make(const TypeIntPrototype& t, int w, bool dual); protected: virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; @@ -591,17 +599,16 @@ class TypeInt : public TypeInteger { virtual uint hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous - const jint _lo, _hi; // Lower bound, upper bound - const juint _ulo, _uhi; - const juint _zeros, _ones; + const jint _lo, _hi; // Lower bound, upper bound in the signed domain + const juint _ulo, _uhi; // Lower bound, upper bound in the unsigned domain + const juint _zeros, _ones; // Bits that are known to be 0 or 1 static const TypeInt* cast(const Type* t) { return t->is_int(); } static const TypeInt* try_cast(const Type* t) { return t->isa_int(); } static const TypeInt* make(jint lo); // must always specify w static const Type* make(jint lo, jint hi, int w); - static const Type* make_bits(juint zeros, juint ones, int w); - static const Type* make(jint lo, jint hi, juint ulo, juint uhi, juint zeros, juint ones, int w); + static const Type* make(const TypeIntPrototype& t, int w); // Check for single integer bool is_con() const { return _lo == _hi; } @@ -658,8 +665,8 @@ class TypeInt : public TypeInteger { // Class of long integer ranges, the set of integers between a lower bound and // an upper bound, inclusive. class TypeLong : public TypeInteger { - TypeLong(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w, bool dual); - static const Type* make(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w, bool dual); + TypeLong(const TypeIntPrototype& t, int w, bool dual); + static const Type* make(const TypeIntPrototype& t, int w, bool dual); protected: // Do not kill _widen bits. virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; @@ -670,17 +677,16 @@ class TypeLong : public TypeInteger { virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous public: - const jlong _lo, _hi; // Lower bound, upper bound - const julong _ulo, _uhi; - const julong _zeros, _ones; + const jlong _lo, _hi; // Lower bound, upper bound in the signed domain + const julong _ulo, _uhi; // Lower bound, upper bound in the unsigned domain + const julong _zeros, _ones; // Bits that are known to be 0 or 1 static const TypeLong* cast(const Type* t) { return t->is_long(); } static const TypeLong* try_cast(const Type* t) { return t->isa_long(); } static const TypeLong* make(jlong lo); // must always specify w static const Type* make(jlong lo, jlong hi, int w); - static const Type* make_bits(julong zeros, julong ones, int w); - static const Type* make(jlong lo, jlong hi, julong ulo, julong uhi, julong zeros, julong ones, int w); + static const Type* make(const TypeIntPrototype& t, int w); // Check for single integer bool is_con() const { return _lo == _hi; } diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 2b45fd133a125..86a75d6ab6f61 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -52,48 +52,38 @@ static void test_normalize_constraints_simple() { { T lo = MIN2(a, b); T hi = MAX2(a, b); - T nlo = lo; - T nhi = hi; - U nulo = std::numeric_limits::min(); - U nuhi = std::numeric_limits::max(); - U nzeros = 0; - U nones = 0; - bool empty = false; - normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); - DEBUG_ONLY(verify_constraints(nlo, nhi, nulo, nuhi, nzeros, nones)); - ASSERT_FALSE(empty); - ASSERT_EQ(lo, nlo); - ASSERT_EQ(hi, nhi); + TypeIntPrototype t{{lo, hi}, {std::numeric_limits::min(), std::numeric_limits::max()}, + {0, 0}}; + auto new_t = t.normalize_constraints(); + ASSERT_TRUE(new_t.first); + DEBUG_ONLY(new_t.second.verify_constraints()); + ASSERT_EQ(lo, new_t.second._srange._lo); + ASSERT_EQ(hi, new_t.second._srange._hi); if (U(lo) <= U(hi)) { - ASSERT_EQ(U(lo), nulo); - ASSERT_EQ(U(hi), nuhi); + ASSERT_EQ(U(lo), new_t.second._urange._lo); + ASSERT_EQ(U(hi), new_t.second._urange._hi); } else { - ASSERT_EQ(std::numeric_limits::min(), nulo); - ASSERT_EQ(std::numeric_limits::max(), nuhi); + ASSERT_EQ(std::numeric_limits::min(), new_t.second._urange._lo); + ASSERT_EQ(std::numeric_limits::max(), new_t.second._urange._hi); } } { U ulo = MIN2(a, b); U uhi = MAX2(a, b); - T nlo = std::numeric_limits::min(); - T nhi = std::numeric_limits::max(); - U nulo = ulo; - U nuhi = uhi; - U nzeros = 0; - U nones = 0; - bool empty = false; - normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); - DEBUG_ONLY(verify_constraints(nlo, nhi, nulo, nuhi, nzeros, nones)); - ASSERT_FALSE(empty); - ASSERT_EQ(ulo, nulo); - ASSERT_EQ(uhi, nuhi); + TypeIntPrototype t{{std::numeric_limits::min(), std::numeric_limits::max()}, + {ulo, uhi}, {0, 0}}; + auto new_t = t.normalize_constraints(); + ASSERT_TRUE(new_t.first); + DEBUG_ONLY(new_t.second.verify_constraints()); + ASSERT_EQ(ulo, new_t.second._urange._lo); + ASSERT_EQ(uhi, new_t.second._urange._hi); if (T(ulo) <= T(uhi)) { - ASSERT_EQ(T(ulo), nlo); - ASSERT_EQ(T(uhi), nhi); + ASSERT_EQ(T(ulo), new_t.second._srange._lo); + ASSERT_EQ(T(uhi), new_t.second._srange._hi); } else { - ASSERT_EQ(std::numeric_limits::min(), nlo); - ASSERT_EQ(std::numeric_limits::max(), nhi); + ASSERT_EQ(std::numeric_limits::min(), new_t.second._srange._lo); + ASSERT_EQ(std::numeric_limits::max(), new_t.second._srange._hi); } } @@ -101,20 +91,15 @@ static void test_normalize_constraints_simple() { U intersection = a & b; U zeros = a ^ intersection; U ones = b ^ intersection; - T nlo = std::numeric_limits::min(); - T nhi = std::numeric_limits::max(); - U nulo = std::numeric_limits::min(); - U nuhi = std::numeric_limits::max(); - U nzeros = zeros; - U nones = ones; - bool empty = false; - normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); - DEBUG_ONLY(verify_constraints(nlo, nhi, nulo, nuhi, nzeros, nones)); - ASSERT_FALSE(empty); - ASSERT_EQ(zeros, nzeros); - ASSERT_EQ(ones, nones); - ASSERT_EQ(ones, nulo); - ASSERT_EQ(~zeros, nuhi); + TypeIntPrototype t{{std::numeric_limits::min(), std::numeric_limits::max()}, + {std::numeric_limits::min(), std::numeric_limits::max()}, {zeros, ones}}; + auto new_t = t.normalize_constraints(); + ASSERT_TRUE(new_t.first); + DEBUG_ONLY(new_t.second.verify_constraints()); + ASSERT_EQ(zeros, new_t.second._bits._zeros); + ASSERT_EQ(ones, new_t.second._bits._ones); + ASSERT_EQ(ones, new_t.second._urange._lo); + ASSERT_EQ(~zeros, new_t.second._urange._hi); } } } @@ -138,33 +123,22 @@ static void test_normalize_constraints_random() { U intersection = b1 & b2; U zeros = b1 ^ intersection; U ones = b2 ^ intersection; - T nlo = lo; - T nhi = hi; - U nulo = ulo; - U nuhi = uhi; - U nzeros = zeros; - U nones = ones; - bool empty = false; - normalize_constraints(empty, nlo, nhi, nulo, nuhi, nzeros, nones); - auto contains = [](T lo, T hi, U ulo, U uhi, U zeros, U ones, T value) { - U u = value; - return value >= lo && value <= hi && u >= ulo && u <= uhi && - (u & zeros) == 0 && (~u & ones) == 0; - }; + TypeIntPrototype t{{lo, hi}, {ulo, uhi}, {zeros, ones}}; + auto new_t = t.normalize_constraints(); #ifdef ASSERT - if (!empty) { - verify_constraints(nlo, nhi, nulo, nuhi, nzeros, nones); + if (new_t.first) { + new_t.second.verify_constraints(); } -#endif // ASSERT for (int j = 0; j < samples; j++) { T v = uniform_random(); - if (empty) { - ASSERT_FALSE(contains(lo, hi, ulo, uhi, zeros, ones, v)); + if (!new_t.first) { + ASSERT_FALSE(t.contains(v)); } else { - ASSERT_EQ(contains(lo, hi, ulo, uhi, zeros, ones, v), contains(nlo, nhi, nulo, nuhi, nzeros, nones, v)); + ASSERT_EQ(t.contains(v), new_t.second.contains(v)); } } } +#endif // ASSERT } TEST_VM(opto, normalize_constraints) { From 1faa48b5b2dbbf09a122c1bf4f16a0d00a788fa6 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Tue, 23 Jan 2024 03:55:22 +0800 Subject: [PATCH 07/45] fix release build --- test/hotspot/gtest/opto/test_rangeinference.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 86a75d6ab6f61..d44a6701e8c9b 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -29,6 +29,8 @@ #include +#ifdef ASSERT + template static U uniform_random(); @@ -108,7 +110,6 @@ template static void test_normalize_constraints_random() { constexpr int samples = 1000; constexpr int parameters = 1000; - int non_empty = 0; for (int i = 0; i < parameters; i++) { T s1 = uniform_random(); T s2 = uniform_random(); @@ -125,7 +126,6 @@ static void test_normalize_constraints_random() { U ones = b2 ^ intersection; TypeIntPrototype t{{lo, hi}, {ulo, uhi}, {zeros, ones}}; auto new_t = t.normalize_constraints(); -#ifdef ASSERT if (new_t.first) { new_t.second.verify_constraints(); } @@ -138,7 +138,6 @@ static void test_normalize_constraints_random() { } } } -#endif // ASSERT } TEST_VM(opto, normalize_constraints) { @@ -147,3 +146,5 @@ TEST_VM(opto, normalize_constraints) { test_normalize_constraints_random(); test_normalize_constraints_random(); } + +#endif // ASSERT From 6e2e6c567541f07f9bc6f02c65f3c3ce137aef26 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 15 Mar 2024 12:03:31 +0700 Subject: [PATCH 08/45] add comments --- src/hotspot/share/opto/type.cpp | 16 ++++++++-------- src/hotspot/share/opto/type.hpp | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 0c6be6ed7c854..d6a52cd9e3394 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1639,12 +1639,12 @@ const Type* TypeInt::xdual() const { } const Type* TypeInt::widen(const Type* old, const Type* limit) const { - assert(!_dual, ""); + assert(!_dual, "dual types should only be used for join calculation"); return int_type_widen(this, old->isa_int(), limit->isa_int()); } const Type* TypeInt::narrow(const Type* old) const { - assert(!_dual, ""); + assert(!_dual, "dual types should only be used for join calculation"); if (old == nullptr) { return this; } @@ -1654,12 +1654,12 @@ const Type* TypeInt::narrow(const Type* old) const { //-----------------------------filter------------------------------------------ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) const { - assert(!_dual, ""); + assert(!_dual, "dual types should only be used for join calculation"); const TypeInt* ft = join_helper(kills, include_speculative)->isa_int(); if (ft == nullptr) { return Type::TOP; // Canonical empty value } - assert(!ft->_dual, ""); + assert(!ft->_dual, "dual types should only be used for join calculation"); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. @@ -1769,12 +1769,12 @@ const Type* TypeLong::xdual() const { } const Type* TypeLong::widen(const Type* old, const Type* limit) const { - assert(!_dual, ""); + assert(!_dual, "dual types should only be used for join calculation"); return int_type_widen(this, old->isa_long(), limit->isa_long()); } const Type* TypeLong::narrow(const Type* old) const { - assert(!_dual, ""); + assert(!_dual, "dual types should only be used for join calculation"); if (old == nullptr) { return this; } @@ -1784,12 +1784,12 @@ const Type* TypeLong::narrow(const Type* old) const { //-----------------------------filter------------------------------------------ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) const { - assert(!_dual, ""); + assert(!_dual, "dual types should only be used for join calculation"); const TypeLong* ft = join_helper(kills, include_speculative)->isa_long(); if (ft == nullptr) { return Type::TOP; // Canonical empty value } - assert(!ft->_dual, ""); + assert(!ft->_dual, "dual types should only be used for join calculation"); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 5f53c0f7ff1a1..01fb618dd0b35 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -614,8 +614,11 @@ class TypeInt : public TypeInteger { bool is_con() const { return _lo == _hi; } bool is_con(jint i) const { return is_con() && _lo == i; } jint get_con() const { assert(is_con(), ""); return _lo; } + // Check if a TypeInt is a subset of this TypeInt (i.e. all elements of the + // argument are also elements of this type) bool contains(jint i) const; bool contains(const TypeInt* t) const; + // Excluding the cases where this and t are the same bool properly_contains(const TypeInt* t) const; virtual bool is_finite() const; // Has a finite value @@ -692,8 +695,11 @@ class TypeLong : public TypeInteger { bool is_con() const { return _lo == _hi; } bool is_con(jlong i) const { return is_con() && _lo == i; } jlong get_con() const { assert(is_con(), "" ); return _lo; } + // Check if a TypeLong is a subset of this TypeLong (i.e. all elements of the + // argument are also elements of this type) bool contains(jlong i) const; bool contains(const TypeLong* t) const; + // Excluding the cases where this and t are the same bool properly_contains(const TypeLong* t) const; // Check for positive 32-bit value. From d5ad9f1a3d1068e6e87bd9da99ac06582ecd9afe Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 14 Aug 2024 23:24:25 +0700 Subject: [PATCH 09/45] fix compile errors --- src/hotspot/share/opto/loopnode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index b4c134570e63f..cef6bac7eec48 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -941,7 +941,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // inner_iters_max may not fit in a signed integer (iterating from // Long.MIN_VALUE to Long.MAX_VALUE for instance). Use an unsigned // min. - const TypeInteger* inner_iters_actual_range = TypeInteger::make(0, iters_limit, Type::WidenMin, bt); + const TypeInteger* inner_iters_actual_range = TypeInteger::make(0, iters_limit, Type::WidenMin, bt)->is_integer(bt); Node* inner_iters_actual = MaxNode::unsigned_min(inner_iters_max, inner_iters_limit, inner_iters_actual_range, _igvn); Node* inner_iters_actual_int; @@ -952,7 +952,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // the loop limit is less or equal to max_jint - stride - 1 (if stride is positive but a similar argument exists for // a negative stride). We add a CastII here to guarantee that, when the counted loop is created in a subsequent loop // opts pass, an accurate range of values for the limits is found. - const TypeInt* inner_iters_actual_int_range = TypeInt::make(0, iters_limit, Type::WidenMin); + const TypeInt* inner_iters_actual_int_range = TypeInt::make(0, iters_limit, Type::WidenMin)->is_int(); inner_iters_actual_int = new CastIINode(outer_head, inner_iters_actual_int, inner_iters_actual_int_range, ConstraintCastNode::UnconditionalDependency); _igvn.register_new_node_with_optimizer(inner_iters_actual_int); } else { From 2c3807bdfa73f9b610408885ece778b0f0f9c66c Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 4 Sep 2024 03:27:15 +0700 Subject: [PATCH 10/45] address reviews --- src/hotspot/share/opto/rangeinference.cpp | 284 +++++++++++------- src/hotspot/share/opto/rangeinference.hpp | 39 ++- src/hotspot/share/opto/type.cpp | 168 +++++------ src/hotspot/share/opto/type.hpp | 29 +- .../gtest/opto/test_rangeinference.cpp | 92 +++--- 5 files changed, 334 insertions(+), 278 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index aa7795898a18b..a5f0ad260db01 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -37,61 +37,72 @@ class AdjustResult { T _data; }; -template -class NormalizeSimpleResult { +template +class SimpleCanonicalResult { public: bool _present; - RangeInt _bounds; - KnownBits _bits; + RangeInt _bounds; + KnownBits _bits; }; // Try to tighten the bound constraints from the known bit information // E.g: if lo = 0 but the lowest bit is always 1 then we can tighten // lo = 1 -template -static AdjustResult> -adjust_bounds_from_bits(const RangeInt& bounds, const KnownBits& bits) { - static_assert(std::is_unsigned::value, ""); - - auto adjust_lo = [](T lo, const KnownBits& bits) { - constexpr size_t W = sizeof(T) * 8; - T zero_violation = lo & bits._zeros; - T one_violation = ~lo & bits._ones; +template +static AdjustResult> +adjust_bounds_from_bits(const RangeInt& bounds, const KnownBits& bits) { + // Find the minimum value that is not less than lo and satisfies bits + auto adjust_lo = [](U lo, const KnownBits& bits) { + constexpr size_t W = sizeof(U) * 8; + // Violation of lo with respects to bits + U zero_violation = lo & bits._zeros; + U one_violation = ~lo & bits._ones; if (zero_violation == one_violation) { + // This means lo does not violate bits, it is the result + assert(zero_violation == 0, ""); return lo; } if (zero_violation < one_violation) { - // Align the last violation of ones unset all the lower bits - // so we don't care about violations of zeros - juint last_violation = W - 1 - count_leading_zeros(one_violation); - T alignment = T(1) << last_violation; + // This means that the first bit that does not satisfy the bit + // requirement is a 0 that should be a 1 + // Try to set that bit, the smallest value will have all the following + // bits being zeros + // E.g: lo = 10010010, zeros = 00100100, ones = 01001000 + juint first_violation = W - 1 - count_leading_zeros(one_violation); + U alignment = U(1) << first_violation; + // 11000000, notice that all bits after the second digit are zeroed, + // which automatically satisfies the unset bit requirement lo = (lo & -alignment) + alignment; - return lo | bits._ones; + // Simply satisfy the set bit requirement + return lo | bits._ones; // 11001000 } - // Suppose lo = 00110010, zeros = 01010010, ones = 10001000 - // Since the 4-th bit must be 0, we need to align up the lower bound. - // This results in lo = 01000000, but then the 6-th bit does not match, - // align up again gives us 10000000. - // We can align up directly to 10000000 by finding the first place after - // the highest mismatch such that both the corresponding bits are unset. - // Since all bits lower than the alignment are unset we don't need to - // align for the violations of ones anymore. - juint last_violation = W - 1 - count_leading_zeros(zero_violation); - T find_mask = std::numeric_limits::max() << last_violation; - T either = lo | bits._zeros; - T tmp = ~either & find_mask; - T alignment = tmp & (-tmp); - lo = (lo & -alignment) + alignment; - return lo | bits._ones; + // This is more difficult because trying to unset a bit requires us to flip + // some bits before it (higher bits) + // Suppose lo = 11000110, zeros = 00001010, ones = 10010001 + // Since the 2-nd bit must be 0, we need to align up the lower bound. + // This results in lo = 11001000, but then the 4-th bit does not match, + // align up again gives us 11010000 + // We can align up directly to 11010000 by finding the last place before + // the first mismatch such that it is 0 in lo and not required to be unset + juint first_violation = W - 1 - count_leading_zeros(zero_violation); + U find_mask = std::numeric_limits::max() << first_violation; // 11111100 + U either = lo | bits._zeros; // 11001110 + U tmp = ~either & find_mask; // 00110000 + U alignment = tmp & (-tmp); // 00010000 + lo = (lo & -alignment) + alignment; // 11010000 + return lo | bits._ones; // 11010001 }; - T new_lo = adjust_lo(bounds._lo, bits); + U new_lo = adjust_lo(bounds._lo, bits); if (new_lo < bounds._lo) { + // This means we wrapped around, which means no value not less than lo + // satisfies bits return {true, false, {}}; } - T new_hi = ~adjust_lo(~bounds._hi, {bits._ones, bits._zeros}); + // Adjust hi by adjusting its bitwise negation + U new_hi = ~adjust_lo(~bounds._hi, {bits._ones, bits._zeros}); if (new_hi > bounds._hi) { return {true, false, {}}; } @@ -101,17 +112,20 @@ adjust_bounds_from_bits(const RangeInt& bounds, const KnownBits& bits) { return {progress, present, {new_lo, new_hi}}; } -// Try to tighten the known bit constraints from the bound information +// Try to tighten the known bit constraints from the bound information by +// extracting the common prefix of lo and hi and combining with the current +// bit constraints // E.g: if lo = 0 and hi = 10, then all but the lowest 4 bits must be 0 -template -static AdjustResult> -adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { - static_assert(std::is_unsigned::value, ""); - T mismatch = bounds._lo ^ bounds._hi; - T match_mask = mismatch == 0 ? std::numeric_limits::max() - : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); - T new_zeros = bits._zeros | (match_mask &~ bounds._lo); - T new_ones = bits._ones | (match_mask & bounds._lo); +template +static AdjustResult> +adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { + // Find the mask to filter the common prefix + U mismatch = bounds._lo ^ bounds._hi; + U match_mask = mismatch == 0 ? std::numeric_limits::max() + : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); + // match_mask & bounds._lo is the common prefix + U new_zeros = bits._zeros | (match_mask &~ bounds._lo); + U new_ones = bits._ones | (match_mask & bounds._lo); bool progress = (new_zeros != bits._zeros) || (new_ones != bits._ones); bool present = ((new_zeros & new_ones) == 0); return {progress, present, {new_zeros, new_ones}}; @@ -120,14 +134,14 @@ adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { // Try to tighten both the bounds and the bits at the same time // Iteratively tighten 1 using the other until no progress is made. // This function converges because bit constraints converge fast. -template -static NormalizeSimpleResult -normalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { - AdjustResult> nbits = adjust_bits_from_bounds(bits, bounds); +template +static SimpleCanonicalResult +normalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { + AdjustResult> nbits = adjust_bits_from_bounds(bits, bounds); if (!nbits._present) { return {false, {}, {}}; } - AdjustResult> nbounds{true, true, bounds}; + AdjustResult> nbounds{true, true, bounds}; while (true) { nbounds = adjust_bounds_from_bits(nbounds._data, nbits._data); if (!nbounds._progress || !nbounds._present) { @@ -145,14 +159,14 @@ normalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits // the set and for each bit position that is not constrained, there exists 2 // values with the bit value at that position being set and unset, respectively, // such that both belong to the set represented by the constraints. -template -Pair> -TypeIntPrototype::normalize_constraints() const { - static_assert(std::is_signed::value, ""); +template +CanonicalizedTypeIntPrototype +TypeIntPrototype::canonicalize_constraints() const { + static_assert(std::is_signed::value, ""); static_assert(std::is_unsigned::value, ""); - static_assert(sizeof(T) == sizeof(U), ""); + static_assert(sizeof(S) == sizeof(U), ""); - RangeInt srange = _srange; + RangeInt srange = _srange; RangeInt urange = _urange; if (srange._lo > srange._hi || urange._lo > urange._hi || @@ -160,24 +174,24 @@ TypeIntPrototype::normalize_constraints() const { return {false, {}}; } - if (T(urange._lo) > T(urange._hi)) { - if (T(urange._hi) < srange._lo) { - urange._hi = std::numeric_limits::max(); - } else if (T(urange._lo) > srange._hi) { - urange._lo = std::numeric_limits::min(); + if (S(urange._lo) > S(urange._hi)) { + if (S(urange._hi) < srange._lo) { + urange._hi = std::numeric_limits::max(); + } else if (S(urange._lo) > srange._hi) { + urange._lo = std::numeric_limits::min(); } } - if (T(urange._lo) <= T(urange._hi)) { + if (S(urange._lo) <= S(urange._hi)) { // [lo, hi] and [ulo, uhi] represent the same range - urange._lo = MAX2(urange._lo, srange._lo); - urange._hi = MIN2(urange._hi, srange._hi); + urange._lo = MAX2(urange._lo, srange._lo); + urange._hi = MIN2(urange._hi, srange._hi); if (urange._lo > urange._hi) { return {false, {}}; } auto type = normalize_constraints_simple(urange, _bits); - return {type._present, {{T(type._bounds._lo), T(type._bounds._hi)}, + return {type._present, {{S(type._bounds._lo), S(type._bounds._hi)}, type._bounds, type._bits}}; } @@ -191,26 +205,26 @@ TypeIntPrototype::normalize_constraints() const { if (!neg_type._present && !pos_type._present) { return {false, {}}; } else if (!neg_type._present) { - return {true, {{T(pos_type._bounds._lo), T(pos_type._bounds._hi)}, + return {true, {{S(pos_type._bounds._lo), S(pos_type._bounds._hi)}, pos_type._bounds, pos_type._bits}}; } else if (!pos_type._present) { - return {true, {{T(neg_type._bounds._lo), T(neg_type._bounds._hi)}, + return {true, {{S(neg_type._bounds._lo), S(neg_type._bounds._hi)}, neg_type._bounds, neg_type._bits}}; } else { - return {true, {{T(neg_type._bounds._lo), T(pos_type._bounds._hi)}, + return {true, {{S(neg_type._bounds._lo), S(pos_type._bounds._hi)}, {pos_type._bounds._lo, neg_type._bounds._hi}, {neg_type._bits._zeros & pos_type._bits._zeros, neg_type._bits._ones & pos_type._bits._ones}}}; } } -template -int TypeIntPrototype::normalize_widen(int w) const { +template +int TypeIntPrototype::normalize_widen(int w) const { // Certain normalizations keep us sane when comparing types. // The 'SMALLINT' covers constants and also CC and its relatives. if (cardinality_from_bounds(_srange, _urange) <= SMALLINT) { return Type::WidenMin; } - if (_srange._lo == std::numeric_limits::min() && _srange._hi == std::numeric_limits::max() && + if (_srange._lo == std::numeric_limits::min() && _srange._hi == std::numeric_limits::max() && _urange._lo == std::numeric_limits::min() && _urange._hi == std::numeric_limits::max() && _bits._zeros == 0 && _bits._ones == 0) { // bottom type @@ -220,18 +234,18 @@ int TypeIntPrototype::normalize_widen(int w) const { } #ifdef ASSERT -template -bool TypeIntPrototype::contains(T v) const { +template +bool TypeIntPrototype::contains(S v) const { return v >= _srange._lo && v <= _srange._hi && U(v) >= _urange._lo && U(v) <= _urange._hi && (v & _bits._zeros) == 0 && (~v & _bits._ones) == 0; } // Verify that this set representation is canonical -template -void TypeIntPrototype::verify_constraints() const { - static_assert(std::is_signed::value, ""); +template +void TypeIntPrototype::verify_constraints() const { + static_assert(std::is_signed::value, ""); static_assert(std::is_unsigned::value, ""); - static_assert(sizeof(T) == sizeof(U), ""); + static_assert(sizeof(S) == sizeof(U), ""); // Assert that the bounds cannot be further tightened assert(contains(_srange._lo) && contains(_srange._hi) && @@ -262,8 +276,8 @@ template class TypeIntPrototype; // Compute the meet of 2 types, when dual is true, we are actually computing the // join. -template -const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual) { +template +const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual) { // Perform a fast test for common case; meeting the same types together. if (i1 == t2 || t2 == Type::TOP) { return i1; @@ -272,15 +286,15 @@ const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(con if (i2 != nullptr) { if (!dual) { // meet - return make(TypeIntPrototype{{MIN2(i1->_lo, i2->_lo), MAX2(i1->_hi, i2->_hi)}, - {MIN2(i1->_ulo, i2->_ulo), MAX2(i1->_uhi, i2->_uhi)}, - {i1->_zeros & i2->_zeros, i1->_ones & i2->_ones}}, + return make(TypeIntPrototype{{MIN2(i1->_lo, i2->_lo), MAX2(i1->_hi, i2->_hi)}, + {MIN2(i1->_ulo, i2->_ulo), MAX2(i1->_uhi, i2->_uhi)}, + {i1->_zeros & i2->_zeros, i1->_ones & i2->_ones}}, MAX2(i1->_widen, i2->_widen), false); } // join - return make(TypeIntPrototype{{MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi)}, - {MAX2(i1->_ulo, i2->_ulo), MIN2(i1->_uhi, i2->_uhi)}, - {i1->_zeros | i2->_zeros, i1->_ones | i2->_ones}}, + return make(TypeIntPrototype{{MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi)}, + {MAX2(i1->_ulo, i2->_ulo), MIN2(i1->_uhi, i2->_uhi)}, + {i1->_zeros | i2->_zeros, i1->_ones | i2->_ones}}, MIN2(i1->_widen, i2->_widen), true); } @@ -322,7 +336,7 @@ template const Type* int_type_xmeet(const TypeLong* i1, const Type* t2, // convergence by abandoning the bounds template const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { - using T = std::remove_const_t; + using S = std::remove_const_t; using U = std::remove_const_t; if (ot == nullptr) { @@ -360,14 +374,14 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { if (nt->_widen < Type::WidenMax) { // Returned widened new guy - TypeIntPrototype prototype{{nt->_lo, nt->_hi}, {nt->_ulo, nt->_uhi}, {nt->_zeros, nt->_ones}}; + TypeIntPrototype prototype{{nt->_lo, nt->_hi}, {nt->_ulo, nt->_uhi}, {nt->_zeros, nt->_ones}}; return CT::make(prototype, nt->_widen + 1); } // Speed up the convergence by abandoning the bounds, there are only a couple of bits so // they converge fast - T min = std::numeric_limits::min(); - T max = std::numeric_limits::max(); + S min = std::numeric_limits::min(); + S max = std::numeric_limits::max(); U umin = std::numeric_limits::min(); U umax = std::numeric_limits::max(); U zeros = nt->_zeros; @@ -380,7 +394,7 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { zeros |= lt->_zeros; ones |= lt->_ones; } - TypeIntPrototype prototype{{min, max}, {umin, umax}, {zeros, ones}}; + TypeIntPrototype prototype{{min, max}, {umin, umax}, {zeros, ones}}; return CT::make(prototype, Type::WidenMax); } template const Type* int_type_widen(const TypeInt* nt, const TypeInt* ot, const TypeInt* lt); @@ -391,7 +405,7 @@ template const Type* int_type_widen(const TypeLong* nt, const TypeLong* ot, cons // slow convergence template const Type* int_type_narrow(const CT* nt, const CT* ot) { - using T = decltype(CT::_lo); + using S = decltype(CT::_lo); using U = decltype(CT::_ulo); if (nt->singleton() || ot == nullptr) { @@ -419,9 +433,9 @@ const Type* int_type_narrow(const CT* nt, const CT* ot) { } // Only narrow if the range shrinks a lot - U oc = cardinality_from_bounds(RangeInt{ot->_lo, ot->_hi}, + U oc = cardinality_from_bounds(RangeInt{ot->_lo, ot->_hi}, RangeInt{ot->_ulo, ot->_uhi}); - U nc = cardinality_from_bounds(RangeInt{nt->_lo, nt->_hi}, + U nc = cardinality_from_bounds(RangeInt{nt->_lo, nt->_hi}, RangeInt{nt->_ulo, nt->_uhi}); return (nc > (oc >> 1) + (SMALLINT * 2)) ? ot : nt; } @@ -431,7 +445,7 @@ template const Type* int_type_narrow(const TypeLong* nt, const TypeLong* ot); #ifndef PRODUCT template -static const char* intnamenear(T origin, const char* xname, char* buf, size_t buf_size, T n) { +static const char* int_name_near(T origin, const char* xname, char* buf, size_t buf_size, T n) { if (n < origin) { if (n <= origin - 10000) { return nullptr; @@ -449,12 +463,12 @@ static const char* intnamenear(T origin, const char* xname, char* buf, size_t bu } const char* intname(char* buf, size_t buf_size, jint n) { - const char* str = intnamenear(max_jint, "maxint", buf, buf_size, n); + const char* str = int_name_near(max_jint, "maxint", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(min_jint, "minint", buf, buf_size, n); + str = int_name_near(min_jint, "minint", buf, buf_size, n); if (str != nullptr) { return str; } @@ -464,12 +478,12 @@ const char* intname(char* buf, size_t buf_size, jint n) { } const char* uintname(char* buf, size_t buf_size, juint n) { - const char* str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + const char* str = int_name_near(max_juint, "maxuint", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(max_jint, "maxint", buf, buf_size, n); + str = int_name_near(max_jint, "maxint", buf, buf_size, n); if (str != nullptr) { return str; } @@ -479,27 +493,27 @@ const char* uintname(char* buf, size_t buf_size, juint n) { } const char* longname(char* buf, size_t buf_size, jlong n) { - const char* str = intnamenear(max_jlong, "maxlong", buf, buf_size, n); + const char* str = int_name_near(max_jlong, "maxlong", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(min_jlong, "minlong", buf, buf_size, n); + str = int_name_near(min_jlong, "minlong", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + str = int_name_near(max_juint, "maxuint", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(max_jint, "maxint", buf, buf_size, n); + str = int_name_near(max_jint, "maxint", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(min_jint, "minint", buf, buf_size, n); + str = int_name_near(min_jint, "minint", buf, buf_size, n); if (str != nullptr) { return str; } @@ -509,22 +523,22 @@ const char* longname(char* buf, size_t buf_size, jlong n) { } const char* ulongname(char* buf, size_t buf_size, julong n) { - const char* str = intnamenear(max_julong, "maxulong", buf, buf_size, n); + const char* str = int_name_near(max_julong, "maxulong", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(max_jlong, "maxlong", buf, buf_size, n); + str = int_name_near(max_jlong, "maxlong", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(max_juint, "maxuint", buf, buf_size, n); + str = int_name_near(max_juint, "maxuint", buf, buf_size, n); if (str != nullptr) { return str; } - str = intnamenear(max_jint, "maxint", buf, buf_size, n); + str = int_name_near(max_jint, "maxint", buf, buf_size, n); if (str != nullptr) { return str; } @@ -556,4 +570,60 @@ const char* bitname(char* buf, size_t buf_size, U zeros, U ones) { } template const char* bitname(char* buf, size_t buf_size, juint zeros, juint ones); template const char* bitname(char* buf, size_t buf_size, julong zeros, julong ones); + +void int_type_dump(const TypeInt* t, outputStream* st, bool verbose) { + char buf1[40], buf2[40], buf3[40], buf4[40], buf5[40]; + if (int_type_equal(t, TypeInt::INT)) { + st->print("int"); + } else if (t->is_con()) { + st->print("int:%s", intname(buf1, sizeof(buf1), t->get_con())); + } else if (int_type_equal(t, TypeInt::BOOL)) { + st->print("bool"); + } else if (int_type_equal(t, TypeInt::BYTE)) { + st->print("byte"); + } else if (int_type_equal(t, TypeInt::CHAR)) { + st->print("char"); + } else if (int_type_equal(t, TypeInt::SHORT)) { + st->print("short"); + } else { + if (verbose) { + st->print("int:%s..%s ^ %s..%s, bits:%s", + intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi), + uintname(buf3, sizeof(buf3), t->_ulo), uintname(buf4, sizeof(buf4), t->_uhi), + bitname(buf5, sizeof(buf5), t->_zeros, t->_ones)); + } else { + st->print("int:%s..%s ^ %s..%s", + intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi), + uintname(buf3, sizeof(buf3), t->_ulo), uintname(buf4, sizeof(buf4), t->_uhi)); + } + } + + if (t->_widen > 0 && t != TypeInt::INT) { + st->print(", widen: %d", t->_widen); + } +} + +void int_type_dump(const TypeLong* t, outputStream* st, bool verbose) { + char buf1[80], buf2[80], buf3[80], buf4[80], buf5[80]; + if (int_type_equal(t, TypeLong::LONG)) { + st->print("long"); + } else if (t->is_con()) { + st->print("long:%s", longname(buf1, sizeof(buf1), t->get_con())); + } else { + if (verbose) { + st->print("long:%s..%s ^ %s..%s, bits:%s", + longname(buf1, sizeof(buf1), t->_lo), longname(buf2,sizeof(buf2), t-> _hi), + ulongname(buf3, sizeof(buf3), t->_ulo), ulongname(buf4, sizeof(buf4), t->_uhi), + bitname(buf5, sizeof(buf5), t->_zeros, t->_ones)); + } else { + st->print("long:%s..%s ^ %s..%s", + longname(buf1, sizeof(buf1), t->_lo), longname(buf2,sizeof(buf2), t-> _hi), + ulongname(buf3, sizeof(buf3), t->_ulo), ulongname(buf4, sizeof(buf4), t->_uhi)); + } + } + + if (t->_widen > 0 && t != TypeLong::LONG) { + st->print(", widen: %d", t->_widen); + } +} #endif // PRODUCT diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index be999776b9118..4ba6970645210 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -25,9 +25,9 @@ #ifndef SHARE_OPTO_RANGEINFERENCE_HPP #define SHARE_OPTO_RANGEINFERENCE_HPP -#include "utilities/pair.hpp" - class Type; +class TypeInt; +class TypeLong; template class RangeInt { @@ -36,32 +36,42 @@ class RangeInt { T _hi; }; -template +template class KnownBits { public: - T _zeros; - T _ones; + U _zeros; + U _ones; +}; + +template +class TypeIntPrototype; + +template +class CanonicalizedTypeIntPrototype { +public: + bool _present; + TypeIntPrototype _data; }; -template +template class TypeIntPrototype { public: - RangeInt _srange; + RangeInt _srange; RangeInt _urange; KnownBits _bits; - Pair> normalize_constraints() const; + CanonicalizedTypeIntPrototype canonicalize_constraints() const; int normalize_widen(int w) const; #ifdef ASSERT - bool contains(T v) const; + bool contains(S v) const; void verify_constraints() const; #endif // ASSERT }; // The result is tuned down by one since we do not have empty type // and this is not required to be accurate -template -U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { +template +U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { if (U(srange._lo) == urange._lo) { return urange._hi - urange._lo; } @@ -69,8 +79,8 @@ U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) return urange._hi - U(srange._lo) + U(srange._hi) - urange._lo + 1; } -template -const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); +template +const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); template bool int_type_equal(const CT* t1, const CT* t2) { @@ -98,6 +108,9 @@ const char* ulongname(char* buf, size_t buf_size, julong n); template const char* bitname(char* buf, size_t buf_size, U zeros, U ones); + +void int_type_dump(const TypeInt* t, outputStream* st, bool verbose); +void int_type_dump(const TypeLong* t, outputStream* st, bool verbose); #endif // PRODUCT #endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index ed02be7e2ef05..d92be7997f650 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -471,47 +471,47 @@ void Type::Initialize_shared(Compile* current) { TypeInt::MAX = TypeInt::make(max_jint); // Int MAX TypeInt::MIN = TypeInt::make(min_jint); // Int MIN - TypeInt::MINUS_1 = TypeInt::make(-1); // -1 - TypeInt::ZERO = TypeInt::make( 0); // 0 - TypeInt::ONE = TypeInt::make( 1); // 1 - TypeInt::BOOL = TypeInt::make( 0, 1, WidenMin)->is_int(); // 0 or 1, FALSE or TRUE. - TypeInt::CC = TypeInt::make(-1, 1, WidenMin)->is_int(); // -1, 0 or 1, condition codes - TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin)->is_int(); // == TypeInt::MINUS_1 - TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin)->is_int(); // == TypeInt::ONE - TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin)->is_int(); // == TypeInt::ZERO - TypeInt::CC_NE = TypeInt::make(TypeIntPrototype{{-1, 1}, {1, max_juint}, {0, 1}}, WidenMin)->is_int(); - TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin)->is_int(); - TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin)->is_int(); // == TypeInt::BOOL - TypeInt::BYTE = TypeInt::make(-128, 127, WidenMin)->is_int(); // Bytes - TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin)->is_int(); // Unsigned Bytes - TypeInt::CHAR = TypeInt::make(0,65535, WidenMin)->is_int(); // Java chars - TypeInt::SHORT = TypeInt::make(-32768,32767, WidenMin)->is_int(); // Java shorts - TypeInt::NON_ZERO= TypeInt::make(TypeIntPrototype{{min_jint, max_jint}, {1, max_juint}, {0, 0}}, WidenMin)->is_int(); - TypeInt::POS = TypeInt::make(0,max_jint, WidenMin)->is_int(); // Non-neg values - TypeInt::POS1 = TypeInt::make(1,max_jint, WidenMin)->is_int(); // Positive values - TypeInt::INT = TypeInt::make(min_jint, max_jint, WidenMax)->is_int(); // 32-bit integers - TypeInt::SYMINT = TypeInt::make(-max_jint, max_jint, WidenMin)->is_int(); // symmetric range + TypeInt::MINUS_1 = TypeInt::make(-1); // -1 + TypeInt::ZERO = TypeInt::make( 0); // 0 + TypeInt::ONE = TypeInt::make( 1); // 1 + TypeInt::BOOL = TypeInt::make( 0, 1, WidenMin)->is_int(); // 0 or 1, FALSE or TRUE. + TypeInt::CC = TypeInt::make(-1, 1, WidenMin)->is_int(); // -1, 0 or 1, condition codes + TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin)->is_int(); // == TypeInt::MINUS_1 + TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin)->is_int(); // == TypeInt::ONE + TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin)->is_int(); // == TypeInt::ZERO + TypeInt::CC_NE = TypeInt::make(TypeIntPrototype{{-1, 1}, {1, max_juint}, {0, 1}}, WidenMin)->is_int(); + TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin)->is_int(); + TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin)->is_int(); // == TypeInt::BOOL + TypeInt::BYTE = TypeInt::make(-128, 127, WidenMin)->is_int(); // Bytes + TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin)->is_int(); // Unsigned Bytes + TypeInt::CHAR = TypeInt::make(0, 65535, WidenMin)->is_int(); // Java chars + TypeInt::SHORT = TypeInt::make(-32768, 32767, WidenMin)->is_int(); // Java shorts + TypeInt::NON_ZERO = TypeInt::make(TypeIntPrototype{{min_jint, max_jint}, {1, max_juint}, {0, 0}}, WidenMin)->is_int(); + TypeInt::POS = TypeInt::make(0, max_jint, WidenMin)->is_int(); // Non-neg values + TypeInt::POS1 = TypeInt::make(1, max_jint, WidenMin)->is_int(); // Positive values + TypeInt::INT = TypeInt::make(min_jint, max_jint, WidenMax)->is_int(); // 32-bit integers + TypeInt::SYMINT = TypeInt::make(-max_jint, max_jint, WidenMin)->is_int(); // symmetric range TypeInt::TYPE_DOMAIN = TypeInt::INT; // CmpL is overloaded both as the bytecode computation returning - // a trinary (-1,0,+1) integer result AND as an efficient long + // a trinary (-1, 0, +1) integer result AND as an efficient long // compare returning optimizer ideal-type flags. - assert( TypeInt::CC_LT == TypeInt::MINUS_1, "types must match for CmpL to work" ); - assert( TypeInt::CC_GT == TypeInt::ONE, "types must match for CmpL to work" ); - assert( TypeInt::CC_EQ == TypeInt::ZERO, "types must match for CmpL to work" ); - assert( TypeInt::CC_GE == TypeInt::BOOL, "types must match for CmpL to work" ); - - TypeLong::MAX = TypeLong::make(max_jlong); // Long MAX - TypeLong::MIN = TypeLong::make(min_jlong); // Long MIN - TypeLong::MINUS_1 = TypeLong::make(-1); // -1 - TypeLong::ZERO = TypeLong::make( 0); // 0 - TypeLong::ONE = TypeLong::make( 1); // 1 - TypeLong::NON_ZERO= TypeLong::make(TypeIntPrototype{{min_jlong, max_jlong}, {1, max_julong}, {0, 0}}, WidenMin)->is_long(); - TypeLong::POS = TypeLong::make(0,max_jlong, WidenMin)->is_long(); // Non-neg values - TypeLong::NEG = TypeLong::make(min_jlong, -1, WidenMin)->is_long(); - TypeLong::LONG = TypeLong::make(min_jlong,max_jlong,WidenMax)->is_long(); // 64-bit integers - TypeLong::INT = TypeLong::make((jlong)min_jint,(jlong)max_jint,WidenMin)->is_long(); - TypeLong::UINT = TypeLong::make(0,(jlong)max_juint,WidenMin)->is_long(); - TypeLong::TYPE_DOMAIN = TypeLong::LONG; + assert(TypeInt::CC_LT == TypeInt::MINUS_1, "types must match for CmpL to work" ); + assert(TypeInt::CC_GT == TypeInt::ONE, "types must match for CmpL to work" ); + assert(TypeInt::CC_EQ == TypeInt::ZERO, "types must match for CmpL to work" ); + assert(TypeInt::CC_GE == TypeInt::BOOL, "types must match for CmpL to work" ); + + TypeLong::MAX = TypeLong::make(max_jlong); // Long MAX + TypeLong::MIN = TypeLong::make(min_jlong); // Long MIN + TypeLong::MINUS_1 = TypeLong::make(-1); // -1 + TypeLong::ZERO = TypeLong::make( 0); // 0 + TypeLong::ONE = TypeLong::make( 1); // 1 + TypeLong::NON_ZERO = TypeLong::make(TypeIntPrototype{{min_jlong, max_jlong}, {1, max_julong}, {0, 0}}, WidenMin)->is_long(); + TypeLong::POS = TypeLong::make(0, max_jlong, WidenMin)->is_long(); // Non-neg values + TypeLong::NEG = TypeLong::make(min_jlong, -1, WidenMin)->is_long(); + TypeLong::LONG = TypeLong::make(min_jlong, max_jlong, WidenMax)->is_long(); // 64-bit integers + TypeLong::INT = TypeLong::make((jlong)min_jint, (jlong)max_jint,WidenMin)->is_long(); + TypeLong::UINT = TypeLong::make(0, (jlong)max_juint, WidenMin)->is_long(); + TypeLong::TYPE_DOMAIN = TypeLong::LONG; const Type **fboth =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); fboth[0] = Type::CONTROL; @@ -1600,11 +1600,11 @@ TypeInt::TypeInt(const TypeIntPrototype& t, int w, bool dual) } const Type* TypeInt::make(const TypeIntPrototype& t, int w, bool dual) { - auto new_t = t.normalize_constraints(); - if (!new_t.first) { + auto new_t = t.canonicalize_constraints(); + if (!new_t._present) { return dual ? Type::BOTTOM : Type::TOP; } - return (new TypeInt(new_t.second, w, dual))->hashcons()->is_int(); + return (new TypeInt(new_t._data, w, dual))->hashcons()->is_int(); } const TypeInt* TypeInt::make(jint lo) { @@ -1636,21 +1636,21 @@ bool TypeInt::properly_contains(const TypeInt* t) const { } const Type* TypeInt::xmeet(const Type* t) const { - return int_type_xmeet(this, t, TypeInt::make, _dual); + return int_type_xmeet(this, t, TypeInt::make, _is_dual); } const Type* TypeInt::xdual() const { return new TypeInt(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, {_zeros, _ones}}, - _widen, !_dual); + _widen, !_is_dual); } const Type* TypeInt::widen(const Type* old, const Type* limit) const { - assert(!_dual, "dual types should only be used for join calculation"); + assert(!_is_dual, "dual types should only be used for join calculation"); return int_type_widen(this, old->isa_int(), limit->isa_int()); } const Type* TypeInt::narrow(const Type* old) const { - assert(!_dual, "dual types should only be used for join calculation"); + assert(!_is_dual, "dual types should only be used for join calculation"); if (old == nullptr) { return this; } @@ -1660,12 +1660,12 @@ const Type* TypeInt::narrow(const Type* old) const { //-----------------------------filter------------------------------------------ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) const { - assert(!_dual, "dual types should only be used for join calculation"); + assert(!_is_dual, "dual types should only be used for join calculation"); const TypeInt* ft = join_helper(kills, include_speculative)->isa_int(); if (ft == nullptr) { return Type::TOP; // Canonical empty value } - assert(!ft->_dual, "dual types should only be used for join calculation"); + assert(!ft->_is_dual, "dual types should only be used for join calculation"); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. @@ -1680,14 +1680,14 @@ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) // Structural equality check for Type representations bool TypeInt::eq(const Type* t) const { const TypeInt* r = t->is_int(); - return int_type_equal(this, r) && _widen == r->_widen && _dual == r->_dual; + return int_type_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeInt::hash(void) const { return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + - (uint)_zeros + (uint)_ones + (uint)_widen + (uint)_dual + (uint)Type::Int; + (uint)_zeros + (uint)_ones + (uint)_widen + (uint)_is_dual + (uint)Type::Int; } //------------------------------is_finite-------------------------------------- @@ -1730,11 +1730,11 @@ TypeLong::TypeLong(const TypeIntPrototype& t, int w, bool dual) } const Type* TypeLong::make(const TypeIntPrototype& t, int w, bool dual) { - auto new_t = t.normalize_constraints(); - if (!new_t.first) { + auto new_t = t.canonicalize_constraints(); + if (!new_t._present) { return dual ? Type::BOTTOM : Type::TOP; } - return (new TypeLong(new_t.second, w, dual))->hashcons()->is_long(); + return (new TypeLong(new_t._data, w, dual))->hashcons()->is_long(); } const TypeLong* TypeLong::make(jlong lo) { @@ -1766,21 +1766,21 @@ bool TypeLong::properly_contains(const TypeLong* t) const { } const Type *TypeLong::xmeet(const Type* t) const { - return int_type_xmeet(this, t, TypeLong::make, _dual); + return int_type_xmeet(this, t, TypeLong::make, _is_dual); } const Type* TypeLong::xdual() const { return new TypeLong(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, {_zeros, _ones}}, - _widen, !_dual); + _widen, !_is_dual); } const Type* TypeLong::widen(const Type* old, const Type* limit) const { - assert(!_dual, "dual types should only be used for join calculation"); + assert(!_is_dual, "dual types should only be used for join calculation"); return int_type_widen(this, old->isa_long(), limit->isa_long()); } const Type* TypeLong::narrow(const Type* old) const { - assert(!_dual, "dual types should only be used for join calculation"); + assert(!_is_dual, "dual types should only be used for join calculation"); if (old == nullptr) { return this; } @@ -1790,12 +1790,12 @@ const Type* TypeLong::narrow(const Type* old) const { //-----------------------------filter------------------------------------------ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) const { - assert(!_dual, "dual types should only be used for join calculation"); + assert(!_is_dual, "dual types should only be used for join calculation"); const TypeLong* ft = join_helper(kills, include_speculative)->isa_long(); if (ft == nullptr) { return Type::TOP; // Canonical empty value } - assert(!ft->_dual, "dual types should only be used for join calculation"); + assert(!ft->_is_dual, "dual types should only be used for join calculation"); if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. @@ -1810,14 +1810,14 @@ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) // Structural equality check for Type representations bool TypeLong::eq(const Type* t) const { const TypeLong* r = t->is_long(); - return int_type_equal(this, r) && _widen == r->_widen && _dual == r->_dual; + return int_type_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeLong::hash(void) const { return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + - (uint)_zeros + (uint)_ones + (uint)_widen + (uint)_dual + (uint)Type::Long; + (uint)_zeros + (uint)_ones + (uint)_widen + (uint)_is_dual + (uint)Type::Long; } //------------------------------is_finite-------------------------------------- @@ -1839,48 +1839,20 @@ bool TypeLong::empty(void) const { //------------------------------dump2------------------------------------------ #ifndef PRODUCT -void TypeInt::dump2( Dict &d, uint depth, outputStream *st ) const { - char buf1[40], buf2[40], buf3[40], buf4[40], buf5[40]; - if (int_type_equal(this, TypeInt::INT)) { - st->print("int"); - } else if (is_con()) { - st->print("int:%s", intname(buf1, sizeof(buf1), get_con())); - } else if (int_type_equal(this, TypeInt::BOOL)) { - st->print("bool"); - } else if (int_type_equal(this, TypeInt::BYTE)) { - st->print("byte"); - } else if (int_type_equal(this, TypeInt::CHAR)) { - st->print("char"); - } else if (int_type_equal(this, TypeInt::SHORT)) { - st->print("short"); - } else { - st->print("int:%s..%s ^ %s..%s, bits:%s", - intname(buf1, sizeof(buf1), _lo), intname(buf2, sizeof(buf2), _hi), - uintname(buf3, sizeof(buf3), _ulo), uintname(buf4, sizeof(buf4), _uhi), - bitname(buf5, sizeof(buf5), _zeros, _ones)); - } +void TypeInt::dump2(Dict& d, uint depth, outputStream* st) const { + int_type_dump(this, st, false); +} - if (_widen > 0 && this != TypeInt::INT) { - st->print(", widen: %d", _widen); - } +void TypeInt::dump_verbose() const { + int_type_dump(this, tty, true); } -void TypeLong::dump2( Dict &d, uint depth, outputStream *st ) const { - char buf1[80], buf2[80], buf3[80], buf4[80], buf5[80]; - if (int_type_equal(this, TypeLong::LONG)) { - st->print("long"); - } else if (is_con()) { - st->print("long:%s", longname(buf1, sizeof(buf1), get_con())); - } else { - st->print("long:%s..%s ^ %s..%s, bits:%s", - longname(buf1, sizeof(buf1), _lo), longname(buf2,sizeof(buf2), _hi), - ulongname(buf3, sizeof(buf3), _ulo), ulongname(buf4, sizeof(buf4), _uhi), - bitname(buf5, sizeof(buf5), _zeros, _ones)); - } +void TypeLong::dump2(Dict& d, uint depth, outputStream* st) const { + int_type_dump(this, st, false); +} - if (_widen > 0 && this != TypeLong::LONG) { - st->print(", widen: %d", _widen); - } +void TypeLong::dump_verbose() const { + int_type_dump(this, tty, true); } #endif diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index f1a40dc667a4c..5c4528a040625 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -555,15 +555,12 @@ class TypeD : public Type { class TypeInteger : public Type { protected: - TypeInteger(TYPES t, int w, bool dual) : Type(t), _dual(dual), _widen(w) {} + TypeInteger(TYPES t, int w, bool dual) : Type(t), _is_dual(dual), _widen(w) {} - // Previously, we signify that a set is in the dual space if _lo > _hi. - // However, with the addition of unsigned range and known bits, this becomes - // ambiguous whether the set is empty or a dual of a non-empty set. - // As a result, we use this field to denote that a set is a dual set. + // Denote that a set is a dual set. // Dual sets are only used to compute the join of 2 sets, and not used // outside. - const bool _dual; + const bool _is_dual; public: const short _widen; // Limit on times we widen this sucker @@ -599,11 +596,12 @@ class TypeInt : public TypeInteger { virtual uint hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous + // A value is in the set represented by this TypeInt if it satisfies all + // the below constraints, see contains(jint) const jint _lo, _hi; // Lower bound, upper bound in the signed domain const juint _ulo, _uhi; // Lower bound, upper bound in the unsigned domain const juint _zeros, _ones; // Bits that are known to be 0 or 1 - static const TypeInt* cast(const Type* t) { return t->is_int(); } static const TypeInt* try_cast(const Type* t) { return t->isa_int(); } static const TypeInt* make(jint lo); // must always specify w @@ -614,7 +612,7 @@ class TypeInt : public TypeInteger { bool is_con() const { return _lo == _hi; } bool is_con(jint i) const { return is_con() && _lo == i; } jint get_con() const { assert(is_con(), ""); return _lo; } - // Check if a TypeInt is a subset of this TypeInt (i.e. all elements of the + // Check if a jint/TypeInt is a subset of this TypeInt (i.e. all elements of the // argument are also elements of this type) bool contains(jint i) const; bool contains(const TypeInt* t) const; @@ -657,9 +655,10 @@ class TypeInt : public TypeInteger { static const TypeInt* SYMINT; // symmetric range [-max_jint..max_jint] static const TypeInt* TYPE_DOMAIN; // alias for TypeInt::INT - static const TypeInt *as_self(const Type *t) { return t->is_int(); } + static const TypeInt* as_self(const Type* t) { return t->is_int(); } #ifndef PRODUCT - virtual void dump2( Dict &d, uint depth, outputStream *st ) const; + virtual void dump2(Dict& d, uint depth, outputStream* st) const; + void dump_verbose() const; #endif }; @@ -680,11 +679,12 @@ class TypeLong : public TypeInteger { virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous public: + // A value is in the set represented by this TypeLong if it satisfies all + // the below constraints, see contains(jlong) const jlong _lo, _hi; // Lower bound, upper bound in the signed domain const julong _ulo, _uhi; // Lower bound, upper bound in the unsigned domain const julong _zeros, _ones; // Bits that are known to be 0 or 1 - static const TypeLong* cast(const Type* t) { return t->is_long(); } static const TypeLong* try_cast(const Type* t) { return t->isa_long(); } static const TypeLong* make(jlong lo); // must always specify w @@ -695,7 +695,7 @@ class TypeLong : public TypeInteger { bool is_con() const { return _lo == _hi; } bool is_con(jlong i) const { return is_con() && _lo == i; } jlong get_con() const { assert(is_con(), "" ); return _lo; } - // Check if a TypeLong is a subset of this TypeLong (i.e. all elements of the + // Check if a jlong/TypeLong is a subset of this TypeLong (i.e. all elements of the // argument are also elements of this type) bool contains(jlong i) const; bool contains(const TypeLong* t) const; @@ -729,10 +729,11 @@ class TypeLong : public TypeInteger { static const TypeLong* TYPE_DOMAIN; // alias for TypeLong::LONG // static convenience methods. - static const TypeLong *as_self(const Type *t) { return t->is_long(); } + static const TypeLong* as_self(const Type* t) { return t->is_long(); } #ifndef PRODUCT - virtual void dump2( Dict &d, uint, outputStream *st ) const;// Specialized per-Type dumping + virtual void dump2(Dict& d, uint, outputStream* st) const;// Specialized per-Type dumping + void dump_verbose() const; #endif }; diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index d44a6701e8c9b..4bfea4e6305a8 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -44,48 +44,48 @@ julong uniform_random() { return (julong(os::random()) << 32) | julong(juint(os::random())); } -template +template static void test_normalize_constraints_simple() { constexpr int parameters = 10; for (int i = 0; i < parameters; i++) { - T a = uniform_random(); - T b = uniform_random(); + S a = uniform_random(); + S b = uniform_random(); { - T lo = MIN2(a, b); - T hi = MAX2(a, b); - TypeIntPrototype t{{lo, hi}, {std::numeric_limits::min(), std::numeric_limits::max()}, + S lo = MIN2(a, b); + S hi = MAX2(a, b); + TypeIntPrototype t{{lo, hi}, {std::numeric_limits::min(), std::numeric_limits::max()}, {0, 0}}; - auto new_t = t.normalize_constraints(); - ASSERT_TRUE(new_t.first); - DEBUG_ONLY(new_t.second.verify_constraints()); - ASSERT_EQ(lo, new_t.second._srange._lo); - ASSERT_EQ(hi, new_t.second._srange._hi); + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + DEBUG_ONLY(new_t._data.verify_constraints()); + ASSERT_EQ(lo, new_t._data._srange._lo); + ASSERT_EQ(hi, new_t._data._srange._hi); if (U(lo) <= U(hi)) { - ASSERT_EQ(U(lo), new_t.second._urange._lo); - ASSERT_EQ(U(hi), new_t.second._urange._hi); + ASSERT_EQ(U(lo), new_t._data._urange._lo); + ASSERT_EQ(U(hi), new_t._data._urange._hi); } else { - ASSERT_EQ(std::numeric_limits::min(), new_t.second._urange._lo); - ASSERT_EQ(std::numeric_limits::max(), new_t.second._urange._hi); + ASSERT_EQ(std::numeric_limits::min(), new_t._data._urange._lo); + ASSERT_EQ(std::numeric_limits::max(), new_t._data._urange._hi); } } { U ulo = MIN2(a, b); U uhi = MAX2(a, b); - TypeIntPrototype t{{std::numeric_limits::min(), std::numeric_limits::max()}, + TypeIntPrototype t{{std::numeric_limits::min(), std::numeric_limits::max()}, {ulo, uhi}, {0, 0}}; - auto new_t = t.normalize_constraints(); - ASSERT_TRUE(new_t.first); - DEBUG_ONLY(new_t.second.verify_constraints()); - ASSERT_EQ(ulo, new_t.second._urange._lo); - ASSERT_EQ(uhi, new_t.second._urange._hi); - if (T(ulo) <= T(uhi)) { - ASSERT_EQ(T(ulo), new_t.second._srange._lo); - ASSERT_EQ(T(uhi), new_t.second._srange._hi); + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + DEBUG_ONLY(new_t._data.verify_constraints()); + ASSERT_EQ(ulo, new_t._data._urange._lo); + ASSERT_EQ(uhi, new_t._data._urange._hi); + if (S(ulo) <= S(uhi)) { + ASSERT_EQ(S(ulo), new_t._data._srange._lo); + ASSERT_EQ(S(uhi), new_t._data._srange._hi); } else { - ASSERT_EQ(std::numeric_limits::min(), new_t.second._srange._lo); - ASSERT_EQ(std::numeric_limits::max(), new_t.second._srange._hi); + ASSERT_EQ(std::numeric_limits::min(), new_t._data._srange._lo); + ASSERT_EQ(std::numeric_limits::max(), new_t._data._srange._hi); } } @@ -93,28 +93,28 @@ static void test_normalize_constraints_simple() { U intersection = a & b; U zeros = a ^ intersection; U ones = b ^ intersection; - TypeIntPrototype t{{std::numeric_limits::min(), std::numeric_limits::max()}, + TypeIntPrototype t{{std::numeric_limits::min(), std::numeric_limits::max()}, {std::numeric_limits::min(), std::numeric_limits::max()}, {zeros, ones}}; - auto new_t = t.normalize_constraints(); - ASSERT_TRUE(new_t.first); - DEBUG_ONLY(new_t.second.verify_constraints()); - ASSERT_EQ(zeros, new_t.second._bits._zeros); - ASSERT_EQ(ones, new_t.second._bits._ones); - ASSERT_EQ(ones, new_t.second._urange._lo); - ASSERT_EQ(~zeros, new_t.second._urange._hi); + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + DEBUG_ONLY(new_t._data.verify_constraints()); + ASSERT_EQ(zeros, new_t._data._bits._zeros); + ASSERT_EQ(ones, new_t._data._bits._ones); + ASSERT_EQ(ones, new_t._data._urange._lo); + ASSERT_EQ(~zeros, new_t._data._urange._hi); } } } -template +template static void test_normalize_constraints_random() { constexpr int samples = 1000; constexpr int parameters = 1000; for (int i = 0; i < parameters; i++) { - T s1 = uniform_random(); - T s2 = uniform_random(); - T lo = MIN2(s1, s2); - T hi = MAX2(s1, s2); + S s1 = uniform_random(); + S s2 = uniform_random(); + S lo = MIN2(s1, s2); + S hi = MAX2(s1, s2); U u1 = uniform_random(); U u2 = uniform_random(); U ulo = MIN2(u1, u2); @@ -124,17 +124,17 @@ static void test_normalize_constraints_random() { U intersection = b1 & b2; U zeros = b1 ^ intersection; U ones = b2 ^ intersection; - TypeIntPrototype t{{lo, hi}, {ulo, uhi}, {zeros, ones}}; - auto new_t = t.normalize_constraints(); - if (new_t.first) { - new_t.second.verify_constraints(); + TypeIntPrototype t{{lo, hi}, {ulo, uhi}, {zeros, ones}}; + auto new_t = t.canonicalize_constraints(); + if (new_t._present) { + new_t._data.verify_constraints(); } for (int j = 0; j < samples; j++) { - T v = uniform_random(); - if (!new_t.first) { + S v = uniform_random(); + if (!new_t._present) { ASSERT_FALSE(t.contains(v)); } else { - ASSERT_EQ(t.contains(v), new_t.second.contains(v)); + ASSERT_EQ(t.contains(v), new_t._data.contains(v)); } } } From ae4738502213544c36001deec71a750ba33e4156 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 4 Sep 2024 03:35:12 +0700 Subject: [PATCH 11/45] move static_asserts --- src/hotspot/share/opto/rangeinference.cpp | 8 -------- src/hotspot/share/opto/rangeinference.hpp | 6 ++++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index a5f0ad260db01..140a5c459dfbb 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -162,10 +162,6 @@ normalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits template CanonicalizedTypeIntPrototype TypeIntPrototype::canonicalize_constraints() const { - static_assert(std::is_signed::value, ""); - static_assert(std::is_unsigned::value, ""); - static_assert(sizeof(S) == sizeof(U), ""); - RangeInt srange = _srange; RangeInt urange = _urange; if (srange._lo > srange._hi || @@ -243,10 +239,6 @@ bool TypeIntPrototype::contains(S v) const { // Verify that this set representation is canonical template void TypeIntPrototype::verify_constraints() const { - static_assert(std::is_signed::value, ""); - static_assert(std::is_unsigned::value, ""); - static_assert(sizeof(S) == sizeof(U), ""); - // Assert that the bounds cannot be further tightened assert(contains(_srange._lo) && contains(_srange._hi) && contains(_urange._lo) && contains(_urange._hi), ""); diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 4ba6970645210..1857db5924782 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -38,6 +38,8 @@ class RangeInt { template class KnownBits { + static_assert(std::is_unsigned::value, ""); + public: U _zeros; U _ones; @@ -56,6 +58,10 @@ class CanonicalizedTypeIntPrototype { template class TypeIntPrototype { public: + static_assert(std::is_signed::value, ""); + static_assert(std::is_unsigned::value, ""); + static_assert(sizeof(S) == sizeof(U), ""); + RangeInt _srange; RangeInt _urange; KnownBits _bits; From 2bf545fb20f0fe3dd26ff2bedc0a4ff8071b92f1 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 5 Sep 2024 02:07:43 +0700 Subject: [PATCH 12/45] add more comments, group KnownBits --- src/hotspot/share/opto/rangeinference.cpp | 268 +++++++++++++++------- src/hotspot/share/opto/rangeinference.hpp | 28 ++- src/hotspot/share/opto/type.cpp | 30 +-- src/hotspot/share/opto/type.hpp | 5 +- 4 files changed, 224 insertions(+), 107 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 140a5c459dfbb..f668193b7fb7e 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -32,79 +32,135 @@ constexpr juint SMALLINT = 3; // a value too insignificant to consider widening template class AdjustResult { public: + // Denote whether there is a change in _data compared to the previous + // iteration bool _progress; - bool _present; + bool _present; // whether the constraints are contradictory T _data; + + static AdjustResult make_empty() { + return {true, false, {}}; + } }; +// In the canonical form, [lo, hi] intersects with [ulo, uhi] can result in 2 +// cases: +// - [lo, hi] is the same as [ulo, uhi], lo and hi are both >= 0 or both < 0 +// - [lo, hi] is not the same as [ulo, uhi], which results in the intersections +// being [lo, uhi] and [ulo, hi], lo and uhi are < 0 while ulo and hi are >= 0 +// This class deals with each interval with both bounds being >= 0 or < 0 template class SimpleCanonicalResult { + static_assert(std::is_unsigned::value, "bit info should be unsigned"); public: - bool _present; + bool _present; // whether this is an empty set RangeInt _bounds; KnownBits _bits; + + static SimpleCanonicalResult make_empty() { + return {false, {}, {}}; + } }; -// Try to tighten the bound constraints from the known bit information -// E.g: if lo = 0 but the lowest bit is always 1 then we can tighten -// lo = 1 +// Find the minimum value that is not less than lo and satisfies bits +template +static U adjust_lo(U lo, const KnownBits& bits) { + constexpr size_t W = sizeof(U) * 8; + // Violation of lo with respects to bits + // E.g: lo = 1100 + // zeros = 0100 + // ones = 1001 + // zero_violation = 0100, i.e the second bit should be zero, but it is 1 in + // lo. Similarly, one_violation = 0001, i.e the forth bit should be one, but + // it is 0 in lo. These make lo not satisfy the bit constraints, which + // results in us having to find the smallest value that satisfies bits + U zero_violation = lo & bits._zeros; + U one_violation = ~lo & bits._ones; + if (zero_violation == one_violation) { + // This means lo does not violate bits, it is the result + assert(zero_violation == 0, ""); + return lo; + } + + if (zero_violation < one_violation) { + // This means that the first bit that does not satisfy the bit + // requirement is a 0 that should be a 1, try to set that bit + // E.g: lo = 10010010, zeros = 00100100, ones = 01001000 + // first_violation is the position of the violation counting from the + // lowest bit up (0-based) + juint first_violation = W - 1 - count_leading_zeros(one_violation); + U alignment = U(1) << first_violation; + // This is the first value which have the violated bit set, which means + // that the result should not be smaller than this + // 11000000, notice that all bits after the second digit are zeroed, + // which automatically satisfies bits._zeros + lo = (lo & -alignment) + alignment; + // Simply satisfy bits._ones + return lo | bits._ones; // 11001000 + } + + // This is more difficult because trying to unset a bit requires us to flip + // some bits before it (higher bits) + // Suppose lo = 11000110, zeros = 00001010, ones = 10000001 + // The smallest value with the 7-th bit unset would be 11001000 but then the + // 5-th bit does not match, the smallest value with both bits unset would be + // 11010000 + // We can obtain this number directly by finding the last place before + // the first mismatch such that it is 0 in lo and not required to be unset + juint first_violation = W - 1 - count_leading_zeros(zero_violation); + // This mask out all bits after the first violation + U find_mask = std::numeric_limits::max() << first_violation; // 11111100 + // The smallest value which satisfies bits._zeros will need to set a bit in + // lo that is previously unset (because the value needs to be larger than lo) + // and that bit will need to be unset in bits._zeros as well + U either = lo | bits._zeros; // 11001110 + // The bit we want to set is the last bit unset in either that stands before + // the first violation, which is the last set bit of tmp + U tmp = ~either & find_mask; // 00110000 + // Isolate the last bit + U alignment = tmp & (-tmp); // 00010000 + // Set the bit and unset all the bit after, this is the smallest value that + // satisfies bits._zeros + lo = (lo & -alignment) + alignment; // 11010000 + // Satisfy bits._ones + return lo | bits._ones; // 11010001 +} + +// Try to tighten the bound constraints from the known bit information. I.e, we +// find the smallest value not smaller than lo, as well as the largest value +// not larger than hi both of which satisfy bits +// E.g: lo = 0010, hi = 1001 +// zeros = 0011 +// ones = 0000 +// -> 4-aligned +// +// 0 1 2 3 4 5 6 7 8 9 10 +// 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 +// bits: ok . . . ok . . . ok . . +// bounds: lo hi +// adjust: --------> lo hi <--- template static AdjustResult> adjust_bounds_from_bits(const RangeInt& bounds, const KnownBits& bits) { - // Find the minimum value that is not less than lo and satisfies bits - auto adjust_lo = [](U lo, const KnownBits& bits) { - constexpr size_t W = sizeof(U) * 8; - // Violation of lo with respects to bits - U zero_violation = lo & bits._zeros; - U one_violation = ~lo & bits._ones; - if (zero_violation == one_violation) { - // This means lo does not violate bits, it is the result - assert(zero_violation == 0, ""); - return lo; - } - - if (zero_violation < one_violation) { - // This means that the first bit that does not satisfy the bit - // requirement is a 0 that should be a 1 - // Try to set that bit, the smallest value will have all the following - // bits being zeros - // E.g: lo = 10010010, zeros = 00100100, ones = 01001000 - juint first_violation = W - 1 - count_leading_zeros(one_violation); - U alignment = U(1) << first_violation; - // 11000000, notice that all bits after the second digit are zeroed, - // which automatically satisfies the unset bit requirement - lo = (lo & -alignment) + alignment; - // Simply satisfy the set bit requirement - return lo | bits._ones; // 11001000 - } - - // This is more difficult because trying to unset a bit requires us to flip - // some bits before it (higher bits) - // Suppose lo = 11000110, zeros = 00001010, ones = 10010001 - // Since the 2-nd bit must be 0, we need to align up the lower bound. - // This results in lo = 11001000, but then the 4-th bit does not match, - // align up again gives us 11010000 - // We can align up directly to 11010000 by finding the last place before - // the first mismatch such that it is 0 in lo and not required to be unset - juint first_violation = W - 1 - count_leading_zeros(zero_violation); - U find_mask = std::numeric_limits::max() << first_violation; // 11111100 - U either = lo | bits._zeros; // 11001110 - U tmp = ~either & find_mask; // 00110000 - U alignment = tmp & (-tmp); // 00010000 - lo = (lo & -alignment) + alignment; // 11010000 - return lo | bits._ones; // 11010001 - }; - U new_lo = adjust_lo(bounds._lo, bits); if (new_lo < bounds._lo) { // This means we wrapped around, which means no value not less than lo // satisfies bits - return {true, false, {}}; - } - // Adjust hi by adjusting its bitwise negation + return AdjustResult>::make_empty(); + } + + // We need to find the largest value not larger than hi that satisfies bits + // One possible method is to do similar to adjust_lo, just with the other + // direction + // However, we can observe that if v satisfies {bits._zeros, bits._ones}, + // then ~v would satisfy {bits._ones, bits._zeros}. Combine with the fact + // that ~ is a strictly decreasing function, if new_hi is the largest value + // not larger than hi that satisfies {bits._zeros, bits._ones}, then ~new_hi + // is the smallest value not smaller than ~hi that satisfies + // {bits._ones, bits._zeros} U new_hi = ~adjust_lo(~bounds._hi, {bits._ones, bits._zeros}); if (new_hi > bounds._hi) { - return {true, false, {}}; + return AdjustResult>::make_empty(); } bool progress = (new_lo != bounds._lo) || (new_hi != bounds._hi); @@ -115,15 +171,22 @@ adjust_bounds_from_bits(const RangeInt& bounds, const KnownBits& bits) { // Try to tighten the known bit constraints from the bound information by // extracting the common prefix of lo and hi and combining with the current // bit constraints -// E.g: if lo = 0 and hi = 10, then all but the lowest 4 bits must be 0 +// E.g: lo = 010011 +// hi = 010100, +// then all values in [lo, hi] would be +// 010*** template static AdjustResult> adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { - // Find the mask to filter the common prefix + // Find the mask to filter the common prefix, all values between bounds._lo + // and bounds._hi should share this common prefix in terms of bits U mismatch = bounds._lo ^ bounds._hi; + // Find the first mismatch, all bits before it is the same in bounds._lo and + // bounds._hi U match_mask = mismatch == 0 ? std::numeric_limits::max() : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); - // match_mask & bounds._lo is the common prefix + // match_mask & bounds._lo is the common prefix, extract zeros and ones from + // it U new_zeros = bits._zeros | (match_mask &~ bounds._lo); U new_ones = bits._ones | (match_mask & bounds._lo); bool progress = (new_zeros != bits._zeros) || (new_ones != bits._ones); @@ -133,15 +196,20 @@ adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { // Try to tighten both the bounds and the bits at the same time // Iteratively tighten 1 using the other until no progress is made. -// This function converges because bit constraints converge fast. +// This function converges because at each iteration, some bits that are +// unknown is made known. As there are at most 64 bits, the number of +// iterations should not be larger than 64 template static SimpleCanonicalResult -normalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { +canonicalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { AdjustResult> nbits = adjust_bits_from_bounds(bits, bounds); if (!nbits._present) { - return {false, {}, {}}; + return SimpleCanonicalResult::make_empty(); } AdjustResult> nbounds{true, true, bounds}; + // Since bits are derived from bounds in the previous iteration and vice + // versa, if one does not show progress, the other will also not show + // progress, so we terminate early while (true) { nbounds = adjust_bounds_from_bits(nbounds._data, nbits._data); if (!nbounds._progress || !nbounds._present) { @@ -164,12 +232,15 @@ CanonicalizedTypeIntPrototype TypeIntPrototype::canonicalize_constraints() const { RangeInt srange = _srange; RangeInt urange = _urange; + // Trivial contradictions if (srange._lo > srange._hi || urange._lo > urange._hi || (_bits._zeros & _bits._ones) != 0) { - return {false, {}}; + return CanonicalizedTypeIntPrototype::make_empty(); } + // Trivially canonicalize the bounds so that srange._lo and urange._hi are + // both < 0 or >= 0. The same for srange._hi and urange._ulo if (S(urange._lo) > S(urange._hi)) { if (S(urange._hi) < srange._lo) { urange._hi = std::numeric_limits::max(); @@ -183,10 +254,10 @@ TypeIntPrototype::canonicalize_constraints() const { urange._lo = MAX2(urange._lo, srange._lo); urange._hi = MIN2(urange._hi, srange._hi); if (urange._lo > urange._hi) { - return {false, {}}; + return CanonicalizedTypeIntPrototype::make_empty(); } - auto type = normalize_constraints_simple(urange, _bits); + auto type = canonicalize_constraints_simple(urange, _bits); return {type._present, {{S(type._bounds._lo), S(type._bounds._hi)}, type._bounds, type._bits}}; } @@ -195,11 +266,11 @@ TypeIntPrototype::canonicalize_constraints() const { // [lo, uhi], which consists of negative values // [ulo, hi] which consists of non-negative values // We process these 2 separately and combine the results - auto neg_type = normalize_constraints_simple({U(srange._lo), urange._hi}, _bits); - auto pos_type = normalize_constraints_simple({urange._lo, U(srange._hi)}, _bits); + auto neg_type = canonicalize_constraints_simple({U(srange._lo), urange._hi}, _bits); + auto pos_type = canonicalize_constraints_simple({urange._lo, U(srange._hi)}, _bits); if (!neg_type._present && !pos_type._present) { - return {false, {}}; + return CanonicalizedTypeIntPrototype::make_empty(); } else if (!neg_type._present) { return {true, {{S(pos_type._bounds._lo), S(pos_type._bounds._hi)}, pos_type._bounds, pos_type._bits}}; @@ -232,8 +303,8 @@ int TypeIntPrototype::normalize_widen(int w) const { #ifdef ASSERT template bool TypeIntPrototype::contains(S v) const { - return v >= _srange._lo && v <= _srange._hi && U(v) >= _urange._lo && U(v) <= _urange._hi && - (v & _bits._zeros) == 0 && (~v & _bits._ones) == 0; + U u = v; + return v >= _srange._lo && v <= _srange._hi && u >= _urange._lo && u <= _urange._hi && _bits.is_satisfied_by(u); } // Verify that this set representation is canonical @@ -280,13 +351,13 @@ const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(con // meet return make(TypeIntPrototype{{MIN2(i1->_lo, i2->_lo), MAX2(i1->_hi, i2->_hi)}, {MIN2(i1->_ulo, i2->_ulo), MAX2(i1->_uhi, i2->_uhi)}, - {i1->_zeros & i2->_zeros, i1->_ones & i2->_ones}}, + {i1->_bits._zeros & i2->_bits._zeros, i1->_bits._ones & i2->_bits._ones}}, MAX2(i1->_widen, i2->_widen), false); } // join return make(TypeIntPrototype{{MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi)}, {MAX2(i1->_ulo, i2->_ulo), MIN2(i1->_uhi, i2->_uhi)}, - {i1->_zeros | i2->_zeros, i1->_ones | i2->_ones}}, + {i1->_bits._zeros | i2->_bits._zeros, i1->_bits._ones | i2->_bits._ones}}, MIN2(i1->_widen, i2->_widen), true); } @@ -366,7 +437,7 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { if (nt->_widen < Type::WidenMax) { // Returned widened new guy - TypeIntPrototype prototype{{nt->_lo, nt->_hi}, {nt->_ulo, nt->_uhi}, {nt->_zeros, nt->_ones}}; + TypeIntPrototype prototype{{nt->_lo, nt->_hi}, {nt->_ulo, nt->_uhi}, nt->_bits}; return CT::make(prototype, nt->_widen + 1); } @@ -376,15 +447,15 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { S max = std::numeric_limits::max(); U umin = std::numeric_limits::min(); U umax = std::numeric_limits::max(); - U zeros = nt->_zeros; - U ones = nt->_ones; + U zeros = nt->_bits._zeros; + U ones = nt->_bits._ones; if (lt != nullptr) { min = lt->_lo; max = lt->_hi; umin = lt->_ulo; umax = lt->_uhi; - zeros |= lt->_zeros; - ones |= lt->_ones; + zeros |= lt->_bits._zeros; + ones |= lt->_bits._ones; } TypeIntPrototype prototype{{min, max}, {umin, umax}, {zeros, ones}}; return CT::make(prototype, Type::WidenMax); @@ -420,7 +491,7 @@ const Type* int_type_narrow(const CT* nt, const CT* ot) { } // Bits change - if (ot->_zeros != nt->_zeros || ot->_ones != nt->_ones) { + if (ot->_bits._zeros != nt->_bits._zeros || ot->_bits._ones != nt->_bits._ones) { return nt; } @@ -579,14 +650,29 @@ void int_type_dump(const TypeInt* t, outputStream* st, bool verbose) { st->print("short"); } else { if (verbose) { - st->print("int:%s..%s ^ %s..%s, bits:%s", + st->print("int:%s..%s, %s..%s, %s", intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi), uintname(buf3, sizeof(buf3), t->_ulo), uintname(buf4, sizeof(buf4), t->_uhi), - bitname(buf5, sizeof(buf5), t->_zeros, t->_ones)); + bitname(buf5, sizeof(buf5), t->_bits._zeros, t->_bits._ones)); } else { - st->print("int:%s..%s ^ %s..%s", - intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi), - uintname(buf3, sizeof(buf3), t->_ulo), uintname(buf4, sizeof(buf4), t->_uhi)); + if (t->_lo >= 0) { + if (t->_hi == max_jint) { + st->print("int:>=%s", intname(buf1, sizeof(buf1), t->_lo)); + } else { + st->print("int:%s..%s", intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi)); + } + } else if (t->_hi < 0) { + if (t->_lo == min_jint) { + st->print("int:<=%s", intname(buf1, sizeof(buf1), t->_hi)); + } else { + st->print("int:%s..%s", intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi)); + } + } else { + st->print("int:%s..%s, %s..%s", + intname(buf1, sizeof(buf1), t->_lo), intname(buf2, sizeof(buf2), t->_hi), + uintname(buf3, sizeof(buf3), t->_ulo), uintname(buf4, sizeof(buf4), t->_uhi)); + } + } } @@ -606,11 +692,25 @@ void int_type_dump(const TypeLong* t, outputStream* st, bool verbose) { st->print("long:%s..%s ^ %s..%s, bits:%s", longname(buf1, sizeof(buf1), t->_lo), longname(buf2,sizeof(buf2), t-> _hi), ulongname(buf3, sizeof(buf3), t->_ulo), ulongname(buf4, sizeof(buf4), t->_uhi), - bitname(buf5, sizeof(buf5), t->_zeros, t->_ones)); + bitname(buf5, sizeof(buf5), t->_bits._zeros, t->_bits._ones)); } else { - st->print("long:%s..%s ^ %s..%s", - longname(buf1, sizeof(buf1), t->_lo), longname(buf2,sizeof(buf2), t-> _hi), - ulongname(buf3, sizeof(buf3), t->_ulo), ulongname(buf4, sizeof(buf4), t->_uhi)); + if (t->_lo >= 0) { + if (t->_hi == max_jint) { + st->print("long:>=%s", longname(buf1, sizeof(buf1), t->_lo)); + } else { + st->print("long:%s..%s", longname(buf1, sizeof(buf1), t->_lo), longname(buf2, sizeof(buf2), t->_hi)); + } + } else if (t->_hi < 0) { + if (t->_lo == min_jint) { + st->print("long:<=%s", longname(buf1, sizeof(buf1), t->_hi)); + } else { + st->print("long:%s..%s", longname(buf1, sizeof(buf1), t->_lo), longname(buf2, sizeof(buf2), t->_hi)); + } + } else { + st->print("long:%s..%s ^ %s..%s", + longname(buf1, sizeof(buf1), t->_lo), longname(buf2,sizeof(buf2), t-> _hi), + ulongname(buf3, sizeof(buf3), t->_ulo), ulongname(buf4, sizeof(buf4), t->_uhi)); + } } } diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 1857db5924782..497e2443b6688 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -25,6 +25,8 @@ #ifndef SHARE_OPTO_RANGEINFERENCE_HPP #define SHARE_OPTO_RANGEINFERENCE_HPP +#include + class Type; class TypeInt; class TypeLong; @@ -36,13 +38,29 @@ class RangeInt { T _hi; }; +/** + * Bits that are known to be 0 or 1. A value v satisfies this constraint iff + * (v & zeros) == 0 && (~v & ones) == 0. I.e, all bits that is set in zeros + * must be unset in v, and all bits that is set in ones must be set in v. + * + * E.g: + * zeros: 00110100 + * ones: 10000010 + * Then: 10001010 would satisfy the bit constraints + * while: 10011000 would not since the bit at the 4th position violates + * zeros and the bit at the 7th position violates ones + */ template class KnownBits { - static_assert(std::is_unsigned::value, ""); + static_assert(std::is_unsigned::value, "bit info should be unsigned"); public: U _zeros; U _ones; + + bool is_satisfied_by(U v) const { + return (v & _zeros) == 0 && (~v & _ones) == 0; + } }; template @@ -53,6 +71,10 @@ class CanonicalizedTypeIntPrototype { public: bool _present; TypeIntPrototype _data; + + static CanonicalizedTypeIntPrototype make_empty() { + return {false, {}}; + } }; template @@ -91,13 +113,13 @@ const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(con template bool int_type_equal(const CT* t1, const CT* t2) { return t1->_lo == t2->_lo && t1->_hi == t2->_hi && t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && - t1->_zeros == t2->_zeros && t1->_ones == t2->_ones; + t1->_bits._zeros == t2->_bits._zeros && t1->_bits._ones == t2->_bits._ones; } template bool int_type_subset(const CT* super, const CT* sub) { return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && - (super->_zeros &~ sub->_zeros) == 0 && (super->_ones &~ sub->_ones) == 0; + (super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0; } template diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index d92be7997f650..21a2a43ee629f 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1593,9 +1593,8 @@ const TypeInt* TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] const TypeInt* TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT TypeInt::TypeInt(const TypeIntPrototype& t, int w, bool dual) - : TypeInteger(Int, t.normalize_widen(w), dual), - _lo(t._srange._lo), _hi(t._srange._hi), _ulo(t._urange._lo), _uhi(t._urange._hi), - _zeros(t._bits._zeros), _ones(t._bits._ones) { + : TypeInteger(Int, t.normalize_widen(w), dual), _lo(t._srange._lo), _hi(t._srange._hi), + _ulo(t._urange._lo), _uhi(t._urange._hi), _bits(t._bits) { DEBUG_ONLY(t.verify_constraints()); } @@ -1623,8 +1622,7 @@ const Type* TypeInt::make(const TypeIntPrototype& t, int w) { bool TypeInt::contains(jint i) const { juint u = i; - return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && - (u & _zeros) == 0 && (~u & _ones) == 0; + return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u); } bool TypeInt::contains(const TypeInt* t) const { @@ -1640,7 +1638,7 @@ const Type* TypeInt::xmeet(const Type* t) const { } const Type* TypeInt::xdual() const { - return new TypeInt(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, {_zeros, _ones}}, + return new TypeInt(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, _bits}, _widen, !_is_dual); } @@ -1669,8 +1667,7 @@ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. - return (new TypeInt(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, - {ft->_zeros, ft->_ones}}, + return (new TypeInt(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, ft->_bits}, this->_widen, false))->hashcons(); } return ft; @@ -1687,7 +1684,7 @@ bool TypeInt::eq(const Type* t) const { // Type-specific hashing function. uint TypeInt::hash(void) const { return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + - (uint)_zeros + (uint)_ones + (uint)_widen + (uint)_is_dual + (uint)Type::Int; + (uint)_bits._zeros + (uint)_bits._ones + (uint)_widen + (uint)_is_dual + (uint)Type::Int; } //------------------------------is_finite-------------------------------------- @@ -1723,9 +1720,8 @@ const TypeLong* TypeLong::UINT; // 32-bit unsigned subrange const TypeLong* TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG TypeLong::TypeLong(const TypeIntPrototype& t, int w, bool dual) - : TypeInteger(Long, t.normalize_widen(w), dual), - _lo(t._srange._lo), _hi(t._srange._hi), _ulo(t._urange._lo), _uhi(t._urange._hi), - _zeros(t._bits._zeros), _ones(t._bits._ones) { + : TypeInteger(Long, t.normalize_widen(w), dual), _lo(t._srange._lo), _hi(t._srange._hi), + _ulo(t._urange._lo), _uhi(t._urange._hi), _bits(t._bits) { DEBUG_ONLY(t.verify_constraints()); } @@ -1753,8 +1749,7 @@ const Type* TypeLong::make(const TypeIntPrototype& t, int w) { bool TypeLong::contains(jlong i) const { julong u = i; - return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && - (u & _zeros) == 0 && (~u & _ones) == 0; + return i >= _lo && i <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u); } bool TypeLong::contains(const TypeLong* t) const { @@ -1770,7 +1765,7 @@ const Type *TypeLong::xmeet(const Type* t) const { } const Type* TypeLong::xdual() const { - return new TypeLong(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, {_zeros, _ones}}, + return new TypeLong(TypeIntPrototype{{_lo, _hi}, {_ulo, _uhi}, _bits}, _widen, !_is_dual); } @@ -1799,8 +1794,7 @@ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) if (ft->_widen < this->_widen) { // Do not allow the value of kill->_widen to affect the outcome. // The widen bits must be allowed to run freely through the graph. - return (new TypeLong(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, - {ft->_zeros, ft->_ones}}, + return (new TypeLong(TypeIntPrototype{{ft->_lo, ft->_hi}, {ft->_ulo, ft->_uhi}, ft->_bits}, this->_widen, false))->hashcons(); } return ft; @@ -1817,7 +1811,7 @@ bool TypeLong::eq(const Type* t) const { // Type-specific hashing function. uint TypeLong::hash(void) const { return (uint)_lo + (uint)_hi + (uint)_ulo + (uint)_uhi + - (uint)_zeros + (uint)_ones + (uint)_widen + (uint)_is_dual + (uint)Type::Long; + (uint)_bits._zeros + (uint)_bits._ones + (uint)_widen + (uint)_is_dual + (uint)Type::Long; } //------------------------------is_finite-------------------------------------- diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 5c4528a040625..6451998097bd9 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -27,6 +27,7 @@ #include "opto/adlcVMDeps.hpp" #include "opto/compile.hpp" +#include "opto/rangeinference.hpp" #include "runtime/handles.hpp" // Portions of code courtesy of Clifford Click @@ -600,7 +601,7 @@ class TypeInt : public TypeInteger { // the below constraints, see contains(jint) const jint _lo, _hi; // Lower bound, upper bound in the signed domain const juint _ulo, _uhi; // Lower bound, upper bound in the unsigned domain - const juint _zeros, _ones; // Bits that are known to be 0 or 1 + const KnownBits _bits; static const TypeInt* try_cast(const Type* t) { return t->isa_int(); } static const TypeInt* make(jint lo); @@ -683,7 +684,7 @@ class TypeLong : public TypeInteger { // the below constraints, see contains(jlong) const jlong _lo, _hi; // Lower bound, upper bound in the signed domain const julong _ulo, _uhi; // Lower bound, upper bound in the unsigned domain - const julong _zeros, _ones; // Bits that are known to be 0 or 1 + const KnownBits _bits; static const TypeLong* try_cast(const Type* t) { return t->isa_long(); } static const TypeLong* make(jlong lo); From 4f4a6bea72e5b4d763c11467fe6d157fe44ef37c Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 5 Sep 2024 02:22:56 +0700 Subject: [PATCH 13/45] fix build --- src/hotspot/share/opto/rangeinference.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 497e2443b6688..fbfb300b1b875 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_OPTO_RANGEINFERENCE_HPP #define SHARE_OPTO_RANGEINFERENCE_HPP +#include "utilities/globalDefinitions.hpp" #include class Type; From 8d14f8ee0cba8423388cbeb3d6b166f0bff491f6 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 5 Sep 2024 02:46:00 +0700 Subject: [PATCH 14/45] fix build --- src/hotspot/share/opto/rangeinference.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index fbfb300b1b875..1f56022d0fb00 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -28,6 +28,7 @@ #include "utilities/globalDefinitions.hpp" #include +class outputStream; class Type; class TypeInt; class TypeLong; From f1648217069542d74483322dcfe11897e8a1dc93 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 5 Sep 2024 22:29:53 +0700 Subject: [PATCH 15/45] more explanation --- src/hotspot/share/opto/rangeinference.cpp | 100 ++++++++++++++++------ 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index f668193b7fb7e..b22b4ad288b48 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -82,48 +82,96 @@ static U adjust_lo(U lo, const KnownBits& bits) { return lo; } + // The principal here is that, consider the first bit in result that is + // different from the corresponding bit in lo, since result is larger than lo + // the bit must be unset in lo and set in result. As result should be the + // smallest value, the position of this bit should be as low as possible + // E.g: 1 2 3 4 5 6 + // lo = 1 0 0 1 1 0 + // x = 1 0 1 0 1 0 + // y = 0 1 1 1 1 1 + // x would be larger than lo since the first different bit is the 3rd one, + // while y is smaller than lo because the first different bit is the 1st bit. + // Next consider: + // x1 = 1 0 1 0 1 0 + // x2 = 1 0 0 1 1 1 + // Both x1 and x2 are larger than lo, but x1 > x2 since its first different + // bit from lo is the 3rd one, while with x2 it is the 7th one. As a result, + // if both x1 and x2 satisfy bits, x2 would be closer to our true result. if (zero_violation < one_violation) { - // This means that the first bit that does not satisfy the bit - // requirement is a 0 that should be a 1, try to set that bit - // E.g: lo = 10010010, zeros = 00100100, ones = 01001000 + // This means that the first bit that does not satisfy the bit requirement + // is a 0 that should be a 1, this may be the first different bit we want + // to find. + // E.g: 1 2 3 4 5 6 7 8 + // lo = 1 0 0 1 0 0 1 0 + // zeros = 0 0 1 0 0 1 0 0 + // ones = 0 1 0 0 1 0 1 0 + // 1-vio = 0 1 0 0 1 0 0 0 + // 0-vio = 0 0 0 0 0 0 0 0 + // Since the result must have the 2nd bit set, it must be at least: + // 1 1 0 0 0 0 0 0 + // This value must satisfy zeros, because all bits before the 2nd bit have + // already satisfied zeros, and all bits after the 2nd bit are all 0 now. + // Continue the logic with each set bit in ones, we will set each bit in + // our new lo (11000000 -> 11001000 -> 11001010). The final value is our + // result. + // Implementationwise, from 11000000 we can just | with ones to obtain the + // final result. + // first_violation is the position of the violation counting from the // lowest bit up (0-based) - juint first_violation = W - 1 - count_leading_zeros(one_violation); + juint first_violation = W - 1 - count_leading_zeros(one_violation); // 6 + // 0 1 0 0 0 0 0 0 U alignment = U(1) << first_violation; // This is the first value which have the violated bit set, which means // that the result should not be smaller than this - // 11000000, notice that all bits after the second digit are zeroed, - // which automatically satisfies bits._zeros + // 1 1 0 0 0 0 0 0 lo = (lo & -alignment) + alignment; - // Simply satisfy bits._ones - return lo | bits._ones; // 11001000 + // 1 1 0 0 1 0 1 0 + return lo | bits._ones; } // This is more difficult because trying to unset a bit requires us to flip - // some bits before it (higher bits) - // Suppose lo = 11000110, zeros = 00001010, ones = 10000001 - // The smallest value with the 7-th bit unset would be 11001000 but then the - // 5-th bit does not match, the smallest value with both bits unset would be - // 11010000 - // We can obtain this number directly by finding the last place before - // the first mismatch such that it is 0 in lo and not required to be unset - juint first_violation = W - 1 - count_leading_zeros(zero_violation); - // This mask out all bits after the first violation - U find_mask = std::numeric_limits::max() << first_violation; // 11111100 - // The smallest value which satisfies bits._zeros will need to set a bit in - // lo that is previously unset (because the value needs to be larger than lo) - // and that bit will need to be unset in bits._zeros as well - U either = lo | bits._zeros; // 11001110 + // some bits before it (higher bits). + // Consider the first bit that is change, it must not be set already, and it + // must not be set in zeros. As a result, it must be the last bit before the + // first bit violation that is unset in both zeros and lo. + // E.g: 1 2 3 4 5 6 7 8 + // lo = 1 0 0 0 1 1 1 0 + // zeros = 0 0 0 1 0 1 0 0 + // ones = 1 0 0 0 0 0 1 1 + // 1-vio = 0 0 0 0 0 0 0 1 + // 0-vio = 0 0 0 0 0 1 0 0 + // The first violation is the 6th bit, which should be 0. The 5th cannot be + // the first different bit we are looking for, because it is already 1, the + // 4th bit also cannot be, because it must be 0. As a result, the first + // different bit between the result and lo must be the 3rd bit. As a result, + // the result must not be smaller than: + // 1 0 1 0 0 0 0 0 + // This one satisfies zeros so we can use the logic in the previous case to + // obtain our final result, which is: + // 1 0 1 0 0 0 1 1 + + juint first_violation = W - count_leading_zeros(zero_violation); + // This mask out all bits from the first violation + // 1 1 1 1 1 0 0 0 + U find_mask = std::numeric_limits::max() << first_violation; + // 1 0 0 1 1 1 1 0 + U either = lo | bits._zeros; // The bit we want to set is the last bit unset in either that stands before // the first violation, which is the last set bit of tmp - U tmp = ~either & find_mask; // 00110000 + // 0 1 1 0 0 0 0 0 + U tmp = ~either & find_mask; // Isolate the last bit - U alignment = tmp & (-tmp); // 00010000 + // 0 0 1 0 0 0 0 0 + U alignment = tmp & (-tmp); // Set the bit and unset all the bit after, this is the smallest value that // satisfies bits._zeros - lo = (lo & -alignment) + alignment; // 11010000 + // 1 0 1 0 0 0 0 0 + lo = (lo & -alignment) + alignment; // Satisfy bits._ones - return lo | bits._ones; // 11010001 + // 1 0 1 0 0 0 1 1 + return lo | bits._ones; } // Try to tighten the bound constraints from the known bit information. I.e, we From 5990628a8337b9040128857f34359b169326eb23 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 5 Sep 2024 22:31:53 +0700 Subject: [PATCH 16/45] rename tests --- test/hotspot/gtest/opto/test_rangeinference.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 4bfea4e6305a8..c6dff0932b451 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -45,7 +45,7 @@ julong uniform_random() { } template -static void test_normalize_constraints_simple() { +static void test_canonicalize_constraints_simple() { constexpr int parameters = 10; for (int i = 0; i < parameters; i++) { S a = uniform_random(); @@ -107,7 +107,7 @@ static void test_normalize_constraints_simple() { } template -static void test_normalize_constraints_random() { +static void test_canonicalize_constraints_random() { constexpr int samples = 1000; constexpr int parameters = 1000; for (int i = 0; i < parameters; i++) { @@ -140,11 +140,11 @@ static void test_normalize_constraints_random() { } } -TEST_VM(opto, normalize_constraints) { - test_normalize_constraints_simple(); - test_normalize_constraints_simple(); - test_normalize_constraints_random(); - test_normalize_constraints_random(); +TEST_VM(opto, canonicalize_constraints) { + test_canonicalize_constraints_simple(); + test_canonicalize_constraints_simple(); + test_canonicalize_constraints_random(); + test_canonicalize_constraints_random(); } #endif // ASSERT From 123e055a51c2a6f672f5536feac34ce42af76b1e Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 6 Sep 2024 00:33:27 +0700 Subject: [PATCH 17/45] make should return the correct type --- src/hotspot/share/opto/addnode.cpp | 8 +-- src/hotspot/share/opto/castnode.cpp | 2 +- src/hotspot/share/opto/compile.cpp | 4 +- src/hotspot/share/opto/convertnode.cpp | 8 +-- src/hotspot/share/opto/graphKit.cpp | 2 +- src/hotspot/share/opto/ifnode.cpp | 6 +- src/hotspot/share/opto/loopPredicate.cpp | 2 +- src/hotspot/share/opto/loopnode.cpp | 4 +- src/hotspot/share/opto/macroArrayCopy.cpp | 2 +- src/hotspot/share/opto/mulnode.cpp | 8 +-- src/hotspot/share/opto/rangeinference.cpp | 4 +- src/hotspot/share/opto/subnode.cpp | 10 +-- src/hotspot/share/opto/type.cpp | 78 ++++++++++++----------- src/hotspot/share/opto/type.hpp | 14 ++-- 14 files changed, 77 insertions(+), 75 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 9018e06c5276f..9a7d93dc469ba 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -988,8 +988,8 @@ const Type* XorINode::Value(PhaseGVN* phase) const { (t2i->_lo >= 0) && (t2i->_hi > 0)) { // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const Type* t1x = TypeInt::make(0, round_down_power_of_2(t1i->_hi) + (round_down_power_of_2(t1i->_hi) - 1), t1i->_widen); - const Type* t2x = TypeInt::make(0, round_down_power_of_2(t2i->_hi) + (round_down_power_of_2(t2i->_hi) - 1), t2i->_widen); + const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(t1i->_hi) + (round_down_power_of_2(t1i->_hi) - 1), t1i->_widen); + const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(t2i->_hi) + (round_down_power_of_2(t2i->_hi) - 1), t2i->_widen); return t1x->meet(t2x); } return AddNode::Value(phase); @@ -1073,8 +1073,8 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { (t2l->_lo >= 0) && (t2l->_hi > 0)) { // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const Type* t1x = TypeLong::make(0, round_down_power_of_2(t1l->_hi) + (round_down_power_of_2(t1l->_hi) - 1), t1l->_widen); - const Type* t2x = TypeLong::make(0, round_down_power_of_2(t2l->_hi) + (round_down_power_of_2(t2l->_hi) - 1), t2l->_widen); + const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(t1l->_hi) + (round_down_power_of_2(t1l->_hi) - 1), t1l->_widen); + const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(t2l->_hi) + (round_down_power_of_2(t2l->_hi) - 1), t2l->_widen); return t1x->meet(t2x); } return AddNode::Value(phase); diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index a5d1baf72b325..0309c1e8149e8 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -321,7 +321,7 @@ Node* CastLLNode::Ideal(PhaseGVN* phase, bool can_reshape) { assert(tl->_lo >= t_in_l->_lo && tl->_hi <= t_in_l->_hi, "CastLL type should be narrower than or equal to the type of its input"); assert((tl != t_in_l) == (tl->_lo > t_in_l->_lo || tl->_hi < t_in_l->_hi), "if type differs then this nodes's type must be narrower"); if (tl != t_in_l) { - const TypeInt* ti = TypeInt::make(checked_cast(tl->_lo), checked_cast(tl->_hi), tl->_widen)->is_int(); + const TypeInt* ti = TypeInt::make(checked_cast(tl->_lo), checked_cast(tl->_hi), tl->_widen); Node* castii = phase->transform(new CastIINode(in(0), in1->in(1), ti)); Node* convi2l = in1->clone(); convi2l->set_req(1, castii); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index ea3d6079bfdf5..206a0cfee682a 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -4482,7 +4482,7 @@ Node* Compile::conv_I2X_index(PhaseGVN* phase, Node* idx, const TypeInt* sizetyp if (sizetype != nullptr && sizetype->_hi > 0) { index_max = sizetype->_hi - 1; } - const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax)->is_int(); + const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax); idx = constrained_convI2L(phase, idx, iidxtype, ctrl); #endif return idx; @@ -4499,7 +4499,7 @@ Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* value = new CastIINode(ctrl, value, itype, carry_dependency ? ConstraintCastNode::StrongDependency : ConstraintCastNode::RegularDependency, true /* range check dependency */); value = phase->transform(value); } - const TypeLong* ltype = TypeLong::make(itype->_lo, itype->_hi, itype->_widen)->is_long(); + const TypeLong* ltype = TypeLong::make(itype->_lo, itype->_hi, itype->_widen); return phase->transform(new ConvI2LNode(value, ltype)); } diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index b7422ac3cdb65..0a2131782a237 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -648,8 +648,8 @@ static bool compute_updates_ranges(const TypeInteger* tx, const TypeInteger* ty, } int widen = MAX2(tx->widen_limit(), ty->widen_limit()); - rx = TypeInteger::make(rxlo, rxhi, widen, out_bt)->is_integer(out_bt); - ry = TypeInteger::make(rylo, ryhi, widen, out_bt)->is_integer(out_bt); + rx = TypeInteger::make(rxlo, rxhi, widen, out_bt); + ry = TypeInteger::make(rylo, ryhi, widen, out_bt); return true; } @@ -791,7 +791,7 @@ const Type* ConvL2INode::Value(PhaseGVN* phase) const { // Easy case. ti = TypeInt::make((jint)tl->get_con()); } else if (tl->_lo >= min_jint && tl->_hi <= max_jint) { - ti = TypeInt::make((jint)tl->_lo, (jint)tl->_hi, tl->_widen)->is_int(); + ti = TypeInt::make((jint)tl->_lo, (jint)tl->_hi, tl->_widen); } return ti->filter(_type); } diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 5497c2d060ae4..93dd4b7308b62 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -3884,7 +3884,7 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) if (size_max > tilen->_hi && tilen->_hi >= 0) { size_max = tilen->_hi; } - const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin)->is_int(); + const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin); // Only do a narrow I2L conversion if the range check passed. IfNode* iff = new IfNode(control(), initial_slow_test, PROB_MIN, COUNT_UNKNOWN); diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index daea61e5ae4fa..ce13640f81594 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -676,9 +676,9 @@ const TypeInt* IfNode::filtered_int_type(PhaseGVN* gvn, Node* val, Node* if_proj const TypeInt* val_t = gvn->type(val)->isa_int(); if (val_t != nullptr && !val_t->singleton() && cmp2_t->is_con()) { if (val_t->_lo == lo) { - return TypeInt::make(val_t->_lo + 1, val_t->_hi, val_t->_widen)->is_int(); + return TypeInt::make(val_t->_lo + 1, val_t->_hi, val_t->_widen); } else if (val_t->_hi == hi) { - return TypeInt::make(val_t->_lo, val_t->_hi - 1, val_t->_widen)->is_int(); + return TypeInt::make(val_t->_lo, val_t->_hi - 1, val_t->_widen); } } // Can't refine type @@ -708,7 +708,7 @@ const TypeInt* IfNode::filtered_int_type(PhaseGVN* gvn, Node* val, Node* if_proj default: break; } - const TypeInt* rtn_t = TypeInt::make(lo, hi, cmp2_t->_widen)->is_int(); + const TypeInt* rtn_t = TypeInt::make(lo, hi, cmp2_t->_widen); return rtn_t; } } diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 282b16d20e143..5e585a406f20c 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -798,7 +798,7 @@ BoolNode* PhaseIdealLoop::rc_predicate(Node* ctrl, const int scale, Node* offset ConINode* con_stride = _igvn.intcon(stride); set_ctrl(con_stride, C->root()); max_idx_expr = new SubINode(limit, con_stride); - idx_type = TypeInt::make(limit_lo - stride, limit_hi - stride, limit_type->_widen)->is_int(); + idx_type = TypeInt::make(limit_lo - stride, limit_hi - stride, limit_type->_widen); } else { // May overflow overflow = true; diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index cef6bac7eec48..b4c134570e63f 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -941,7 +941,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // inner_iters_max may not fit in a signed integer (iterating from // Long.MIN_VALUE to Long.MAX_VALUE for instance). Use an unsigned // min. - const TypeInteger* inner_iters_actual_range = TypeInteger::make(0, iters_limit, Type::WidenMin, bt)->is_integer(bt); + const TypeInteger* inner_iters_actual_range = TypeInteger::make(0, iters_limit, Type::WidenMin, bt); Node* inner_iters_actual = MaxNode::unsigned_min(inner_iters_max, inner_iters_limit, inner_iters_actual_range, _igvn); Node* inner_iters_actual_int; @@ -952,7 +952,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // the loop limit is less or equal to max_jint - stride - 1 (if stride is positive but a similar argument exists for // a negative stride). We add a CastII here to guarantee that, when the counted loop is created in a subsequent loop // opts pass, an accurate range of values for the limits is found. - const TypeInt* inner_iters_actual_int_range = TypeInt::make(0, iters_limit, Type::WidenMin)->is_int(); + const TypeInt* inner_iters_actual_int_range = TypeInt::make(0, iters_limit, Type::WidenMin); inner_iters_actual_int = new CastIINode(outer_head, inner_iters_actual_int, inner_iters_actual_int_range, ConstraintCastNode::UnconditionalDependency); _igvn.register_new_node_with_optimizer(inner_iters_actual_int); } else { diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp index 1962a5d095bf6..9600a3c6c2501 100644 --- a/src/hotspot/share/opto/macroArrayCopy.cpp +++ b/src/hotspot/share/opto/macroArrayCopy.cpp @@ -56,7 +56,7 @@ Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType el #ifdef _LP64 // see comment in GraphKit::array_element_address int index_max = max_jint - 1; // array size is max_jint, index is one less - const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax)->is_long(); + const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax); idx = transform_later( new ConvI2LNode(idx, lidxtype) ); #endif Node* scale = new LShiftXNode(idx, intcon(shift)); diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 9c54a56e9e982..1f22c60832388 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -1397,7 +1397,7 @@ const Type* RShiftINode::Value(PhaseGVN* phase) const { jint lo = (jint)r1->_lo >> (jint)shift; jint hi = (jint)r1->_hi >> (jint)shift; assert(lo <= hi, "must have valid bounds"); - const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen))->is_int(); + const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen)); #ifdef ASSERT // Make sure we get the sign-capture idiom correct. if (shift == BitsPerJavaInteger-1) { @@ -1467,7 +1467,7 @@ const Type* RShiftLNode::Value(PhaseGVN* phase) const { jlong lo = (jlong)r1->_lo >> (jlong)shift; jlong hi = (jlong)r1->_hi >> (jlong)shift; assert(lo <= hi, "must have valid bounds"); - const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen))->is_long(); + const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen)); #ifdef ASSERT // Make sure we get the sign-capture idiom correct. if (shift == (2*BitsPerJavaInteger)-1) { @@ -1653,7 +1653,7 @@ const Type* URShiftINode::Value(PhaseGVN* phase) const { hi = MAX2(neg_hi, pos_hi); // == -1 >>> shift; } assert(lo <= hi, "must have valid bounds"); - const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen))->is_int(); + const TypeInt* ti = TypeInt::make(lo, hi, MAX2(r1->_widen,r2->_widen)); #ifdef ASSERT // Make sure we get the sign-capture idiom correct. if (shift == BitsPerJavaInteger-1) { @@ -1800,7 +1800,7 @@ const Type* URShiftLNode::Value(PhaseGVN* phase) const { hi = neg_hi > pos_hi ? neg_hi : pos_hi; } assert(lo <= hi, "must have valid bounds"); - const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen))->is_long(); + const TypeLong* tl = TypeLong::make(lo, hi, MAX2(r1->_widen,r2->_widen)); #ifdef ASSERT // Make sure we get the sign-capture idiom correct. if (shift == BitsPerJavaLong - 1) { diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index b22b4ad288b48..a3b66dd929bad 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -486,7 +486,7 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { if (nt->_widen < Type::WidenMax) { // Returned widened new guy TypeIntPrototype prototype{{nt->_lo, nt->_hi}, {nt->_ulo, nt->_uhi}, nt->_bits}; - return CT::make(prototype, nt->_widen + 1); + return CT::try_make(prototype, nt->_widen + 1); } // Speed up the convergence by abandoning the bounds, there are only a couple of bits so @@ -506,7 +506,7 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { ones |= lt->_bits._ones; } TypeIntPrototype prototype{{min, max}, {umin, umax}, {zeros, ones}}; - return CT::make(prototype, Type::WidenMax); + return CT::try_make(prototype, Type::WidenMax); } template const Type* int_type_widen(const TypeInt* nt, const TypeInt* ot, const TypeInt* lt); template const Type* int_type_widen(const TypeLong* nt, const TypeLong* ot, const TypeLong* lt); diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index 7d02b9010622c..7eb7922c5fbd8 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -848,8 +848,8 @@ const Type* CmpUNode::Value(PhaseGVN* phase) const { if ((underflow != overflow) && (hi_tr1 < lo_tr2)) { // Overflow only on one boundary, compare 2 separate type ranges. int w = MAX2(r0->_widen, r1->_widen); // _widen does not matter here - const TypeInt* tr1 = TypeInt::make(lo_tr1, hi_tr1, w)->is_int(); - const TypeInt* tr2 = TypeInt::make(lo_tr2, hi_tr2, w)->is_int(); + const TypeInt* tr1 = TypeInt::make(lo_tr1, hi_tr1, w); + const TypeInt* tr2 = TypeInt::make(lo_tr2, hi_tr2, w); const TypeInt* cmp1 = sub(tr1, t2)->is_int(); const TypeInt* cmp2 = sub(tr2, t2)->is_int(); // Compute union, so that cmp handles all possible results from the two cases @@ -1460,8 +1460,8 @@ Node* BoolNode::fold_cmpI(PhaseGVN* phase, SubNode* cmp, Node* cmp1, int cmp_op, // Overflow on one boundary, compute resulting type ranges: // tr1 [MIN_INT, hi_int] and tr2 [lo_int, MAX_INT] int w = MAX2(r0->_widen, r1->_widen); // _widen does not matter here - const TypeInt* tr1 = TypeInt::make(min_jint, hi_int, w)->is_int(); - const TypeInt* tr2 = TypeInt::make(lo_int, max_jint, w)->is_int(); + const TypeInt* tr1 = TypeInt::make(min_jint, hi_int, w); + const TypeInt* tr2 = TypeInt::make(lo_int, max_jint, w); // Compare second input of cmp to both type ranges const Type* sub_tr1 = cmp->sub(tr1, cmp2_type); const Type* sub_tr2 = cmp->sub(tr2, cmp2_type); diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 21a2a43ee629f..db422785b9318 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -474,23 +474,23 @@ void Type::Initialize_shared(Compile* current) { TypeInt::MINUS_1 = TypeInt::make(-1); // -1 TypeInt::ZERO = TypeInt::make( 0); // 0 TypeInt::ONE = TypeInt::make( 1); // 1 - TypeInt::BOOL = TypeInt::make( 0, 1, WidenMin)->is_int(); // 0 or 1, FALSE or TRUE. - TypeInt::CC = TypeInt::make(-1, 1, WidenMin)->is_int(); // -1, 0 or 1, condition codes - TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin)->is_int(); // == TypeInt::MINUS_1 - TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin)->is_int(); // == TypeInt::ONE - TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin)->is_int(); // == TypeInt::ZERO - TypeInt::CC_NE = TypeInt::make(TypeIntPrototype{{-1, 1}, {1, max_juint}, {0, 1}}, WidenMin)->is_int(); - TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin)->is_int(); - TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin)->is_int(); // == TypeInt::BOOL - TypeInt::BYTE = TypeInt::make(-128, 127, WidenMin)->is_int(); // Bytes - TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin)->is_int(); // Unsigned Bytes - TypeInt::CHAR = TypeInt::make(0, 65535, WidenMin)->is_int(); // Java chars - TypeInt::SHORT = TypeInt::make(-32768, 32767, WidenMin)->is_int(); // Java shorts - TypeInt::NON_ZERO = TypeInt::make(TypeIntPrototype{{min_jint, max_jint}, {1, max_juint}, {0, 0}}, WidenMin)->is_int(); - TypeInt::POS = TypeInt::make(0, max_jint, WidenMin)->is_int(); // Non-neg values - TypeInt::POS1 = TypeInt::make(1, max_jint, WidenMin)->is_int(); // Positive values - TypeInt::INT = TypeInt::make(min_jint, max_jint, WidenMax)->is_int(); // 32-bit integers - TypeInt::SYMINT = TypeInt::make(-max_jint, max_jint, WidenMin)->is_int(); // symmetric range + TypeInt::BOOL = TypeInt::make( 0, 1, WidenMin); // 0 or 1, FALSE or TRUE. + TypeInt::CC = TypeInt::make(-1, 1, WidenMin); // -1, 0 or 1, condition codes + TypeInt::CC_LT = TypeInt::make(-1,-1, WidenMin); // == TypeInt::MINUS_1 + TypeInt::CC_GT = TypeInt::make( 1, 1, WidenMin); // == TypeInt::ONE + TypeInt::CC_EQ = TypeInt::make( 0, 0, WidenMin); // == TypeInt::ZERO + TypeInt::CC_NE = TypeInt::try_make(TypeIntPrototype{{-1, 1}, {1, max_juint}, {0, 1}}, WidenMin)->is_int(); + TypeInt::CC_LE = TypeInt::make(-1, 0, WidenMin); + TypeInt::CC_GE = TypeInt::make( 0, 1, WidenMin); // == TypeInt::BOOL + TypeInt::BYTE = TypeInt::make(-128, 127, WidenMin); // Bytes + TypeInt::UBYTE = TypeInt::make(0, 255, WidenMin); // Unsigned Bytes + TypeInt::CHAR = TypeInt::make(0, 65535, WidenMin); // Java chars + TypeInt::SHORT = TypeInt::make(-32768, 32767, WidenMin); // Java shorts + TypeInt::NON_ZERO = TypeInt::try_make(TypeIntPrototype{{min_jint, max_jint}, {1, max_juint}, {0, 0}}, WidenMin)->is_int(); + TypeInt::POS = TypeInt::make(0, max_jint, WidenMin); // Non-neg values + TypeInt::POS1 = TypeInt::make(1, max_jint, WidenMin); // Positive values + TypeInt::INT = TypeInt::make(min_jint, max_jint, WidenMax); // 32-bit integers + TypeInt::SYMINT = TypeInt::make(-max_jint, max_jint, WidenMin); // symmetric range TypeInt::TYPE_DOMAIN = TypeInt::INT; // CmpL is overloaded both as the bytecode computation returning // a trinary (-1, 0, +1) integer result AND as an efficient long @@ -505,12 +505,12 @@ void Type::Initialize_shared(Compile* current) { TypeLong::MINUS_1 = TypeLong::make(-1); // -1 TypeLong::ZERO = TypeLong::make( 0); // 0 TypeLong::ONE = TypeLong::make( 1); // 1 - TypeLong::NON_ZERO = TypeLong::make(TypeIntPrototype{{min_jlong, max_jlong}, {1, max_julong}, {0, 0}}, WidenMin)->is_long(); - TypeLong::POS = TypeLong::make(0, max_jlong, WidenMin)->is_long(); // Non-neg values - TypeLong::NEG = TypeLong::make(min_jlong, -1, WidenMin)->is_long(); - TypeLong::LONG = TypeLong::make(min_jlong, max_jlong, WidenMax)->is_long(); // 64-bit integers - TypeLong::INT = TypeLong::make((jlong)min_jint, (jlong)max_jint,WidenMin)->is_long(); - TypeLong::UINT = TypeLong::make(0, (jlong)max_juint, WidenMin)->is_long(); + TypeLong::NON_ZERO = TypeLong::try_make(TypeIntPrototype{{min_jlong, max_jlong}, {1, max_julong}, {0, 0}}, WidenMin)->is_long(); + TypeLong::POS = TypeLong::make(0, max_jlong, WidenMin); // Non-neg values + TypeLong::NEG = TypeLong::make(min_jlong, -1, WidenMin); + TypeLong::LONG = TypeLong::make(min_jlong, max_jlong, WidenMax); // 64-bit integers + TypeLong::INT = TypeLong::make((jlong)min_jint, (jlong)max_jint,WidenMin); + TypeLong::UINT = TypeLong::make(0, (jlong)max_juint, WidenMin); TypeLong::TYPE_DOMAIN = TypeLong::LONG; const Type **fboth =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*)); @@ -1518,7 +1518,7 @@ bool TypeD::empty(void) const { return false; // always exactly a singleton } -const Type* TypeInteger::make(jlong lo, jlong hi, int w, BasicType bt) { +const TypeInteger* TypeInteger::make(jlong lo, jlong hi, int w, BasicType bt) { if (bt == T_INT) { return TypeInt::make(checked_cast(lo), checked_cast(hi), w); } @@ -1598,7 +1598,7 @@ TypeInt::TypeInt(const TypeIntPrototype& t, int w, bool dual) DEBUG_ONLY(t.verify_constraints()); } -const Type* TypeInt::make(const TypeIntPrototype& t, int w, bool dual) { +const Type* TypeInt::try_make(const TypeIntPrototype& t, int w, bool dual) { auto new_t = t.canonicalize_constraints(); if (!new_t._present) { return dual ? Type::BOTTOM : Type::TOP; @@ -1612,12 +1612,13 @@ const TypeInt* TypeInt::make(jint lo) { WidenMin, false))->hashcons()->is_int(); } -const Type* TypeInt::make(jint lo, jint hi, int w) { - return make(TypeIntPrototype{{lo, hi}, {0, max_juint}, {0, 0}}, w); +const TypeInt* TypeInt::make(jint lo, jint hi, int w) { + assert(lo <= hi, "must be legal bounds"); + return try_make(TypeIntPrototype{{lo, hi}, {0, max_juint}, {0, 0}}, w)->is_int(); } -const Type* TypeInt::make(const TypeIntPrototype& t, int w) { - return make(t, w, false); +const Type* TypeInt::try_make(const TypeIntPrototype& t, int w) { + return try_make(t, w, false); } bool TypeInt::contains(jint i) const { @@ -1634,7 +1635,7 @@ bool TypeInt::properly_contains(const TypeInt* t) const { } const Type* TypeInt::xmeet(const Type* t) const { - return int_type_xmeet(this, t, TypeInt::make, _is_dual); + return int_type_xmeet(this, t, TypeInt::try_make, _is_dual); } const Type* TypeInt::xdual() const { @@ -1725,7 +1726,7 @@ TypeLong::TypeLong(const TypeIntPrototype& t, int w, bool dual) DEBUG_ONLY(t.verify_constraints()); } -const Type* TypeLong::make(const TypeIntPrototype& t, int w, bool dual) { +const Type* TypeLong::try_make(const TypeIntPrototype& t, int w, bool dual) { auto new_t = t.canonicalize_constraints(); if (!new_t._present) { return dual ? Type::BOTTOM : Type::TOP; @@ -1739,12 +1740,13 @@ const TypeLong* TypeLong::make(jlong lo) { WidenMin, false))->hashcons()->is_long(); } -const Type* TypeLong::make(jlong lo, jlong hi, int w) { - return make(TypeIntPrototype{{lo, hi}, {0, max_julong}, {0, 0}}, w); +const TypeLong* TypeLong::make(jlong lo, jlong hi, int w) { + assert(lo <= hi, "must be legal bounds"); + return try_make(TypeIntPrototype{{lo, hi}, {0, max_julong}, {0, 0}}, w)->is_long(); } -const Type* TypeLong::make(const TypeIntPrototype& t, int w) { - return make(t, w, false); +const Type* TypeLong::try_make(const TypeIntPrototype& t, int w) { + return try_make(t, w, false); } bool TypeLong::contains(jlong i) const { @@ -1761,7 +1763,7 @@ bool TypeLong::properly_contains(const TypeLong* t) const { } const Type *TypeLong::xmeet(const Type* t) const { - return int_type_xmeet(this, t, TypeLong::make, _is_dual); + return int_type_xmeet(this, t, TypeLong::try_make, _is_dual); } const Type* TypeLong::xdual() const { @@ -2071,7 +2073,7 @@ inline const TypeInt* normalize_array_size(const TypeInt* size) { // of their index types. Pick minimum wideness, since that is the // forced wideness of small ranges anyway. if (size->_widen != Type::WidenMin) - return TypeInt::make(size->_lo, size->_hi, Type::WidenMin)->is_int(); + return TypeInt::make(size->_lo, size->_hi, Type::WidenMin); else return size; } @@ -4542,7 +4544,7 @@ const TypeInt* TypeAryPtr::narrow_size_type(const TypeInt* size) const { if (!chg) { return size; } - return TypeInt::make(lo, hi, Type::WidenMin)->is_int(); + return TypeInt::make(lo, hi, Type::WidenMin); } //-------------------------------cast_to_size---------------------------------- diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 6451998097bd9..02fcd8e7ffb78 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -572,7 +572,7 @@ class TypeInteger : public Type { bool is_con() const { return lo_as_long() == hi_as_long(); } virtual short widen_limit() const { return _widen; } - static const Type* make(jlong lo, jlong hi, int w, BasicType bt); + static const TypeInteger* make(jlong lo, jlong hi, int w, BasicType bt); static const TypeInteger* bottom(BasicType type); static const TypeInteger* zero(BasicType type); @@ -587,7 +587,7 @@ class TypeInteger : public Type { // upper bound, inclusive. class TypeInt : public TypeInteger { TypeInt(const TypeIntPrototype& t, int w, bool dual); - static const Type* make(const TypeIntPrototype& t, int w, bool dual); + static const Type* try_make(const TypeIntPrototype& t, int w, bool dual); protected: virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; @@ -606,8 +606,8 @@ class TypeInt : public TypeInteger { static const TypeInt* try_cast(const Type* t) { return t->isa_int(); } static const TypeInt* make(jint lo); // must always specify w - static const Type* make(jint lo, jint hi, int w); - static const Type* make(const TypeIntPrototype& t, int w); + static const TypeInt* make(jint lo, jint hi, int w); + static const Type* try_make(const TypeIntPrototype& t, int w); // Check for single integer bool is_con() const { return _lo == _hi; } @@ -669,7 +669,7 @@ class TypeInt : public TypeInteger { // an upper bound, inclusive. class TypeLong : public TypeInteger { TypeLong(const TypeIntPrototype& t, int w, bool dual); - static const Type* make(const TypeIntPrototype& t, int w, bool dual); + static const Type* try_make(const TypeIntPrototype& t, int w, bool dual); protected: // Do not kill _widen bits. virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; @@ -689,8 +689,8 @@ class TypeLong : public TypeInteger { static const TypeLong* try_cast(const Type* t) { return t->isa_long(); } static const TypeLong* make(jlong lo); // must always specify w - static const Type* make(jlong lo, jlong hi, int w); - static const Type* make(const TypeIntPrototype& t, int w); + static const TypeLong* make(jlong lo, jlong hi, int w); + static const Type* try_make(const TypeIntPrototype& t, int w); // Check for single integer bool is_con() const { return _lo == _hi; } From 089c566b63de4313ceefb5c58d5f3a6d27a5b10c Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 6 Sep 2024 00:42:43 +0700 Subject: [PATCH 18/45] add trivial test cases --- test/hotspot/gtest/opto/test_rangeinference.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index c6dff0932b451..eef0f479ab3d1 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "opto/rangeinference.hpp" +#include "opto/type.hpp" #include "runtime/os.hpp" #include "unittest.hpp" @@ -44,6 +45,20 @@ julong uniform_random() { return (julong(os::random()) << 32) | julong(juint(os::random())); } +static void test_canonicalize_constraints_trivial() { + ASSERT_FALSE(TypeInt::NON_ZERO->contains(0)); + ASSERT_TRUE(TypeInt::NON_ZERO->contains(1)); + ASSERT_TRUE(TypeInt::NON_ZERO->contains(-1)); + ASSERT_TRUE(TypeInt::CC_NE->contains(-1)); + ASSERT_TRUE(TypeInt::CC_NE->contains(1)); + ASSERT_FALSE(TypeInt::CC_NE->contains(0)); + ASSERT_FALSE(TypeInt::CC_NE->contains(-2)); + ASSERT_FALSE(TypeInt::CC_NE->contains(2)); + ASSERT_FALSE(TypeLong::NON_ZERO->contains(0L)); + ASSERT_TRUE(TypeLong::NON_ZERO->contains(1L)); + ASSERT_TRUE(TypeLong::NON_ZERO->contains(-1L)); +} + template static void test_canonicalize_constraints_simple() { constexpr int parameters = 10; @@ -141,6 +156,7 @@ static void test_canonicalize_constraints_random() { } TEST_VM(opto, canonicalize_constraints) { + test_canonicalize_constraints_trivial(); test_canonicalize_constraints_simple(); test_canonicalize_constraints_simple(); test_canonicalize_constraints_random(); From 2e3955d51c24108fcba39bd9d0564147fce961a0 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 6 Sep 2024 01:09:04 +0700 Subject: [PATCH 19/45] fix builds --- test/hotspot/gtest/opto/test_rangeinference.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index eef0f479ab3d1..cfab24381030a 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -54,9 +54,9 @@ static void test_canonicalize_constraints_trivial() { ASSERT_FALSE(TypeInt::CC_NE->contains(0)); ASSERT_FALSE(TypeInt::CC_NE->contains(-2)); ASSERT_FALSE(TypeInt::CC_NE->contains(2)); - ASSERT_FALSE(TypeLong::NON_ZERO->contains(0L)); - ASSERT_TRUE(TypeLong::NON_ZERO->contains(1L)); - ASSERT_TRUE(TypeLong::NON_ZERO->contains(-1L)); + ASSERT_FALSE(TypeLong::NON_ZERO->contains(jlong(0))); + ASSERT_TRUE(TypeLong::NON_ZERO->contains(jlong(1))); + ASSERT_TRUE(TypeLong::NON_ZERO->contains(jlong(-1))); } template From 9b70213e53b01aa09478942009876afd86c11e22 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sun, 8 Sep 2024 12:02:44 +0700 Subject: [PATCH 20/45] change (~v & ones) == 0 to (v & ones) == ones --- src/hotspot/share/opto/rangeinference.cpp | 2 +- src/hotspot/share/opto/rangeinference.hpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index a3b66dd929bad..e6e641ebdce23 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -235,7 +235,7 @@ adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); // match_mask & bounds._lo is the common prefix, extract zeros and ones from // it - U new_zeros = bits._zeros | (match_mask &~ bounds._lo); + U new_zeros = bits._zeros | (match_mask & ~bounds._lo); U new_ones = bits._ones | (match_mask & bounds._lo); bool progress = (new_zeros != bits._zeros) || (new_ones != bits._ones); bool present = ((new_zeros & new_ones) == 0); diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 1f56022d0fb00..00bfc12f66674 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -42,7 +42,7 @@ class RangeInt { /** * Bits that are known to be 0 or 1. A value v satisfies this constraint iff - * (v & zeros) == 0 && (~v & ones) == 0. I.e, all bits that is set in zeros + * (v & zeros) == 0 && (v & ones) == ones. I.e, all bits that is set in zeros * must be unset in v, and all bits that is set in ones must be set in v. * * E.g: @@ -51,6 +51,9 @@ class RangeInt { * Then: 10001010 would satisfy the bit constraints * while: 10011000 would not since the bit at the 4th position violates * zeros and the bit at the 7th position violates ones + * + * A KnownBits is sane if there is no position at which a bit must be both set + * and unset at the same time. That is (zeros & ones) == 0. */ template class KnownBits { @@ -61,7 +64,7 @@ class KnownBits { U _ones; bool is_satisfied_by(U v) const { - return (v & _zeros) == 0 && (~v & _ones) == 0; + return (v & _zeros) == 0 && (v & _ones) == _ones; } }; From 81f4e15b414a2a0c5a9de51dd1da2a96cdd0914a Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Tue, 10 Sep 2024 19:13:16 +0700 Subject: [PATCH 21/45] add doc to TypeInt, rename parameters, remove unused methods --- src/hotspot/share/opto/rangeinference.cpp | 103 ++++++++++++---------- src/hotspot/share/opto/rangeinference.hpp | 2 +- src/hotspot/share/opto/type.cpp | 8 -- src/hotspot/share/opto/type.hpp | 77 +++++++++++++--- 4 files changed, 120 insertions(+), 70 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index e6e641ebdce23..6b65e6ae51cf5 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -45,10 +45,11 @@ class AdjustResult { // In the canonical form, [lo, hi] intersects with [ulo, uhi] can result in 2 // cases: -// - [lo, hi] is the same as [ulo, uhi], lo and hi are both >= 0 or both < 0 +// - [lo, hi] is the same as [ulo, uhi], lo and hi are both >= 0 or both < 0. // - [lo, hi] is not the same as [ulo, uhi], which results in the intersections -// being [lo, uhi] and [ulo, hi], lo and uhi are < 0 while ulo and hi are >= 0 -// This class deals with each interval with both bounds being >= 0 or < 0 +// being [lo, uhi] and [ulo, hi], lo and uhi are < 0 while ulo and hi are >= 0. +// This class deals with each interval with both bounds being >= 0 or < 0 in +// the signed domain. template class SimpleCanonicalResult { static_assert(std::is_unsigned::value, "bit info should be unsigned"); @@ -288,11 +289,17 @@ TypeIntPrototype::canonicalize_constraints() const { } // Trivially canonicalize the bounds so that srange._lo and urange._hi are - // both < 0 or >= 0. The same for srange._hi and urange._ulo + // both < 0 or >= 0. The same for srange._hi and urange._ulo. See TypeInt for + // detailed explanation. if (S(urange._lo) > S(urange._hi)) { + // This means that S(urange._lo) >= 0 and S(urange._hi) < 0 if (S(urange._hi) < srange._lo) { + // This means that there should be no element in the interval + // [min_S, S(urange._hi)], tighten urange._hi to max_S urange._hi = std::numeric_limits::max(); } else if (S(urange._lo) > srange._hi) { + // This means that there should be no element in the interval + // [S(urange._lo), max_S], tighten urange._lo to min_S urange._lo = std::numeric_limits::min(); } } @@ -446,47 +453,47 @@ template const Type* int_type_xmeet(const TypeLong* i1, const Type* t2, // first, after WidenMax attempts, if the type has still not converged we speed up the // convergence by abandoning the bounds template -const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { +const Type* int_type_widen(const CT* new_type, const CT* old_type, const CT* limit_type) { using S = std::remove_const_t; using U = std::remove_const_t; - if (ot == nullptr) { - return nt; + if (old_type == nullptr) { + return new_type; } // If new guy is equal to old guy, no widening - if (int_type_equal(nt, ot)) { - return ot; + if (int_type_equal(new_type, old_type)) { + return old_type; } // If old guy contains new, then we probably widened too far & dropped to // bottom. Return the wider fellow. - if (int_type_subset(ot, nt)) { - return ot; + if (int_type_subset(old_type, new_type)) { + return old_type; } // Neither contains each other, weird? // fatal("Integer value range is not subset"); // return this; - if (!int_type_subset(nt, ot)) { + if (!int_type_subset(new_type, old_type)) { return CT::TYPE_DOMAIN; } // If old guy was a constant, do not bother - if (ot->singleton()) { - return nt; + if (old_type->singleton()) { + return new_type; } // If new guy contains old, then we widened // If new guy is already wider than old, no widening - if (nt->_widen > ot->_widen) { - return nt; + if (new_type->_widen > old_type->_widen) { + return new_type; } - if (nt->_widen < Type::WidenMax) { + if (new_type->_widen < Type::WidenMax) { // Returned widened new guy - TypeIntPrototype prototype{{nt->_lo, nt->_hi}, {nt->_ulo, nt->_uhi}, nt->_bits}; - return CT::try_make(prototype, nt->_widen + 1); + TypeIntPrototype prototype{{new_type->_lo, new_type->_hi}, {new_type->_ulo, new_type->_uhi}, new_type->_bits}; + return CT::try_make(prototype, new_type->_widen + 1); } // Speed up the convergence by abandoning the bounds, there are only a couple of bits so @@ -495,63 +502,63 @@ const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt) { S max = std::numeric_limits::max(); U umin = std::numeric_limits::min(); U umax = std::numeric_limits::max(); - U zeros = nt->_bits._zeros; - U ones = nt->_bits._ones; - if (lt != nullptr) { - min = lt->_lo; - max = lt->_hi; - umin = lt->_ulo; - umax = lt->_uhi; - zeros |= lt->_bits._zeros; - ones |= lt->_bits._ones; + U zeros = new_type->_bits._zeros; + U ones = new_type->_bits._ones; + if (limit_type != nullptr) { + min = limit_type->_lo; + max = limit_type->_hi; + umin = limit_type->_ulo; + umax = limit_type->_uhi; + zeros |= limit_type->_bits._zeros; + ones |= limit_type->_bits._ones; } TypeIntPrototype prototype{{min, max}, {umin, umax}, {zeros, ones}}; return CT::try_make(prototype, Type::WidenMax); } -template const Type* int_type_widen(const TypeInt* nt, const TypeInt* ot, const TypeInt* lt); -template const Type* int_type_widen(const TypeLong* nt, const TypeLong* ot, const TypeLong* lt); +template const Type* int_type_widen(const TypeInt* new_type, const TypeInt* old_type, const TypeInt* limit_type); +template const Type* int_type_widen(const TypeLong* new_type, const TypeLong* old_type, const TypeLong* limit_type); // Called by PhiNode::Value during GVN, monotonically narrow the value set, only // narrow if the bits change or if the bounds are tightened enough to avoid // slow convergence template -const Type* int_type_narrow(const CT* nt, const CT* ot) { +const Type* int_type_narrow(const CT* new_type, const CT* old_type) { using S = decltype(CT::_lo); using U = decltype(CT::_ulo); - if (nt->singleton() || ot == nullptr) { - return nt; + if (new_type->singleton() || old_type == nullptr) { + return new_type; } // If new guy is equal to old guy, no narrowing - if (int_type_equal(nt, ot)) { - return ot; + if (int_type_equal(new_type, old_type)) { + return old_type; } // If old guy was maximum range, allow the narrowing - if (int_type_equal(ot, CT::TYPE_DOMAIN)) { - return nt; + if (int_type_equal(old_type, CT::TYPE_DOMAIN)) { + return new_type; } // Doesn't narrow; pretty weird - if (!int_type_subset(ot, nt)) { - return nt; + if (!int_type_subset(old_type, new_type)) { + return new_type; } // Bits change - if (ot->_bits._zeros != nt->_bits._zeros || ot->_bits._ones != nt->_bits._ones) { - return nt; + if (old_type->_bits._zeros != new_type->_bits._zeros || old_type->_bits._ones != new_type->_bits._ones) { + return new_type; } // Only narrow if the range shrinks a lot - U oc = cardinality_from_bounds(RangeInt{ot->_lo, ot->_hi}, - RangeInt{ot->_ulo, ot->_uhi}); - U nc = cardinality_from_bounds(RangeInt{nt->_lo, nt->_hi}, - RangeInt{nt->_ulo, nt->_uhi}); - return (nc > (oc >> 1) + (SMALLINT * 2)) ? ot : nt; + U oc = cardinality_from_bounds(RangeInt{old_type->_lo, old_type->_hi}, + RangeInt{old_type->_ulo, old_type->_uhi}); + U nc = cardinality_from_bounds(RangeInt{new_type->_lo, new_type->_hi}, + RangeInt{new_type->_ulo, new_type->_uhi}); + return (nc > (oc >> 1) + (SMALLINT * 2)) ? old_type : new_type; } -template const Type* int_type_narrow(const TypeInt* nt, const TypeInt* ot); -template const Type* int_type_narrow(const TypeLong* nt, const TypeLong* ot); +template const Type* int_type_narrow(const TypeInt* new_type, const TypeInt* old_type); +template const Type* int_type_narrow(const TypeLong* new_type, const TypeLong* old_type); #ifndef PRODUCT diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 00bfc12f66674..68820a88f37b8 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -109,7 +109,7 @@ U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) return urange._hi - urange._lo; } - return urange._hi - U(srange._lo) + U(srange._hi) - urange._lo + 1; + return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + 1; } template diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index db422785b9318..99584d105b1e9 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1630,10 +1630,6 @@ bool TypeInt::contains(const TypeInt* t) const { return int_type_subset(this, t); } -bool TypeInt::properly_contains(const TypeInt* t) const { - return int_type_subset(this, t) && !int_type_equal(this, t); -} - const Type* TypeInt::xmeet(const Type* t) const { return int_type_xmeet(this, t, TypeInt::try_make, _is_dual); } @@ -1758,10 +1754,6 @@ bool TypeLong::contains(const TypeLong* t) const { return int_type_subset(this, t); } -bool TypeLong::properly_contains(const TypeLong* t) const { - return int_type_subset(this, t) && !int_type_equal(this, t); -} - const Type *TypeLong::xmeet(const Type* t) const { return int_type_xmeet(this, t, TypeLong::try_make, _is_dual); } diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 02fcd8e7ffb78..2269d2e5d600b 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -580,11 +580,69 @@ class TypeInteger : public Type { static const TypeInteger* minus_1(BasicType type); }; - - -//------------------------------TypeInt---------------------------------------- -// Class of integer ranges, the set of integers between a lower bound and an -// upper bound, inclusive. +/** + * Definition: + * + * A TypeInt represents a set of jint values. An jint v is an element of a + * TypeInt iff: + * + * v >= _lo && v <= _hi && juint(v) >= _ulo && juint(v) <= _uhi && _bits.is_satisfied_by(v) + * + * Multiple set of parameters can represent the same set. + * E.g: consider 2 TypeInt t1, t2 + * + * t1._lo = 2, t1._hi = 7, t1._ulo = 0, t1._uhi = 5, t1._bits._zeros = 0x0, t1._bits._ones = 0x1 + * t2._lo = 3, t2._hi = 5, t2._ulo = 3, t2._uhi = 5, t2._bits._zeros = 0xFFFFFFF8, t2._bits._ones = 0x1 + * + * Then, t1 and t2 both represent the set {3, 5}. We can also see that the + * constraints of t2 are optimal. I.e there exists no TypeInt t3 which also + * represents {3, 5} such that: + * + * t3._lo > t2._lo || t3._hi < t2._hi || t3._ulo > t2._ulo || t3._uhi < t2._uhi || + * (t3._bits._zeros & t2._bis._zeros) != t3._bits._zeros || (t3._bits._ones & t2._bits._ones) != t3._bits._ones + * + * The last 2 conditions mean that the bits in t3._bits._zeros is not a subset + * of those in t2._bits._zeros, the same applies to _bits._ones + * + * As a result, every TypeInt is canonicalized to its optimal form upon + * construction. This makes it easier to reason about them in optimizations. + * E.g a TypeInt t with t._lo < 0 will definitely contain negative values. It + * also makes it trivial to determine if a TypeInt instance is a subset of + * another. + * + * Properties: + * + * 1. Since every TypeInt instance is canonicalized, all the bounds must also + * be elements of such TypeInt. Or else, we can tighted the bounds by narrowing + * it by one, which contradicts the assumption of the TypeInt being canonical. + * + * 2. Either _lo == jint(_lo) and _hi == jint(_uhi), or all elements of a + * TypeInt lie in the intervals [_lo, jint(_uhi)] or [jint(_ulo), _hi] + * + * Proof: For 2 jint value x, y such that they are both >= 0 or < 0. Then: + * + * x <= y iff juint(x) <= juint(y) + * + * Then, we have: + * + * For a TypeInt t, there are 3 possible cases: + * + * a. t._lo >= 0. Since 0 <= t._lo <= jint(t._ulo), we have: + * + * juint(t._lo) <= juint(jint(t._ulo)) == t._ulo <= juint(t._lo) + * + * Which means that t._lo == jint(t._ulo). Similarly, t._hi == jint(t._uhi). + * + * b. t._hi < 0. Similarly, t._lo == jint(t._ulo) and t._hi == jint(t._uhi) + * + * c. t._lo < 0, t._hi >= 0. Then jint(t._ulo) >= 0 and jint(t._uhi) < 0. In + * this case, all elements of t belongs to either [t._lo, jint(t._uhi)] or + * [jint(t._ulo), t._hi]. + * + * This property is useful for our analysis of TypeInt values. Additionally, it + * can be seen that _lo and jint(_uhi) are both < 0 or >= 0, and the same + * applies to jint(_ulo) and _hi. + */ class TypeInt : public TypeInteger { TypeInt(const TypeIntPrototype& t, int w, bool dual); static const Type* try_make(const TypeIntPrototype& t, int w, bool dual); @@ -617,8 +675,6 @@ class TypeInt : public TypeInteger { // argument are also elements of this type) bool contains(jint i) const; bool contains(const TypeInt* t) const; - // Excluding the cases where this and t are the same - bool properly_contains(const TypeInt* t) const; virtual bool is_finite() const; // Has a finite value @@ -663,10 +719,7 @@ class TypeInt : public TypeInteger { #endif }; - -//------------------------------TypeLong--------------------------------------- -// Class of long integer ranges, the set of integers between a lower bound and -// an upper bound, inclusive. +// Similar to TypeInt class TypeLong : public TypeInteger { TypeLong(const TypeIntPrototype& t, int w, bool dual); static const Type* try_make(const TypeIntPrototype& t, int w, bool dual); @@ -700,8 +753,6 @@ class TypeLong : public TypeInteger { // argument are also elements of this type) bool contains(jlong i) const; bool contains(const TypeLong* t) const; - // Excluding the cases where this and t are the same - bool properly_contains(const TypeLong* t) const; // Check for positive 32-bit value. int is_positive_int() const { return _lo >= 0 && _hi <= (jlong)max_jint; } From a77e8f4a7a86b814118881b5edfb41f5ad0ffd68 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 11 Sep 2024 00:58:45 +0700 Subject: [PATCH 22/45] remove leftover code --- src/hotspot/share/opto/rangeinference.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 6b65e6ae51cf5..29a89ef2c9f4c 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -473,8 +473,6 @@ const Type* int_type_widen(const CT* new_type, const CT* old_type, const CT* lim } // Neither contains each other, weird? - // fatal("Integer value range is not subset"); - // return this; if (!int_type_subset(new_type, old_type)) { return CT::TYPE_DOMAIN; } From 256437859ba900be87520deb8ad2186fbf02341d Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 12 Sep 2024 23:44:17 +0700 Subject: [PATCH 23/45] refine comments --- src/hotspot/share/opto/rangeinference.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 29a89ef2c9f4c..aea0cdbfd679b 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -72,9 +72,9 @@ static U adjust_lo(U lo, const KnownBits& bits) { // zeros = 0100 // ones = 1001 // zero_violation = 0100, i.e the second bit should be zero, but it is 1 in - // lo. Similarly, one_violation = 0001, i.e the forth bit should be one, but - // it is 0 in lo. These make lo not satisfy the bit constraints, which - // results in us having to find the smallest value that satisfies bits + // lo. Similarly, one_violation = 0001, i.e the LSB should be one, but it is + // 0 in lo. These make lo not satisfy the bit constraints, which results in + // us having to find the smallest value that satisfies bits U zero_violation = lo & bits._zeros; U one_violation = ~lo & bits._ones; if (zero_violation == one_violation) { @@ -83,10 +83,10 @@ static U adjust_lo(U lo, const KnownBits& bits) { return lo; } - // The principal here is that, consider the first bit in result that is + // The principle here is that, consider the first bit in result that is // different from the corresponding bit in lo, since result is larger than lo - // the bit must be unset in lo and set in result. As result should be the - // smallest value, the position of this bit should be as low as possible + // the bit must be 0 in lo and 1 in result. As result should be the smallest + // value, this bit should be the rightmost one possible. // E.g: 1 2 3 4 5 6 // lo = 1 0 0 1 1 0 // x = 1 0 1 0 1 0 @@ -113,8 +113,8 @@ static U adjust_lo(U lo, const KnownBits& bits) { // 1 1 0 0 0 0 0 0 // This value must satisfy zeros, because all bits before the 2nd bit have // already satisfied zeros, and all bits after the 2nd bit are all 0 now. - // Continue the logic with each set bit in ones, we will set each bit in - // our new lo (11000000 -> 11001000 -> 11001010). The final value is our + // Continue the logic with each 1 bit in ones, we will set each bit in our + // new lo (11000000 -> 11001000 -> 11001010). The final value is our // result. // Implementationwise, from 11000000 we can just | with ones to obtain the // final result. @@ -134,9 +134,9 @@ static U adjust_lo(U lo, const KnownBits& bits) { // This is more difficult because trying to unset a bit requires us to flip // some bits before it (higher bits). - // Consider the first bit that is change, it must not be set already, and it - // must not be set in zeros. As a result, it must be the last bit before the - // first bit violation that is unset in both zeros and lo. + // Consider the first bit that is changed, it must not be 1 already, and it + // must not be 1 in zeros. As a result, it must be the last bit before the + // first bit violation that is 0 in both zeros and lo. // E.g: 1 2 3 4 5 6 7 8 // lo = 1 0 0 0 1 1 1 0 // zeros = 0 0 0 1 0 1 0 0 From 8a5370a141cd96f8b627805b549ecb1727c340ce Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 19 Sep 2024 03:16:44 +0700 Subject: [PATCH 24/45] add comments, refactor functions to helper class --- src/hotspot/share/opto/rangeinference.cpp | 57 +++++----- src/hotspot/share/opto/rangeinference.hpp | 130 +++++++++++++--------- src/hotspot/share/opto/type.cpp | 28 ++--- 3 files changed, 122 insertions(+), 93 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index aea0cdbfd679b..469d413b273f6 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -29,13 +29,12 @@ constexpr juint SMALLINT = 3; // a value too insignificant to consider widening +// This represents the result of an iterative calculation template class AdjustResult { public: - // Denote whether there is a change in _data compared to the previous - // iteration - bool _progress; - bool _present; // whether the constraints are contradictory + bool _progress; // whether there is progress compared to the last iteration + bool _present; // whether the calculation arrives at contradiction T _data; static AdjustResult make_empty() { @@ -277,7 +276,7 @@ canonicalize_constraints_simple(const RangeInt& bounds, const KnownBits& b // values with the bit value at that position being set and unset, respectively, // such that both belong to the set represented by the constraints. template -CanonicalizedTypeIntPrototype +typename TypeIntPrototype::CanonicalizedTypeIntPrototype TypeIntPrototype::canonicalize_constraints() const { RangeInt srange = _srange; RangeInt urange = _urange; @@ -285,7 +284,7 @@ TypeIntPrototype::canonicalize_constraints() const { if (srange._lo > srange._hi || urange._lo > urange._hi || (_bits._zeros & _bits._ones) != 0) { - return CanonicalizedTypeIntPrototype::make_empty(); + return CanonicalizedTypeIntPrototype::make_empty(); } // Trivially canonicalize the bounds so that srange._lo and urange._hi are @@ -309,7 +308,7 @@ TypeIntPrototype::canonicalize_constraints() const { urange._lo = MAX2(urange._lo, srange._lo); urange._hi = MIN2(urange._hi, srange._hi); if (urange._lo > urange._hi) { - return CanonicalizedTypeIntPrototype::make_empty(); + return CanonicalizedTypeIntPrototype::make_empty(); } auto type = canonicalize_constraints_simple(urange, _bits); @@ -325,7 +324,7 @@ TypeIntPrototype::canonicalize_constraints() const { auto pos_type = canonicalize_constraints_simple({urange._lo, U(srange._hi)}, _bits); if (!neg_type._present && !pos_type._present) { - return CanonicalizedTypeIntPrototype::make_empty(); + return CanonicalizedTypeIntPrototype::make_empty(); } else if (!neg_type._present) { return {true, {{S(pos_type._bounds._lo), S(pos_type._bounds._hi)}, pos_type._bounds, pos_type._bits}}; @@ -343,7 +342,7 @@ template int TypeIntPrototype::normalize_widen(int w) const { // Certain normalizations keep us sane when comparing types. // The 'SMALLINT' covers constants and also CC and its relatives. - if (cardinality_from_bounds(_srange, _urange) <= SMALLINT) { + if (TypeIntHelper::cardinality_from_bounds(_srange, _urange) <= SMALLINT) { return Type::WidenMin; } if (_srange._lo == std::numeric_limits::min() && _srange._hi == std::numeric_limits::max() && @@ -395,7 +394,7 @@ template class TypeIntPrototype; // Compute the meet of 2 types, when dual is true, we are actually computing the // join. template -const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual) { +const Type* TypeIntHelper::int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual) { // Perform a fast test for common case; meeting the same types together. if (i1 == t2 || t2 == Type::TOP) { return i1; @@ -444,16 +443,16 @@ const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(con return nullptr; } } -template const Type* int_type_xmeet(const TypeInt* i1, const Type* t2, - const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); -template const Type* int_type_xmeet(const TypeLong* i1, const Type* t2, - const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); +template const Type* TypeIntHelper::int_type_xmeet(const TypeInt* i1, const Type* t2, + const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); +template const Type* TypeIntHelper::int_type_xmeet(const TypeLong* i1, const Type* t2, + const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); // Called in PhiNode::Value during CCP, monotically widen the value set, do so rigorously // first, after WidenMax attempts, if the type has still not converged we speed up the // convergence by abandoning the bounds template -const Type* int_type_widen(const CT* new_type, const CT* old_type, const CT* limit_type) { +const Type* TypeIntHelper::int_type_widen(const CT* new_type, const CT* old_type, const CT* limit_type) { using S = std::remove_const_t; using U = std::remove_const_t; @@ -513,14 +512,14 @@ const Type* int_type_widen(const CT* new_type, const CT* old_type, const CT* lim TypeIntPrototype prototype{{min, max}, {umin, umax}, {zeros, ones}}; return CT::try_make(prototype, Type::WidenMax); } -template const Type* int_type_widen(const TypeInt* new_type, const TypeInt* old_type, const TypeInt* limit_type); -template const Type* int_type_widen(const TypeLong* new_type, const TypeLong* old_type, const TypeLong* limit_type); +template const Type* TypeIntHelper::int_type_widen(const TypeInt* new_type, const TypeInt* old_type, const TypeInt* limit_type); +template const Type* TypeIntHelper::int_type_widen(const TypeLong* new_type, const TypeLong* old_type, const TypeLong* limit_type); // Called by PhiNode::Value during GVN, monotonically narrow the value set, only // narrow if the bits change or if the bounds are tightened enough to avoid // slow convergence template -const Type* int_type_narrow(const CT* new_type, const CT* old_type) { +const Type* TypeIntHelper::int_type_narrow(const CT* new_type, const CT* old_type) { using S = decltype(CT::_lo); using U = decltype(CT::_ulo); @@ -555,8 +554,8 @@ const Type* int_type_narrow(const CT* new_type, const CT* old_type) { RangeInt{new_type->_ulo, new_type->_uhi}); return (nc > (oc >> 1) + (SMALLINT * 2)) ? old_type : new_type; } -template const Type* int_type_narrow(const TypeInt* new_type, const TypeInt* old_type); -template const Type* int_type_narrow(const TypeLong* new_type, const TypeLong* old_type); +template const Type* TypeIntHelper::int_type_narrow(const TypeInt* new_type, const TypeInt* old_type); +template const Type* TypeIntHelper::int_type_narrow(const TypeLong* new_type, const TypeLong* old_type); #ifndef PRODUCT @@ -578,7 +577,7 @@ static const char* int_name_near(T origin, const char* xname, char* buf, size_t return buf; } -const char* intname(char* buf, size_t buf_size, jint n) { +const char* TypeIntHelper::intname(char* buf, size_t buf_size, jint n) { const char* str = int_name_near(max_jint, "maxint", buf, buf_size, n); if (str != nullptr) { return str; @@ -593,7 +592,7 @@ const char* intname(char* buf, size_t buf_size, jint n) { return buf; } -const char* uintname(char* buf, size_t buf_size, juint n) { +const char* TypeIntHelper::uintname(char* buf, size_t buf_size, juint n) { const char* str = int_name_near(max_juint, "maxuint", buf, buf_size, n); if (str != nullptr) { return str; @@ -608,7 +607,7 @@ const char* uintname(char* buf, size_t buf_size, juint n) { return buf; } -const char* longname(char* buf, size_t buf_size, jlong n) { +const char* TypeIntHelper::longname(char* buf, size_t buf_size, jlong n) { const char* str = int_name_near(max_jlong, "maxlong", buf, buf_size, n); if (str != nullptr) { return str; @@ -638,7 +637,7 @@ const char* longname(char* buf, size_t buf_size, jlong n) { return buf; } -const char* ulongname(char* buf, size_t buf_size, julong n) { +const char* TypeIntHelper::ulongname(char* buf, size_t buf_size, julong n) { const char* str = int_name_near(max_julong, "maxulong", buf, buf_size, n); if (str != nullptr) { return str; @@ -664,7 +663,7 @@ const char* ulongname(char* buf, size_t buf_size, julong n) { } template -const char* bitname(char* buf, size_t buf_size, U zeros, U ones) { +const char* TypeIntHelper::bitname(char* buf, size_t buf_size, U zeros, U ones) { constexpr juint W = sizeof(U) * 8; if (buf_size < W + 1) { @@ -684,10 +683,10 @@ const char* bitname(char* buf, size_t buf_size, U zeros, U ones) { buf[W] = 0; return buf; } -template const char* bitname(char* buf, size_t buf_size, juint zeros, juint ones); -template const char* bitname(char* buf, size_t buf_size, julong zeros, julong ones); +template const char* TypeIntHelper::bitname(char* buf, size_t buf_size, juint zeros, juint ones); +template const char* TypeIntHelper::bitname(char* buf, size_t buf_size, julong zeros, julong ones); -void int_type_dump(const TypeInt* t, outputStream* st, bool verbose) { +void TypeIntHelper::int_type_dump(const TypeInt* t, outputStream* st, bool verbose) { char buf1[40], buf2[40], buf3[40], buf4[40], buf5[40]; if (int_type_equal(t, TypeInt::INT)) { st->print("int"); @@ -734,7 +733,7 @@ void int_type_dump(const TypeInt* t, outputStream* st, bool verbose) { } } -void int_type_dump(const TypeLong* t, outputStream* st, bool verbose) { +void TypeIntHelper::int_type_dump(const TypeLong* t, outputStream* st, bool verbose) { char buf1[80], buf2[80], buf3[80], buf4[80], buf5[80]; if (int_type_equal(t, TypeLong::LONG)) { st->print("long"); diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 68820a88f37b8..9149b27c43608 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -33,6 +33,7 @@ class Type; class TypeInt; class TypeLong; +// A simple range in the signed or unsigned domain template class RangeInt { public: @@ -42,8 +43,17 @@ class RangeInt { /** * Bits that are known to be 0 or 1. A value v satisfies this constraint iff - * (v & zeros) == 0 && (v & ones) == ones. I.e, all bits that is set in zeros - * must be unset in v, and all bits that is set in ones must be set in v. + * (v & zeros) == 0 && (v & ones) == ones. I.e, any bit that is 1 in zeros must + * be 0 in v, and any bit that is 1 in ones must be 1 in v. + * + * I.e, for each bit position from 0 to sizeof(U) - 1, the corresponding bits + * of zeros, ones and the allowed bit in v must follow: + * + * zeros ones allowed bits + * 0 0 0 or 1 + * 1 0 0 + * 0 1 1 + * 1 1 none (impossible state) * * E.g: * zeros: 00110100 @@ -68,20 +78,9 @@ class KnownBits { } }; -template -class TypeIntPrototype; - -template -class CanonicalizedTypeIntPrototype { -public: - bool _present; - TypeIntPrototype _data; - - static CanonicalizedTypeIntPrototype make_empty() { - return {false, {}}; - } -}; - +// All the information needed to construct a TypeInt/TypeLong, the constraints +// here may be arbitrary and need to be canonicalized to construct a +// TypeInt/TypeLong template class TypeIntPrototype { public: @@ -93,7 +92,29 @@ class TypeIntPrototype { RangeInt _urange; KnownBits _bits; - CanonicalizedTypeIntPrototype canonicalize_constraints() const; +private: + friend class TypeInt; + friend class TypeLong; + + template + friend void test_canonicalize_constraints_simple(); + + template + friend void test_canonicalize_constraints_random(); + + // A canonicalized version of a TypeIntPrototype, if the prototype represents + // an empty type, _present is false, otherwise, _data is canonical + class CanonicalizedTypeIntPrototype { + public: + bool _present; // whether this is an empty set + TypeIntPrototype _data; + + static CanonicalizedTypeIntPrototype make_empty() { + return {false, {}}; + } + }; + + CanonicalizedTypeIntPrototype canonicalize_constraints() const; int normalize_widen(int w) const; #ifdef ASSERT bool contains(S v) const; @@ -101,49 +122,58 @@ class TypeIntPrototype { #endif // ASSERT }; -// The result is tuned down by one since we do not have empty type -// and this is not required to be accurate -template -U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { - if (U(srange._lo) == urange._lo) { - return urange._hi - urange._lo; +// Various helper functions for TypeInt/TypeLong operations +class TypeIntHelper { +public: + // Calculate the cardinality of a TypeInt/TypeLong ignoring the bits + // constraints, the result is tuned down by 1 to ensure the bottom type is + // correctly calculated + template + static U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { + static_assert(std::is_signed::value, ""); + static_assert(std::is_unsigned::value, ""); + static_assert(sizeof(S) == sizeof(U), ""); + + if (U(srange._lo) == urange._lo) { + return urange._hi - urange._lo; + } + + return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + 1; } - return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + 1; -} + template + static const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); -template -const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); - -template -bool int_type_equal(const CT* t1, const CT* t2) { - return t1->_lo == t2->_lo && t1->_hi == t2->_hi && t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && - t1->_bits._zeros == t2->_bits._zeros && t1->_bits._ones == t2->_bits._ones; -} + template + static bool int_type_equal(const CT* t1, const CT* t2) { + return t1->_lo == t2->_lo && t1->_hi == t2->_hi && t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && + t1->_bits._zeros == t2->_bits._zeros && t1->_bits._ones == t2->_bits._ones; + } -template -bool int_type_subset(const CT* super, const CT* sub) { - return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && - (super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0; -} + template + static bool int_type_subset(const CT* super, const CT* sub) { + return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && + (super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0; + } -template -const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt); + template + static const Type* int_type_widen(const CT* nt, const CT* ot, const CT* lt); -template -const Type* int_type_narrow(const CT* nt, const CT* ot); + template + static const Type* int_type_narrow(const CT* nt, const CT* ot); #ifndef PRODUCT -const char* intname(char* buf, size_t buf_size, jint n); -const char* uintname(char* buf, size_t buf_size, juint n); -const char* longname(char* buf, size_t buf_size, jlong n); -const char* ulongname(char* buf, size_t buf_size, julong n); + static const char* intname(char* buf, size_t buf_size, jint n); + static const char* uintname(char* buf, size_t buf_size, juint n); + static const char* longname(char* buf, size_t buf_size, jlong n); + static const char* ulongname(char* buf, size_t buf_size, julong n); -template -const char* bitname(char* buf, size_t buf_size, U zeros, U ones); + template + static const char* bitname(char* buf, size_t buf_size, U zeros, U ones); -void int_type_dump(const TypeInt* t, outputStream* st, bool verbose); -void int_type_dump(const TypeLong* t, outputStream* st, bool verbose); + static void int_type_dump(const TypeInt* t, outputStream* st, bool verbose); + static void int_type_dump(const TypeLong* t, outputStream* st, bool verbose); #endif // PRODUCT +}; #endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 99584d105b1e9..54d0aefcd27f0 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1627,11 +1627,11 @@ bool TypeInt::contains(jint i) const { } bool TypeInt::contains(const TypeInt* t) const { - return int_type_subset(this, t); + return TypeIntHelper::int_type_subset(this, t); } const Type* TypeInt::xmeet(const Type* t) const { - return int_type_xmeet(this, t, TypeInt::try_make, _is_dual); + return TypeIntHelper::int_type_xmeet(this, t, TypeInt::try_make, _is_dual); } const Type* TypeInt::xdual() const { @@ -1641,7 +1641,7 @@ const Type* TypeInt::xdual() const { const Type* TypeInt::widen(const Type* old, const Type* limit) const { assert(!_is_dual, "dual types should only be used for join calculation"); - return int_type_widen(this, old->isa_int(), limit->isa_int()); + return TypeIntHelper::int_type_widen(this, old->isa_int(), limit->isa_int()); } const Type* TypeInt::narrow(const Type* old) const { @@ -1650,7 +1650,7 @@ const Type* TypeInt::narrow(const Type* old) const { return this; } - return int_type_narrow(this, old->isa_int()); + return TypeIntHelper::int_type_narrow(this, old->isa_int()); } //-----------------------------filter------------------------------------------ @@ -1674,7 +1674,7 @@ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) // Structural equality check for Type representations bool TypeInt::eq(const Type* t) const { const TypeInt* r = t->is_int(); - return int_type_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; + return TypeIntHelper::int_type_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; } //------------------------------hash------------------------------------------- @@ -1751,11 +1751,11 @@ bool TypeLong::contains(jlong i) const { } bool TypeLong::contains(const TypeLong* t) const { - return int_type_subset(this, t); + return TypeIntHelper::int_type_subset(this, t); } const Type *TypeLong::xmeet(const Type* t) const { - return int_type_xmeet(this, t, TypeLong::try_make, _is_dual); + return TypeIntHelper::int_type_xmeet(this, t, TypeLong::try_make, _is_dual); } const Type* TypeLong::xdual() const { @@ -1765,7 +1765,7 @@ const Type* TypeLong::xdual() const { const Type* TypeLong::widen(const Type* old, const Type* limit) const { assert(!_is_dual, "dual types should only be used for join calculation"); - return int_type_widen(this, old->isa_long(), limit->isa_long()); + return TypeIntHelper::int_type_widen(this, old->isa_long(), limit->isa_long()); } const Type* TypeLong::narrow(const Type* old) const { @@ -1774,7 +1774,7 @@ const Type* TypeLong::narrow(const Type* old) const { return this; } - return int_type_narrow(this, old->isa_long()); + return TypeIntHelper::int_type_narrow(this, old->isa_long()); } //-----------------------------filter------------------------------------------ @@ -1798,7 +1798,7 @@ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) // Structural equality check for Type representations bool TypeLong::eq(const Type* t) const { const TypeLong* r = t->is_long(); - return int_type_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; + return TypeIntHelper::int_type_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; } //------------------------------hash------------------------------------------- @@ -1828,19 +1828,19 @@ bool TypeLong::empty(void) const { //------------------------------dump2------------------------------------------ #ifndef PRODUCT void TypeInt::dump2(Dict& d, uint depth, outputStream* st) const { - int_type_dump(this, st, false); + TypeIntHelper::int_type_dump(this, st, false); } void TypeInt::dump_verbose() const { - int_type_dump(this, tty, true); + TypeIntHelper::int_type_dump(this, tty, true); } void TypeLong::dump2(Dict& d, uint depth, outputStream* st) const { - int_type_dump(this, st, false); + TypeIntHelper::int_type_dump(this, st, false); } void TypeLong::dump_verbose() const { - int_type_dump(this, tty, true); + TypeIntHelper::int_type_dump(this, tty, true); } #endif From 644bcedfb14c8d76b7b9d6746178cb7a59f009b3 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 19 Sep 2024 21:10:28 +0700 Subject: [PATCH 25/45] address reviews --- src/hotspot/share/opto/rangeinference.cpp | 155 ++++++++++++---------- src/hotspot/share/opto/rangeinference.hpp | 8 +- 2 files changed, 92 insertions(+), 71 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 469d413b273f6..6112427e40489 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -33,9 +33,9 @@ constexpr juint SMALLINT = 3; // a value too insignificant to consider widening template class AdjustResult { public: - bool _progress; // whether there is progress compared to the last iteration - bool _present; // whether the calculation arrives at contradiction - T _data; + bool _progress; // whether there is progress compared to the last iteration + bool _is_result_consistent; // whether the calculation arrives at contradiction + T _result; static AdjustResult make_empty() { return {true, false, {}}; @@ -62,7 +62,12 @@ class SimpleCanonicalResult { } }; -// Find the minimum value that is not less than lo and satisfies bits +// Find the minimum value that is not less than lo and satisfies bits. +// Here, we view a number in binary as a bit string. As a result, the first +// bit refers to the highest bit (the MSB), the last bit refers to the lowest +// bit (the LSB), a bit comes before (being higher than) another if it is more +// significant, and a bit comes after (being lower than) another if it is less +// significant. template static U adjust_lo(U lo, const KnownBits& bits) { constexpr size_t W = sizeof(U) * 8; @@ -71,9 +76,9 @@ static U adjust_lo(U lo, const KnownBits& bits) { // zeros = 0100 // ones = 1001 // zero_violation = 0100, i.e the second bit should be zero, but it is 1 in - // lo. Similarly, one_violation = 0001, i.e the LSB should be one, but it is - // 0 in lo. These make lo not satisfy the bit constraints, which results in - // us having to find the smallest value that satisfies bits + // lo. Similarly, one_violation = 0001, i.e the last bit should be one, but + // it is 0 in lo. These make lo not satisfy the bit constraints, which + // results in us having to find the smallest value that satisfies bits U zero_violation = lo & bits._zeros; U one_violation = ~lo & bits._ones; if (zero_violation == one_violation) { @@ -85,7 +90,7 @@ static U adjust_lo(U lo, const KnownBits& bits) { // The principle here is that, consider the first bit in result that is // different from the corresponding bit in lo, since result is larger than lo // the bit must be 0 in lo and 1 in result. As result should be the smallest - // value, this bit should be the rightmost one possible. + // value, this bit should be the last one possible. // E.g: 1 2 3 4 5 6 // lo = 1 0 0 1 1 0 // x = 1 0 1 0 1 0 @@ -98,10 +103,21 @@ static U adjust_lo(U lo, const KnownBits& bits) { // Both x1 and x2 are larger than lo, but x1 > x2 since its first different // bit from lo is the 3rd one, while with x2 it is the 7th one. As a result, // if both x1 and x2 satisfy bits, x2 would be closer to our true result. + + // The algorithm depends on whether the first violation violates zeros or + // ones, if it violates zeros, we have the bit being 1 in zero_violation and + // 0 in one_violation. Since all higher bits are 0 in zero_violation and + // one_violation, we have zero_violation > one_violation. Similarly, if the + // first violation violates ones, we have zero_violation < one_violation. if (zero_violation < one_violation) { // This means that the first bit that does not satisfy the bit requirement // is a 0 that should be a 1, this may be the first different bit we want - // to find. + // to find. The smallest value higher than lo with this bit being 1 would + // have all lower bits being 0. This value satisfies zeros, because all + // bits before the first violation have already satisfied zeros, and all + // bits after the first violation are 0. To satisfy 1, simply | this value + // with ones + // // E.g: 1 2 3 4 5 6 7 8 // lo = 1 0 0 1 0 0 1 0 // zeros = 0 0 1 0 0 1 0 0 @@ -112,11 +128,7 @@ static U adjust_lo(U lo, const KnownBits& bits) { // 1 1 0 0 0 0 0 0 // This value must satisfy zeros, because all bits before the 2nd bit have // already satisfied zeros, and all bits after the 2nd bit are all 0 now. - // Continue the logic with each 1 bit in ones, we will set each bit in our - // new lo (11000000 -> 11001000 -> 11001010). The final value is our - // result. - // Implementationwise, from 11000000 we can just | with ones to obtain the - // final result. + // Just | this value with ones to obtain the final result. // first_violation is the position of the violation counting from the // lowest bit up (0-based) @@ -129,49 +141,54 @@ static U adjust_lo(U lo, const KnownBits& bits) { lo = (lo & -alignment) + alignment; // 1 1 0 0 1 0 1 0 return lo | bits._ones; + } else { + // This is more difficult because trying to unset a bit requires us to flip + // some bits before it. + // Consider the first bit that is changed, it must not be 1 already, and it + // must not be 1 in zeros. As a result, it must be the last bit before the + // first bit violation that is 0 in both zeros and lo. As a result, the + // smallest number not smaller than lo that satisfies zeros would have this + // bit being 1 and all lower bits being 0. Similar to the case with + // zero_violation < one_violation, | this value with ones gives us the + // final result. + // + // E.g: 1 2 3 4 5 6 7 8 + // lo = 1 0 0 0 1 1 1 0 + // zeros = 0 0 0 1 0 1 0 0 + // ones = 1 0 0 0 0 0 1 1 + // 1-vio = 0 0 0 0 0 0 0 1 + // 0-vio = 0 0 0 0 0 1 0 0 + // The first violation is the 6th bit, which should be 0. The 5th cannot be + // the first different bit we are looking for, because it is already 1, the + // 4th bit also cannot be, because it must be 0. As a result, the first + // different bit between the result and lo must be the 3rd bit. As a result, + // the result must not be smaller than: + // 1 0 1 0 0 0 0 0 + // This one satisfies zeros so we can use the logic in the previous case to + // obtain our final result, which is: + // 1 0 1 0 0 0 1 1 + + juint first_violation = W - count_leading_zeros(zero_violation); + // This mask out all bits from the first violation + // 1 1 1 1 1 0 0 0 + U find_mask = std::numeric_limits::max() << first_violation; + // 1 0 0 1 1 1 1 0 + U either = lo | bits._zeros; + // The bit we want to set is the last bit unset in either that stands before + // the first violation, which is the last set bit of tmp + // 0 1 1 0 0 0 0 0 + U tmp = ~either & find_mask; + // Isolate the last bit + // 0 0 1 0 0 0 0 0 + U alignment = tmp & (-tmp); + // Set the bit and unset all the bit after, this is the smallest value that + // satisfies bits._zeros + // 1 0 1 0 0 0 0 0 + lo = (lo & -alignment) + alignment; + // Satisfy bits._ones + // 1 0 1 0 0 0 1 1 + return lo | bits._ones; } - - // This is more difficult because trying to unset a bit requires us to flip - // some bits before it (higher bits). - // Consider the first bit that is changed, it must not be 1 already, and it - // must not be 1 in zeros. As a result, it must be the last bit before the - // first bit violation that is 0 in both zeros and lo. - // E.g: 1 2 3 4 5 6 7 8 - // lo = 1 0 0 0 1 1 1 0 - // zeros = 0 0 0 1 0 1 0 0 - // ones = 1 0 0 0 0 0 1 1 - // 1-vio = 0 0 0 0 0 0 0 1 - // 0-vio = 0 0 0 0 0 1 0 0 - // The first violation is the 6th bit, which should be 0. The 5th cannot be - // the first different bit we are looking for, because it is already 1, the - // 4th bit also cannot be, because it must be 0. As a result, the first - // different bit between the result and lo must be the 3rd bit. As a result, - // the result must not be smaller than: - // 1 0 1 0 0 0 0 0 - // This one satisfies zeros so we can use the logic in the previous case to - // obtain our final result, which is: - // 1 0 1 0 0 0 1 1 - - juint first_violation = W - count_leading_zeros(zero_violation); - // This mask out all bits from the first violation - // 1 1 1 1 1 0 0 0 - U find_mask = std::numeric_limits::max() << first_violation; - // 1 0 0 1 1 1 1 0 - U either = lo | bits._zeros; - // The bit we want to set is the last bit unset in either that stands before - // the first violation, which is the last set bit of tmp - // 0 1 1 0 0 0 0 0 - U tmp = ~either & find_mask; - // Isolate the last bit - // 0 0 1 0 0 0 0 0 - U alignment = tmp & (-tmp); - // Set the bit and unset all the bit after, this is the smallest value that - // satisfies bits._zeros - // 1 0 1 0 0 0 0 0 - lo = (lo & -alignment) + alignment; - // Satisfy bits._ones - // 1 0 1 0 0 0 1 1 - return lo | bits._ones; } // Try to tighten the bound constraints from the known bit information. I.e, we @@ -251,7 +268,7 @@ template static SimpleCanonicalResult canonicalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { AdjustResult> nbits = adjust_bits_from_bounds(bits, bounds); - if (!nbits._present) { + if (!nbits._is_result_consistent) { return SimpleCanonicalResult::make_empty(); } AdjustResult> nbounds{true, true, bounds}; @@ -259,13 +276,13 @@ canonicalize_constraints_simple(const RangeInt& bounds, const KnownBits& b // versa, if one does not show progress, the other will also not show // progress, so we terminate early while (true) { - nbounds = adjust_bounds_from_bits(nbounds._data, nbits._data); - if (!nbounds._progress || !nbounds._present) { - return {nbounds._present, nbounds._data, nbits._data}; + nbounds = adjust_bounds_from_bits(nbounds._result, nbits._result); + if (!nbounds._progress || !nbounds._is_result_consistent) { + return {nbounds._is_result_consistent, nbounds._result, nbits._result}; } - nbits = adjust_bits_from_bounds(nbits._data, nbounds._data); - if (!nbits._progress || !nbits._present) { - return {nbits._present, nbounds._data, nbits._data}; + nbits = adjust_bits_from_bounds(nbits._result, nbounds._result); + if (!nbits._progress || !nbits._is_result_consistent) { + return {nbits._is_result_consistent, nbounds._result, nbits._result}; } } } @@ -374,16 +391,16 @@ void TypeIntPrototype::verify_constraints() const { } else { RangeInt neg_range{U(_srange._lo), _urange._hi}; auto neg_bits = adjust_bits_from_bounds(_bits, neg_range); - assert(neg_bits._present, ""); - assert(!adjust_bounds_from_bits(neg_range, neg_bits._data)._progress, ""); + assert(neg_bits._is_result_consistent, ""); + assert(!adjust_bounds_from_bits(neg_range, neg_bits._result)._progress, ""); RangeInt pos_range{_urange._lo, U(_srange._hi)}; auto pos_bits = adjust_bits_from_bounds(_bits, pos_range); - assert(pos_bits._present, ""); - assert(!adjust_bounds_from_bits(pos_range, pos_bits._data)._progress, ""); + assert(pos_bits._is_result_consistent, ""); + assert(!adjust_bounds_from_bits(pos_range, pos_bits._result)._progress, ""); - assert((neg_bits._data._zeros & pos_bits._data._zeros) == _bits._zeros && - (neg_bits._data._ones & pos_bits._data._ones) == _bits._ones, ""); + assert((neg_bits._result._zeros & pos_bits._result._zeros) == _bits._zeros && + (neg_bits._result._ones & pos_bits._result._ones) == _bits._ones, ""); } } #endif // ASSERT diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 9149b27c43608..cb0efe79e4f30 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -126,8 +126,8 @@ class TypeIntPrototype { class TypeIntHelper { public: // Calculate the cardinality of a TypeInt/TypeLong ignoring the bits - // constraints, the result is tuned down by 1 to ensure the bottom type is - // correctly calculated + // constraints, the return value is the cardinality minus 1 to not overflow + // with the bottom type template static U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { static_assert(std::is_signed::value, ""); @@ -135,9 +135,13 @@ class TypeIntHelper { static_assert(sizeof(S) == sizeof(U), ""); if (U(srange._lo) == urange._lo) { + // srange is the same as urange + assert(U(srange._hi) == urange._hi, ""); return urange._hi - urange._lo; } + // srange intersects with urange in 2 intervals [srange._lo, urange._hi] + // and [urange._lo, srange._hi] return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + 1; } From f2d3f3b107abdf2ac96b179f529e35ad3f392e2f Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 20 Sep 2024 00:06:22 +0700 Subject: [PATCH 26/45] formality --- src/hotspot/share/opto/rangeinference.cpp | 150 ++++++++++++++++------ 1 file changed, 111 insertions(+), 39 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 6112427e40489..2e095f70d3393 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -87,22 +87,102 @@ static U adjust_lo(U lo, const KnownBits& bits) { return lo; } - // The principle here is that, consider the first bit in result that is - // different from the corresponding bit in lo, since result is larger than lo - // the bit must be 0 in lo and 1 in result. As result should be the smallest - // value, this bit should be the last one possible. - // E.g: 1 2 3 4 5 6 - // lo = 1 0 0 1 1 0 - // x = 1 0 1 0 1 0 - // y = 0 1 1 1 1 1 - // x would be larger than lo since the first different bit is the 3rd one, - // while y is smaller than lo because the first different bit is the 1st bit. - // Next consider: - // x1 = 1 0 1 0 1 0 - // x2 = 1 0 0 1 1 1 - // Both x1 and x2 are larger than lo, but x1 > x2 since its first different - // bit from lo is the 3rd one, while with x2 it is the 7th one. As a result, - // if both x1 and x2 satisfy bits, x2 would be closer to our true result. + /* + 1. Intuition: + Call res the lowest value not smaller than lo that satisfies bits, consider + the first bit in res that is different from the corresponding bit in lo, + since res is larger than lo the bit must be 0 in lo and 1 in res. Since res + must satisify bits the bit must be 0 in zeros. Finally, as res should be the + smallest value, this bit should be the last one possible. + + E.g: 1 2 3 4 5 6 + lo = 1 0 0 1 1 0 + x = 1 0 1 0 1 0 + y = 0 1 1 1 1 1 + x would be larger than lo since the first different bit is the 3rd one, + while y is smaller than lo because the first different bit is the 1st bit. + Next, consider: + x1 = 1 0 1 0 1 0 + x2 = 1 0 0 1 1 1 + Both x1 and x2 are larger than lo, but x1 > x2 since its first different + bit from lo is the 3rd one, while with x2 it is the 7th one. As a result, + if both x1 and x2 satisfy bits, x2 would be closer to our true result. + + 2. Formality: + Call i the largest value such that (with v[0] being the first bit of v, v[1] + being the second bit of v and so on): + + - lo[x] satisfies bits for 0 <= x < i + - zeros[i] = 0 + - lo[i] = 0 + + Consider v: + + - v[x] = lo[x], for 0 <= x < i + - v[i] = 1 + - v[x] = ones[x], for j > i + + We will prove that v is the smallest value not smaller than lo that + satisfies bits. + + Call r the smallest value not smaller than lo that satisfies bits. + + a. Firstly, we prove that r <= v: + + Trivially, lo < v since lo[i] < v[i] and lo[x] == v[x] for x < i. + + As established above, the first (i + 1) bits of v satisfy bits. + The remaining bits satisfy zeros, since any bit x > i such that zeros[x] == 1, v[x] == ones[x] == 0 + They also satisfy ones, since any bit j > i such that ones[x] == 1, v[x] == ones[x] == 1 + + As a result, v > lo and v satisfies bits since all of its bits satisfy bits. Which + means r <= v since r is the smallest such value. + + b. Secondly, we prove that r >= v. Suppose r < v: + + Since r < v, there must be a bit position j that: + + r[j] == 0, v[j] == 1 + r[x] == v[x], for x < j + + - If j < i + r[j] == 0, v[j] == lo[j] == 1 + r[x] == v[x] == lo[x], for x < j + + This means r < lo, which contradicts that r >= lo + + - If j == i + This means that lo[i] == r[i]. Call k the bit position such that: + + r[k] == 1, lo[k] == 0 + r[x] == lo[x], for x < k + + k > i since r[x] == lo[x], for x <= i + lo[x] satisfies bits for 0 <= x < k + zeros[k] == 0 + This contradicts the assumption that i being the largest value satisfying such conditions. + + - If j > i: + ones[j] == v[j] == 1, which contradicts that r satisfies bits. + + All cases lead to contradictions, which mean r < v is incorrect, which means + that r >= v. + + As a result, r == v, which means the value v having the above form is the + lowest value not smaller than lo that satisfies bits. + + Our objective now is to find the largest value i that satisfies: + - lo[x] satisfies bits for 0 <= x < i + - zeros[i] = 0 + - lo[i] = 0 + + Call j the largest value such that lo[x] satisfies bits for 0 <= x < j. This + means that j is the smallest value such that lo[j] does not satisfy bits. We + call this the first violation. i then can be computed as the largest value + <= j such that: + + zeros[i] == lo[i] == 0 + */ // The algorithm depends on whether the first violation violates zeros or // ones, if it violates zeros, we have the bit being 1 in zero_violation and @@ -111,12 +191,9 @@ static U adjust_lo(U lo, const KnownBits& bits) { // first violation violates ones, we have zero_violation < one_violation. if (zero_violation < one_violation) { // This means that the first bit that does not satisfy the bit requirement - // is a 0 that should be a 1, this may be the first different bit we want - // to find. The smallest value higher than lo with this bit being 1 would - // have all lower bits being 0. This value satisfies zeros, because all - // bits before the first violation have already satisfied zeros, and all - // bits after the first violation are 0. To satisfy 1, simply | this value - // with ones + // is a 0 that should be a 1. Obviously, since the bit at that position in + // ones is 1, the same bit in zeros is 0. Which means this is the value of + // i we are looking for. // // E.g: 1 2 3 4 5 6 7 8 // lo = 1 0 0 1 0 0 1 0 @@ -128,29 +205,23 @@ static U adjust_lo(U lo, const KnownBits& bits) { // 1 1 0 0 0 0 0 0 // This value must satisfy zeros, because all bits before the 2nd bit have // already satisfied zeros, and all bits after the 2nd bit are all 0 now. - // Just | this value with ones to obtain the final result. + // Just OR this value with ones to obtain the final result. // first_violation is the position of the violation counting from the - // lowest bit up (0-based) + // lowest bit up (0-based), since i == 2, first_difference == 6 juint first_violation = W - 1 - count_leading_zeros(one_violation); // 6 // 0 1 0 0 0 0 0 0 U alignment = U(1) << first_violation; - // This is the first value which have the violated bit set, which means + // This is the first value which have the violated bit being 1, which means // that the result should not be smaller than this // 1 1 0 0 0 0 0 0 lo = (lo & -alignment) + alignment; // 1 1 0 0 1 0 1 0 return lo | bits._ones; } else { - // This is more difficult because trying to unset a bit requires us to flip - // some bits before it. - // Consider the first bit that is changed, it must not be 1 already, and it - // must not be 1 in zeros. As a result, it must be the last bit before the - // first bit violation that is 0 in both zeros and lo. As a result, the - // smallest number not smaller than lo that satisfies zeros would have this - // bit being 1 and all lower bits being 0. Similar to the case with - // zero_violation < one_violation, | this value with ones gives us the - // final result. + // This means that the first bit that does not satisfy the bit requirement + // is a 1 that should be a 0. Trace backward to find i which is the last + // bit that is 0 in both lo and zeros. // // E.g: 1 2 3 4 5 6 7 8 // lo = 1 0 0 0 1 1 1 0 @@ -174,15 +245,16 @@ static U adjust_lo(U lo, const KnownBits& bits) { U find_mask = std::numeric_limits::max() << first_violation; // 1 0 0 1 1 1 1 0 U either = lo | bits._zeros; - // The bit we want to set is the last bit unset in either that stands before - // the first violation, which is the last set bit of tmp + // i is the last bit being 0 in either that stands before the first + // violation, which is the last set bit of tmp // 0 1 1 0 0 0 0 0 U tmp = ~either & find_mask; - // Isolate the last bit + // i == 2 here, shortcut the calculation instead of explicitly spelling out + // i // 0 0 1 0 0 0 0 0 U alignment = tmp & (-tmp); - // Set the bit and unset all the bit after, this is the smallest value that - // satisfies bits._zeros + // Set the bit at i and unset all the bit after, this is the smallest value + // that satisfies bits._zeros // 1 0 1 0 0 0 0 0 lo = (lo & -alignment) + alignment; // Satisfy bits._ones From c440a72a8064a508b829b2ab57c4b8cbf6a37c5f Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 20 Sep 2024 22:35:15 +0700 Subject: [PATCH 27/45] comment adjust_lo empty case --- src/hotspot/share/opto/rangeinference.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 2e095f70d3393..159b2a3055579 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -62,7 +62,10 @@ class SimpleCanonicalResult { } }; -// Find the minimum value that is not less than lo and satisfies bits. +// Find the minimum value that is not less than lo and satisfies bits. If there +// does not exist one such number, the calculation will overflow and return a +// value < lo. +// // Here, we view a number in binary as a bit string. As a result, the first // bit refers to the highest bit (the MSB), the last bit refers to the lowest // bit (the LSB), a bit comes before (being higher than) another if it is more @@ -215,9 +218,11 @@ static U adjust_lo(U lo, const KnownBits& bits) { // This is the first value which have the violated bit being 1, which means // that the result should not be smaller than this // 1 1 0 0 0 0 0 0 - lo = (lo & -alignment) + alignment; + U new_lo = (lo & -alignment) + alignment; // 1 1 0 0 1 0 1 0 - return lo | bits._ones; + new_lo |= bits._ones; + assert(lo < new_lo, "this case cannot overflow"); + return new_lo; } else { // This means that the first bit that does not satisfy the bit requirement // is a 1 that should be a 0. Trace backward to find i which is the last @@ -256,10 +261,12 @@ static U adjust_lo(U lo, const KnownBits& bits) { // Set the bit at i and unset all the bit after, this is the smallest value // that satisfies bits._zeros // 1 0 1 0 0 0 0 0 - lo = (lo & -alignment) + alignment; + U new_lo = (lo & -alignment) + alignment; // Satisfy bits._ones // 1 0 1 0 0 0 1 1 - return lo | bits._ones; + new_lo |= bits._ones; + assert(lo < new_lo || new_lo == bits._ones, "overflow must return bits._ones"); + return new_lo; } } From 4858e12c2c6d52d29e37df681f39625338318b80 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sat, 21 Sep 2024 00:30:25 +0700 Subject: [PATCH 28/45] address reviews --- src/hotspot/share/opto/rangeinference.hpp | 10 ++++++---- src/hotspot/share/opto/type.hpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index cb0efe79e4f30..49c1c7318277d 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -150,14 +150,16 @@ class TypeIntHelper { template static bool int_type_equal(const CT* t1, const CT* t2) { - return t1->_lo == t2->_lo && t1->_hi == t2->_hi && t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && - t1->_bits._zeros == t2->_bits._zeros && t1->_bits._ones == t2->_bits._ones; + return t1->_lo == t2->_lo && t1->_hi == t2->_hi && + t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && + t1->_bits._zeros == t2->_bits._zeros && t1->_bits._ones == t2->_bits._ones; } template static bool int_type_subset(const CT* super, const CT* sub) { - return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && - (super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0; + return super->_lo <= sub->_lo && super->_hi >= sub->_hi && + super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && + (super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0; } template diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 2269d2e5d600b..a650562393d94 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -616,7 +616,7 @@ class TypeInteger : public Type { * be elements of such TypeInt. Or else, we can tighted the bounds by narrowing * it by one, which contradicts the assumption of the TypeInt being canonical. * - * 2. Either _lo == jint(_lo) and _hi == jint(_uhi), or all elements of a + * 2. Either _lo == jint(_ulo) and _hi == jint(_uhi), or all elements of a * TypeInt lie in the intervals [_lo, jint(_uhi)] or [jint(_ulo), _hi] * * Proof: For 2 jint value x, y such that they are both >= 0 or < 0. Then: From 468834f5623b8a41edffd71cf82c31c6fd2b6200 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 13 Nov 2024 20:55:39 +0700 Subject: [PATCH 29/45] further reviews --- src/hotspot/share/opto/rangeinference.cpp | 34 ++++---- src/hotspot/share/opto/rangeinference.hpp | 4 +- src/hotspot/share/opto/type.cpp | 10 ++- src/hotspot/share/opto/type.hpp | 86 +++++++++++++++---- .../gtest/opto/test_rangeinference.cpp | 4 - 5 files changed, 94 insertions(+), 44 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 159b2a3055579..b32b420a4ee52 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -27,7 +27,9 @@ #include "opto/type.hpp" #include "utilities/tuple.hpp" -constexpr juint SMALLINT = 3; // a value too insignificant to consider widening +// If the cardinality of a TypeInt is below this threshold, use min widen, see +// TypeIntPrototype::normalize_widen +constexpr juint SMALL_TYPEINT_THRESHOLD = 3; // This represents the result of an iterative calculation template @@ -437,8 +439,8 @@ TypeIntPrototype::canonicalize_constraints() const { template int TypeIntPrototype::normalize_widen(int w) const { // Certain normalizations keep us sane when comparing types. - // The 'SMALLINT' covers constants and also CC and its relatives. - if (TypeIntHelper::cardinality_from_bounds(_srange, _urange) <= SMALLINT) { + // The 'SMALL_TYPEINT_THRESHOLD' covers constants and also CC and its relatives. + if (TypeIntHelper::cardinality_from_bounds(_srange, _urange) <= SMALL_TYPEINT_THRESHOLD) { return Type::WidenMin; } if (_srange._lo == std::numeric_limits::min() && _srange._hi == std::numeric_limits::max() && @@ -557,18 +559,18 @@ const Type* TypeIntHelper::int_type_widen(const CT* new_type, const CT* old_type } // If new guy is equal to old guy, no widening - if (int_type_equal(new_type, old_type)) { + if (int_type_is_equal(new_type, old_type)) { return old_type; } // If old guy contains new, then we probably widened too far & dropped to // bottom. Return the wider fellow. - if (int_type_subset(old_type, new_type)) { + if (int_type_is_subset(old_type, new_type)) { return old_type; } // Neither contains each other, weird? - if (!int_type_subset(new_type, old_type)) { + if (!int_type_is_subset(new_type, old_type)) { return CT::TYPE_DOMAIN; } @@ -624,17 +626,17 @@ const Type* TypeIntHelper::int_type_narrow(const CT* new_type, const CT* old_typ } // If new guy is equal to old guy, no narrowing - if (int_type_equal(new_type, old_type)) { + if (int_type_is_equal(new_type, old_type)) { return old_type; } // If old guy was maximum range, allow the narrowing - if (int_type_equal(old_type, CT::TYPE_DOMAIN)) { + if (int_type_is_equal(old_type, CT::TYPE_DOMAIN)) { return new_type; } // Doesn't narrow; pretty weird - if (!int_type_subset(old_type, new_type)) { + if (!int_type_is_subset(old_type, new_type)) { return new_type; } @@ -648,7 +650,7 @@ const Type* TypeIntHelper::int_type_narrow(const CT* new_type, const CT* old_typ RangeInt{old_type->_ulo, old_type->_uhi}); U nc = cardinality_from_bounds(RangeInt{new_type->_lo, new_type->_hi}, RangeInt{new_type->_ulo, new_type->_uhi}); - return (nc > (oc >> 1) + (SMALLINT * 2)) ? old_type : new_type; + return (nc > (oc >> 1) + (SMALL_TYPEINT_THRESHOLD * 2)) ? old_type : new_type; } template const Type* TypeIntHelper::int_type_narrow(const TypeInt* new_type, const TypeInt* old_type); template const Type* TypeIntHelper::int_type_narrow(const TypeLong* new_type, const TypeLong* old_type); @@ -784,17 +786,17 @@ template const char* TypeIntHelper::bitname(char* buf, size_t buf_size, julong z void TypeIntHelper::int_type_dump(const TypeInt* t, outputStream* st, bool verbose) { char buf1[40], buf2[40], buf3[40], buf4[40], buf5[40]; - if (int_type_equal(t, TypeInt::INT)) { + if (int_type_is_equal(t, TypeInt::INT)) { st->print("int"); } else if (t->is_con()) { st->print("int:%s", intname(buf1, sizeof(buf1), t->get_con())); - } else if (int_type_equal(t, TypeInt::BOOL)) { + } else if (int_type_is_equal(t, TypeInt::BOOL)) { st->print("bool"); - } else if (int_type_equal(t, TypeInt::BYTE)) { + } else if (int_type_is_equal(t, TypeInt::BYTE)) { st->print("byte"); - } else if (int_type_equal(t, TypeInt::CHAR)) { + } else if (int_type_is_equal(t, TypeInt::CHAR)) { st->print("char"); - } else if (int_type_equal(t, TypeInt::SHORT)) { + } else if (int_type_is_equal(t, TypeInt::SHORT)) { st->print("short"); } else { if (verbose) { @@ -831,7 +833,7 @@ void TypeIntHelper::int_type_dump(const TypeInt* t, outputStream* st, bool verbo void TypeIntHelper::int_type_dump(const TypeLong* t, outputStream* st, bool verbose) { char buf1[80], buf2[80], buf3[80], buf4[80], buf5[80]; - if (int_type_equal(t, TypeLong::LONG)) { + if (int_type_is_equal(t, TypeLong::LONG)) { st->print("long"); } else if (t->is_con()) { st->print("long:%s", longname(buf1, sizeof(buf1), t->get_con())); diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 49c1c7318277d..7c3b1d73faad4 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -149,14 +149,14 @@ class TypeIntHelper { static const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); template - static bool int_type_equal(const CT* t1, const CT* t2) { + static bool int_type_is_equal(const CT* t1, const CT* t2) { return t1->_lo == t2->_lo && t1->_hi == t2->_hi && t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi && t1->_bits._zeros == t2->_bits._zeros && t1->_bits._ones == t2->_bits._ones; } template - static bool int_type_subset(const CT* super, const CT* sub) { + static bool int_type_is_subset(const CT* super, const CT* sub) { return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && (super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0; diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index ed955f7bfa7d6..37c4153c5087a 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1627,7 +1627,7 @@ bool TypeInt::contains(jint i) const { } bool TypeInt::contains(const TypeInt* t) const { - return TypeIntHelper::int_type_subset(this, t); + return TypeIntHelper::int_type_is_subset(this, t); } const Type* TypeInt::xmeet(const Type* t) const { @@ -1674,7 +1674,7 @@ const Type* TypeInt::filter_helper(const Type* kills, bool include_speculative) // Structural equality check for Type representations bool TypeInt::eq(const Type* t) const { const TypeInt* r = t->is_int(); - return TypeIntHelper::int_type_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; + return TypeIntHelper::int_type_is_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; } //------------------------------hash------------------------------------------- @@ -1751,7 +1751,7 @@ bool TypeLong::contains(jlong i) const { } bool TypeLong::contains(const TypeLong* t) const { - return TypeIntHelper::int_type_subset(this, t); + return TypeIntHelper::int_type_is_subset(this, t); } const Type *TypeLong::xmeet(const Type* t) const { @@ -1798,7 +1798,7 @@ const Type* TypeLong::filter_helper(const Type* kills, bool include_speculative) // Structural equality check for Type representations bool TypeLong::eq(const Type* t) const { const TypeLong* r = t->is_long(); - return TypeIntHelper::int_type_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; + return TypeIntHelper::int_type_is_equal(this, r) && _widen == r->_widen && _is_dual == r->_is_dual; } //------------------------------hash------------------------------------------- @@ -2099,6 +2099,7 @@ const Type *TypeAry::xmeet( const Type *t ) const { const Type* size = _size->xmeet(a->_size); const TypeInt* isize = size->isa_int(); if (isize == nullptr) { + assert(size == Type::TOP || size == Type::BOTTOM, ""); return size; } return TypeAry::make(_elem->meet_speculative(a->_elem), @@ -4688,6 +4689,7 @@ const Type *TypeAryPtr::xmeet_helper(const Type *t) const { const Type* tm = _ary->meet_speculative(tap->_ary); const TypeAry* tary = tm->isa_ary(); if (tary == nullptr) { + assert(tm == Type::TOP || tm == Type::BOTTOM, ""); return tm; } PTR ptr = meet_ptr(tap->ptr()); diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 148704a313a69..219cfce50ad34 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -610,38 +610,88 @@ class TypeInteger : public Type { * also makes it trivial to determine if a TypeInt instance is a subset of * another. * - * Properties: + * Lemmas: * * 1. Since every TypeInt instance is canonicalized, all the bounds must also - * be elements of such TypeInt. Or else, we can tighted the bounds by narrowing + * be elements of such TypeInt. Or else, we can tighten the bounds by narrowing * it by one, which contradicts the assumption of the TypeInt being canonical. * - * 2. Either _lo == jint(_ulo) and _hi == jint(_uhi), or all elements of a - * TypeInt lie in the intervals [_lo, jint(_uhi)] or [jint(_ulo), _hi] + * 2. _lo <= jint(_ulo) + * _lo <= _hi + * _lo <= jint(_uhi) + * _ulo <= juint(_lo) + * _ulo <= _uhi + * _ulo <= juint(_hi) + * _hi >= jint(_uhi) + * _hi >= _lo + * _hi >= jint(_ulo) + * _hi >= jint(_uhi) + * _uhi >= juint(_hi) + * _uhi >= juint(_lo) + * _uhi >= _ulo * - * Proof: For 2 jint value x, y such that they are both >= 0 or < 0. Then: + * Proof of lemma 2: * - * x <= y iff juint(x) <= juint(y) + * _lo <= jint(_ulo): + * According the lemma 1, _ulo is an element of the TypeInt, so in the signed + * domain, it must not be less than the smallest element of that TypeInt, which + * is _lo. Which means that _lo <= _ulo in the signed domain, or in a more + * programmatical way, _lo <= jint(_ulo). + * The other inequalities can be proved in a similar manner. * - * Then, we have: + * 3. Either _lo == jint(_ulo) and _hi == jint(_uhi), or all elements of a + * TypeInt lie in the intervals [_lo, jint(_uhi)] or [jint(_ulo), _hi] (note + * that these intervals are disjoint in this case). * - * For a TypeInt t, there are 3 possible cases: + * Proof of lemma 3: + * We have a preliminary: For 2 jint value x, y such that they are both >= 0 + * or both < 0. Then: * - * a. t._lo >= 0. Since 0 <= t._lo <= jint(t._ulo), we have: + * x <= y iff juint(x) <= juint(y) + * I.e. x <= y in the signed domain iff x <= y in the unsigned domain * - * juint(t._lo) <= juint(jint(t._ulo)) == t._ulo <= juint(t._lo) + * Then, we have: * - * Which means that t._lo == jint(t._ulo). Similarly, t._hi == jint(t._uhi). + * For a TypeInt t, there are 3 possible cases: * - * b. t._hi < 0. Similarly, t._lo == jint(t._ulo) and t._hi == jint(t._uhi) + * a. t._lo >= 0. Since 0 <= t._lo <= jint(t._ulo) (lemma 2), we have: * - * c. t._lo < 0, t._hi >= 0. Then jint(t._ulo) >= 0 and jint(t._uhi) < 0. In - * this case, all elements of t belongs to either [t._lo, jint(t._uhi)] or - * [jint(t._ulo), t._hi]. + * juint(t._lo) <= juint(jint(t._ulo)) == t._ulo <= juint(t._lo) * - * This property is useful for our analysis of TypeInt values. Additionally, it - * can be seen that _lo and jint(_uhi) are both < 0 or >= 0, and the same - * applies to jint(_ulo) and _hi. + * Which means that t._lo == jint(t._ulo). + * + * Furthermore, 0 <= t._lo <= t._hi and 0 <= t._lo <= jint(t._uhi) (lemma 2), + * since t._hi >= jint(t._uhi), we have: + * + * juint(t._hi) >= juint(jint(t._uhi)) == t._uhi >= juint(t._hi) + * + * Which means that t._hi = jint(t._uhi) + * + * b. t._hi < 0. Similarly, t._lo == jint(t._ulo) and t._hi == jint(t._uhi) + * + * c. t._lo < 0, t._hi >= 0. + * + * Since t._ulo <= juint(t._hi), we must have jint(t._ulo) >= 0 because all + * negative values is larger than all positive values in the unsigned domain. + * + * Since t._uhi >= juint(t._lo), we must have jint(t._uhi) < 0, similar to the + * reasoning above. + * + * In this case, all elements of t belongs to either [t._lo, jint(t._uhi)] or + * [jint(t._ulo), t._hi]. + * + * Below is an illustration of the TypeInt in this case, the intervals that the + * elements can be in are marked using the = symbol. Note how the negative range + * in the signed domain wrap around in the unsigned domain. + * + * Signed: + * -----lo=========uhi---------0--------ulo==========hi----- + * Unsigned: + * 0--------ulo==========hi----------lo=========uhi--------- + * + * This property is useful for our analysis of TypeInt values. Additionally, it + * can be seen that _lo and jint(_uhi) are both < 0 or both >= 0, and the same + * applies to jint(_ulo) and _hi. */ class TypeInt : public TypeInteger { TypeInt(const TypeIntPrototype& t, int w, bool dual); diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index cfab24381030a..08249bac9e281 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -30,8 +30,6 @@ #include -#ifdef ASSERT - template static U uniform_random(); @@ -162,5 +160,3 @@ TEST_VM(opto, canonicalize_constraints) { test_canonicalize_constraints_random(); test_canonicalize_constraints_random(); } - -#endif // ASSERT From c2d7d36ea2171ca75d6196c16f3ad58de2469b05 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 13 Nov 2024 21:01:16 +0700 Subject: [PATCH 30/45] whitespace --- src/hotspot/share/opto/type.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 219cfce50ad34..b8d0aecc95c3a 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -670,7 +670,7 @@ class TypeInteger : public Type { * b. t._hi < 0. Similarly, t._lo == jint(t._ulo) and t._hi == jint(t._uhi) * * c. t._lo < 0, t._hi >= 0. - * + * * Since t._ulo <= juint(t._hi), we must have jint(t._ulo) >= 0 because all * negative values is larger than all positive values in the unsigned domain. * @@ -679,11 +679,11 @@ class TypeInteger : public Type { * * In this case, all elements of t belongs to either [t._lo, jint(t._uhi)] or * [jint(t._ulo), t._hi]. - * + * * Below is an illustration of the TypeInt in this case, the intervals that the * elements can be in are marked using the = symbol. Note how the negative range * in the signed domain wrap around in the unsigned domain. - * + * * Signed: * -----lo=========uhi---------0--------ulo==========hi----- * Unsigned: From dcc9030f8105c151b8277f4d8aae10162f9ff049 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 13 Nov 2024 21:37:19 +0700 Subject: [PATCH 31/45] build failures --- test/hotspot/gtest/opto/test_rangeinference.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 08249bac9e281..2258ddb87b07c 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -140,7 +140,7 @@ static void test_canonicalize_constraints_random() { TypeIntPrototype t{{lo, hi}, {ulo, uhi}, {zeros, ones}}; auto new_t = t.canonicalize_constraints(); if (new_t._present) { - new_t._data.verify_constraints(); + DEBUG_ONLY(new_t._data.verify_constraints()); } for (int j = 0; j < samples; j++) { S v = uniform_random(); From 71646530c620b130b775f137aaaed70fcd4a7b68 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 13 Nov 2024 22:41:45 +0700 Subject: [PATCH 32/45] build failure --- test/hotspot/gtest/opto/test_rangeinference.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 2258ddb87b07c..c98148df6da94 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -145,9 +145,9 @@ static void test_canonicalize_constraints_random() { for (int j = 0; j < samples; j++) { S v = uniform_random(); if (!new_t._present) { - ASSERT_FALSE(t.contains(v)); + DEBUG_ONLY(ASSERT_FALSE(t.contains(v))); } else { - ASSERT_EQ(t.contains(v), new_t._data.contains(v)); + DEBUG_ONLY(ASSERT_EQ(t.contains(v), new_t._data.contains(v))); } } } From cf1de627f9bcc7f0d465f8dc49b35abef5944757 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 6 Dec 2024 19:18:24 +0700 Subject: [PATCH 33/45] move try_cast to Type --- src/hotspot/share/opto/rangeinference.cpp | 2 +- src/hotspot/share/opto/type.hpp | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index b32b420a4ee52..c1dcd9d333cc7 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -497,7 +497,7 @@ const Type* TypeIntHelper::int_type_xmeet(const CT* i1, const Type* t2, const Ty if (i1 == t2 || t2 == Type::TOP) { return i1; } - const CT* i2 = CT::try_cast(t2); + const CT* i2 = t2->try_cast(); if (i2 != nullptr) { if (!dual) { // meet diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index b8d0aecc95c3a..bda9c6e5a3b3f 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -285,6 +285,9 @@ class Type { float getf() const; double getd() const; + template + const TypeClass* try_cast() const; + const TypeInt *is_int() const; const TypeInt *isa_int() const; // Returns null if not an Int const TypeInteger* is_integer(BasicType bt) const; @@ -711,7 +714,6 @@ class TypeInt : public TypeInteger { const juint _ulo, _uhi; // Lower bound, upper bound in the unsigned domain const KnownBits _bits; - static const TypeInt* try_cast(const Type* t) { return t->isa_int(); } static const TypeInt* make(jint lo); // must always specify w static const TypeInt* make(jint lo, jint hi, int w); @@ -789,7 +791,6 @@ class TypeLong : public TypeInteger { const julong _ulo, _uhi; // Lower bound, upper bound in the unsigned domain const KnownBits _bits; - static const TypeLong* try_cast(const Type* t) { return t->isa_long(); } static const TypeLong* make(jlong lo); // must always specify w static const TypeLong* make(jlong lo, jlong hi, int w); @@ -2087,6 +2088,16 @@ inline double Type::getd() const { return ((TypeD*)this)->_d; } +template <> +inline const TypeInt* Type::try_cast() const { + return isa_int(); +} + +template <> +inline const TypeLong* Type::try_cast() const { + return isa_long(); +} + inline const TypeInteger *Type::is_integer(BasicType bt) const { assert((bt == T_INT && _base == Int) || (bt == T_LONG && _base == Long), "Not an Int"); return (TypeInteger*)this; From 4d33014257a8b84096a4274e0d3e6c1da91fbfcc Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Sat, 4 Jan 2025 23:07:45 +0700 Subject: [PATCH 34/45] copyright --- src/hotspot/share/opto/castnode.cpp | 2 +- src/hotspot/share/opto/compile.cpp | 2 +- src/hotspot/share/opto/compile.hpp | 2 +- src/hotspot/share/opto/graphKit.cpp | 2 +- src/hotspot/share/opto/ifnode.cpp | 2 +- src/hotspot/share/opto/rangeinference.cpp | 2 +- src/hotspot/share/opto/rangeinference.hpp | 2 +- src/hotspot/share/opto/type.cpp | 2 +- src/hotspot/share/opto/type.hpp | 22 +++++++++---------- .../gtest/opto/test_rangeinference.cpp | 2 +- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index e884d1a9ed08b..112fbe8b1c1a9 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 912298430903e..0f5761f348019 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index b48f253ae7ee7..bd57bd9353ea8 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index cda3f96c43aa8..73fbe1153304b 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, 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 diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 16c15c4dc6863..93d6f1815b372 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, 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 diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index c1dcd9d333cc7..55c980e80d015 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 7c3b1d73faad4..bc0fe84288d36 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 1b1455abd59d4..722fc2d4cf23e 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index a6adafaf279b3..5b601df6691e2 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -2091,16 +2091,6 @@ inline double Type::getd() const { return ((TypeD*)this)->_d; } -template <> -inline const TypeInt* Type::try_cast() const { - return isa_int(); -} - -template <> -inline const TypeLong* Type::try_cast() const { - return isa_long(); -} - inline const TypeInteger *Type::is_integer(BasicType bt) const { assert((bt == T_INT && _base == Int) || (bt == T_LONG && _base == Long), "Not an Int"); return (TypeInteger*)this; @@ -2333,6 +2323,16 @@ inline const TypeLong* Type::cast() const { return is_long(); } +template <> +inline const TypeInt* Type::try_cast() const { + return isa_int(); +} + +template <> +inline const TypeLong* Type::try_cast() const { + return isa_long(); +} + // =============================================================== // Things that need to be 64-bits in the 64-bit build but // 32-bits in the 32-bit build. Done this way to get full diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index c98148df6da94..c23250903b503 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 From c4d46c8700ba072ec81eaff4430748c9200e8cfb Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 22 Jan 2025 22:37:54 +0700 Subject: [PATCH 35/45] remove precompiled.hpp --- src/hotspot/share/opto/rangeinference.cpp | 1 - test/hotspot/gtest/opto/test_rangeinference.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 55c980e80d015..4598d6abb62fc 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -22,7 +22,6 @@ * */ -#include "precompiled.hpp" #include "opto/rangeinference.hpp" #include "opto/type.hpp" #include "utilities/tuple.hpp" diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index c23250903b503..e8ec2817a8049 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -22,7 +22,6 @@ * */ -#include "precompiled.hpp" #include "opto/rangeinference.hpp" #include "opto/type.hpp" #include "runtime/os.hpp" From ac1ddfc5816d910ec4e019f9f05b4b7b8f55238c Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Mon, 27 Jan 2025 23:51:33 +0700 Subject: [PATCH 36/45] Emmanuel's review --- src/hotspot/share/opto/rangeinference.cpp | 65 ++++++++++++++--------- src/hotspot/share/opto/rangeinference.hpp | 15 +++++- src/hotspot/share/opto/type.cpp | 52 +++++++++--------- src/hotspot/share/opto/type.hpp | 12 ++--- 4 files changed, 85 insertions(+), 59 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 4598d6abb62fc..1596bc9ffa4c9 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -34,22 +34,28 @@ constexpr juint SMALL_TYPEINT_THRESHOLD = 3; template class AdjustResult { public: - bool _progress; // whether there is progress compared to the last iteration - bool _is_result_consistent; // whether the calculation arrives at contradiction + bool _progress; // whether there is progress compared to the last iteration + bool _present; // whether the result is empty, typically due to the calculation arriving at contradiction T _result; + bool empty() const { + return !_present; + } + static AdjustResult make_empty() { return {true, false, {}}; } }; -// In the canonical form, [lo, hi] intersects with [ulo, uhi] can result in 2 -// cases: +// In the canonical form, when intersecting [lo, hi] in the signed domain with +// [ulo, uhi] in the unsigned domain, there can be 2 cases (see TypeInt in +// type.hpp for more details): // - [lo, hi] is the same as [ulo, uhi], lo and hi are both >= 0 or both < 0. // - [lo, hi] is not the same as [ulo, uhi], which results in the intersections -// being [lo, uhi] and [ulo, hi], lo and uhi are < 0 while ulo and hi are >= 0. +// being [lo, uhi] and [ulo, hi], lo and uhi are < 0 while ulo and hi are >= 0. // This class deals with each interval with both bounds being >= 0 or < 0 in -// the signed domain. +// the signed domain. We call it Simple because a canonicalized TypeIntPrototype +// may contain 1 or 2 SimpleCanonicalResult. template class SimpleCanonicalResult { static_assert(std::is_unsigned::value, "bit info should be unsigned"); @@ -58,6 +64,10 @@ class SimpleCanonicalResult { RangeInt _bounds; KnownBits _bits; + bool empty() const { + return !_present; + } + static SimpleCanonicalResult make_empty() { return {false, {}, {}}; } @@ -339,30 +349,33 @@ adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { return {progress, present, {new_zeros, new_ones}}; } -// Try to tighten both the bounds and the bits at the same time -// Iteratively tighten 1 using the other until no progress is made. -// This function converges because at each iteration, some bits that are -// unknown is made known. As there are at most 64 bits, the number of -// iterations should not be larger than 64 +// Try to tighten both the bounds and the bits at the same time. +// Iteratively tighten one using the other until no progress is made. +// This function converges because at each iteration, some bits that are unknown +// are made known. As there are at most 64 bits, the number of iterations should +// not be larger than 64. +// This function is called simple because it deals with a SimpleCanonicalResult, +// and a canonicalization of a TypeIntPrototype may require 1 or 2 calls to this +// function, one for the non-negative range and one for the negative range. template static SimpleCanonicalResult canonicalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { - AdjustResult> nbits = adjust_bits_from_bounds(bits, bounds); - if (!nbits._is_result_consistent) { + AdjustResult> canonicalized_bits = adjust_bits_from_bounds(bits, bounds); + if (canonicalized_bits.empty()) { return SimpleCanonicalResult::make_empty(); } - AdjustResult> nbounds{true, true, bounds}; + AdjustResult> canonicalized_bounds{true, true, bounds}; // Since bits are derived from bounds in the previous iteration and vice // versa, if one does not show progress, the other will also not show // progress, so we terminate early while (true) { - nbounds = adjust_bounds_from_bits(nbounds._result, nbits._result); - if (!nbounds._progress || !nbounds._is_result_consistent) { - return {nbounds._is_result_consistent, nbounds._result, nbits._result}; + canonicalized_bounds = adjust_bounds_from_bits(canonicalized_bounds._result, canonicalized_bits._result); + if (!canonicalized_bounds._progress || canonicalized_bounds.empty()) { + return {canonicalized_bounds._present, canonicalized_bounds._result, canonicalized_bits._result}; } - nbits = adjust_bits_from_bounds(nbits._result, nbounds._result); - if (!nbits._progress || !nbits._is_result_consistent) { - return {nbits._is_result_consistent, nbounds._result, nbits._result}; + canonicalized_bits = adjust_bits_from_bounds(canonicalized_bits._result, canonicalized_bounds._result); + if (!canonicalized_bits._progress || canonicalized_bits.empty()) { + return {canonicalized_bits._present, canonicalized_bounds._result, canonicalized_bits._result}; } } } @@ -420,12 +433,12 @@ TypeIntPrototype::canonicalize_constraints() const { auto neg_type = canonicalize_constraints_simple({U(srange._lo), urange._hi}, _bits); auto pos_type = canonicalize_constraints_simple({urange._lo, U(srange._hi)}, _bits); - if (!neg_type._present && !pos_type._present) { + if (neg_type.empty() && pos_type.empty()) { return CanonicalizedTypeIntPrototype::make_empty(); - } else if (!neg_type._present) { + } else if (neg_type.empty()) { return {true, {{S(pos_type._bounds._lo), S(pos_type._bounds._hi)}, pos_type._bounds, pos_type._bits}}; - } else if (!pos_type._present) { + } else if (pos_type.empty()) { return {true, {{S(neg_type._bounds._lo), S(neg_type._bounds._hi)}, neg_type._bounds, neg_type._bits}}; } else { @@ -471,12 +484,12 @@ void TypeIntPrototype::verify_constraints() const { } else { RangeInt neg_range{U(_srange._lo), _urange._hi}; auto neg_bits = adjust_bits_from_bounds(_bits, neg_range); - assert(neg_bits._is_result_consistent, ""); + assert(neg_bits._present, ""); assert(!adjust_bounds_from_bits(neg_range, neg_bits._result)._progress, ""); RangeInt pos_range{_urange._lo, U(_srange._hi)}; auto pos_bits = adjust_bits_from_bounds(_bits, pos_range); - assert(pos_bits._is_result_consistent, ""); + assert(pos_bits._present, ""); assert(!adjust_bounds_from_bits(pos_range, pos_bits._result)._progress, ""); assert((neg_bits._result._zeros & pos_bits._result._zeros) == _bits._zeros && @@ -491,7 +504,7 @@ template class TypeIntPrototype; // Compute the meet of 2 types, when dual is true, we are actually computing the // join. template -const Type* TypeIntHelper::int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual) { +const Type* TypeIntHelper::int_type_xmeet(const CT* i1, const Type* t2, make_type_t make, bool dual) { // Perform a fast test for common case; meeting the same types together. if (i1 == t2 || t2 == Type::TOP) { return i1; diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index bc0fe84288d36..9e5681ca3c6f8 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -109,6 +109,10 @@ class TypeIntPrototype { bool _present; // whether this is an empty set TypeIntPrototype _data; + bool empty() const { + return !_present; + } + static CanonicalizedTypeIntPrototype make_empty() { return {false, {}}; } @@ -137,16 +141,22 @@ class TypeIntHelper { if (U(srange._lo) == urange._lo) { // srange is the same as urange assert(U(srange._hi) == urange._hi, ""); + // The cardinality is (hi - lo + 1), we return the result minus 1 return urange._hi - urange._lo; } // srange intersects with urange in 2 intervals [srange._lo, urange._hi] // and [urange._lo, srange._hi] + // The cardinality is (uhi - lo + 1) + (hi - ulo + 1), we return the result + // minus 1 return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + 1; } + template + using make_type_t = const Type* (*)(const TypeIntPrototype&, int, bool); + template - static const Type* int_type_xmeet(const CT* i1, const Type* t2, const Type* (*make)(const TypeIntPrototype&, int, bool), bool dual); + static const Type* int_type_xmeet(const CT* i1, const Type* t2, make_type_t make, bool dual); template static bool int_type_is_equal(const CT* t1, const CT* t2) { @@ -159,6 +169,9 @@ class TypeIntHelper { static bool int_type_is_subset(const CT* super, const CT* sub) { return super->_lo <= sub->_lo && super->_hi >= sub->_hi && super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi && + // All bits that are known in super must also be known to be the same + // value in sub, &~ (and not) is the same as a set subtraction on bit + // sets (super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0; } diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 5ba37c7c7a166..cee42c1cb6701 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1599,33 +1599,33 @@ const TypeInt* TypeInt::INT; // 32-bit integers const TypeInt* TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] const TypeInt* TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT -TypeInt::TypeInt(const TypeIntPrototype& t, int w, bool dual) - : TypeInteger(Int, t.normalize_widen(w), dual), _lo(t._srange._lo), _hi(t._srange._hi), +TypeInt::TypeInt(const TypeIntPrototype& t, int widen, bool dual) + : TypeInteger(Int, t.normalize_widen(widen), dual), _lo(t._srange._lo), _hi(t._srange._hi), _ulo(t._urange._lo), _uhi(t._urange._hi), _bits(t._bits) { DEBUG_ONLY(t.verify_constraints()); } -const Type* TypeInt::try_make(const TypeIntPrototype& t, int w, bool dual) { - auto new_t = t.canonicalize_constraints(); - if (!new_t._present) { +const Type* TypeInt::try_make(const TypeIntPrototype& t, int widen, bool dual) { + auto canonicalized_t = t.canonicalize_constraints(); + if (canonicalized_t.empty()) { return dual ? Type::BOTTOM : Type::TOP; } - return (new TypeInt(new_t._data, w, dual))->hashcons()->is_int(); + return (new TypeInt(canonicalized_t._data, widen, dual))->hashcons()->is_int(); } -const TypeInt* TypeInt::make(jint lo) { - juint ulo = lo; - return (new TypeInt(TypeIntPrototype{{lo, lo}, {ulo, ulo}, {~ulo, ulo}}, +const TypeInt* TypeInt::make(jint con) { + juint ucon = con; + return (new TypeInt(TypeIntPrototype{{con, con}, {ucon, ucon}, {~ucon, ucon}}, WidenMin, false))->hashcons()->is_int(); } -const TypeInt* TypeInt::make(jint lo, jint hi, int w) { +const TypeInt* TypeInt::make(jint lo, jint hi, int widen) { assert(lo <= hi, "must be legal bounds"); - return try_make(TypeIntPrototype{{lo, hi}, {0, max_juint}, {0, 0}}, w)->is_int(); + return try_make(TypeIntPrototype{{lo, hi}, {0, max_juint}, {0, 0}}, widen)->is_int(); } -const Type* TypeInt::try_make(const TypeIntPrototype& t, int w) { - return try_make(t, w, false); +const Type* TypeInt::try_make(const TypeIntPrototype& t, int widen) { + return try_make(t, widen, false); } bool TypeInt::contains(jint i) const { @@ -1723,33 +1723,33 @@ const TypeLong* TypeLong::INT; // 32-bit subrange const TypeLong* TypeLong::UINT; // 32-bit unsigned subrange const TypeLong* TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG -TypeLong::TypeLong(const TypeIntPrototype& t, int w, bool dual) - : TypeInteger(Long, t.normalize_widen(w), dual), _lo(t._srange._lo), _hi(t._srange._hi), +TypeLong::TypeLong(const TypeIntPrototype& t, int widen, bool dual) + : TypeInteger(Long, t.normalize_widen(widen), dual), _lo(t._srange._lo), _hi(t._srange._hi), _ulo(t._urange._lo), _uhi(t._urange._hi), _bits(t._bits) { DEBUG_ONLY(t.verify_constraints()); } -const Type* TypeLong::try_make(const TypeIntPrototype& t, int w, bool dual) { - auto new_t = t.canonicalize_constraints(); - if (!new_t._present) { +const Type* TypeLong::try_make(const TypeIntPrototype& t, int widen, bool dual) { + auto canonicalized_t = t.canonicalize_constraints(); + if (canonicalized_t.empty()) { return dual ? Type::BOTTOM : Type::TOP; } - return (new TypeLong(new_t._data, w, dual))->hashcons()->is_long(); + return (new TypeLong(canonicalized_t._data, widen, dual))->hashcons()->is_long(); } -const TypeLong* TypeLong::make(jlong lo) { - julong ulo = lo; - return (new TypeLong(TypeIntPrototype{{lo, lo}, {ulo, ulo}, {~ulo, ulo}}, +const TypeLong* TypeLong::make(jlong con) { + julong ucon = con; + return (new TypeLong(TypeIntPrototype{{con, con}, {ucon, ucon}, {~ucon, ucon}}, WidenMin, false))->hashcons()->is_long(); } -const TypeLong* TypeLong::make(jlong lo, jlong hi, int w) { +const TypeLong* TypeLong::make(jlong lo, jlong hi, int widen) { assert(lo <= hi, "must be legal bounds"); - return try_make(TypeIntPrototype{{lo, hi}, {0, max_julong}, {0, 0}}, w)->is_long(); + return try_make(TypeIntPrototype{{lo, hi}, {0, max_julong}, {0, 0}}, widen)->is_long(); } -const Type* TypeLong::try_make(const TypeIntPrototype& t, int w) { - return try_make(t, w, false); +const Type* TypeLong::try_make(const TypeIntPrototype& t, int widen) { + return try_make(t, widen, false); } bool TypeLong::contains(jlong i) const { diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 2ebb64af77907..38d129577e172 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -701,7 +701,7 @@ class TypeInteger : public Type { */ class TypeInt : public TypeInteger { TypeInt(const TypeIntPrototype& t, int w, bool dual); - static const Type* try_make(const TypeIntPrototype& t, int w, bool dual); + static const Type* try_make(const TypeIntPrototype& t, int widen, bool dual); protected: virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; @@ -719,8 +719,8 @@ class TypeInt : public TypeInteger { static const TypeInt* make(jint lo); // must always specify w - static const TypeInt* make(jint lo, jint hi, int w); - static const Type* try_make(const TypeIntPrototype& t, int w); + static const TypeInt* make(jint lo, jint hi, int widen); + static const Type* try_make(const TypeIntPrototype& t, int widen); // Check for single integer bool is_con() const { return _lo == _hi; } @@ -777,7 +777,7 @@ class TypeInt : public TypeInteger { // Similar to TypeInt class TypeLong : public TypeInteger { TypeLong(const TypeIntPrototype& t, int w, bool dual); - static const Type* try_make(const TypeIntPrototype& t, int w, bool dual); + static const Type* try_make(const TypeIntPrototype& t, int widen, bool dual); protected: // Do not kill _widen bits. virtual const Type* filter_helper(const Type* kills, bool include_speculative) const; @@ -796,8 +796,8 @@ class TypeLong : public TypeInteger { static const TypeLong* make(jlong lo); // must always specify w - static const TypeLong* make(jlong lo, jlong hi, int w); - static const Type* try_make(const TypeIntPrototype& t, int w); + static const TypeLong* make(jlong lo, jlong hi, int widen); + static const Type* try_make(const TypeIntPrototype& t, int widen); // Check for single integer bool is_con() const { return _lo == _hi; } From f85eff5247e6e73ad48c96c92a1957e039750373 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Tue, 28 Jan 2025 10:14:32 +0700 Subject: [PATCH 37/45] make con --- src/hotspot/share/opto/type.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 38d129577e172..6a4dafcd2a495 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -717,7 +717,7 @@ class TypeInt : public TypeInteger { const juint _ulo, _uhi; // Lower bound, upper bound in the unsigned domain const KnownBits _bits; - static const TypeInt* make(jint lo); + static const TypeInt* make(jint con); // must always specify w static const TypeInt* make(jint lo, jint hi, int widen); static const Type* try_make(const TypeIntPrototype& t, int widen); @@ -794,7 +794,7 @@ class TypeLong : public TypeInteger { const julong _ulo, _uhi; // Lower bound, upper bound in the unsigned domain const KnownBits _bits; - static const TypeLong* make(jlong lo); + static const TypeLong* make(jlong con); // must always specify w static const TypeLong* make(jlong lo, jlong hi, int widen); static const Type* try_make(const TypeIntPrototype& t, int widen); From 01dc22d69f02e5cface7303a2ade3020ff589c09 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 29 Jan 2025 23:19:36 +0700 Subject: [PATCH 38/45] exhaustive tests --- src/hotspot/share/opto/rangeinference.cpp | 55 +++--- src/hotspot/share/opto/rangeinference.hpp | 17 +- src/hotspot/share/utilities/intn_t.hpp | 156 ++++++++++++++++++ .../gtest/opto/test_rangeinference.cpp | 57 ++++++- 4 files changed, 253 insertions(+), 32 deletions(-) create mode 100644 src/hotspot/share/utilities/intn_t.hpp diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 1596bc9ffa4c9..49ea7827910c0 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -24,6 +24,7 @@ #include "opto/rangeinference.hpp" #include "opto/type.hpp" +#include "utilities/intn_t.hpp" #include "utilities/tuple.hpp" // If the cardinality of a TypeInt is below this threshold, use min widen, see @@ -58,7 +59,7 @@ class AdjustResult { // may contain 1 or 2 SimpleCanonicalResult. template class SimpleCanonicalResult { - static_assert(std::is_unsigned::value, "bit info should be unsigned"); + static_assert(U(-1) > U(0), "bit info should be unsigned"); public: bool _present; // whether this is an empty set RangeInt _bounds; @@ -84,7 +85,6 @@ class SimpleCanonicalResult { // significant. template static U adjust_lo(U lo, const KnownBits& bits) { - constexpr size_t W = sizeof(U) * 8; // Violation of lo with respects to bits // E.g: lo = 1100 // zeros = 0100 @@ -97,7 +97,7 @@ static U adjust_lo(U lo, const KnownBits& bits) { U one_violation = ~lo & bits._ones; if (zero_violation == one_violation) { // This means lo does not violate bits, it is the result - assert(zero_violation == 0, ""); + assert(zero_violation == U(0), ""); return lo; } @@ -222,10 +222,13 @@ static U adjust_lo(U lo, const KnownBits& bits) { // Just OR this value with ones to obtain the final result. // first_violation is the position of the violation counting from the - // lowest bit up (0-based), since i == 2, first_difference == 6 - juint first_violation = W - 1 - count_leading_zeros(one_violation); // 6 + // highest bit down (0-based), since i == 2, first_difference == 1 + juint first_violation = count_leading_zeros(one_violation); // 1 // 0 1 0 0 0 0 0 0 - U alignment = U(1) << first_violation; + // This is the same as U(1) << (W - 1 - first_violation), we avoid using + // W so that this can be used with intn_t + U alignment = one_violation == U(1) ? U(1) + : (std::numeric_limits::max() >> (first_violation + 1)) + U(1); // This is the first value which have the violated bit being 1, which means // that the result should not be smaller than this // 1 1 0 0 0 0 0 0 @@ -255,10 +258,12 @@ static U adjust_lo(U lo, const KnownBits& bits) { // obtain our final result, which is: // 1 0 1 0 0 0 1 1 - juint first_violation = W - count_leading_zeros(zero_violation); + juint first_violation = count_leading_zeros(zero_violation); // This mask out all bits from the first violation // 1 1 1 1 1 0 0 0 - U find_mask = std::numeric_limits::max() << first_violation; + // This is the same as max << (W - first_violation), we avoid using W so + // that this can be used with intn_t + U find_mask = ~(std::numeric_limits::max() >> first_violation); // 1 0 0 1 1 1 1 0 U either = lo | bits._zeros; // i is the last bit being 0 in either that stands before the first @@ -338,14 +343,14 @@ adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { U mismatch = bounds._lo ^ bounds._hi; // Find the first mismatch, all bits before it is the same in bounds._lo and // bounds._hi - U match_mask = mismatch == 0 ? std::numeric_limits::max() - : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); + U match_mask = mismatch == U(0) ? std::numeric_limits::max() + : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); // match_mask & bounds._lo is the common prefix, extract zeros and ones from // it U new_zeros = bits._zeros | (match_mask & ~bounds._lo); U new_ones = bits._ones | (match_mask & bounds._lo); bool progress = (new_zeros != bits._zeros) || (new_ones != bits._ones); - bool present = ((new_zeros & new_ones) == 0); + bool present = ((new_zeros & new_ones) == U(0)); return {progress, present, {new_zeros, new_ones}}; } @@ -393,7 +398,7 @@ TypeIntPrototype::canonicalize_constraints() const { // Trivial contradictions if (srange._lo > srange._hi || urange._lo > urange._hi || - (_bits._zeros & _bits._ones) != 0) { + (_bits._zeros & _bits._ones) != U(0)) { return CanonicalizedTypeIntPrototype::make_empty(); } @@ -405,19 +410,19 @@ TypeIntPrototype::canonicalize_constraints() const { if (S(urange._hi) < srange._lo) { // This means that there should be no element in the interval // [min_S, S(urange._hi)], tighten urange._hi to max_S - urange._hi = std::numeric_limits::max(); + urange._hi = U(std::numeric_limits::max()); } else if (S(urange._lo) > srange._hi) { // This means that there should be no element in the interval // [S(urange._lo), max_S], tighten urange._lo to min_S - urange._lo = std::numeric_limits::min(); + urange._lo = U(std::numeric_limits::min()); } } if (S(urange._lo) <= S(urange._hi)) { // [lo, hi] and [ulo, uhi] represent the same range - urange._lo = MAX2(urange._lo, srange._lo); - urange._hi = MIN2(urange._hi, srange._hi); - if (urange._lo > urange._hi) { + urange._lo = U(MAX2(S(urange._lo), srange._lo)); + urange._hi = U(MIN2(S(urange._hi), srange._hi)); + if (urange._lo > urange._hi || S(urange._lo) > S(urange._hi)) { return CanonicalizedTypeIntPrototype::make_empty(); } @@ -449,25 +454,25 @@ TypeIntPrototype::canonicalize_constraints() const { } template -int TypeIntPrototype::normalize_widen(int w) const { +int TypeIntPrototype::normalize_widen(int widen) const { // Certain normalizations keep us sane when comparing types. // The 'SMALL_TYPEINT_THRESHOLD' covers constants and also CC and its relatives. - if (TypeIntHelper::cardinality_from_bounds(_srange, _urange) <= SMALL_TYPEINT_THRESHOLD) { + if (TypeIntHelper::cardinality_from_bounds(_srange, _urange) <= U(SMALL_TYPEINT_THRESHOLD)) { return Type::WidenMin; } if (_srange._lo == std::numeric_limits::min() && _srange._hi == std::numeric_limits::max() && _urange._lo == std::numeric_limits::min() && _urange._hi == std::numeric_limits::max() && - _bits._zeros == 0 && _bits._ones == 0) { + _bits._zeros == U(0) && _bits._ones == U(0)) { // bottom type return Type::WidenMax; } - return w; + return widen; } #ifdef ASSERT template bool TypeIntPrototype::contains(S v) const { - U u = v; + U u(v); return v >= _srange._lo && v <= _srange._hi && u >= _urange._lo && u <= _urange._hi && _bits.is_satisfied_by(u); } @@ -476,7 +481,7 @@ template void TypeIntPrototype::verify_constraints() const { // Assert that the bounds cannot be further tightened assert(contains(_srange._lo) && contains(_srange._hi) && - contains(_urange._lo) && contains(_urange._hi), ""); + contains(S(_urange._lo)) && contains(S(_urange._hi)), ""); // Assert that the bits cannot be further tightened if (U(_srange._lo) == _urange._lo) { @@ -500,6 +505,10 @@ void TypeIntPrototype::verify_constraints() const { template class TypeIntPrototype; template class TypeIntPrototype; +template class TypeIntPrototype, uintn_t<1>>; +template class TypeIntPrototype, uintn_t<2>>; +template class TypeIntPrototype, uintn_t<3>>; +template class TypeIntPrototype, uintn_t<4>>; // Compute the meet of 2 types, when dual is true, we are actually computing the // join. diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 9e5681ca3c6f8..79a3f231a55f3 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -67,14 +67,14 @@ class RangeInt { */ template class KnownBits { - static_assert(std::is_unsigned::value, "bit info should be unsigned"); + static_assert(U(-1) > U(0), "bit info should be unsigned"); public: U _zeros; U _ones; bool is_satisfied_by(U v) const { - return (v & _zeros) == 0 && (v & _ones) == _ones; + return (v & _zeros) == U(0) && (v & _ones) == _ones; } }; @@ -84,8 +84,8 @@ class KnownBits { template class TypeIntPrototype { public: - static_assert(std::is_signed::value, ""); - static_assert(std::is_unsigned::value, ""); + static_assert(S(-1) < S(0), ""); + static_assert(U(-1) > U(0), ""); static_assert(sizeof(S) == sizeof(U), ""); RangeInt _srange; @@ -96,6 +96,9 @@ class TypeIntPrototype { friend class TypeInt; friend class TypeLong; + template + friend void test_canonicalize_constraints_exhaustive(); + template friend void test_canonicalize_constraints_simple(); @@ -134,8 +137,8 @@ class TypeIntHelper { // with the bottom type template static U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { - static_assert(std::is_signed::value, ""); - static_assert(std::is_unsigned::value, ""); + static_assert(S(-1) < S(0), ""); + static_assert(U(-1) > U(0), ""); static_assert(sizeof(S) == sizeof(U), ""); if (U(srange._lo) == urange._lo) { @@ -149,7 +152,7 @@ class TypeIntHelper { // and [urange._lo, srange._hi] // The cardinality is (uhi - lo + 1) + (hi - ulo + 1), we return the result // minus 1 - return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + 1; + return (urange._hi - U(srange._lo)) + (U(srange._hi) - urange._lo) + U(1); } template diff --git a/src/hotspot/share/utilities/intn_t.hpp b/src/hotspot/share/utilities/intn_t.hpp new file mode 100644 index 0000000000000..6902eae1dcdaf --- /dev/null +++ b/src/hotspot/share/utilities/intn_t.hpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2025, 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. + * + */ + +#ifndef SHARE_UTILITIES_INTN_T_HPP +#define SHARE_UTILITIES_INTN_T_HPP + +#include "utilities/count_leading_zeros.hpp" + +#include + +template +class uintn_t; + +template +class intn_t { + static_assert(n > 0 && n <= 8, "should not be larger than char"); + +private: + uint _v; + + constexpr static uint _mask = (1 << n) - 1; + + friend class uintn_t; + +public: + explicit constexpr intn_t(int v) : _v(v) {} + constexpr intn_t() : _v(0) {} + constexpr intn_t(const intn_t&) = default; + explicit constexpr intn_t(uintn_t v); + + operator int() const { + int shift = 32 - n; + return int(_v << shift) >> shift; + } + + constexpr static int min = std::numeric_limits::max() << (n - 1); + constexpr static int max = (1 << (n - 1)) - 1; + static_assert(min < max, ""); + + constexpr bool operator==(intn_t o) const { return (_v & _mask) == (o._v & _mask); } + constexpr bool operator<(intn_t o) const { + // Shift the highest bit of intn_t to the highest bit of the int representation + int shift = 32 - n; + return int(_v << shift) < int(o._v << shift); + } + constexpr bool operator>(intn_t o) const { return o < *this; } + constexpr bool operator<=(intn_t o) const { return !(o < *this); } + constexpr bool operator>=(intn_t o) const { return !(*this < o); } +}; + +template +class uintn_t { + static_assert(n > 0 && n <= 8, "should not be larger than char"); + +private: + uint _v; + + constexpr static uint _mask = (1 << n) - 1; + + friend class intn_t; + + template + friend unsigned count_leading_zeros(T); + +public: + explicit constexpr uintn_t(int v) : _v(v) {} + constexpr uintn_t() : _v(0) {} + constexpr uintn_t(const uintn_t&) = default; + explicit constexpr uintn_t(intn_t v) : _v(v._v) {} + operator uint() const { return _v & _mask; } + + constexpr static int min = 0; + constexpr static int max = _mask; + static_assert(min < max, ""); + + constexpr bool operator==(uintn_t o) const { return (_v & _mask) == (o._v & _mask); } + constexpr bool operator!=(uintn_t o) const { return !(*this == o); } + constexpr bool operator<(uintn_t o) const { return (_v & _mask) < (o._v & _mask); } + constexpr bool operator>(uintn_t o) const { return o < *this; } + constexpr bool operator<=(uintn_t o) const { return !(o < *this); } + constexpr bool operator>=(uintn_t o) const { return !(*this < o); } + constexpr uintn_t operator+(uintn_t o) const { return uintn_t(_v + o._v); } + constexpr uintn_t operator-(uintn_t o) const { return uintn_t(_v - o._v); } + constexpr uintn_t operator&(uintn_t o) const { return uintn_t(_v & o._v); } + constexpr uintn_t operator|(uintn_t o) const { return uintn_t(_v | o._v); } + constexpr uintn_t operator^(uintn_t o) const { return uintn_t(_v ^ o._v); } + constexpr uintn_t operator>>(unsigned int s) const { return uintn_t((_v & _mask) >> s); } + constexpr uintn_t operator<<(unsigned int s) const { return uintn_t(_v << s); } + constexpr uintn_t operator~() const { return uintn_t(~_v); } + constexpr uintn_t operator-() const { return uintn_t(-_v); } + constexpr uintn_t& operator|=(uintn_t o) { _v |= o._v; return *this; } +}; + +template +constexpr intn_t::intn_t(uintn_t v) : _v(v._v) {} + +namespace std { + +template +class numeric_limits> { +public: + constexpr static intn_t min() { return intn_t(intn_t::min); } + constexpr static intn_t max() { return intn_t(intn_t::max); } +}; + +template +class numeric_limits> { +public: + constexpr static uintn_t min() { return uintn_t(uintn_t::min); } + constexpr static uintn_t max() { return uintn_t(uintn_t::max); } +}; + +} + +template <> +inline unsigned count_leading_zeros>(uintn_t<1> v) { + return count_leading_zeros(v._v & uintn_t<1>::_mask) - (32 - 1); +} + +template <> +inline unsigned count_leading_zeros>(uintn_t<2> v) { + return count_leading_zeros(v._v & uintn_t<2>::_mask) - (32 - 2); +} + +template <> +inline unsigned count_leading_zeros>(uintn_t<3> v) { + return count_leading_zeros(v._v & uintn_t<3>::_mask) - (32 - 3); +} + +template <> +inline unsigned count_leading_zeros>(uintn_t<4> v) { + return count_leading_zeros(v._v & uintn_t<4>::_mask) - (32 - 4); +} + +#endif // SHARE_UTILITIES_INTN_T_HPP diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index e8ec2817a8049..d6ff90110689a 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -25,10 +25,9 @@ #include "opto/rangeinference.hpp" #include "opto/type.hpp" #include "runtime/os.hpp" +#include "utilities/intn_t.hpp" #include "unittest.hpp" -#include - template static U uniform_random(); @@ -56,6 +55,56 @@ static void test_canonicalize_constraints_trivial() { ASSERT_TRUE(TypeLong::NON_ZERO->contains(jlong(-1))); } +template +static void test_canonicalize_constraints_exhaustive() { + { + TypeIntPrototype t{{S(0), S(0)}, {U(0), U(0)}, {U(-1), U(0)}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + DEBUG_ONLY(ASSERT_TRUE(t.contains(S(0)))); + DEBUG_ONLY(ASSERT_FALSE(t.contains(S(1)))); + } + { + TypeIntPrototype t{{S(0), S(0)}, {U(1), U(1)}, {U(-1), U(0)}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_FALSE(new_t._present); + DEBUG_ONLY(ASSERT_FALSE(t.contains(S(0)))); + DEBUG_ONLY(ASSERT_FALSE(t.contains(S(1)))); + } + { + TypeIntPrototype t{{S(S::min), S(S::max)}, {U(U::min), U(U::max)}, {U(0), U(0)}}; + auto new_t = t.canonicalize_constraints(); + ASSERT_TRUE(new_t._present); + for (int v = S::min; v <= S::max; v++) { + DEBUG_ONLY(ASSERT_TRUE(t.contains(S(v)))); + } + } + for (int lo = S::min; lo <= S::max; lo++) { + for (int hi = lo; hi <= S::max; hi++) { + for (int ulo = U::min; ulo <= U::max; ulo++) { + for (int uhi = ulo; uhi <= U::max; uhi++) { + for (int zeros = U::min; zeros <= U::max; zeros++) { + for (int ones = U::min; ones <= U::max; ones++) { + TypeIntPrototype t{{S(lo), S(hi)}, {U(ulo), U(uhi)}, {U(zeros), U(ones)}}; + auto new_t = t.canonicalize_constraints(); + if (new_t._present) { + DEBUG_ONLY(new_t._data.verify_constraints()); + } + for (int v = S::min; v <= S::max; v++) { + if (!new_t._present) { + DEBUG_ONLY(ASSERT_FALSE(t.contains(S(v)))); + } else { + DEBUG_ONLY(ASSERT_EQ(t.contains(S(v)), new_t._data.contains(S(v)))); + } + } + } + } + } + } + } + } +} + template static void test_canonicalize_constraints_simple() { constexpr int parameters = 10; @@ -154,6 +203,10 @@ static void test_canonicalize_constraints_random() { TEST_VM(opto, canonicalize_constraints) { test_canonicalize_constraints_trivial(); + test_canonicalize_constraints_exhaustive, uintn_t<1>>(); + test_canonicalize_constraints_exhaustive, uintn_t<2>>(); + test_canonicalize_constraints_exhaustive, uintn_t<3>>(); + test_canonicalize_constraints_exhaustive, uintn_t<4>>(); test_canonicalize_constraints_simple(); test_canonicalize_constraints_simple(); test_canonicalize_constraints_random(); From c33576a54c4e4fdb7dbf517a91223b5b9f9b5501 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 29 Jan 2025 23:32:16 +0700 Subject: [PATCH 39/45] assignment operator --- src/hotspot/share/utilities/intn_t.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/utilities/intn_t.hpp b/src/hotspot/share/utilities/intn_t.hpp index 6902eae1dcdaf..0628d574e1b47 100644 --- a/src/hotspot/share/utilities/intn_t.hpp +++ b/src/hotspot/share/utilities/intn_t.hpp @@ -47,9 +47,10 @@ class intn_t { explicit constexpr intn_t(int v) : _v(v) {} constexpr intn_t() : _v(0) {} constexpr intn_t(const intn_t&) = default; + constexpr intn_t& operator=(const intn_t&) = default; explicit constexpr intn_t(uintn_t v); - operator int() const { + constexpr operator int() const { int shift = 32 - n; return int(_v << shift) >> shift; } @@ -87,8 +88,9 @@ class uintn_t { explicit constexpr uintn_t(int v) : _v(v) {} constexpr uintn_t() : _v(0) {} constexpr uintn_t(const uintn_t&) = default; + constexpr uintn_t& operator=(const uintn_t&) = default; explicit constexpr uintn_t(intn_t v) : _v(v._v) {} - operator uint() const { return _v & _mask; } + constexpr operator uint() const { return _v & _mask; } constexpr static int min = 0; constexpr static int max = _mask; From cf5609190c08ca7b678e585d246410e6c375dac5 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 30 Jan 2025 13:17:29 +0700 Subject: [PATCH 40/45] refine first_violation --- src/hotspot/share/opto/rangeinference.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 49ea7827910c0..c4c21071bdafe 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -224,11 +224,10 @@ static U adjust_lo(U lo, const KnownBits& bits) { // first_violation is the position of the violation counting from the // highest bit down (0-based), since i == 2, first_difference == 1 juint first_violation = count_leading_zeros(one_violation); // 1 + // 1 0 0 0 0 0 0 0 + U highest_bit = (std::numeric_limits::max() >> 1) + U(1); // 0 1 0 0 0 0 0 0 - // This is the same as U(1) << (W - 1 - first_violation), we avoid using - // W so that this can be used with intn_t - U alignment = one_violation == U(1) ? U(1) - : (std::numeric_limits::max() >> (first_violation + 1)) + U(1); + U alignment = highest_bit >> first_violation; // This is the first value which have the violated bit being 1, which means // that the result should not be smaller than this // 1 1 0 0 0 0 0 0 @@ -261,8 +260,6 @@ static U adjust_lo(U lo, const KnownBits& bits) { juint first_violation = count_leading_zeros(zero_violation); // This mask out all bits from the first violation // 1 1 1 1 1 0 0 0 - // This is the same as max << (W - first_violation), we avoid using W so - // that this can be used with intn_t U find_mask = ~(std::numeric_limits::max() >> first_violation); // 1 0 0 1 1 1 1 0 U either = lo | bits._zeros; From a84991771c32aafc498c57b746ea02382aff7f2e Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 30 Jan 2025 13:48:24 +0700 Subject: [PATCH 41/45] clean up intn_t --- src/hotspot/share/opto/rangeinference.cpp | 6 +-- .../share/utilities/count_leading_zeros.hpp | 5 +- src/hotspot/share/utilities/intn_t.hpp | 51 +++++++------------ test/hotspot/gtest/utilities/test_intn_t.cpp | 51 +++++++++++++++++++ 4 files changed, 74 insertions(+), 39 deletions(-) create mode 100644 test/hotspot/gtest/utilities/test_intn_t.cpp diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index c4c21071bdafe..f68e3a0d6326a 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -223,7 +223,7 @@ static U adjust_lo(U lo, const KnownBits& bits) { // first_violation is the position of the violation counting from the // highest bit down (0-based), since i == 2, first_difference == 1 - juint first_violation = count_leading_zeros(one_violation); // 1 + juint first_violation = count_leading_zeros(one_violation); // 1 // 1 0 0 0 0 0 0 0 U highest_bit = (std::numeric_limits::max() >> 1) + U(1); // 0 1 0 0 0 0 0 0 @@ -257,7 +257,7 @@ static U adjust_lo(U lo, const KnownBits& bits) { // obtain our final result, which is: // 1 0 1 0 0 0 1 1 - juint first_violation = count_leading_zeros(zero_violation); + juint first_violation = count_leading_zeros(zero_violation); // This mask out all bits from the first violation // 1 1 1 1 1 0 0 0 U find_mask = ~(std::numeric_limits::max() >> first_violation); @@ -341,7 +341,7 @@ adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { // Find the first mismatch, all bits before it is the same in bounds._lo and // bounds._hi U match_mask = mismatch == U(0) ? std::numeric_limits::max() - : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); + : ~(std::numeric_limits::max() >> count_leading_zeros(mismatch)); // match_mask & bounds._lo is the common prefix, extract zeros and ones from // it U new_zeros = bits._zeros | (match_mask & ~bounds._lo); diff --git a/src/hotspot/share/utilities/count_leading_zeros.hpp b/src/hotspot/share/utilities/count_leading_zeros.hpp index d6cbed9a355e9..bd4c637768758 100644 --- a/src/hotspot/share/utilities/count_leading_zeros.hpp +++ b/src/hotspot/share/utilities/count_leading_zeros.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -36,7 +36,8 @@ // We implement and support variants for 8, 16, 32 and 64 bit integral types. template struct CountLeadingZerosImpl; -template unsigned count_leading_zeros(T v) { +template ::value)> +unsigned count_leading_zeros(T v) { assert(v != 0, "precondition"); return CountLeadingZerosImpl::doit(v); } diff --git a/src/hotspot/share/utilities/intn_t.hpp b/src/hotspot/share/utilities/intn_t.hpp index 0628d574e1b47..997bdee0df3a0 100644 --- a/src/hotspot/share/utilities/intn_t.hpp +++ b/src/hotspot/share/utilities/intn_t.hpp @@ -50,7 +50,7 @@ class intn_t { constexpr intn_t& operator=(const intn_t&) = default; explicit constexpr intn_t(uintn_t v); - constexpr operator int() const { + explicit constexpr operator int() const { int shift = 32 - n; return int(_v << shift) >> shift; } @@ -60,16 +60,15 @@ class intn_t { static_assert(min < max, ""); constexpr bool operator==(intn_t o) const { return (_v & _mask) == (o._v & _mask); } - constexpr bool operator<(intn_t o) const { - // Shift the highest bit of intn_t to the highest bit of the int representation - int shift = 32 - n; - return int(_v << shift) < int(o._v << shift); - } - constexpr bool operator>(intn_t o) const { return o < *this; } - constexpr bool operator<=(intn_t o) const { return !(o < *this); } - constexpr bool operator>=(intn_t o) const { return !(*this < o); } + constexpr bool operator<(intn_t o) const { return int(*this) < int(o); } + constexpr bool operator>(intn_t o) const { return int(*this) > int(o); } + constexpr bool operator<=(intn_t o) const { return int(*this) <= int(o); } + constexpr bool operator>=(intn_t o) const { return int(*this) >= int(o); } }; +template +unsigned count_leading_zeros(uintn_t); + template class uintn_t { static_assert(n > 0 && n <= 8, "should not be larger than char"); @@ -81,8 +80,7 @@ class uintn_t { friend class intn_t; - template - friend unsigned count_leading_zeros(T); + friend unsigned count_leading_zeros(uintn_t); public: explicit constexpr uintn_t(int v) : _v(v) {} @@ -90,18 +88,18 @@ class uintn_t { constexpr uintn_t(const uintn_t&) = default; constexpr uintn_t& operator=(const uintn_t&) = default; explicit constexpr uintn_t(intn_t v) : _v(v._v) {} - constexpr operator uint() const { return _v & _mask; } + explicit constexpr operator uint() const { return _v & _mask; } constexpr static int min = 0; constexpr static int max = _mask; static_assert(min < max, ""); constexpr bool operator==(uintn_t o) const { return (_v & _mask) == (o._v & _mask); } - constexpr bool operator!=(uintn_t o) const { return !(*this == o); } + constexpr bool operator!=(uintn_t o) const { return (_v & _mask) != (o._v & _mask); } constexpr bool operator<(uintn_t o) const { return (_v & _mask) < (o._v & _mask); } - constexpr bool operator>(uintn_t o) const { return o < *this; } - constexpr bool operator<=(uintn_t o) const { return !(o < *this); } - constexpr bool operator>=(uintn_t o) const { return !(*this < o); } + constexpr bool operator>(uintn_t o) const { return (_v & _mask) > (o._v & _mask); } + constexpr bool operator<=(uintn_t o) const { return (_v & _mask) <= (o._v & _mask); } + constexpr bool operator>=(uintn_t o) const { return (_v & _mask) >= (o._v & _mask); } constexpr uintn_t operator+(uintn_t o) const { return uintn_t(_v + o._v); } constexpr uintn_t operator-(uintn_t o) const { return uintn_t(_v - o._v); } constexpr uintn_t operator&(uintn_t o) const { return uintn_t(_v & o._v); } @@ -135,24 +133,9 @@ class numeric_limits> { } -template <> -inline unsigned count_leading_zeros>(uintn_t<1> v) { - return count_leading_zeros(v._v & uintn_t<1>::_mask) - (32 - 1); -} - -template <> -inline unsigned count_leading_zeros>(uintn_t<2> v) { - return count_leading_zeros(v._v & uintn_t<2>::_mask) - (32 - 2); -} - -template <> -inline unsigned count_leading_zeros>(uintn_t<3> v) { - return count_leading_zeros(v._v & uintn_t<3>::_mask) - (32 - 3); -} - -template <> -inline unsigned count_leading_zeros>(uintn_t<4> v) { - return count_leading_zeros(v._v & uintn_t<4>::_mask) - (32 - 4); +template +inline unsigned count_leading_zeros(uintn_t v) { + return count_leading_zeros(v._v & uintn_t::_mask) - (32 - n); } #endif // SHARE_UTILITIES_INTN_T_HPP diff --git a/test/hotspot/gtest/utilities/test_intn_t.cpp b/test/hotspot/gtest/utilities/test_intn_t.cpp new file mode 100644 index 0000000000000..fe078e4203233 --- /dev/null +++ b/test/hotspot/gtest/utilities/test_intn_t.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025, 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. + * + */ + +#include "utilities/intn_t.hpp" +#include "unittest.hpp" + +template +static void test_intn_t() { + static_assert(std::numeric_limits>::min() <= intn_t(-1) && intn_t(-1) < intn_t(0) && intn_t(0) <= std::numeric_limits>::max(), "basic sanity"); + for (int i = intn_t::min; i <= intn_t::max; i++) { + ASSERT_EQ(i, int(intn_t(i))); + if (i > intn_t::min) { + ASSERT_TRUE(intn_t(i - 1) < intn_t(i)); + } + if (i < intn_t::max) { + ASSERT_TRUE(intn_t(i) < intn_t(i + 1)); + } + } +} + +TEST(utilities, intn_t) { + test_intn_t<1>(); + test_intn_t<2>(); + test_intn_t<3>(); + test_intn_t<4>(); + test_intn_t<5>(); + test_intn_t<6>(); + test_intn_t<7>(); + test_intn_t<8>(); +} From 1d34a54d8078c10e63c9ea1f32ac2e0ca9375c1f Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 30 Jan 2025 14:08:57 +0700 Subject: [PATCH 42/45] include --- src/hotspot/share/utilities/count_leading_zeros.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/utilities/count_leading_zeros.hpp b/src/hotspot/share/utilities/count_leading_zeros.hpp index bd4c637768758..122507743d351 100644 --- a/src/hotspot/share/utilities/count_leading_zeros.hpp +++ b/src/hotspot/share/utilities/count_leading_zeros.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_UTILITIES_COUNT_LEADING_ZEROS_HPP #define SHARE_UTILITIES_COUNT_LEADING_ZEROS_HPP +#include "metaprogramming/enableIf.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" From ee07d2911ea2a2c5cf1d867ff8e95e3f4cf1a2d4 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 30 Jan 2025 22:48:23 +0700 Subject: [PATCH 43/45] number lemmas --- src/hotspot/share/opto/rangeinference.cpp | 169 +++++++++++++--------- src/hotspot/share/opto/type.hpp | 120 +++++++++------ 2 files changed, 170 insertions(+), 119 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index f68e3a0d6326a..d414b7a941294 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -48,21 +48,14 @@ class AdjustResult { } }; -// In the canonical form, when intersecting [lo, hi] in the signed domain with -// [ulo, uhi] in the unsigned domain, there can be 2 cases (see TypeInt in -// type.hpp for more details): -// - [lo, hi] is the same as [ulo, uhi], lo and hi are both >= 0 or both < 0. -// - [lo, hi] is not the same as [ulo, uhi], which results in the intersections -// being [lo, uhi] and [ulo, hi], lo and uhi are < 0 while ulo and hi are >= 0. -// This class deals with each interval with both bounds being >= 0 or < 0 in -// the signed domain. We call it Simple because a canonicalized TypeIntPrototype -// may contain 1 or 2 SimpleCanonicalResult. +// This is the result of canonicalizing a simple interval (see TypeInt at +// type.hpp) template class SimpleCanonicalResult { static_assert(U(-1) > U(0), "bit info should be unsigned"); public: - bool _present; // whether this is an empty set - RangeInt _bounds; + bool _present; // whether this is an empty set + RangeInt _bounds; // The bounds must be in the same half of the integer domain (see TypeInt) KnownBits _bits; bool empty() const { @@ -82,7 +75,8 @@ class SimpleCanonicalResult { // bit refers to the highest bit (the MSB), the last bit refers to the lowest // bit (the LSB), a bit comes before (being higher than) another if it is more // significant, and a bit comes after (being lower than) another if it is less -// significant. +// significant. For a value n with w bits, we denote n[0] the first (highest) +// bit of n, n[1] the second bit, ..., n[w - 1] the last (lowest) bit of n. template static U adjust_lo(U lo, const KnownBits& bits) { // Violation of lo with respects to bits @@ -92,7 +86,7 @@ static U adjust_lo(U lo, const KnownBits& bits) { // zero_violation = 0100, i.e the second bit should be zero, but it is 1 in // lo. Similarly, one_violation = 0001, i.e the last bit should be one, but // it is 0 in lo. These make lo not satisfy the bit constraints, which - // results in us having to find the smallest value that satisfies bits + // results in us having to find the smallest value that satisfies bits. U zero_violation = lo & bits._zeros; U one_violation = ~lo & bits._ones; if (zero_violation == one_violation) { @@ -103,11 +97,12 @@ static U adjust_lo(U lo, const KnownBits& bits) { /* 1. Intuition: - Call res the lowest value not smaller than lo that satisfies bits, consider - the first bit in res that is different from the corresponding bit in lo, - since res is larger than lo the bit must be 0 in lo and 1 in res. Since res - must satisify bits the bit must be 0 in zeros. Finally, as res should be the - smallest value, this bit should be the last one possible. + Call r the lowest value not smaller than lo that satisfies bits, consider the + first bit in r that is different from the corresponding bit in lo: + - Since r is larger than lo the bit must be 0 in lo and 1 in r + - Since r must satisify bits the bit must be 0 in zeros + - Since r should be the smallest value, this bit should be the lowest one + possible E.g: 1 2 3 4 5 6 lo = 1 0 0 1 1 0 @@ -123,79 +118,110 @@ static U adjust_lo(U lo, const KnownBits& bits) { if both x1 and x2 satisfy bits, x2 would be closer to our true result. 2. Formality: - Call i the largest value such that (with v[0] being the first bit of v, v[1] - being the second bit of v and so on): + Call i the largest bit index such that: - - lo[x] satisfies bits for 0 <= x < i - - zeros[i] = 0 - - lo[i] = 0 + - lo[x] satisfies bits for 0 <= x < i (2.1) + - zeros[i] = 0 (2.2) + - lo[i] = 0 (2.3) Consider v: - - v[x] = lo[x], for 0 <= x < i - - v[i] = 1 - - v[x] = ones[x], for j > i + - v[x] = lo[x], for 0 <= x < i (2.4) + - v[i] = 1 (2.5) + - v[x] = ones[x], for x > i (2.6) We will prove that v is the smallest value not smaller than lo that satisfies bits. - Call r the smallest value not smaller than lo that satisfies bits. + Call r the smallest value not smaller than lo that satisfies bits. Since lo + does not satisfy bits, lo < r (2.7) a. Firstly, we prove that r <= v: - Trivially, lo < v since lo[i] < v[i] and lo[x] == v[x] for x < i. + Trivially, lo < v since: + lo[x] = v[x], for 0 <= x < i (according to 2.4) + lo[i] < v[i] (according to 2.3 and 2.5, lo[i] == 0 < v[i] == 1) + bits at x > i have lower significance, and are thus irrelevant - As established above, the first (i + 1) bits of v satisfy bits. - The remaining bits satisfy zeros, since any bit x > i such that zeros[x] == 1, v[x] == ones[x] == 0 - They also satisfy ones, since any bit j > i such that ones[x] == 1, v[x] == ones[x] == 1 + As established above, the first (i + 1) bits of v satisfy bits. + The remaining bits satisfy zeros, since any bit x > i such that zeros[x] == 1, + v[x] == ones[x] == 0 (according to 2.6, we assume bits is not contradictory here) + They also satisfy ones, since any bit j > i such that ones[x] == 1, v[x] == ones[x] == 1 (according to 2.6) - As a result, v > lo and v satisfies bits since all of its bits satisfy bits. Which - means r <= v since r is the smallest such value. + As a result, v > lo and v satisfies bits since all of its bits satisfy bits. Which + means r <= v since r is the smallest such value. - b. Secondly, we prove that r >= v. Suppose r < v: + b. Secondly, we prove that r == v. Suppose r < v: - Since r < v, there must be a bit position j that: + Since r < v, there must be a bit position j that: - r[j] == 0, v[j] == 1 - r[x] == v[x], for x < j + r[j] == 0 (2.b.1) + v[j] == 1 (2.b.2) + r[x] == v[x], for x < j (2.b.3) - - If j < i - r[j] == 0, v[j] == lo[j] == 1 - r[x] == v[x] == lo[x], for x < j + - If j < i + r[j] == 0 (according to 2.b.1) + v[j] == lo[j] (according to 2.4 because j < i) + v[j] == 1 (according to 2.b.2) + r[x] == v[x], for x < j (according to 2.b.3) + v[x] == lo[x], for x < j (according to 2,4 because j < i) - This means r < lo, which contradicts that r >= lo + This means that: + r[j] == 0 + lo[j] == 1 + r[x] == lo[x], for x < j - - If j == i - This means that lo[i] == r[i]. Call k the bit position such that: + Which leads to r < lo, which contradicts that r >= lo - r[k] == 1, lo[k] == 0 - r[x] == lo[x], for x < k + - If j == i + Since lo[i] == 0 (according to 2.3) and r[i] == 0 (according to 2.b.1), + we have lo[i] == r[i]. Since r > lo (according to 2.7), there must exist + a bit index k such that: - k > i since r[x] == lo[x], for x <= i - lo[x] satisfies bits for 0 <= x < k - zeros[k] == 0 - This contradicts the assumption that i being the largest value satisfying such conditions. + r[k] == 1 + lo[k] == 0 + r[x] == lo[x], for x < k - - If j > i: - ones[j] == v[j] == 1, which contradicts that r satisfies bits. + Then, since we have: + r[x] == v[x], for x < i (according to 2.b.3 because j == i) + v[x] == lo[x], for x < i (according to 2.4) + r[j] == 0 (according to 2.b.1) + lo[j] == 0 (according to 2.3) - All cases lead to contradictions, which mean r < v is incorrect, which means - that r >= v. + this leads to: r[x] == lo[x], for x <= i + while r[k] == 1 != lo[k] == 0, we can conclude that k > i - As a result, r == v, which means the value v having the above form is the - lowest value not smaller than lo that satisfies bits. + However, since: + lo[x] satisfies bits for 0 <= x < k (because r satisfies bits and lo[x] == r[x] for 0 <= x < k) + zeros[k] == 0 (because r[k] == 1, which means zeros[k] != 1 because r satisfies bits) + lo[k] == 0 (the definition of k above) - Our objective now is to find the largest value i that satisfies: - - lo[x] satisfies bits for 0 <= x < i - - zeros[i] = 0 - - lo[i] = 0 + This contradicts the assumption that i is the largest bit index satisfying such conditions. - Call j the largest value such that lo[x] satisfies bits for 0 <= x < j. This - means that j is the smallest value such that lo[j] does not satisfy bits. We - call this the first violation. i then can be computed as the largest value - <= j such that: + - If j > i + ones[j] == v[j] (according to 2.6 since j > i) + v[j] == 1 (according to 2.b.2) + r[j] == 0 (according to 2.b.1) - zeros[i] == lo[i] == 0 + This means that r[j] == 0 and ones[j] == 1, this contradicts the assumption that r + satisfies bits. + + All cases lead to contradictions, which mean r < v is incorrect, which means + that r == v, which means the value v having the above form is the + lowest value not smaller than lo that satisfies bits. + + 3. Conclusion + Our objective now is to find the largest value i that satisfies: + - lo[x] satisfies bits for 0 <= x < i + - zeros[i] = 0 + - lo[i] = 0 + + Call j the largest value such that lo[x] satisfies bits for 0 <= x < j. This + means that j is the smallest value such that lo[j] does not satisfy bits. We + call this the first violation. i then can be computed as the largest value + <= j such that: + + zeros[i] == lo[i] == 0 */ // The algorithm depends on whether the first violation violates zeros or @@ -356,9 +382,8 @@ adjust_bits_from_bounds(const KnownBits& bits, const RangeInt& bounds) { // This function converges because at each iteration, some bits that are unknown // are made known. As there are at most 64 bits, the number of iterations should // not be larger than 64. -// This function is called simple because it deals with a SimpleCanonicalResult, -// and a canonicalization of a TypeIntPrototype may require 1 or 2 calls to this -// function, one for the non-negative range and one for the negative range. +// This function is called simple because it deals with a simple intervals (see +// TypeInt at type.hpp). template static SimpleCanonicalResult canonicalize_constraints_simple(const RangeInt& bounds, const KnownBits& bits) { @@ -415,8 +440,11 @@ TypeIntPrototype::canonicalize_constraints() const { } } + // Now [srange._lo, jint(urange._hi)] and [jint(urange._lo), srange._hi] are + // both simple intervals (as defined in TypeInt at type.hpp), we process them + // separately and combine the results if (S(urange._lo) <= S(urange._hi)) { - // [lo, hi] and [ulo, uhi] represent the same range + // The 2 simple intervals should be tightened to the same result urange._lo = U(MAX2(S(urange._lo), srange._lo)); urange._hi = U(MIN2(S(urange._hi), srange._hi)); if (urange._lo > urange._hi || S(urange._lo) > S(urange._hi)) { @@ -428,10 +456,7 @@ TypeIntPrototype::canonicalize_constraints() const { type._bounds, type._bits}}; } - // [lo, hi] intersects with [ulo, uhi] in 2 ranges: - // [lo, uhi], which consists of negative values - // [ulo, hi] which consists of non-negative values - // We process these 2 separately and combine the results + // The 2 simple intervals can be tightened into 2 separate results auto neg_type = canonicalize_constraints_simple({U(srange._lo), urange._hi}, _bits); auto pos_type = canonicalize_constraints_simple({urange._lo, U(srange._hi)}, _bits); diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 6a4dafcd2a495..215cade97c1c3 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -589,28 +589,30 @@ class TypeInteger : public Type { /** * Definition: * - * A TypeInt represents a set of jint values. An jint v is an element of a - * TypeInt iff: + * A TypeInt represents a set of non-empty jint values. A jint v is an element + * of a TypeInt iff: * * v >= _lo && v <= _hi && juint(v) >= _ulo && juint(v) <= _uhi && _bits.is_satisfied_by(v) * - * Multiple set of parameters can represent the same set. + * Multiple sets of parameters can represent the same set. * E.g: consider 2 TypeInt t1, t2 * - * t1._lo = 2, t1._hi = 7, t1._ulo = 0, t1._uhi = 5, t1._bits._zeros = 0x0, t1._bits._ones = 0x1 + * t1._lo = 2, t1._hi = 7, t1._ulo = 0, t1._uhi = 5, t1._bits._zeros = 0x00000000, t1._bits._ones = 0x1 * t2._lo = 3, t2._hi = 5, t2._ulo = 3, t2._uhi = 5, t2._bits._zeros = 0xFFFFFFF8, t2._bits._ones = 0x1 * * Then, t1 and t2 both represent the set {3, 5}. We can also see that the - * constraints of t2 are optimal. I.e there exists no TypeInt t3 which also - * represents {3, 5} such that: + * constraints of t2 are tightest possible. I.e there exists no TypeInt t3 + * which also represents {3, 5} such that: * * t3._lo > t2._lo || t3._hi < t2._hi || t3._ulo > t2._ulo || t3._uhi < t2._uhi || - * (t3._bits._zeros & t2._bis._zeros) != t3._bits._zeros || (t3._bits._ones & t2._bits._ones) != t3._bits._ones + * (t3._bits._zeros &~ t2._bis._zeros) != 0 || (t3._bits._ones &~ t2._bits._ones) != 0 * - * The last 2 conditions mean that the bits in t3._bits._zeros is not a subset - * of those in t2._bits._zeros, the same applies to _bits._ones + * The 5-th condition mean that the subtraction of the bitsets represented by + * t3._bits._zeros and t2._bits._zeros is not empty, which means that the + * bits in t3._bits._zeros is not a subset of those in t2._bits._zeros, the + * same applies to _bits._ones * - * As a result, every TypeInt is canonicalized to its optimal form upon + * As a result, every TypeInt is canonicalized to its tightest form upon * construction. This makes it easier to reason about them in optimizations. * E.g a TypeInt t with t._lo < 0 will definitely contain negative values. It * also makes it trivial to determine if a TypeInt instance is a subset of @@ -618,40 +620,46 @@ class TypeInteger : public Type { * * Lemmas: * - * 1. Since every TypeInt instance is canonicalized, all the bounds must also - * be elements of such TypeInt. Or else, we can tighten the bounds by narrowing - * it by one, which contradicts the assumption of the TypeInt being canonical. + * 1. Since every TypeInt instance is non-empty and canonicalized, all the + * bounds must also be elements of such TypeInt. Or else, we can tighten the + * bounds by narrowing it by one, which contradicts the assumption of the + * TypeInt being canonical. * - * 2. _lo <= jint(_ulo) - * _lo <= _hi - * _lo <= jint(_uhi) - * _ulo <= juint(_lo) - * _ulo <= _uhi - * _ulo <= juint(_hi) - * _hi >= jint(_uhi) - * _hi >= _lo - * _hi >= jint(_ulo) - * _hi >= jint(_uhi) - * _uhi >= juint(_hi) - * _uhi >= juint(_lo) - * _uhi >= _ulo + * 2. + * 2.1. _lo <= jint(_ulo) + * 2.2. _lo <= _hi + * 2.3. _lo <= jint(_uhi) + * 2.4. _ulo <= juint(_lo) + * 2.5. _ulo <= juint(_hi) + * 2.6. _ulo <= _uhi + * 2.7. _hi >= _lo + * 2.8. _hi >= jint(_ulo) + * 2.9. _hi >= jint(_uhi) + * 2.10. _uhi >= juint(_lo) + * 2.11. _uhi >= _ulo + * 2.12. _uhi >= juint(_hi) * * Proof of lemma 2: * - * _lo <= jint(_ulo): - * According the lemma 1, _ulo is an element of the TypeInt, so in the signed - * domain, it must not be less than the smallest element of that TypeInt, which - * is _lo. Which means that _lo <= _ulo in the signed domain, or in a more - * programmatical way, _lo <= jint(_ulo). + * 2.1. _lo <= jint(_ulo): + * According the lemma 1, _ulo is an element of the TypeInt, so in the + * signed domain, it must not be less than the smallest element of that + * TypeInt, which is _lo. Which means that _lo <= _ulo in the signed + * domain, or in a more programmatical way, _lo <= jint(_ulo). + * 2.2. _lo <= _hi: + * According the lemma 1, _hi is an element of the TypeInt, so in the + * signed domain, it must not be less than the smallest element of that + * TypeInt, which is _lo. Which means that _lo <= _hi. + * * The other inequalities can be proved in a similar manner. * * 3. Either _lo == jint(_ulo) and _hi == jint(_uhi), or all elements of a - * TypeInt lie in the intervals [_lo, jint(_uhi)] or [jint(_ulo), _hi] (note - * that these intervals are disjoint in this case). + * TypeInt lie in the intervals [_lo, jint(_uhi)] or [jint(_ulo), _hi] (note + * that these intervals are disjoint in this case). * * Proof of lemma 3: - * We have a preliminary: For 2 jint value x, y such that they are both >= 0 - * or both < 0. Then: + * Lemma 3.1: For 2 jint value x, y such that they are both >= 0 or both < 0. + * Then: * * x <= y iff juint(x) <= juint(y) * I.e. x <= y in the signed domain iff x <= y in the unsigned domain @@ -660,16 +668,24 @@ class TypeInteger : public Type { * * For a TypeInt t, there are 3 possible cases: * - * a. t._lo >= 0. Since 0 <= t._lo <= jint(t._ulo) (lemma 2), we have: + * a. t._lo >= 0, we have: * - * juint(t._lo) <= juint(jint(t._ulo)) == t._ulo <= juint(t._lo) + * 0 <= t_lo <= jint(t._ulo) (lemma 2.1) + * juint(t._lo) <= juint(jint(t._ulo)) (lemma 3.1) + * == t._ulo (juint(jint(v)) == v with juint v) + * <= juint(t._lo) (lemma 2.4) * * Which means that t._lo == jint(t._ulo). * - * Furthermore, 0 <= t._lo <= t._hi and 0 <= t._lo <= jint(t._uhi) (lemma 2), - * since t._hi >= jint(t._uhi), we have: + * Furthermore, + * + * 0 <= t._lo <= t._hi (lemma 2.2) + * 0 <= t._lo <= jint(t._uhi) (lemma 2.3) + * t._hi >= jint(t._uhi) (lemma 2.9) * - * juint(t._hi) >= juint(jint(t._uhi)) == t._uhi >= juint(t._hi) + * juint(t._hi) >= juint(jint(t._uhi)) (lemma 3.1) + * == t._uhi (juint(jint(v)) == v with juint v) + * >= juint(t._hi) (lemma 2.12) * * Which means that t._hi = jint(t._uhi) * @@ -677,11 +693,12 @@ class TypeInteger : public Type { * * c. t._lo < 0, t._hi >= 0. * - * Since t._ulo <= juint(t._hi), we must have jint(t._ulo) >= 0 because all - * negative values is larger than all positive values in the unsigned domain. + * Since t._ulo <= juint(t._hi) (lemma 2.5), we must have jint(t._ulo) >= 0 + * because all negative values is larger than all non-negative values in the + * unsigned domain. * - * Since t._uhi >= juint(t._lo), we must have jint(t._uhi) < 0, similar to the - * reasoning above. + * Since t._uhi >= juint(t._lo) (lemma 2.10), we must have jint(t._uhi) < 0 + * similar to the reasoning above. * * In this case, all elements of t belongs to either [t._lo, jint(t._uhi)] or * [jint(t._ulo), t._hi]. @@ -695,9 +712,18 @@ class TypeInteger : public Type { * Unsigned: * 0--------ulo==========hi----------lo=========uhi--------- * - * This property is useful for our analysis of TypeInt values. Additionally, it - * can be seen that _lo and jint(_uhi) are both < 0 or both >= 0, and the same - * applies to jint(_ulo) and _hi. + * This property is useful for our analysis of TypeInt values. Additionally, + * it can be seen that _lo and jint(_uhi) are both < 0 or both >= 0, and the + * same applies to jint(_ulo) and _hi. + * + * We call [_lo, jint(_uhi)] and [jint(_ulo), _hi] "simple intervals". Then, + * a TypeInt consists of 2 simple intervals, each of which has its bounds + * being both >= 0 or both < 0. If both simple intervals lie in the same half + * of the integer domain, they must be the same (i.e _lo == jint(_ulo) and + * _hi == jint(_uhi)). Otherwise, [_lo, jint(_uhi)] must lie in the negative + * half and [jint(_ulo), _hi] must lie in the non-negative half of the signed + * domain (equivalently, [_lo, jint(_uhi)] must lie in the upper half and + * [jint(_ulo), _hi] must lie in the lower half of the unsigned domain). */ class TypeInt : public TypeInteger { TypeInt(const TypeIntPrototype& t, int w, bool dual); From d87e036b8b13197c90b4feaa40aed431c464b692 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 30 Jan 2025 23:10:22 +0700 Subject: [PATCH 44/45] harden SimpleCanonicalResult --- src/hotspot/share/opto/rangeinference.cpp | 26 +++++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index d414b7a941294..0dfecf5a3d289 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -54,16 +54,28 @@ template class SimpleCanonicalResult { static_assert(U(-1) > U(0), "bit info should be unsigned"); public: - bool _present; // whether this is an empty set - RangeInt _bounds; // The bounds must be in the same half of the integer domain (see TypeInt) - KnownBits _bits; + const bool _present; // whether this is an empty set + const RangeInt _bounds; // The bounds must be in the same half of the integer domain (see TypeInt) + const KnownBits _bits; + + SimpleCanonicalResult(bool present, const RangeInt& bounds, const KnownBits& bits) + : _present(present), _bounds(bounds), _bits(bits) { + if (!present) { + return; + } + // Do some verification + assert(bits.is_satisfied_by(bounds._lo) && bits.is_satisfied_by(bounds._hi), "must be canonical"); + // 0b1000... + constexpr U mid_point = (std::numeric_limits::max() >> 1) + U(1); + assert((bounds._lo < mid_point) == (bounds._hi < mid_point), "must be a simple interval"); + } bool empty() const { return !_present; } static SimpleCanonicalResult make_empty() { - return {false, {}, {}}; + return SimpleCanonicalResult(false, {}, {}); } }; @@ -251,7 +263,7 @@ static U adjust_lo(U lo, const KnownBits& bits) { // highest bit down (0-based), since i == 2, first_difference == 1 juint first_violation = count_leading_zeros(one_violation); // 1 // 1 0 0 0 0 0 0 0 - U highest_bit = (std::numeric_limits::max() >> 1) + U(1); + constexpr U highest_bit = (std::numeric_limits::max() >> 1) + U(1); // 0 1 0 0 0 0 0 0 U alignment = highest_bit >> first_violation; // This is the first value which have the violated bit being 1, which means @@ -398,11 +410,11 @@ canonicalize_constraints_simple(const RangeInt& bounds, const KnownBits& b while (true) { canonicalized_bounds = adjust_bounds_from_bits(canonicalized_bounds._result, canonicalized_bits._result); if (!canonicalized_bounds._progress || canonicalized_bounds.empty()) { - return {canonicalized_bounds._present, canonicalized_bounds._result, canonicalized_bits._result}; + return SimpleCanonicalResult(canonicalized_bounds._present, canonicalized_bounds._result, canonicalized_bits._result); } canonicalized_bits = adjust_bits_from_bounds(canonicalized_bits._result, canonicalized_bounds._result); if (!canonicalized_bits._progress || canonicalized_bits.empty()) { - return {canonicalized_bits._present, canonicalized_bounds._result, canonicalized_bits._result}; + return SimpleCanonicalResult(canonicalized_bits._present, canonicalized_bounds._result, canonicalized_bits._result); } } } From 55860366ab54a9e7b6e14a6f07d436a82fa8e7af Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 14 Feb 2025 19:50:56 +0700 Subject: [PATCH 45/45] refine comments --- src/hotspot/share/opto/rangeinference.cpp | 104 +++++++++++----------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp index 85be3426737c9..8423f181549c5 100644 --- a/src/hotspot/share/opto/rangeinference.cpp +++ b/src/hotspot/share/opto/rangeinference.cpp @@ -130,40 +130,46 @@ static U adjust_lo(U lo, const KnownBits& bits) { if both x1 and x2 satisfy bits, x2 would be closer to our true result. 2. Formality: + + Call r the smallest value not smaller than lo that satisfies bits. Since lo + does not satisfy bits, lo < r (2.1) + Call i the largest bit index such that: - - lo[x] satisfies bits for 0 <= x < i (2.1) - - zeros[i] = 0 (2.2) - - lo[i] = 0 (2.3) + - lo[x] satisfies bits for 0 <= x < i (2.2) + - zeros[i] = 0 (2.3) + - lo[i] = 0 (2.4) Consider v: - - v[x] = lo[x], for 0 <= x < i (2.4) - - v[i] = 1 (2.5) - - v[x] = ones[x], for x > i (2.6) + - v[x] = lo[x], for 0 <= x < i (2.5) + - v[i] = 1 (2.6) + - v[x] = ones[x], for x > i (2.7) - We will prove that v is the smallest value not smaller than lo that - satisfies bits. - - Call r the smallest value not smaller than lo that satisfies bits. Since lo - does not satisfy bits, lo < r (2.7) + We will prove that v == r. a. Firstly, we prove that r <= v: - Trivially, lo < v since: - lo[x] = v[x], for 0 <= x < i (according to 2.4) - lo[i] < v[i] (according to 2.3 and 2.5, lo[i] == 0 < v[i] == 1) + a.1. We have lo < v since: + lo[x] == v[x], for 0 <= x < i (according to 2.5) + lo[i] < v[i] (according to 2.4 and 2.6, lo[i] == 0 < v[i] == 1) bits at x > i have lower significance, and are thus irrelevant - As established above, the first (i + 1) bits of v satisfy bits. - The remaining bits satisfy zeros, since any bit x > i such that zeros[x] == 1, - v[x] == ones[x] == 0 (according to 2.6, we assume bits is not contradictory here) - They also satisfy ones, since any bit j > i such that ones[x] == 1, v[x] == ones[x] == 1 (according to 2.6) + a.2. We have v satisfies bits, this is because: + v[x] satisfies bits for 0 <= x < i (according to 2.2 and 2.5) + v[i] satisfies bits: + According to 2.3 and 2.6, v[i] == 1 and zeros[i] == 0, v[i] does not violate + bits, which means v[i] satisfies bits + v[x] satisfies bits for x > i: + Assume bits is not contradictory, we cannot have: + ones[x] == 1, v[x] == 0 (according to 2.7, v[x] == ones[x]) + zeros[x] == 1, v[x] == 1 (according to 2.7, ones[x] == v[x] == 1, which means + bits is contradictory) - As a result, v > lo and v satisfies bits since all of its bits satisfy bits. Which - means r <= v since r is the smallest such value. + From a.1 and a.2, v > lo and v satisfies bits. Which means r <= v since r is the + smallest such value. - b. Secondly, we prove that r == v. Suppose r < v: + b. Secondly, from r <= v, we prove that r == v. Suppose the contradiction r < v: Since r < v, there must be a bit position j that: @@ -171,56 +177,50 @@ static U adjust_lo(U lo, const KnownBits& bits) { v[j] == 1 (2.b.2) r[x] == v[x], for x < j (2.b.3) - - If j < i - r[j] == 0 (according to 2.b.1) - v[j] == lo[j] (according to 2.4 because j < i) - v[j] == 1 (according to 2.b.2) - r[x] == v[x], for x < j (according to 2.b.3) - v[x] == lo[x], for x < j (according to 2,4 because j < i) - + b.1. If j < i This means that: - r[j] == 0 - lo[j] == 1 - r[x] == lo[x], for x < j + r[j] == 0 (according to 2.b.1) + lo[j] == 1 (according to 2.b.2 and 2.5, lo[j] == v[j] == 1 because j < i) + r[x] == lo[x], for x < j (according to 2.b.3 and 2.5, lo[x] == v[x] == r[x] with x < j < i) + bits at x > j have lower significance, and are thus irrelevant Which leads to r < lo, which contradicts that r >= lo - - If j == i - Since lo[i] == 0 (according to 2.3) and r[i] == 0 (according to 2.b.1), - we have lo[i] == r[i]. Since r > lo (according to 2.7), there must exist - a bit index k such that: + b.2. If j == i + Since r > lo (according to 2.1), there must exist a bit index k such that: - r[k] == 1 - lo[k] == 0 - r[x] == lo[x], for x < k + r[k] == 1 (2.b.2.1) + lo[k] == 0 (2.b.2.2) + r[x] == lo[x], for x < k (2.b.2.3) Then, since we have: - r[x] == v[x], for x < i (according to 2.b.3 because j == i) - v[x] == lo[x], for x < i (according to 2.4) - r[j] == 0 (according to 2.b.1) - lo[j] == 0 (according to 2.3) + r[x] == v[x], for x < i (according to 2.b.3) + v[x] == lo[x], for x < i (according to 2.5) + r[i] == 0 (according to 2.b.1 because i == j) + lo[i] == 0 (according to 2.4) this leads to: r[x] == lo[x], for x <= i while r[k] == 1 != lo[k] == 0, we can conclude that k > i However, since: - lo[x] satisfies bits for 0 <= x < k (because r satisfies bits and lo[x] == r[x] for 0 <= x < k) - zeros[k] == 0 (because r[k] == 1, which means zeros[k] != 1 because r satisfies bits) - lo[k] == 0 (the definition of k above) + lo[x] satisfies bits for 0 <= x < k: + According to 2.b.2.3, lo[x] == r[x] and r satisfies bits + zeros[k] == 0 (according to 2.b.2.1, r[k] == 1 and r satisfies bits) + lo[k] == 0 (according to 2.b.2.2) This contradicts the assumption that i is the largest bit index satisfying such conditions. - - If j > i - ones[j] == v[j] (according to 2.6 since j > i) - v[j] == 1 (according to 2.b.2) - r[j] == 0 (according to 2.b.1) + b.3. If j > i + ones[j] == v[j] (according to 2.7 since j > i) + v[j] == 1 (according to 2.b.2) + r[j] == 0 (according to 2.b.1) This means that r[j] == 0 and ones[j] == 1, this contradicts the assumption that r satisfies bits. - All cases lead to contradictions, which mean r < v is incorrect, which means - that r == v, which means the value v having the above form is the - lowest value not smaller than lo that satisfies bits. + All cases lead to contradictions, which mean r < v is incorrect, which means that + r == v, which means the value v having the above form is the lowest value not smaller + than lo that satisfies bits. 3. Conclusion Our objective now is to find the largest value i that satisfies: