diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index 5956b1738f1ee..0a35573d93e38 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -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 diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index 281b28237c85c..23087f164aeab 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -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 @@ -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--------------------------------------- diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 6a927ee49a784..83ff37c9a30e5 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -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 @@ -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. diff --git a/test/hotspot/jtreg/compiler/c2/TestAbs.java b/test/hotspot/jtreg/compiler/c2/TestAbs.java index 6dfbbbfd5b638..be9dd4c623fcb 100644 --- a/test/hotspot/jtreg/compiler/c2/TestAbs.java +++ b/test/hotspot/jtreg/compiler/c2/TestAbs.java @@ -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 @@ -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); } + } }