Skip to content

Commit

Permalink
8276673: Optimize abs operations in C2 compiler
Browse files Browse the repository at this point in the history
The patch aims to help optimize Math.abs() mainly from these three parts:
1) Remove redundant instructions for abs with constant values
2) Remove redundant instructions for abs with char type
3) Convert some common abs operations to ideal forms

1. Remove redundant instructions for abs with constant values

If we can decide the value of the input node for function Math.abs()
at compile-time, we can substitute the Abs node with the absolute
value of the constant and don't have to calculate it at runtime.

For example,
  int[] a
  for (int i = 0; i < SIZE; i++) {
    a[i] = Math.abs(-38);
  }

Before the patch, the generated code for the testcase above is:
...
  mov   w10, #0xffffffda
  cmp   w10, wzr
  cneg  w17, w10, lt
  dup   v16.8h, w17
...
After the patch, the generated code for the testcase above is :
...
  movi  v16.4s, #0x26
...

2. Remove redundant instructions for abs with char type

In Java semantics, as the char type is always non-negative, we
could actually remove the absI node in the C2 middle end.

As for vectorization part, in current SLP, the vectorization of
Math.abs() with char type is intentionally disabled after
JDK-8261022 because it generates incorrect result before. After
removing the AbsI node in the middle end, Math.abs(char) can be
vectorized naturally.

For example,

  char[] a;
  char[] b;
  for (int i = 0; i < SIZE; i++) {
    b[i] = (char) Math.abs(a[i]);
  }

Before the patch, the generated assembly code for the testcase
above is:

