8236721: C2 should better optimize not-equal integer comparisons

Narrow the type if an integer value is found to be unequal to it's lower/upper boundary.

Reviewed-by: roland, neliasso
This commit is contained in:
Tobias Hartmann 2020-01-15 08:44:53 +01:00
parent e4aa87b816
commit f243173a18
3 changed files with 180 additions and 10 deletions

View File

@ -289,7 +289,7 @@ class IfNode : public MultiBranchNode {
private:
// Helper methods for fold_compares
bool cmpi_folds(PhaseIterGVN* igvn);
bool cmpi_folds(PhaseIterGVN* igvn, bool fold_ne = false);
bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn);
bool has_shared_region(ProjNode* proj, ProjNode*& success, ProjNode*& fail);
bool has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNode*& fail, PhaseIterGVN* igvn);

View File

@ -615,9 +615,19 @@ const TypeInt* IfNode::filtered_int_type(PhaseGVN* gvn, Node* val, Node* if_proj
jint hi = cmp2_t->_hi;
BoolTest::mask msk = if_proj->Opcode() == Op_IfTrue ? bol->_test._test : bol->_test.negate();
switch (msk) {
case BoolTest::ne:
case BoolTest::ne: {
// If val is compared to its lower or upper bound, we can narrow the type
const TypeInt* val_t = gvn->type(val)->isa_int();
if (val_t != NULL && !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);
} else if (val_t->_hi == hi) {
return TypeInt::make(val_t->_lo, val_t->_hi - 1, val_t->_widen);
}
}
// Can't refine type
return NULL;
}
case BoolTest::eq:
return cmp2_t;
case BoolTest::lt:
@ -691,7 +701,7 @@ const TypeInt* IfNode::filtered_int_type(PhaseGVN* gvn, Node* val, Node* if_proj
//
// Is the comparison for this If suitable for folding?
bool IfNode::cmpi_folds(PhaseIterGVN* igvn) {
bool IfNode::cmpi_folds(PhaseIterGVN* igvn, bool fold_ne) {
return in(1) != NULL &&
in(1)->is_Bool() &&
in(1)->in(1) != NULL &&
@ -699,7 +709,8 @@ bool IfNode::cmpi_folds(PhaseIterGVN* igvn) {
in(1)->in(1)->in(2) != NULL &&
in(1)->in(1)->in(2) != igvn->C->top() &&
(in(1)->as_Bool()->_test.is_less() ||
in(1)->as_Bool()->_test.is_greater());
in(1)->as_Bool()->_test.is_greater() ||
(fold_ne && in(1)->as_Bool()->_test._test == BoolTest::ne));
}
// Is a dominating control suitable for folding with this if?
@ -709,7 +720,7 @@ bool IfNode::is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn) {
ctrl->in(0) != NULL &&
ctrl->in(0)->Opcode() == Op_If &&
ctrl->in(0)->outcnt() == 2 &&
ctrl->in(0)->as_If()->cmpi_folds(igvn) &&
ctrl->in(0)->as_If()->cmpi_folds(igvn, true) &&
// Must compare same value
ctrl->in(0)->in(1)->in(1)->in(1) != NULL &&
ctrl->in(0)->in(1)->in(1)->in(1) == in(1)->in(1)->in(1);
@ -871,7 +882,7 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f
// sets the lower bound if any.
Node* adjusted_lim = NULL;
if (lo_type != NULL && hi_type != NULL && hi_type->_lo > lo_type->_hi &&
hi_type->_hi == max_jint && lo_type->_lo == min_jint) {
hi_type->_hi == max_jint && lo_type->_lo == min_jint && lo_test != BoolTest::ne) {
assert((dom_bool->_test.is_less() && !proj->_con) ||
(dom_bool->_test.is_greater() && proj->_con), "incorrect test");
// this test was canonicalized
@ -912,7 +923,7 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f
}
}
} else if (lo_type != NULL && hi_type != NULL && lo_type->_lo > hi_type->_hi &&
lo_type->_hi == max_jint && hi_type->_lo == min_jint) {
lo_type->_hi == max_jint && hi_type->_lo == min_jint && lo_test != BoolTest::ne) {
// this_bool = <
// dom_bool = < (proj = True) or dom_bool = >= (proj = False)
@ -963,7 +974,7 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f
}
}
} else {
const TypeInt* failtype = filtered_int_type(igvn, n, proj);
const TypeInt* failtype = filtered_int_type(igvn, n, proj);
if (failtype != NULL) {
const TypeInt* type2 = filtered_int_type(igvn, n, fail);
if (type2 != NULL) {
@ -1600,11 +1611,11 @@ Node* IfNode::simple_subsuming(PhaseIterGVN* igvn) {
return this;
}
// Map BoolTest to local table ecoding. The BoolTest (e)numerals
// Map BoolTest to local table encoding. The BoolTest (e)numerals
// { eq = 0, ne = 4, le = 5, ge = 7, lt = 3, gt = 1 }
// are mapped to table indices, while the remaining (e)numerals in BoolTest
// { overflow = 2, no_overflow = 6, never = 8, illegal = 9 }
// are ignored (these are not modelled in the table).
// are ignored (these are not modeled in the table).
//
static int subsuming_bool_test_encode(Node* node) {
precond(node->is_Bool());

View File

@ -0,0 +1,159 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8236721
* @summary Test folding of != integer comparisons.
* @run main/othervm -XX:CompileCommand=compileonly,compiler.c2.TestFoldNECompares::test*
* -XX:CompileCommand=inline,compiler.c2.TestFoldNECompares::getNarrowInt*
* -Xbatch -XX:-TieredCompilation
* compiler.c2.TestFoldNECompares
*/
package compiler.c2;
public class TestFoldNECompares {
public static int getNarrowInt(boolean b, int lo, int hi) {
return b ? lo : hi;
}
public static void test1(boolean b) {
int i = getNarrowInt(b, 42, 142);
// i: 42..142
if (i != 42) {
// i: 43..142
if (i <= 42) {
throw new RuntimeException("Should not reach here");
}
}
}
public static void test2(boolean b) {
int i = getNarrowInt(b, 42, 142);
// i: 42..142
if (i != 42) {
// i: 43..142
if (i > 42) {
} else {
throw new RuntimeException("Should not reach here");
}
}
}
public static void test3(boolean b) {
int i = getNarrowInt(b, 42, 142);
// i: 42..142
if (i == 42) {
} else {
// i: 43..142
if (i <= 42) {
throw new RuntimeException("Should not reach here");
}
}
}
public static void test4(boolean b) {
int i = getNarrowInt(b, 42, 142);
// i: 42..142
if (i == 42) {
} else {
// i: 43..142
if (i > 42) {
} else {
throw new RuntimeException("Should not reach here");
}
}
}
public static void test5(boolean b) {
int i = getNarrowInt(b, 42, 142);
// i: 42..142
if (i != 142) {
// i: 42..141
if (i >= 142) {
throw new RuntimeException("Should not reach here");
}
}
}
public static void test6(boolean b) {
int i = getNarrowInt(b, 42, 142);
// i: 42..142
if (i != 142) {
// i: 42..141
if (i < 142) {
} else {
throw new RuntimeException("Should not reach here");
}
}
}
public static void test7(boolean b) {
int i = getNarrowInt(b, 42, 142);
// i: 42..142
if (i == 142) {
} else {
// i: 42..141
if (i >= 142) {
throw new RuntimeException("Should not reach here");
}
}
}
public static void test8(boolean b) {
int i = getNarrowInt(b, 42, 142);
// i: 42..142
if (i == 142) {
} else {
// i: 42..141
if (i < 142) {
} else {
throw new RuntimeException("Should not reach here");
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 100_000; ++i) {
boolean b = ((i % 2) == 0);
test1(b);
test2(b);
test3(b);
test4(b);
test5(b);
test6(b);
test7(b);
test8(b);
}
}
}