8267239: C1: RangeCheckElimination for % operator if divisor is IntConstant

Reviewed-by: thartmann, neliasso
This commit is contained in:
Yi Yang 2021-05-19 09:05:05 +00:00 committed by Tobias Hartmann
parent 2d407e12fa
commit 0cf7e5784b
3 changed files with 99 additions and 10 deletions

@ -228,6 +228,24 @@ void RangeCheckEliminator::Visitor::do_ArithmeticOp(ArithmeticOp *ao) {
Bound* y_bound = _rce->get_bound(y);
if (x_bound->lower() >= 0 && x_bound->lower_instr() == NULL && y->as_ArrayLength() != NULL) {
_bound = new Bound(0, NULL, -1, y);
} else if (y->type()->as_IntConstant() && y->type()->as_IntConstant()->value() != 0) {
// The binary % operator is said to yield the remainder of its operands from an implied division; the
// left-hand operand is the dividend and the right-hand operand is the divisor.
//
// % operator follows from this rule that the result of the remainder operation can be negative only
// if the dividend is negative, and can be positive only if the dividend is positive. Moreover, the
// magnitude of the result is always less than the magnitude of the divisor(See JLS 15.17.3).
//
// So if y is a constant integer and not equal to 0, then we can deduce the bound of remainder operation:
// x % -y ==> [0, y - 1] Apply RCE
// x % y ==> [0, y - 1] Apply RCE
// -x % y ==> [-y + 1, 0]
// -x % -y ==> [-y + 1, 0]
if (x_bound->has_lower() && x_bound->lower() >= 0) {
_bound = new Bound(0, NULL, y->type()->as_IntConstant()->value() - 1, NULL);
} else {
_bound = new Bound();
}
} else {
_bound = new Bound();
}
@ -805,6 +823,15 @@ void RangeCheckEliminator::process_access_indexed(BlockBegin *loop_header, Block
array_bound = get_bound(ai->array());
}
TRACE_RANGE_CHECK_ELIMINATION(
tty->fill_to(block->dominator_depth()*2);
tty->print("Index bound: ");
index_bound->print();
tty->print(", Array bound: ");
array_bound->print();
tty->cr();
);
if (in_array_bound(index_bound, ai->array()) ||
(index_bound && array_bound && index_bound->is_smaller(array_bound) && !index_bound->lower_instr() && index_bound->lower() >= 0)) {
TRACE_RANGE_CHECK_ELIMINATION(
@ -1250,7 +1277,6 @@ RangeCheckEliminator::Bound::~Bound() {
// Bound constructor
RangeCheckEliminator::Bound::Bound() {
init();
this->_lower = min_jint;
this->_upper = max_jint;
this->_lower_instr = NULL;
@ -1259,7 +1285,6 @@ RangeCheckEliminator::Bound::Bound() {
// Bound constructor
RangeCheckEliminator::Bound::Bound(int lower, Value lower_instr, int upper, Value upper_instr) {
init();
assert(!lower_instr || !lower_instr->as_Constant() || !lower_instr->type()->as_IntConstant(), "Must not be constant!");
assert(!upper_instr || !upper_instr->as_Constant() || !upper_instr->type()->as_IntConstant(), "Must not be constant!");
this->_lower = lower;
@ -1273,7 +1298,6 @@ RangeCheckEliminator::Bound::Bound(Instruction::Condition cond, Value v, int con
assert(!v || (v->type() && (v->type()->as_IntType() || v->type()->as_ObjectType())), "Type must be array or integer!");
assert(!v || !v->as_Constant() || !v->type()->as_IntConstant(), "Must not be constant!");
init();
if (cond == Instruction::eql) {
_lower = constant;
_lower_instr = v;
@ -1327,10 +1351,6 @@ void RangeCheckEliminator::Bound::add_constant(int value) {
this->_upper += value;
}
// Init
void RangeCheckEliminator::Bound::init() {
}
// or
void RangeCheckEliminator::Bound::or_op(Bound *b) {
// Watch out, bound is not guaranteed not to overflow!

@ -104,9 +104,6 @@ public:
void remove_lower();
void add_constant(int value);
Bound *copy();
private:
void init();
};

@ -0,0 +1,72 @@
/*
* Copyright (c) 2021, Alibaba Group Holding Limited. 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 8267239
* @author Yi Yang
* @summary apply RCE for % operations
* @requires vm.compiler1.enabled
* @library /test/lib
* @run main/othervm -XX:TieredStopAtLevel=1 -XX:+TieredCompilation
* -XX:CompileCommand=compileonly,*ArithmeticRemRCE.test*
* compiler.c1.ArithmeticRemRCE
*/
package compiler.c1;
import jdk.test.lib.Asserts;
public class ArithmeticRemRCE {
static int field = 1000;
static void test1() {
// seq should be loop invariant, so we can not put it into static fields
int[] seq = new int[1000];
for (int i = 0; i < seq.length; i++) {
seq[i] = i;
}
for (int i = 0; i < 1024; i++) {
int constVal = 10;
Asserts.assertTrue(0 <= seq[i % 5] && seq[i % 5] <= 4);
Asserts.assertTrue(0 <= seq[i % -5] && seq[i % -5] <= 4);
Asserts.assertTrue(0 <= seq[i % constVal] && seq[i % constVal] <= 9);
Asserts.assertTrue(0 <= seq[i % -constVal] && seq[i % -constVal] <= 9);
Asserts.assertTrue(seq[i % 1] == 0);
// will not apply RCE
Asserts.assertTrue(0 <= seq[i % field] && seq[i % field] <= 999);
Asserts.assertTrue(0 <= seq[i % -field] && seq[i % -field] <= 999);
}
}
public static void main(String... args) throws Exception {
for (int i = 0; i < 10_000; i++) {
test1();
}
}
}