8335444: Generalize implementation of AndNode mul_ring
Reviewed-by: chagedorn, qamai, dfenacci
This commit is contained in:
parent
33525226b9
commit
92431049fd
@ -594,6 +594,69 @@ 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.
|
||||
@ -601,29 +664,10 @@ const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) {
|
||||
// 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);
|
||||
const TypeInt* r0 = t0->is_int();
|
||||
const TypeInt* r1 = t1->is_int();
|
||||
|
||||
// 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);
|
||||
|
||||
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 {
|
||||
@ -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);
|
||||
const TypeLong* r0 = t0->is_long();
|
||||
const TypeLong* r1 = t1->is_long();
|
||||
|
||||
// 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);
|
||||
|
||||
return TypeLong::LONG; // No constants to be had
|
||||
return and_value<TypeLong>(r0, r1);
|
||||
}
|
||||
|
||||
const Type* AndLNode::Value(PhaseGVN* phase) const {
|
||||
|
@ -27,7 +27,8 @@ import compiler.lib.ir_framework.*;
|
||||
|
||||
/*
|
||||
* @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
|
||||
@ -38,7 +39,7 @@ public class AndINodeIdealizationTests {
|
||||
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();
|
||||
@ -47,7 +48,12 @@ public class AndINodeIdealizationTests {
|
||||
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);
|
||||
}
|
||||
@ -56,6 +62,14 @@ public class AndINodeIdealizationTests {
|
||||
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
|
||||
@ -74,4 +88,60 @@ public class AndINodeIdealizationTests {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ import compiler.lib.ir_framework.*;
|
||||
|
||||
/*
|
||||
* @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
|
||||
@ -38,7 +39,7 @@ public class AndLNodeIdealizationTests {
|
||||
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();
|
||||
@ -47,7 +48,12 @@ public class AndLNodeIdealizationTests {
|
||||
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);
|
||||
}
|
||||
@ -55,6 +61,14 @@ public class AndLNodeIdealizationTests {
|
||||
@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
|
||||
@ -65,4 +79,60 @@ public class AndLNodeIdealizationTests {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -71,12 +72,9 @@ public class BasicBooleanOpTest extends VectorizationTestRunner {
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(applyIfCPUFeature = {"asimd", "true"},
|
||||
@IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"},
|
||||
phase = CompilePhase.BEFORE_MACRO_EXPANSION,
|
||||
counts = {IRNode.AND_VB, ">0"})
|
||||
@IR(applyIfCPUFeatureAnd = {"avx512f", "false", "sse2", "true"},
|
||||
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++) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -38,6 +38,9 @@ public abstract class TypeVectorOperations {
|
||||
@Param({"512", /* "1024", */ "2048"})
|
||||
public int COUNT;
|
||||
|
||||
private boolean[] boolsA;
|
||||
private boolean[] boolsB;
|
||||
private boolean[] resZ;
|
||||
private byte[] bytesA;
|
||||
private byte[] bytesB;
|
||||
private byte[] resB;
|
||||
@ -58,6 +61,9 @@ public abstract class TypeVectorOperations {
|
||||
|
||||
@Setup
|
||||
public void init() {
|
||||
boolsA = new boolean[COUNT];
|
||||
boolsB = new boolean[COUNT];
|
||||
resZ = new boolean[COUNT];
|
||||
bytesA = new byte[COUNT];
|
||||
bytesB = new byte[COUNT];
|
||||
resB = new byte[COUNT];
|
||||
@ -73,6 +79,8 @@ public abstract class TypeVectorOperations {
|
||||
resF = new float[COUNT];
|
||||
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
boolsA[i] = r.nextBoolean();
|
||||
boolsB[i] = r.nextBoolean();
|
||||
shorts[i] = (short) r.nextInt(Short.MAX_VALUE + 1);
|
||||
ints[i] = r.nextInt();
|
||||
longs[i] = r.nextLong();
|
||||
@ -366,6 +374,13 @@ public abstract class TypeVectorOperations {
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void andZ() {
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
resZ[i] = boolsA[i] & boolsB[i];
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@Fork(jvmArgsPrepend = {"-XX:+UseCMoveUnconditionally", "-XX:+UseVectorCmov"})
|
||||
public void cmoveD() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user