mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8347459: C2: missing transformation for chain of shifts/multiplications by constants
Reviewed-by: dfenacci, epeter
This commit is contained in:
parent
3d3b782037
commit
bdcac98673
@ -3606,20 +3606,208 @@ Node *StoreNode::Ideal_masked_input(PhaseGVN *phase, uint mask) {
|
||||
|
||||
//------------------------------Ideal_sign_extended_input----------------------
|
||||
// Check for useless sign-extension before a partial-word store
|
||||
// (StoreB ... (RShiftI _ (LShiftI _ valIn conIL ) conIR) )
|
||||
// If (conIL == conIR && conIR <= num_bits) this simplifies to
|
||||
// (StoreB ... (valIn) )
|
||||
Node *StoreNode::Ideal_sign_extended_input(PhaseGVN *phase, int num_bits) {
|
||||
Node *val = in(MemNode::ValueIn);
|
||||
if( val->Opcode() == Op_RShiftI ) {
|
||||
const TypeInt *t = phase->type( val->in(2) )->isa_int();
|
||||
if( t && t->is_con() && (t->get_con() <= num_bits) ) {
|
||||
Node *shl = val->in(1);
|
||||
if( shl->Opcode() == Op_LShiftI ) {
|
||||
const TypeInt *t2 = phase->type( shl->in(2) )->isa_int();
|
||||
if( t2 && t2->is_con() && (t2->get_con() == t->get_con()) ) {
|
||||
set_req_X(MemNode::ValueIn, shl->in(1), phase);
|
||||
return this;
|
||||
// (StoreB ... (RShiftI _ (LShiftI _ v conIL) conIR))
|
||||
// If (conIL == conIR && conIR <= num_rejected_bits) this simplifies to
|
||||
// (StoreB ... (v))
|
||||
// If (conIL > conIR) under some conditions, it can be simplified into
|
||||
// (StoreB ... (LShiftI _ v (conIL - conIR)))
|
||||
// This case happens when the value of the store was itself a left shift, that
|
||||
// gets merged into the inner left shift of the sign-extension. For instance,
|
||||
// if we have
|
||||
// array_of_shorts[0] = (short)(v << 2)
|
||||
// We get a structure such as:
|
||||
// (StoreB ... (RShiftI _ (LShiftI _ (LShiftI _ v 2) 16) 16))
|
||||
// that is simplified into
|
||||
// (StoreB ... (RShiftI _ (LShiftI _ v 18) 16)).
|
||||
// It is thus useful to handle cases where conIL > conIR. But this simplification
|
||||
// does not always hold. Let's see in which cases it's valid.
|
||||
//
|
||||
// Let's assume we have the following 32 bits integer v:
|
||||
// +----------------------------------+
|
||||
// | v[0..31] |
|
||||
// +----------------------------------+
|
||||
// 31 0
|
||||
// that will be stuffed in 8 bits byte after a shift left and a shift right of
|
||||
// potentially different magnitudes.
|
||||
// We denote num_rejected_bits the number of bits of the discarded part. In this
|
||||
// case, num_rejected_bits == 24.
|
||||
//
|
||||
// Statement (proved further below in case analysis):
|
||||
// Given:
|
||||
// - 0 <= conIL < BitsPerJavaInteger (no wrap in shift, enforced by maskShiftAmount)
|
||||
// - 0 <= conIR < BitsPerJavaInteger (no wrap in shift, enforced by maskShiftAmount)
|
||||
// - conIL >= conIR
|
||||
// - num_rejected_bits >= conIR
|
||||
// Then this form:
|
||||
// (RShiftI _ (LShiftI _ v conIL) conIR)
|
||||
// can be replaced with this form:
|
||||
// (LShiftI _ v (conIL-conIR))
|
||||
//
|
||||
// Note: We only have to show that the non-rejected lowest bits (8 bits for byte)
|
||||
// have to be correct, as the higher bits are rejected / truncated by the store.
|
||||
//
|
||||
// The hypotheses
|
||||
// 0 <= conIL < BitsPerJavaInteger
|
||||
// 0 <= conIR < BitsPerJavaInteger
|
||||
// are ensured by maskShiftAmount (called from ::Ideal of shift nodes). Indeed,
|
||||
// (v << 31) << 2 must be simplified into 0, not into v << 33 (which is equivalent
|
||||
// to v << 1).
|
||||
//
|
||||
//
|
||||
// If you don't like case analysis, jump after the conclusion.
|
||||
// ### Case 1 : conIL == conIR
|
||||
// ###### Case 1.1: conIL == conIR == num_rejected_bits
|
||||
// If we do the shift left then right by 24 bits, we get:
|
||||
// after: v << 24
|
||||
// +---------+------------------------+
|
||||
// | v[0..7] | 0 |
|
||||
// +---------+------------------------+
|
||||
// 31 24 23 0
|
||||
// after: (v << 24) >> 24
|
||||
// +------------------------+---------+
|
||||
// | sign bit | v[0..7] |
|
||||
// +------------------------+---------+
|
||||
// 31 8 7 0
|
||||
// The non-rejected bits (bits kept by the store, that is the 8 lower bits of the
|
||||
// result) are the same before and after, so, indeed, simplifying is correct.
|
||||
|
||||
// ###### Case 1.2: conIL == conIR < num_rejected_bits
|
||||
// If we do the shift left then right by 22 bits, we get:
|
||||
// after: v << 22
|
||||
// +---------+------------------------+
|
||||
// | v[0..9] | 0 |
|
||||
// +---------+------------------------+
|
||||
// 31 22 21 0
|
||||
// after: (v << 22) >> 22
|
||||
// +------------------------+---------+
|
||||
// | sign bit | v[0..9] |
|
||||
// +------------------------+---------+
|
||||
// 31 10 9 0
|
||||
// The non-rejected bits are the 8 lower bits of v. The bits 8 and 9 of v are still
|
||||
// present in (v << 22) >> 22 but will be dropped by the store. The simplification is
|
||||
// still correct.
|
||||
|
||||
// ###### But! Case 1.3: conIL == conIR > num_rejected_bits
|
||||
// If we do the shift left then right by 26 bits, we get:
|
||||
// after: v << 26
|
||||
// +---------+------------------------+
|
||||
// | v[0..5] | 0 |
|
||||
// +---------+------------------------+
|
||||
// 31 26 25 0
|
||||
// after: (v << 26) >> 26
|
||||
// +------------------------+---------+
|
||||
// | sign bit | v[0..5] |
|
||||
// +------------------------+---------+
|
||||
// 31 6 5 0
|
||||
// The non-rejected bits are made of
|
||||
// - 0-5 => the bits 0 to 5 of v
|
||||
// - 6-7 => the sign bit of v[0..5] (that is v[5])
|
||||
// Simplifying this as v is not correct.
|
||||
// The condition conIR <= num_rejected_bits is indeed necessary in Case 1
|
||||
//
|
||||
// ### Case 2: conIL > conIR
|
||||
// ###### Case 2.1: num_rejected_bits == conIR
|
||||
// We take conIL == 26 for this example.
|
||||
// after: v << 26
|
||||
// +---------+------------------------+
|
||||
// | v[0..5] | 0 |
|
||||
// +---------+------------------------+
|
||||
// 31 26 25 0
|
||||
// after: (v << 26) >> 24
|
||||
// +------------------+---------+-----+
|
||||
// | sign bit | v[0..5] | 0 |
|
||||
// +------------------+---------+-----+
|
||||
// 31 8 7 2 1 0
|
||||
// The non-rejected bits are the 8 lower ones of (v << conIL - conIR).
|
||||
// The bits 6 and 7 of v have been thrown away after the shift left.
|
||||
// The simplification is still correct.
|
||||
//
|
||||
// ###### Case 2.2: num_rejected_bits > conIR.
|
||||
// Let's say conIL == 26 and conIR == 22.
|
||||
// after: v << 26
|
||||
// +---------+------------------------+
|
||||
// | v[0..5] | 0 |
|
||||
// +---------+------------------------+
|
||||
// 31 26 25 0
|
||||
// after: (v << 26) >> 22
|
||||
// +------------------+---------+-----+
|
||||
// | sign bit | v[0..5] | 0 |
|
||||
// +------------------+---------+-----+
|
||||
// 31 10 9 4 3 0
|
||||
// The bits non-rejected by the store are exactly the 8 lower ones of (v << (conIL - conIR)):
|
||||
// - 0-3 => 0
|
||||
// - 4-7 => bits 0 to 3 of v
|
||||
// The simplification is still correct.
|
||||
// The bits 4 and 5 of v are still present in (v << (conIL - conIR)) but they don't
|
||||
// matter as they are not in the 8 lower bits: they will be cut out by the store.
|
||||
//
|
||||
// ###### But! Case 2.3: num_rejected_bits < conIR.
|
||||
// Let's see that this case is not as easy to simplify.
|
||||
// Let's say conIL == 28 and conIR == 26.
|
||||
// after: v << 28
|
||||
// +---------+------------------------+
|
||||
// | v[0..3] | 0 |
|
||||
// +---------+------------------------+
|
||||
// 31 28 27 0
|
||||
// after: (v << 28) >> 26
|
||||
// +------------------+---------+-----+
|
||||
// | sign bit | v[0..3] | 0 |
|
||||
// +------------------+---------+-----+
|
||||
// 31 6 5 2 1 0
|
||||
// The non-rejected bits are made of
|
||||
// - 0-1 => 0
|
||||
// - 2-5 => the bits 0 to 3 of v
|
||||
// - 6-7 => the sign bit of v[0..3] (that is v[3])
|
||||
// Simplifying this as (v << 2) is not correct.
|
||||
// The condition conIR <= num_rejected_bits is indeed necessary in Case 2.
|
||||
//
|
||||
// ### Conclusion:
|
||||
// Our hypotheses are indeed sufficient:
|
||||
// - 0 <= conIL < BitsPerJavaInteger
|
||||
// - 0 <= conIR < BitsPerJavaInteger
|
||||
// - conIL >= conIR
|
||||
// - num_rejected_bits >= conIR
|
||||
//
|
||||
// ### A rationale without case analysis:
|
||||
// After the shift left, conIL upper bits of v are discarded and conIL lower bit
|
||||
// zeroes are added. After the shift right, conIR lower bits of the previous result
|
||||
// are discarded. If conIL >= conIR, we discard only the zeroes we made up during
|
||||
// the shift left, but if conIL < conIR, then we discard also lower bits of v. But
|
||||
// the point of the simplification is to get an expression of the form
|
||||
// (v << (conIL - conIR)). This expression discard only higher bits of v, thus the
|
||||
// simplification is not correct if conIL < conIR.
|
||||
//
|
||||
// Moreover, after the shift right, the higher bit of (v << conIL) is repeated on the
|
||||
// conIR higher bits of ((v << conIL) >> conIR), it's the sign-extension. If
|
||||
// conIR > num_rejected_bits, then at least one artificial copy of this sign bit will
|
||||
// be in the window of the store. Thus ((v << conIL) >> conIR) is not equivalent to
|
||||
// (v << (conIL-conIR)) if conIR > num_rejected_bits.
|
||||
//
|
||||
// We do not treat the case conIL < conIR here since the point of this function is
|
||||
// to skip sign-extensions (that is conIL == conIR == num_rejected_bits). The need
|
||||
// of treating conIL > conIR comes from the cases where the sign-extended value is
|
||||
// also left-shift expression. Computing the sign-extension of a right-shift expression
|
||||
// doesn't yield a situation such as
|
||||
// (StoreB ... (RShiftI _ (LShiftI _ v conIL) conIR))
|
||||
// where conIL < conIR.
|
||||
Node* StoreNode::Ideal_sign_extended_input(PhaseGVN* phase, int num_rejected_bits) {
|
||||
Node* shr = in(MemNode::ValueIn);
|
||||
if (shr->Opcode() == Op_RShiftI) {
|
||||
const TypeInt* conIR = phase->type(shr->in(2))->isa_int();
|
||||
if (conIR != nullptr && conIR->is_con() && conIR->get_con() >= 0 && conIR->get_con() < BitsPerJavaInteger && conIR->get_con() <= num_rejected_bits) {
|
||||
Node* shl = shr->in(1);
|
||||
if (shl->Opcode() == Op_LShiftI) {
|
||||
const TypeInt* conIL = phase->type(shl->in(2))->isa_int();
|
||||
if (conIL != nullptr && conIL->is_con() && conIL->get_con() >= 0 && conIL->get_con() < BitsPerJavaInteger) {
|
||||
if (conIL->get_con() == conIR->get_con()) {
|
||||
set_req_X(MemNode::ValueIn, shl->in(1), phase);
|
||||
return this;
|
||||
}
|
||||
if (conIL->get_con() > conIR->get_con()) {
|
||||
Node* new_shl = phase->transform(new LShiftINode(shl->in(1), phase->intcon(conIL->get_con() - conIR->get_con())));
|
||||
set_req_X(MemNode::ValueIn, new_shl, phase);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,7 +579,7 @@ protected:
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
|
||||
Node *Ideal_masked_input (PhaseGVN *phase, uint mask);
|
||||
Node *Ideal_sign_extended_input(PhaseGVN *phase, int num_bits);
|
||||
Node* Ideal_sign_extended_input(PhaseGVN* phase, int num_rejected_bits);
|
||||
|
||||
public:
|
||||
// We must ensure that stores of object references will be visible
|
||||
|
||||
@ -970,6 +970,43 @@ static int maskShiftAmount(PhaseGVN* phase, Node* shiftNode, int nBits) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Called with
|
||||
// outer_shift = (_ << con0)
|
||||
// We are looking for the pattern:
|
||||
// outer_shift = ((X << con1) << con0)
|
||||
// we denote inner_shift the nested expression (X << con1)
|
||||
//
|
||||
// con0 and con1 are both in [0..nbits), as they are computed by maskShiftAmount.
|
||||
//
|
||||
// There are 2 cases:
|
||||
// if con0 + con1 >= nbits => 0
|
||||
// if con0 + con1 < nbits => X << (con1 + con0)
|
||||
static Node* collapse_nested_shift_left(PhaseGVN* phase, Node* outer_shift, int con0, BasicType bt) {
|
||||
assert(bt == T_LONG || bt == T_INT, "Unexpected type");
|
||||
int nbits = static_cast<int>(bits_per_java_integer(bt));
|
||||
Node* inner_shift = outer_shift->in(1);
|
||||
if (inner_shift->Opcode() != Op_LShift(bt)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int con1 = maskShiftAmount(phase, inner_shift, nbits);
|
||||
if (con1 == 0) { // Either non-const, or actually 0 (up to mask) and then delegated to Identity()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (con0 + con1 >= nbits) {
|
||||
// While it might be tempting to use
|
||||
// phase->zerocon(bt);
|
||||
// it would be incorrect: zerocon caches nodes, while Ideal is only allowed
|
||||
// to return a new node, this or nullptr, but not an old (cached) node.
|
||||
return ConNode::make(TypeInteger::zero(bt));
|
||||
}
|
||||
|
||||
// con0 + con1 < nbits ==> actual shift happens now
|
||||
Node* con0_plus_con1 = phase->intcon(con0 + con1);
|
||||
return LShiftNode::make(inner_shift->in(1), con0_plus_con1, bt);
|
||||
}
|
||||
|
||||
//------------------------------Identity---------------------------------------
|
||||
Node* LShiftINode::Identity(PhaseGVN* phase) {
|
||||
int count = 0;
|
||||
@ -983,6 +1020,9 @@ Node* LShiftINode::Identity(PhaseGVN* phase) {
|
||||
//------------------------------Ideal------------------------------------------
|
||||
// If the right input is a constant, and the left input is an add of a
|
||||
// constant, flatten the tree: (X+con1)<<con0 ==> X<<con0 + con1<<con0
|
||||
//
|
||||
// Also collapse nested left-shifts with constant rhs:
|
||||
// (X << con1) << con2 ==> X << (con1 + con2)
|
||||
Node *LShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
int con = maskShiftAmount(phase, this, BitsPerJavaInteger);
|
||||
if (con == 0) {
|
||||
@ -1095,6 +1135,13 @@ Node *LShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
phase->type(add1->in(2)) == TypeInt::make( bits_mask ) )
|
||||
return new LShiftINode( add1->in(1), in(2) );
|
||||
|
||||
// Performs:
|
||||
// (X << con1) << con2 ==> X << (con1 + con2)
|
||||
Node* doubleShift = collapse_nested_shift_left(phase, this, con, T_INT);
|
||||
if (doubleShift != nullptr) {
|
||||
return doubleShift;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1159,6 +1206,9 @@ Node* LShiftLNode::Identity(PhaseGVN* phase) {
|
||||
//------------------------------Ideal------------------------------------------
|
||||
// If the right input is a constant, and the left input is an add of a
|
||||
// constant, flatten the tree: (X+con1)<<con0 ==> X<<con0 + con1<<con0
|
||||
//
|
||||
// Also collapse nested left-shifts with constant rhs:
|
||||
// (X << con1) << con2 ==> X << (con1 + con2)
|
||||
Node *LShiftLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
int con = maskShiftAmount(phase, this, BitsPerJavaLong);
|
||||
if (con == 0) {
|
||||
@ -1271,6 +1321,13 @@ Node *LShiftLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
phase->type(add1->in(2)) == TypeLong::make( bits_mask ) )
|
||||
return new LShiftLNode( add1->in(1), in(2) );
|
||||
|
||||
// Performs:
|
||||
// (X << con1) << con2 ==> X << (con1 + con2)
|
||||
Node* doubleShift = collapse_nested_shift_left(phase, this, con, T_LONG);
|
||||
if (doubleShift != nullptr) {
|
||||
return doubleShift;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -797,6 +797,14 @@ inline jlong min_signed_integer(BasicType bt) {
|
||||
return min_jlong;
|
||||
}
|
||||
|
||||
inline uint bits_per_java_integer(BasicType bt) {
|
||||
if (bt == T_INT) {
|
||||
return BitsPerJavaInteger;
|
||||
}
|
||||
assert(bt == T_LONG, "int or long only");
|
||||
return BitsPerJavaLong;
|
||||
}
|
||||
|
||||
// Auxiliary math routines
|
||||
// least common multiple
|
||||
extern size_t lcm(size_t a, size_t b);
|
||||
|
||||
@ -25,6 +25,8 @@ package compiler.c2.irTests;
|
||||
import jdk.test.lib.Asserts;
|
||||
import compiler.lib.ir_framework.*;
|
||||
|
||||
import static compiler.lib.generators.Generators.G;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8297384 8303238
|
||||
@ -33,11 +35,26 @@ import compiler.lib.ir_framework.*;
|
||||
* @run driver compiler.c2.irTests.LShiftINodeIdealizationTests
|
||||
*/
|
||||
public class LShiftINodeIdealizationTests {
|
||||
private static final int CON0 = G.ints().next();
|
||||
private static final int CON1 = G.ints().next();
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
}
|
||||
|
||||
@Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8" })
|
||||
@Run(test = {"test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8",
|
||||
"testDoubleShift1",
|
||||
"testDoubleShift2",
|
||||
"testDoubleShift3",
|
||||
"testDoubleShift4",
|
||||
"testDoubleShift5",
|
||||
"testDoubleShift6",
|
||||
"testDoubleShift7",
|
||||
"testDoubleShift8",
|
||||
"testDoubleShift9",
|
||||
"testDoubleShiftSliceAndStore",
|
||||
"testRandom",
|
||||
})
|
||||
public void runMethod() {
|
||||
int a = RunInfo.getRandom().nextInt();
|
||||
int b = RunInfo.getRandom().nextInt();
|
||||
@ -66,6 +83,8 @@ public class LShiftINodeIdealizationTests {
|
||||
Asserts.assertEQ((a >>> 8) << 4, test6(a));
|
||||
Asserts.assertEQ(((a >> 4) & 0xFF) << 8, test7(a));
|
||||
Asserts.assertEQ(((a >>> 4) & 0xFF) << 8, test8(a));
|
||||
|
||||
assertDoubleShiftResult(a);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -131,4 +150,121 @@ public class LShiftINodeIdealizationTests {
|
||||
public int test8(int x) {
|
||||
return ((x >>> 4) & 0xFF) << 8;
|
||||
}
|
||||
|
||||
@DontCompile
|
||||
public void assertDoubleShiftResult(int a) {
|
||||
Asserts.assertEQ((a << 2) << 3, testDoubleShift1(a));
|
||||
Asserts.assertEQ(a << 5, testDoubleShift1(a));
|
||||
|
||||
Asserts.assertEQ(((a << 2) << 3) << 1, testDoubleShift2(a));
|
||||
Asserts.assertEQ(a << 6, testDoubleShift2(a));
|
||||
|
||||
Asserts.assertEQ((a << 31) << 1, testDoubleShift3(a));
|
||||
Asserts.assertEQ(0, testDoubleShift3(a));
|
||||
|
||||
Asserts.assertEQ((a << 1) << 31, testDoubleShift4(a));
|
||||
Asserts.assertEQ(0, testDoubleShift4(a));
|
||||
|
||||
Asserts.assertEQ(((a << 30) << 1) << 1, testDoubleShift5(a));
|
||||
Asserts.assertEQ(0, testDoubleShift5(a));
|
||||
|
||||
Asserts.assertEQ((a * 4) << 3, testDoubleShift6(a));
|
||||
Asserts.assertEQ(a << 5, testDoubleShift6(a));
|
||||
|
||||
Asserts.assertEQ((a << 3) * 4, testDoubleShift7(a));
|
||||
Asserts.assertEQ(a << 5, testDoubleShift7(a));
|
||||
|
||||
Asserts.assertEQ(a << 33, testDoubleShift8(a));
|
||||
Asserts.assertEQ(a << 1, testDoubleShift8(a));
|
||||
|
||||
Asserts.assertEQ((a << 30) << 3, testDoubleShift9(a));
|
||||
Asserts.assertEQ(0, testDoubleShift9(a));
|
||||
|
||||
short[] arr = new short[1];
|
||||
arr[0] = (short)a;
|
||||
Asserts.assertEQ((short)(a << 3), testDoubleShiftSliceAndStore(arr)[0]);
|
||||
|
||||
Asserts.assertEQ((a << CON0) << CON1, testRandom(a));
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks (x << 2) << 3 => x << 5
|
||||
public int testDoubleShift1(int x) {
|
||||
return (x << 2) << 3;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks ((x << 2) << 3) << 1 => x << 6
|
||||
public int testDoubleShift2(int x) {
|
||||
return ((x << 2) << 3) << 1;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.LSHIFT})
|
||||
// Checks (x << 31) << 1 => 0
|
||||
public int testDoubleShift3(int x) {
|
||||
return (x << 31) << 1;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.LSHIFT})
|
||||
// Checks (x << 1) << 31 => 0
|
||||
public int testDoubleShift4(int x) {
|
||||
return (x << 1) << 31;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.LSHIFT})
|
||||
// Checks ((x << 30) << 1) << 1 => 0
|
||||
public int testDoubleShift5(int x) {
|
||||
return ((x << 30) << 1) << 1;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MUL})
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks (x * 4) << 3 => x << 5
|
||||
public int testDoubleShift6(int x) {
|
||||
return (x * 4) << 3;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MUL})
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks (x << 3) * 4 => x << 5
|
||||
public int testDoubleShift7(int x) {
|
||||
return (x << 3) * 4;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks x << 33 => x << 1
|
||||
public int testDoubleShift8(int x) {
|
||||
return x << 33;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.LSHIFT})
|
||||
// Checks (x << 30) << 3 => 0
|
||||
public int testDoubleShift9(int x) {
|
||||
return (x << 30) << 3;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MUL, IRNode.RSHIFT})
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks (short) (a[0] << 3) => (((a[0] << 3) << 16) >> 16) => ((a[0] << 19) >> 16) => (a[0] << 3)
|
||||
public short[] testDoubleShiftSliceAndStore(short[] a) {
|
||||
short[] res = new short[1];
|
||||
res[0] = (short) (a[0] << 3);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LSHIFT, "<= 1"})
|
||||
public int testRandom(int x) {
|
||||
return (x << CON0) << CON1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ package compiler.c2.irTests;
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import compiler.lib.ir_framework.*;
|
||||
import static compiler.lib.generators.Generators.G;
|
||||
|
||||
/*
|
||||
* @test
|
||||
@ -33,11 +34,25 @@ import compiler.lib.ir_framework.*;
|
||||
* @run driver compiler.c2.irTests.LShiftLNodeIdealizationTests
|
||||
*/
|
||||
public class LShiftLNodeIdealizationTests {
|
||||
private static final long CON0 = G.longs().next();
|
||||
private static final long CON1 = G.longs().next();
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
}
|
||||
|
||||
@Run(test = { "test3", "test4", "test5", "test6", "test7", "test8" })
|
||||
@Run(test = {"test3", "test4", "test5", "test6", "test7", "test8",
|
||||
"testDoubleShift1",
|
||||
"testDoubleShift2",
|
||||
"testDoubleShift3",
|
||||
"testDoubleShift4",
|
||||
"testDoubleShift5",
|
||||
"testDoubleShift6",
|
||||
"testDoubleShift7",
|
||||
"testDoubleShift8",
|
||||
"testDoubleShift9",
|
||||
"testRandom",
|
||||
})
|
||||
public void runMethod() {
|
||||
long a = RunInfo.getRandom().nextLong();
|
||||
long b = RunInfo.getRandom().nextLong();
|
||||
@ -64,6 +79,8 @@ public class LShiftLNodeIdealizationTests {
|
||||
Asserts.assertEQ((a >>> 8L) << 4L, test6(a));
|
||||
Asserts.assertEQ(((a >> 4L) & 0xFFL) << 8L, test7(a));
|
||||
Asserts.assertEQ(((a >>> 4L) & 0xFFL) << 8L, test8(a));
|
||||
|
||||
assertDoubleShiftResult(a);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -113,4 +130,107 @@ public class LShiftLNodeIdealizationTests {
|
||||
public long test8(long x) {
|
||||
return ((x >>> 4L) & 0xFFL) << 8L;
|
||||
}
|
||||
|
||||
@DontCompile
|
||||
public void assertDoubleShiftResult(long a) {
|
||||
Asserts.assertEQ((a << 2L) << 3L, testDoubleShift1(a));
|
||||
Asserts.assertEQ(a << 5L, testDoubleShift1(a));
|
||||
|
||||
Asserts.assertEQ(((a << 2L) << 3L) << 1L, testDoubleShift2(a));
|
||||
Asserts.assertEQ(a << 6L, testDoubleShift2(a));
|
||||
|
||||
Asserts.assertEQ((a << 63L) << 1L, testDoubleShift3(a));
|
||||
Asserts.assertEQ(0L, testDoubleShift3(a));
|
||||
|
||||
Asserts.assertEQ((a << 1L) << 63L, testDoubleShift4(a));
|
||||
Asserts.assertEQ(0L, testDoubleShift4(a));
|
||||
|
||||
Asserts.assertEQ(((a << 62L) << 1L) << 1L, testDoubleShift5(a));
|
||||
Asserts.assertEQ(0L, testDoubleShift5(a));
|
||||
|
||||
Asserts.assertEQ((a * 4L) << 3L, testDoubleShift6(a));
|
||||
Asserts.assertEQ(a << 5L, testDoubleShift6(a));
|
||||
|
||||
Asserts.assertEQ((a << 3L) * 4L, testDoubleShift7(a));
|
||||
Asserts.assertEQ(a << 5L, testDoubleShift7(a));
|
||||
|
||||
Asserts.assertEQ(a << 65L, testDoubleShift8(a));
|
||||
Asserts.assertEQ(a << 1L, testDoubleShift8(a));
|
||||
|
||||
Asserts.assertEQ((a << 62L) << 3L, testDoubleShift9(a));
|
||||
Asserts.assertEQ(0L, testDoubleShift9(a));
|
||||
|
||||
Asserts.assertEQ((a << CON0) << CON1, testRandom(a));
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks (x << 2) << 3 => x << 5
|
||||
public long testDoubleShift1(long x) {
|
||||
return (x << 2L) << 3L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks ((x << 2) << 3) << 1 => x << 6
|
||||
public long testDoubleShift2(long x) {
|
||||
return ((x << 2L) << 3L) << 1L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.LSHIFT})
|
||||
// Checks (x << 63) << 1 => 0
|
||||
public long testDoubleShift3(long x) {
|
||||
return (x << 63L) << 1L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.LSHIFT})
|
||||
// Checks (x << 1) << 63 => 0
|
||||
public long testDoubleShift4(long x) {
|
||||
return (x << 1L) << 63L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.LSHIFT})
|
||||
// Checks ((x << 62) << 1) << 1 => 0
|
||||
public long testDoubleShift5(long x) {
|
||||
return ((x << 62L) << 1L) << 1L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MUL})
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks (x * 4) << 3 => x << 5
|
||||
public long testDoubleShift6(long x) {
|
||||
return (x * 4L) << 3L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.MUL})
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks (x << 3) * 4 => x << 5
|
||||
public long testDoubleShift7(long x) {
|
||||
return (x << 3L) * 4L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LSHIFT, "1"})
|
||||
// Checks x << 65 => x << 1
|
||||
public long testDoubleShift8(long x) {
|
||||
return x << 65L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(failOn = {IRNode.LSHIFT})
|
||||
// Checks (x << 62) << 3 => 0
|
||||
public long testDoubleShift9(long x) {
|
||||
return (x << 62L) << 3L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.LSHIFT, "<= 1"})
|
||||
public long testRandom(long x) {
|
||||
return (x << CON0) << CON1;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user