B15:
  add   x13, x21, w20, sxtw openjdk#1
  ldrh  w11, [x13, openjdk#16]
  cmp   w11, wzr
  cneg  w10, w11, lt
  strh  w10, [x13, openjdk#16]
  ldrh  w10, [x13, openjdk#18]
  cmp   w10, wzr
  cneg  w10, w10, lt
  strh  w10, [x13, openjdk#18]
  ...
  add   w20, w20, #0x1
  cmp   w20, w17
  b.lt  B15

After the patch, the generated assembly code is:
B15:
  sbfiz x18, x19, openjdk#1, openjdk#32
  add   x0, x14, x18
  ldr   q16, [x0, openjdk#16]
  add   x18, x21, x18
  str   q16, [x18, openjdk#16]
  ldr   q16, [x0, openjdk#32]
  str   q16, [x18, openjdk#32]
  ...
  add   w19, w19, #0x40
  cmp   w19, w17
  b.lt  B15

3. Convert some common abs operations to ideal forms

The patch overrides some virtual support functions for AbsNode
so that optimization of gvn can work on it. Here are the optimizable
forms:

a) abs(0 - x) => abs(x)

Before the patch:
  ...
  ldr   w13, [x13, openjdk#16]
  neg   w13, w13
  cmp   w13, wzr
  cneg  w14, w13, lt
  ...
After the patch:
  ...
  ldr   w13, [x13, openjdk#16]
  cmp   w13, wzr
  cneg  w13, w13, lt
  ...

b) abs(abs(x))  => abs(x)

Before the patch:
  ...
  ldr   w12, [x12, openjdk#16]
  cmp   w12, wzr
  cneg  w12, w12, lt
  cmp   w12, wzr
  cneg  w12, w12, lt
  ...
After the patch:
  ...
  ldr   w13, [x13, openjdk#16]
  cmp   w13, wzr
  cneg  w13, w13, lt
  ...

Change-Id: I5434c01a225796caaf07ffbb19983f4fe2e206bd
  • Loading branch information
Faye Gao authored and Fei Gao committed Dec 8, 2021
1 parent d7ad546 commit bd0eb29
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 7 deletions.
66 changes: 66 additions & 0 deletions src/hotspot/share/opto/subnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,72 @@ bool BoolNode::is_counted_loop_exit_test() {
return false;
}

//=============================================================================
//------------------------------Value------------------------------------------
const Type* AbsNode::Value(PhaseGVN* phase) const {
const Type* t1 = phase->type(in(1));
if (t1 == Type::TOP) return Type::TOP;

switch (t1->base()) {
case Type::Int: {
const TypeInt* ti = t1->is_int();
if (ti->is_con()) {
// Special case for min_jint: Math.abs(min_jint) = min_jint.
// Do not use C++ abs() for min_jint to avoid undefined behavior.
return (ti->is_con(min_jint)) ? TypeInt::MIN : TypeInt::make(abs(ti->get_con()));
}
break;
}
case Type::Long: {
const TypeLong* tl = t1->is_long();
if (tl->is_con()) {
// Special case for min_jlong: Math.abs(min_jlong) = min_jlong.
// Do not use C++ abs() for min_jlong to avoid undefined behavior.
return (tl->is_con(min_jlong)) ? TypeLong::MIN : TypeLong::make(abs(tl->get_con()));
}
break;
}
case Type::FloatCon:
return TypeF::make(abs(t1->getf()));
case Type::DoubleCon:
return TypeD::make(abs(t1->getd()));
default:
break;
}

return bottom_type();
}

//------------------------------Identity----------------------------------------
Node* AbsNode::Identity(PhaseGVN* phase) {
Node* in1 = in(1);
// No need to do abs for non-negative values
if (phase->type(in1)->higher_equal(TypeInt::POS) ||
phase->type(in1)->higher_equal(TypeLong::POS)) {
return in1;
}
// Convert "abs(abs(x))" into "abs(x)"
if (in1->Opcode() == Opcode()) {
return in1;
}
return this;
}

//------------------------------Ideal------------------------------------------
Node* AbsNode::Ideal(PhaseGVN* phase, bool can_reshape) {
Node* in1 = in(1);
// Convert "abs(0-x)" into "abs(x)"
if (in1->is_Sub() && phase->type(in1->in(1))->is_zero_type()) {
set_req(1, in1->in(2));
PhaseIterGVN* igvn = phase->is_IterGVN();
if (igvn) {
igvn->_worklist.push(in1);
}
return this;
}
return NULL;
}

//=============================================================================
//------------------------------Value------------------------------------------
// Compute sqrt
Expand Down
5 changes: 4 additions & 1 deletion src/hotspot/share/opto/subnode.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2021, 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 @@ -379,6 +379,9 @@ class BoolNode : public Node {
class AbsNode : public Node {
public:
AbsNode( Node *value ) : Node(0,value) {}
virtual Node* Identity(PhaseGVN* phase);
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
virtual const Type* Value(PhaseGVN* phase) const;
};

//------------------------------AbsINode---------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/share/opto/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ class TypeInt : public TypeInteger {

// Check for single integer
int is_con() const { return _lo==_hi; }
bool is_con(int i) const { return is_con() && _lo == i; }
bool is_con(jint i) const { return is_con() && _lo == i; }
jint get_con() const { assert( is_con(), "" ); return _lo; }

virtual bool is_finite() const; // Has a finite value
Expand Down Expand Up @@ -657,7 +657,7 @@ class TypeLong : public TypeInteger {

// Check for single integer
int is_con() const { return _lo==_hi; }
bool is_con(int i) const { return is_con() && _lo == i; }
bool is_con(jlong i) const { return is_con() && _lo == i; }
jlong get_con() const { assert( is_con(), "" ); return _lo; }

// Check for positive 32-bit value.
Expand Down
163 changes: 159 additions & 4 deletions test/hotspot/jtreg/compiler/c2/TestAbs.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,159 @@
* questions.
*/

package compiler.c2;

/*
* @test
* @bug 8248445
* @summary Use of AbsI / AbsL nodes should be limited to supported platforms
* @bug 8248445 8276673
* @summary Abs nodes detection and optimization in C2
* @library /test/lib
* @requires vm.debug == true
*
* @run main/othervm -XX:-TieredCompilation -Xbatch -XX:CompileOnly=java.lang.Math::abs compiler.c2.TestAbs
* @run main/othervm -XX:-TieredCompilation compiler.c2.TestAbs
*/

package compiler.c2;
import jdk.test.lib.Asserts;
import jdk.internal.math.DoubleConsts;
import jdk.internal.math.FloatConsts;

public class TestAbs {
private static int SIZE = 500;

public static char [] cspecial = {
0, 42, 128, 256, 1024, 4096, 65535
};

public static int [] ispecial = {
0, Integer.MAX_VALUE, Integer.MIN_VALUE, -42, 42, -1, 1
};

public static long [] lspecial = {
0, Long.MAX_VALUE, Long.MIN_VALUE, -42, 42, -1, 1
};

public static float [] fspecial = {
0.0f,
-0.0f,
Float.MAX_VALUE,
Float.MIN_VALUE,
-Float.MAX_VALUE,
-Float.MIN_VALUE,
Float.NaN,
Float.POSITIVE_INFINITY,
Float.NEGATIVE_INFINITY,
Integer.MAX_VALUE,
Integer.MIN_VALUE,
Long.MAX_VALUE,
Long.MIN_VALUE,
-1.0f,
1.0f,
-42.0f,
42.0f,
Float.intBitsToFloat((1 << FloatConsts.SIGNIFICAND_WIDTH) |
((1 << FloatConsts.SIGNIFICAND_WIDTH) - 1)),
FloatConsts.MAG_BIT_MASK >>> 1
};

public static double [] dspecial = {
0.0,
-0.0,
Double.MAX_VALUE,
Double.MIN_VALUE,
-Double.MAX_VALUE,
-Double.MIN_VALUE,
Double.NaN,
Double.POSITIVE_INFINITY,
Double.NEGATIVE_INFINITY,
Integer.MAX_VALUE,
Integer.MIN_VALUE,
Long.MIN_VALUE,
Long.MAX_VALUE,
-1,
1,
42,
-42,
Math.PI,
Math.E,
Float.MAX_VALUE,
Float.MIN_VALUE,
-Float.MAX_VALUE,
-Float.MIN_VALUE,
Float.NaN,
Float.POSITIVE_INFINITY,
Float.NEGATIVE_INFINITY,
Double.longBitsToDouble((1L << DoubleConsts.SIGNIFICAND_WIDTH) |
((1L << DoubleConsts.SIGNIFICAND_WIDTH) - 1)),
DoubleConsts.MAG_BIT_MASK >>> 1
};

public static void testAbsConstant() {
// Test abs(constant) optimization for int
Asserts.assertEquals(Integer.MAX_VALUE, Math.abs(Integer.MAX_VALUE));
Asserts.assertEquals(Integer.MIN_VALUE, Math.abs(Integer.MIN_VALUE));
Asserts.assertEquals(Integer.MAX_VALUE, Math.abs(-Integer.MAX_VALUE));

// Test abs(constant) optimization for long
Asserts.assertEquals(Long.MAX_VALUE, Math.abs(Long.MAX_VALUE));
Asserts.assertEquals(Long.MIN_VALUE, Math.abs(Long.MIN_VALUE));
Asserts.assertEquals(Long.MAX_VALUE, Math.abs(-Long.MAX_VALUE));

// Test abs(constant) optimization for float
Asserts.assertEquals(Float.NaN, Math.abs(Float.NaN));
Asserts.assertEquals(Float.POSITIVE_INFINITY, Math.abs(Float.NEGATIVE_INFINITY));
Asserts.assertEquals(Float.POSITIVE_INFINITY, Math.abs(Float.POSITIVE_INFINITY));
Asserts.assertEquals(0.0f, Math.abs(0.0f));
Asserts.assertEquals(0.0f, Math.abs(-0.0f));
Asserts.assertEquals(Float.MAX_VALUE, Math.abs(Float.MAX_VALUE));
Asserts.assertEquals(Float.MIN_VALUE, Math.abs(Float.MIN_VALUE));
Asserts.assertEquals(Float.MAX_VALUE, Math.abs(-Float.MAX_VALUE));
Asserts.assertEquals(Float.MIN_VALUE, Math.abs(-Float.MIN_VALUE));

// Test abs(constant) optimization for double
Asserts.assertEquals(Double.NaN, Math.abs(Double.NaN));
Asserts.assertEquals(Double.POSITIVE_INFINITY, Math.abs(Double.NEGATIVE_INFINITY));
Asserts.assertEquals(Double.POSITIVE_INFINITY, Math.abs(Double.POSITIVE_INFINITY));
Asserts.assertEquals(0.0, Math.abs(0.0));
Asserts.assertEquals(0.0, Math.abs(-0.0));
Asserts.assertEquals(Double.MAX_VALUE, Math.abs(Double.MAX_VALUE));
Asserts.assertEquals(Double.MIN_VALUE, Math.abs(Double.MIN_VALUE));
Asserts.assertEquals(Double.MAX_VALUE, Math.abs(-Double.MAX_VALUE));
Asserts.assertEquals(Double.MIN_VALUE, Math.abs(-Double.MIN_VALUE));
}

private static void testAbsTransformInt(int[] a) {
for (int i = 0; i < a.length; i++) {
Asserts.assertEquals(Math.abs(Math.abs(a[i])), Math.abs(a[i]));
Asserts.assertEquals(Math.abs(0 - a[i]), Math.abs(a[i]));
}
}

private static void testAbsTransformLong(long[] a) {
for (int i = 0; i < a.length; i++) {
Asserts.assertEquals(Math.abs(Math.abs(a[i])), Math.abs(a[i]));
Asserts.assertEquals(Math.abs(0 - a[i]), Math.abs(a[i]));
}
}

private static void testAbsTransformFloat(float[] a) {
for (int i = 0; i < a.length; i++) {
Asserts.assertEquals(Math.abs(Math.abs(a[i])), Math.abs(a[i]));
Asserts.assertEquals(Math.abs(0 - a[i]), Math.abs(a[i]));
}
}

private static void testAbsTransformDouble(double[] a) {
for (int i = 0; i < a.length; i++) {
Asserts.assertEquals(Math.abs(Math.abs(a[i])), Math.abs(a[i]));
Asserts.assertEquals(Math.abs(0 - a[i]), Math.abs(a[i]));
}
}

private static void testAbsOptChar(char[] a) {
for (int i = 0; i < a.length; i++) {
Asserts.assertEquals(a[i], (char) Math.abs(a[i]));
}
}

public static void test() {
// java.lang.Math.abs() collapses into AbsI/AbsL nodes on platforms that support the correspondent nodes
Expand All @@ -45,7 +187,20 @@ public static void test() {
public static void main(String args[]) {
for (int i = 0; i < 20_000; i++) {
test();

testAbsConstant();

// Verify abs(abs(x)) = abs(x) for all types
// Verify abs(0-x) = abs(x) for all types
testAbsTransformInt(ispecial);
testAbsTransformLong(lspecial);
testAbsTransformFloat(fspecial);
testAbsTransformDouble(dspecial);

// Verify abs(non-negative_value) = non-negative_value
testAbsOptChar(cspecial);
}

}
}

0 comments on commit bd0eb29

Please sign in to comment.