Skip to content

Commit

Permalink
8247408: IdealGraph bit check expression canonicalization
Browse files Browse the repository at this point in the history
Reviewed-by: aph, kvn, adinn
  • Loading branch information
Boris Ulasevich authored and sandlerwang committed Dec 7, 2020
1 parent 9d0ad33 commit 1fcd8f9
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/hotspot/share/opto/subnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,34 @@ Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) {
return new BoolNode( cmp, _test.commute() );
}

// Change "bool eq/ne (cmp (and X 16) 16)" into "bool ne/eq (cmp (and X 16) 0)".
if (cop == Op_CmpI &&
(_test._test == BoolTest::eq || _test._test == BoolTest::ne) &&
cmp1->Opcode() == Op_AndI && cmp2->Opcode() == Op_ConI &&
cmp1->in(2)->Opcode() == Op_ConI) {
const TypeInt *t12 = phase->type(cmp2)->isa_int();
const TypeInt *t112 = phase->type(cmp1->in(2))->isa_int();
if (t12 && t12->is_con() && t112 && t112->is_con() &&
t12->get_con() == t112->get_con() && is_power_of_2(t12->get_con())) {
Node *ncmp = phase->transform(new CmpINode(cmp1, phase->intcon(0)));
return new BoolNode(ncmp, _test.negate());
}
}

// Same for long type: change "bool eq/ne (cmp (and X 16) 16)" into "bool ne/eq (cmp (and X 16) 0)".
if (cop == Op_CmpL &&
(_test._test == BoolTest::eq || _test._test == BoolTest::ne) &&
cmp1->Opcode() == Op_AndL && cmp2->Opcode() == Op_ConL &&
cmp1->in(2)->Opcode() == Op_ConL) {
const TypeLong *t12 = phase->type(cmp2)->isa_long();
const TypeLong *t112 = phase->type(cmp1->in(2))->isa_long();
if (t12 && t12->is_con() && t112 && t112->is_con() &&
t12->get_con() == t112->get_con() && is_power_of_2(t12->get_con())) {
Node *ncmp = phase->transform(new CmpLNode(cmp1, phase->longcon(0)));
return new BoolNode(ncmp, _test.negate());
}
}

// Change "bool eq/ne (cmp (xor X 1) 0)" into "bool ne/eq (cmp X 0)".
// The XOR-1 is an idiom used to flip the sense of a bool. We flip the
// test instead.
Expand Down
112 changes: 112 additions & 0 deletions test/hotspot/jtreg/compiler/c2/TestBit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2020, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package compiler.c2;

import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;

/*
* @test
* @bug 8247408
* @summary C2 should convert ((var&16) == 16) to ((var&16) != 0) for power-of-two constants
* @library /test/lib /
*
* @run main/othervm compiler.c2.TestBit
*
* @requires os.arch=="aarch64" | os.arch=="amd64" | os.arch == "ppc64le"
* @requires vm.debug == true & vm.flavor == "server" & !vm.graal.enabled
*/
public class TestBit {

static void runTest(String testName) throws Exception {
String className = "compiler.c2.TestBit";
String[] procArgs = {
"-XX:+PrintOptoAssembly",
"-XX:CompileCommand=compileonly," + className + "::tst*",
className, testName};

ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(procArgs);
OutputAnalyzer output = new OutputAnalyzer(pb.start());

String expectedTestBitInstruction =
"ppc64le".equals(System.getProperty("os.arch")) ? "ANDI" :
"aarch64".equals(System.getProperty("os.arch")) ? "tb" :
"amd64".equals(System.getProperty("os.arch")) ? "test" : null;

if (expectedTestBitInstruction != null) {
output.shouldContain(expectedTestBitInstruction);
} else {
System.err.println("unexpected os.arch");
}
}

static final int ITER = 100000; // ~ Tier4CompileThreshold + compilation time

// dummy volatile variable
public static volatile long c = 0;

// C2 is expected to generate test bit instruction on the test
static void tstBitLong(long value) {
if (1L == (1L & value)) {
c++;
} else {
c--;
}
}

// C2 is expected to generate test bit instruction on the test
static void tstBitInt(int value) {
if (1 == (1 & value)) {
c++;
} else {
c--;
}
}

public static void main(String[] args) throws Exception {
if (args.length == 0) {
// Fork VMs to check their debug compiler output
runTest("tstBitLong");
runTest("tstBitInt");
}
if (args.length > 0) {
// We are in a forked VM to execute the named test
String testName = args[0];
switch (testName) {
case "tstBitLong":
for (int i = 0; i < ITER; i++) {
tstBitLong(i % 2);
}
break;
case "tstBitInt":
for (int i = 0; i < ITER; i++) {
tstBitInt(i % 2);
}
break;
default:
throw new RuntimeException("unexpected test name " + testName);
}
}
}
}
52 changes: 52 additions & 0 deletions test/micro/org/openjdk/bench/vm/compiler/BitTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2020, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.vm.compiler;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;


import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class BitTest {

private static final int COUNT = 1000;

@Benchmark
public int bitTestAndBranch() {
int dummy = 0;
for (int value = 0; value < COUNT; value++) {
dummy++;
if ((value & 32) == 32) {
dummy = value ^ dummy;
} else {
dummy = value ^ value;
}
}
return dummy;
}
}

0 comments on commit 1fcd8f9

Please sign in to comment.