Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 69 additions & 40 deletions src/hotspot/share/opto/mulnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,36 +594,80 @@ const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) {
return TypeLong::LONG;
}

template<typename IntegerType>
static const IntegerType* and_value(const IntegerType* r0, const IntegerType* r1) {
typedef typename IntegerType::NativeType NativeType;
static_assert(std::is_signed<NativeType>::value, "Native type of IntegerType must be signed!");

int widen = MAX2(r0->_widen, r1->_widen);

// If both types are constants, we can calculate a constant result.
if (r0->is_con() && r1->is_con()) {
return IntegerType::make(r0->get_con() & r1->get_con());
}

// If both ranges are positive, the result will range from 0 up to the hi value of the smaller range. The minimum
// of the two constrains the upper bound because any higher value in the other range will see all zeroes, so it will be masked out.
if (r0->_lo >= 0 && r1->_lo >= 0) {
return IntegerType::make(0, MIN2(r0->_hi, r1->_hi), widen);
}

// If only one range is positive, the result will range from 0 up to that range's maximum value.
// For the operation 'x & C' where C is a positive constant, the result will be in the range [0..C]. With that observation,
// we can say that for any integer c such that 0 <= c <= C will also be in the range [0..C]. Therefore, 'x & [c..C]'
// where c >= 0 will be in the range [0..C].
if (r0->_lo >= 0) {
return IntegerType::make(0, r0->_hi, widen);
}

if (r1->_lo >= 0) {
return IntegerType::make(0, r1->_hi, widen);
}

// At this point, all positive ranges will have already been handled, so the only remaining cases will be negative ranges
// and constants.

assert(r0->_lo < 0 && r1->_lo < 0, "positive ranges should already be handled!");

// As two's complement means that both numbers will start with leading 1s, the lower bound of both ranges will contain
// the common leading 1s of both minimum values. In order to count them with count_leading_zeros, the bits are inverted.
NativeType sel_val = ~MIN2(r0->_lo, r1->_lo);

NativeType min;
if (sel_val == 0) {
// Since count_leading_zeros is undefined at 0, we short-circuit the condition where both ranges have a minimum of -1.
min = -1;
} else {
// To get the number of bits to shift, we count the leading 0-bits and then subtract one, as the sign bit is already set.
int shift_bits = count_leading_zeros(sel_val) - 1;
min = std::numeric_limits<NativeType>::min() >> shift_bits;
}

NativeType max;
if (r0->_hi < 0 && r1->_hi < 0) {
// If both ranges are negative, then the same optimization as both positive ranges will apply, and the smaller hi
// value will mask off any bits set by higher values.
max = MIN2(r0->_hi, r1->_hi);
} else {
// In the case of ranges that cross zero, negative values can cause the higher order bits to be set, so the maximum
// positive value can be as high as the larger hi value.
max = MAX2(r0->_hi, r1->_hi);
}

return IntegerType::make(min, max, widen);
}

//=============================================================================
//------------------------------mul_ring---------------------------------------
// Supplied function returns the product of the inputs IN THE CURRENT RING.
// For the logical operations the ring's MUL is really a logical AND function.
// This also type-checks the inputs for sanity. Guaranteed never to
// be passed a TOP or BOTTOM type, these are filtered out by pre-check.
const Type *AndINode::mul_ring( const Type *t0, const Type *t1 ) const {
const TypeInt *r0 = t0->is_int(); // Handy access
const TypeInt *r1 = t1->is_int();
int widen = MAX2(r0->_widen,r1->_widen);

// If either input is a constant, might be able to trim cases
if( !r0->is_con() && !r1->is_con() )
return TypeInt::INT; // No constants to be had

// Both constants? Return bits
if( r0->is_con() && r1->is_con() )
return TypeInt::make( r0->get_con() & r1->get_con() );

if( r0->is_con() && r0->get_con() > 0 )
return TypeInt::make(0, r0->get_con(), widen);
const TypeInt* r0 = t0->is_int();
const TypeInt* r1 = t1->is_int();

if( r1->is_con() && r1->get_con() > 0 )
return TypeInt::make(0, r1->get_con(), widen);

if( r0 == TypeInt::BOOL || r1 == TypeInt::BOOL ) {
return TypeInt::BOOL;
}

return TypeInt::INT; // No constants to be had
return and_value<TypeInt>(r0, r1);
}

const Type* AndINode::Value(PhaseGVN* phase) const {
Expand Down Expand Up @@ -751,25 +795,10 @@ Node *AndINode::Ideal(PhaseGVN *phase, bool can_reshape) {
// This also type-checks the inputs for sanity. Guaranteed never to
// be passed a TOP or BOTTOM type, these are filtered out by pre-check.
const Type *AndLNode::mul_ring( const Type *t0, const Type *t1 ) const {
const TypeLong *r0 = t0->is_long(); // Handy access
const TypeLong *r1 = t1->is_long();
int widen = MAX2(r0->_widen,r1->_widen);

// If either input is a constant, might be able to trim cases
if( !r0->is_con() && !r1->is_con() )
return TypeLong::LONG; // No constants to be had

// Both constants? Return bits
if( r0->is_con() && r1->is_con() )
return TypeLong::make( r0->get_con() & r1->get_con() );

if( r0->is_con() && r0->get_con() > 0 )
return TypeLong::make(CONST64(0), r0->get_con(), widen);

if( r1->is_con() && r1->get_con() > 0 )
return TypeLong::make(CONST64(0), r1->get_con(), widen);
const TypeLong* r0 = t0->is_long();
const TypeLong* r1 = t1->is_long();

return TypeLong::LONG; // No constants to be had
return and_value<TypeLong>(r0, r1);
}

const Type* AndLNode::Value(PhaseGVN* phase) const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@

/*
* @test
* @bug 8297384
* @bug 8297384 8335444
* @key randomness
* @summary Test that Ideal transformations of AndINode* are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.AndINodeIdealizationTests
Expand All @@ -38,7 +39,7 @@ public static void main(String[] args) {
TestFramework.run();
}

@Run(test = { "test1", "test2" })
@Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9", "test10" })
public void runMethod() {
int a = RunInfo.getRandom().nextInt();
int b = RunInfo.getRandom().nextInt();
Expand All @@ -47,7 +48,12 @@ public void runMethod() {
int max = Integer.MAX_VALUE;

assertResult(0, 0);
assertResult(10, 20);
assertResult(10, -20);
assertResult(-10, 20);
assertResult(-10, -20);
assertResult(a, b);
assertResult(b, a);
assertResult(min, min);
assertResult(max, max);
}
Expand All @@ -56,6 +62,14 @@ public void runMethod() {
public void assertResult(int a, int b) {
Asserts.assertEQ((0 - a) & 1, test1(a));
Asserts.assertEQ((~a) & (~b), test2(a, b));
Asserts.assertEQ((a & 15) >= 0, test3(a, b));
Asserts.assertEQ((a & 15) > 15, test4(a, b));
Asserts.assertEQ((a & (b >>> 1)) >= 0, test5(a, b));
Asserts.assertEQ((a & (b >>> 30)) > 3, test6(a, b));
Asserts.assertEQ(((byte)a & -8) >= -128, test7(a, b));
Asserts.assertEQ(((byte)a & -8) <= 127, test8(a, b));
Asserts.assertEQ(((a & 255) & (char)b) > 255, test9(a, b));
Asserts.assertEQ((((a & 1) - 3) & ((b & 2) - 10)) > -8, test10(a, b));
}

@Test
Expand All @@ -74,4 +88,60 @@ public int test1(int x) {
public int test2(int a, int b) {
return (~a) & (~b);
}

@Test
@IR(failOn = { IRNode.AND })
// Checks a & 15 => [0, 15]
public boolean test3(int a, int b) {
return (a & 15) >= 0;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks a & 15 => [0, 15]
public boolean test4(int a, int b) {
return (a & 15) > 15;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks a & [0, int_max] => [0, int_max]
public boolean test5(int a, int b) {
return (a & (b >>> 1)) >= 0;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks a & [0, 3] => [0, 3]
public boolean test6(int a, int b) {
return (a & (b >>> 30)) > 3;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks [-128, 127] & -8 => [-128, 127]
public boolean test7(int a, int b) {
return ((byte)a & -8) >= -128;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks [-128, 127] & -8 => [-128, 127]
public boolean test8(int a, int b) {
return ((byte)a & -8) <= 127;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks that [0, 255] & [0, 65535] => [0, 255]
public boolean test9(int a, int b) {
return ((a & 255) & (char)b) > 255;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks that [-3, -2] & [-10, -8] => [-16, -8]
public boolean test10(int a, int b) {
return (((a & 1) - 3) & ((b & 2) - 10)) > -8;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@

/*
* @test
* @bug 8322589
* @bug 8322589 8335444
* @key randomness
* @summary Test that Ideal transformations of AndLNode* are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.AndLNodeIdealizationTests
Expand All @@ -38,7 +39,7 @@ public static void main(String[] args) {
TestFramework.run();
}

@Run(test = { "test1" })
@Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9" })
public void runMethod() {
long a = RunInfo.getRandom().nextLong();
long b = RunInfo.getRandom().nextLong();
Expand All @@ -47,14 +48,27 @@ public void runMethod() {
long max = Long.MAX_VALUE;

assertResult(0, 0);
assertResult(10, 20);
assertResult(10, -20);
assertResult(-10, 20);
assertResult(-10, -20);
assertResult(a, b);
assertResult(b, a);
assertResult(min, min);
assertResult(max, max);
}

@DontCompile
public void assertResult(long a, long b) {
Asserts.assertEQ((~a) & (~b), test1(a, b));
Asserts.assertEQ((a & 15) >= 0, test2(a, b));
Asserts.assertEQ((a & 15) > 15, test3(a, b));
Asserts.assertEQ((a & (b >>> 1)) >= 0, test4(a, b));
Asserts.assertEQ((a & (b >>> 62)) > 3, test5(a, b));
Asserts.assertEQ(((byte)a & -8L) >= -128, test6(a, b));
Asserts.assertEQ(((byte)a & -8L) <= 127, test7(a, b));
Asserts.assertEQ(((a & 255) & (char)b) > 255, test8(a, b));
Asserts.assertEQ((((a & 1) - 3) & ((b & 2) - 10)) > -8, test9(a, b));
}

@Test
Expand All @@ -65,4 +79,60 @@ public void assertResult(long a, long b) {
public long test1(long a, long b) {
return (~a) & (~b);
}

@Test
@IR(failOn = { IRNode.AND })
// Checks a & 15 => [0, 15]
public boolean test2(long a, long b) {
return (a & 15) >= 0;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks a & 15 => [0, 15]
public boolean test3(long a, long b) {
return (a & 15) > 15;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks a & [0, long_max] => [0, long_max]
public boolean test4(long a, long b) {
return (a & (b >>> 1)) >= 0;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks a & [0, 3] => [0, 3]
public boolean test5(long a, long b) {
return (a & (b >>> 62)) > 3;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks [-128, 127] & -8 => [-128, 127]
public boolean test6(long a, long b) {
return ((byte)a & -8L) >= -128;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks [-128, 127] & -8 => [-128, 127]
public boolean test7(long a, long b) {
return ((byte)a & -8L) <= 127;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks that [0, 255] & [0, 65535] => [0, 255]
public boolean test8(long a, long b) {
return ((a & 255) & (char)b) > 255;
}

@Test
@IR(failOn = { IRNode.AND })
// Checks that [-3, -2] & [-10, -8] => [-16, -8]
public boolean test9(long a, long b) {
return (((a & 1) - 3) & ((b & 2) - 10)) > -8;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2022, 2023, Arm Limited. All rights reserved.
* 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
Expand Down Expand Up @@ -71,12 +72,9 @@ public boolean[] vectorNot() {
}

@Test
@IR(applyIfCPUFeature = {"asimd", "true"},
counts = {IRNode.AND_VB, ">0"})
@IR(applyIfCPUFeatureAnd = {"avx512f", "false", "sse2", "true"},
@IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"},
phase = CompilePhase.BEFORE_MACRO_EXPANSION,
counts = {IRNode.AND_VB, ">0"})
@IR(applyIfCPUFeature = {"avx512f", "true"},
counts = {IRNode.MACRO_LOGIC_V, ">0"})
public boolean[] vectorAnd() {
boolean[] res = new boolean[SIZE];
for (int i = 0; i < SIZE; i++) {
Expand Down
Loading