changeset 18415:6dc4f0be9a70

Add support of lzcnt and tzcnt - support of lzcnt and tzcnt instructions, - unit tests for lzcnt/tzcnt, - ability to emit bsf/bsr in case lzcnt/tzcnt were turned off from the command line, - tightening the stamps produced by ScanBitForward/ReverseNode nodes. Contributed-By: Igor Veresov <igor.veresov@oracle.com>
author Gilles Duboscq <duboscq@ssw.jku.at>
date Wed, 19 Nov 2014 13:32:05 +0100
parents df8d52aba789
children 0c6504598b65
files graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java graal/com.oracle.graal.asm.amd64.test/src/com/oracle/graal/asm/amd64/test/BitOpsTest.java graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CountLeadingZerosNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CountTrailingZerosNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSubstitutions.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotspotGuards.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/IntegerSubstitutions.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/LongSubstitutions.java graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64BitManipulationOp.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/BitOpNodesTest.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BitScanForwardNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BitScanReverseNode.java src/cpu/x86/vm/vmStructs_x86.hpp
diffstat 19 files changed, 728 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java	Wed Nov 19 13:32:05 2014 +0100
@@ -133,6 +133,7 @@
         ERMS,
         AMD_3DNOW_PREFETCH,
         AES,
+        BMI1
     }
 
     private final EnumSet<CPUFeature> features;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.asm.amd64.test/src/com/oracle/graal/asm/amd64/test/BitOpsTest.java	Wed Nov 19 13:32:05 2014 +0100
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2013, 2014, 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 com.oracle.graal.asm.amd64.test;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.compiler.common.UnsafeAccess.*;
+import static org.junit.Assume.*;
+
+import org.junit.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.oracle.graal.amd64.*;
+import com.oracle.graal.amd64.AMD64.*;
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.amd64.*;
+import com.oracle.graal.asm.test.*;
+
+public class BitOpsTest extends AssemblerTest {
+    private static boolean lzcntSupported;
+    private static boolean tzcntSupported;
+
+    @Before
+    public void checkAMD64() {
+        assumeTrue("skipping AMD64 specific test", codeCache.getTarget().arch instanceof AMD64);
+        EnumSet<CPUFeature> features = ((AMD64) codeCache.getTarget().arch).getFeatures();
+        lzcntSupported = features.contains(CPUFeature.LZCNT);
+        tzcntSupported = features.contains(CPUFeature.BMI1);
+    }
+
+    @Test
+    public void lzcntlTest() {
+        if (lzcntSupported) {
+            CodeGenTest test = new CodeGenTest() {
+
+                @Override
+                public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
+                    AMD64Assembler asm = new AMD64Assembler(target, registerConfig);
+                    Register ret = registerConfig.getReturnRegister(Kind.Int);
+                    Register arg = asRegister(cc.getArgument(0));
+                    asm.lzcntl(ret, arg);
+                    asm.ret(0);
+                    return asm.close(true);
+                }
+            };
+            assertReturn("intStub", test, 31, 1);
+        }
+    }
+
+    @Test
+    public void lzcntlMemTest() {
+        if (lzcntSupported) {
+            CodeGenTest test = new CodeGenTest() {
+
+                @Override
+                public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
+                    AMD64Assembler asm = new AMD64Assembler(target, registerConfig);
+                    Register ret = registerConfig.getReturnRegister(Kind.Int);
+                    try {
+                        Field f = IntField.class.getDeclaredField("x");
+                        AMD64Address arg = new AMD64Address(asRegister(cc.getArgument(0)), (int) unsafe.objectFieldOffset(f));
+                        asm.lzcntl(ret, arg);
+                        asm.ret(0);
+                        return asm.close(true);
+                    } catch (Exception e) {
+                        throw new RuntimeException("exception while trying to generate field access:", e);
+                    }
+                }
+            };
+            assertReturn("intFieldStub", test, 31, new IntField(1));
+        }
+    }
+
+    @Test
+    public void lzcntqTest() {
+        if (lzcntSupported) {
+            CodeGenTest test = new CodeGenTest() {
+
+                @Override
+                public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
+                    AMD64Assembler asm = new AMD64Assembler(target, registerConfig);
+                    Register ret = registerConfig.getReturnRegister(Kind.Int);
+                    Register arg = asRegister(cc.getArgument(0));
+                    asm.lzcntq(ret, arg);
+                    asm.ret(0);
+                    return asm.close(true);
+                }
+            };
+            assertReturn("longStub", test, 63, 1L);
+        }
+    }
+
+    @Test
+    public void lzcntqMemTest() {
+        if (lzcntSupported) {
+            CodeGenTest test = new CodeGenTest() {
+
+                @Override
+                public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
+                    AMD64Assembler asm = new AMD64Assembler(target, registerConfig);
+                    Register ret = registerConfig.getReturnRegister(Kind.Int);
+                    try {
+                        Field f = LongField.class.getDeclaredField("x");
+                        AMD64Address arg = new AMD64Address(asRegister(cc.getArgument(0)), (int) unsafe.objectFieldOffset(f));
+                        asm.lzcntq(ret, arg);
+                        asm.ret(0);
+                        return asm.close(true);
+                    } catch (Exception e) {
+                        throw new RuntimeException("exception while trying to generate field access:", e);
+                    }
+                }
+            };
+            assertReturn("longFieldStub", test, 63, new LongField(1));
+        }
+    }
+
+    @Test
+    public void tzcntlTest() {
+        if (tzcntSupported) {
+            CodeGenTest test = new CodeGenTest() {
+
+                @Override
+                public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
+                    AMD64Assembler asm = new AMD64Assembler(target, registerConfig);
+                    Register ret = registerConfig.getReturnRegister(Kind.Int);
+                    Register arg = asRegister(cc.getArgument(0));
+                    asm.tzcntl(ret, arg);
+                    asm.ret(0);
+                    return asm.close(true);
+                }
+            };
+            assertReturn("intStub", test, 31, 0x8000_0000);
+        }
+    }
+
+    @Test
+    public void tzcntlMemTest() {
+        if (tzcntSupported) {
+            CodeGenTest test = new CodeGenTest() {
+
+                @Override
+                public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
+                    AMD64Assembler asm = new AMD64Assembler(target, registerConfig);
+                    Register ret = registerConfig.getReturnRegister(Kind.Int);
+                    try {
+                        Field f = IntField.class.getDeclaredField("x");
+                        AMD64Address arg = new AMD64Address(asRegister(cc.getArgument(0)), (int) unsafe.objectFieldOffset(f));
+                        asm.tzcntl(ret, arg);
+                        asm.ret(0);
+                        return asm.close(true);
+                    } catch (Exception e) {
+                        throw new RuntimeException("exception while trying to generate field access:", e);
+                    }
+                }
+            };
+            assertReturn("intFieldStub", test, 31, new IntField(0x8000_0000));
+        }
+    }
+
+    @Test
+    public void tzcntqTest() {
+        if (tzcntSupported) {
+            CodeGenTest test = new CodeGenTest() {
+
+                @Override
+                public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
+                    AMD64Assembler asm = new AMD64Assembler(target, registerConfig);
+                    Register ret = registerConfig.getReturnRegister(Kind.Int);
+                    Register arg = asRegister(cc.getArgument(0));
+                    asm.tzcntq(ret, arg);
+                    asm.ret(0);
+                    return asm.close(true);
+                }
+            };
+            assertReturn("longStub", test, 63, 0x8000_0000_0000_0000L);
+        }
+    }
+
+    @Test
+    public void tzcntqMemTest() {
+        if (tzcntSupported) {
+            CodeGenTest test = new CodeGenTest() {
+
+                @Override
+                public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
+                    AMD64Assembler asm = new AMD64Assembler(target, registerConfig);
+                    Register ret = registerConfig.getReturnRegister(Kind.Int);
+                    try {
+                        Field f = LongField.class.getDeclaredField("x");
+                        AMD64Address arg = new AMD64Address(asRegister(cc.getArgument(0)), (int) unsafe.objectFieldOffset(f));
+                        asm.tzcntq(ret, arg);
+                        asm.ret(0);
+                        return asm.close(true);
+                    } catch (Exception e) {
+                        throw new RuntimeException("exception while trying to generate field access:", e);
+                    }
+                }
+            };
+            assertReturn("longFieldStub", test, 63, new LongField(0x8000_0000_0000_0000L));
+        }
+    }
+
+    @SuppressWarnings("unused")
+    public static int intStub(int arg) {
+        return 0;
+    }
+
+    @SuppressWarnings("unused")
+    public static int longStub(long arg) {
+        return 0;
+    }
+
+    public static class IntField {
+        public int x;
+
+        IntField(int x) {
+            this.x = x;
+        }
+    }
+
+    public static class LongField {
+        public long x;
+
+        LongField(long x) {
+            this.x = x;
+        }
+    }
+
+    @SuppressWarnings("unused")
+    public static int intFieldStub(IntField arg) {
+        return 0;
+    }
+
+    @SuppressWarnings("unused")
+    public static int longFieldStub(LongField arg) {
+        return 0;
+    }
+}
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Wed Nov 19 13:32:05 2014 +0100
@@ -500,7 +500,6 @@
     }
 
     public final void bsrq(Register dst, Register src) {
-        assert !supports(CPUFeature.LZCNT);
         int encode = prefixqAndEncode(dst.encoding, src.encoding);
         emitByte(0x0F);
         emitByte(0xBD);
@@ -508,7 +507,6 @@
     }
 
     public final void bsrq(Register dst, AMD64Address src) {
-        assert !supports(CPUFeature.LZCNT);
         prefixq(src, dst);
         emitByte(0x0F);
         emitByte(0xBD);
@@ -516,7 +514,6 @@
     }
 
     public final void bsrl(Register dst, Register src) {
-        assert !supports(CPUFeature.LZCNT);
         int encode = prefixAndEncode(dst.encoding, src.encoding);
         emitByte(0x0F);
         emitByte(0xBD);
@@ -524,7 +521,6 @@
     }
 
     public final void bsrl(Register dst, AMD64Address src) {
-        assert !supports(CPUFeature.LZCNT);
         prefix(src, dst);
         emitByte(0x0F);
         emitByte(0xBD);
@@ -1327,6 +1323,42 @@
         nop();
     }
 
+    public final void lzcntl(Register dst, Register src) {
+        assert supports(CPUFeature.LZCNT);
+        emitByte(0xF3);
+        int encode = prefixAndEncode(dst.encoding, src.encoding);
+        emitByte(0x0F);
+        emitByte(0xBD);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void lzcntq(Register dst, Register src) {
+        assert supports(CPUFeature.LZCNT);
+        emitByte(0xF3);
+        int encode = prefixqAndEncode(dst.encoding, src.encoding);
+        emitByte(0x0F);
+        emitByte(0xBD);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void lzcntl(Register dst, AMD64Address src) {
+        assert supports(CPUFeature.LZCNT);
+        emitByte(0xF3);
+        prefix(src, dst);
+        emitByte(0x0F);
+        emitByte(0xBD);
+        emitOperandHelper(dst, src);
+    }
+
+    public final void lzcntq(Register dst, AMD64Address src) {
+        assert supports(CPUFeature.LZCNT);
+        emitByte(0xF3);
+        prefixq(src, dst);
+        emitByte(0x0F);
+        emitByte(0xBD);
+        emitOperandHelper(dst, src);
+    }
+
     public final void nop() {
         nop(1);
     }
@@ -1878,6 +1910,42 @@
         emitOperandHelper(dst, src);
     }
 
+    public final void tzcntl(Register dst, Register src) {
+        assert supports(CPUFeature.BMI1);
+        emitByte(0xF3);
+        int encode = prefixAndEncode(dst.encoding, src.encoding);
+        emitByte(0x0F);
+        emitByte(0xBC);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void tzcntq(Register dst, Register src) {
+        assert supports(CPUFeature.BMI1);
+        emitByte(0xF3);
+        int encode = prefixqAndEncode(dst.encoding, src.encoding);
+        emitByte(0x0F);
+        emitByte(0xBC);
+        emitByte(0xC0 | encode);
+    }
+
+    public final void tzcntl(Register dst, AMD64Address src) {
+        assert supports(CPUFeature.BMI1);
+        emitByte(0xF3);
+        prefix(src, dst);
+        emitByte(0x0F);
+        emitByte(0xBC);
+        emitOperandHelper(dst, src);
+    }
+
+    public final void tzcntq(Register dst, AMD64Address src) {
+        assert supports(CPUFeature.BMI1);
+        emitByte(0xF3);
+        prefixq(src, dst);
+        emitByte(0x0F);
+        emitByte(0xBC);
+        emitOperandHelper(dst, src);
+    }
+
     public final void ucomisd(Register dst, AMD64Address src) {
         assert dst.getRegisterCategory().equals(AMD64.XMM);
         emitByte(0x66);
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Wed Nov 19 13:32:05 2014 +0100
@@ -1004,6 +1004,26 @@
         return result;
     }
 
+    public Value emitCountLeadingZeros(Value value) {
+        Variable result = newVariable(LIRKind.derive(value).changeType(Kind.Int));
+        if (value.getKind().getStackKind() == Kind.Int) {
+            append(new AMD64BitManipulationOp(ILZCNT, result, asAllocatable(value)));
+        } else {
+            append(new AMD64BitManipulationOp(LLZCNT, result, asAllocatable(value)));
+        }
+        return result;
+    }
+
+    public Value emitCountTrailingZeros(Value value) {
+        Variable result = newVariable(LIRKind.derive(value).changeType(Kind.Int));
+        if (value.getKind().getStackKind() == Kind.Int) {
+            append(new AMD64BitManipulationOp(ITZCNT, result, asAllocatable(value)));
+        } else {
+            append(new AMD64BitManipulationOp(LTZCNT, result, asAllocatable(value)));
+        }
+        return result;
+    }
+
     @Override
     public Value emitMathAbs(Value input) {
         Variable result = newVariable(LIRKind.derive(input));
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java	Wed Nov 19 13:32:05 2014 +0100
@@ -85,6 +85,9 @@
         if ((config.x86CPUFeatures & config.cpu3DNOWPREFETCH) != 0) {
             features.add(AMD64.CPUFeature.AMD_3DNOW_PREFETCH);
         }
+        if ((config.x86CPUFeatures & config.cpuBMI1) != 0) {
+            features.add(AMD64.CPUFeature.BMI1);
+        }
         return features;
     }
 
@@ -237,15 +240,15 @@
         } else {
             /*
              * System V Application Binary Interface, AMD64 Architecture Processor Supplement
-             *
+             * 
              * Draft Version 0.96
-             *
+             * 
              * http://www.uclibc.org/docs/psABI-x86_64.pdf
-             *
+             * 
              * 3.2.1
-             *
+             * 
              * ...
-             *
+             * 
              * This subsection discusses usage of each register. Registers %rbp, %rbx and %r12
              * through %r15 "belong" to the calling function and the called function is required to
              * preserve their values. In other words, a called function must preserve these
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java	Wed Nov 19 13:32:05 2014 +0100
@@ -34,6 +34,8 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.replacements.*;
+import com.oracle.graal.replacements.IntegerSubstitutions;
+import com.oracle.graal.replacements.LongSubstitutions;
 import com.oracle.graal.word.phases.*;
 
 /**
@@ -63,7 +65,10 @@
                 }
             } else if (substituteMethod.getName().equals("numberOfLeadingZeros")) {
                 if (config.useCountLeadingZerosInstruction) {
-                    // bsr is lzcnt
+                    return null;
+                }
+            } else if (substituteMethod.getName().equals("numberOfTrailingZeros")) {
+                if (config.useCountTrailingZerosInstruction) {
                     return null;
                 }
             }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Wed Nov 19 13:32:05 2014 +0100
@@ -726,6 +726,7 @@
     @HotSpotVMFlag(name = "UseBiasedLocking") @Stable public boolean useBiasedLocking;
     @HotSpotVMFlag(name = "UsePopCountInstruction") @Stable public boolean usePopCountInstruction;
     @HotSpotVMFlag(name = "UseCountLeadingZerosInstruction", archs = {"amd64"}) @Stable public boolean useCountLeadingZerosInstruction;
+    @HotSpotVMFlag(name = "UseCountTrailingZerosInstruction", archs = {"amd64"}) @Stable public boolean useCountTrailingZerosInstruction;
     @HotSpotVMFlag(name = "UseAESIntrinsics") @Stable public boolean useAESIntrinsics;
     @HotSpotVMFlag(name = "UseCRC32Intrinsics") @Stable public boolean useCRC32Intrinsics;
     @HotSpotVMFlag(name = "UseG1GC") @Stable public boolean useG1GC;
@@ -794,6 +795,7 @@
     @HotSpotVMConstant(name = "VM_Version::CPU_AES", archs = {"amd64"}) @Stable public int cpuAES;
     @HotSpotVMConstant(name = "VM_Version::CPU_ERMS", archs = {"amd64"}) @Stable public int cpuERMS;
     @HotSpotVMConstant(name = "VM_Version::CPU_CLMUL", archs = {"amd64"}) @Stable public int cpuCLMUL;
+    @HotSpotVMConstant(name = "VM_Version::CPU_BMI1", archs = {"amd64"}) @Stable public int cpuBMI1;
 
     // SPARC specific values
     @HotSpotVMField(name = "VM_Version::_features", type = "int", get = HotSpotVMField.Type.VALUE, archs = {"sparc"}) @Stable public int sparcFeatures;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CountLeadingZerosNode.java	Wed Nov 19 13:32:05 2014 +0100
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012, 2014, 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 com.oracle.graal.hotspot.replacements;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodeinfo.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
+
+/**
+ * Count the number of leading zeros.
+ */
+@NodeInfo
+public class CountLeadingZerosNode extends UnaryNode implements LIRLowerable {
+
+    public static CountLeadingZerosNode create(ValueNode value) {
+        return new CountLeadingZerosNode(value);
+    }
+
+    protected CountLeadingZerosNode(ValueNode value) {
+        super(StampFactory.forInteger(Kind.Int, 0, ((PrimitiveStamp) value.stamp()).getBits()), value);
+        assert value.getKind() == Kind.Int || value.getKind() == Kind.Long;
+    }
+
+    @Override
+    public boolean inferStamp() {
+        IntegerStamp valueStamp = (IntegerStamp) getValue().stamp();
+        long mask = CodeUtil.mask(valueStamp.getBits());
+        int min = Long.numberOfLeadingZeros(valueStamp.upMask() & mask);
+        int max = Long.numberOfLeadingZeros(valueStamp.downMask() & mask);
+        return updateStamp(StampFactory.forInteger(Kind.Int, min, max));
+    }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
+        if (forValue.isConstant()) {
+            JavaConstant c = forValue.asJavaConstant();
+            if (forValue.getKind() == Kind.Int) {
+                return ConstantNode.forInt(Integer.numberOfLeadingZeros(c.asInt()));
+            } else {
+                return ConstantNode.forInt(Long.numberOfLeadingZeros(c.asLong()));
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Raw intrinsic for lzcntq instruction.
+     *
+     * @param v
+     * @return number of trailing zeros
+     */
+    @NodeIntrinsic
+    public static native int count(long v);
+
+    /**
+     * Raw intrinsic for lzcntl instruction.
+     *
+     * @param v
+     * @return number of trailing zeros
+     */
+    @NodeIntrinsic
+    public static native int count(int v);
+
+    @Override
+    public void generate(NodeLIRBuilderTool gen) {
+        Value result = gen.getLIRGeneratorTool().emitCountLeadingZeros(gen.operand(getValue()));
+        gen.setResult(this, result);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CountTrailingZerosNode.java	Wed Nov 19 13:32:05 2014 +0100
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012, 2014, 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 com.oracle.graal.hotspot.replacements;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodeinfo.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
+
+/**
+ * Count the number of trailing zeros.
+ */
+@NodeInfo
+public class CountTrailingZerosNode extends UnaryNode implements LIRLowerable {
+
+    public static CountTrailingZerosNode create(ValueNode value) {
+        return new CountTrailingZerosNode(value);
+    }
+
+    protected CountTrailingZerosNode(ValueNode value) {
+        super(StampFactory.forInteger(Kind.Int, 0, ((PrimitiveStamp) value.stamp()).getBits()), value);
+        assert value.getKind() == Kind.Int || value.getKind() == Kind.Long;
+    }
+
+    @Override
+    public boolean inferStamp() {
+        IntegerStamp valueStamp = (IntegerStamp) getValue().stamp();
+        long mask = CodeUtil.mask(valueStamp.getBits());
+        int min = Long.numberOfTrailingZeros(valueStamp.upMask() & mask);
+        int max = Long.numberOfTrailingZeros(valueStamp.downMask() & mask);
+        return updateStamp(StampFactory.forInteger(Kind.Int, min, max));
+    }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
+        if (forValue.isConstant()) {
+            JavaConstant c = forValue.asJavaConstant();
+            if (forValue.getKind() == Kind.Int) {
+                return ConstantNode.forInt(Integer.numberOfTrailingZeros(c.asInt()));
+            } else {
+                return ConstantNode.forInt(Long.numberOfTrailingZeros(c.asLong()));
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Raw intrinsic for tzcntq instruction.
+     *
+     * @param v
+     * @return number of trailing zeros
+     */
+    @NodeIntrinsic
+    public static native int count(long v);
+
+    /**
+     * Raw intrinsic for tzcntl instruction.
+     *
+     * @param v
+     * @return number of trailing zeros
+     */
+    @NodeIntrinsic
+    public static native int count(int v);
+
+    @Override
+    public void generate(NodeLIRBuilderTool gen) {
+        Value result = gen.getLIRGeneratorTool().emitCountTrailingZeros(gen.operand(getValue()));
+        gen.setResult(this, result);
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSubstitutions.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotSubstitutions.java	Wed Nov 19 13:32:05 2014 +0100
@@ -67,5 +67,7 @@
         replacements.registerSubstitutions(CompositeValueClass.class, CompositeValueClassSubstitutions.class);
         replacements.registerSubstitutions(new NamedType("com.sun.crypto.provider.AESCrypt"), AESCryptSubstitutions.class);
         replacements.registerSubstitutions(new NamedType("com.sun.crypto.provider.CipherBlockChaining"), CipherBlockChainingSubstitutions.class);
+        replacements.registerSubstitutions(Integer.class, IntegerSubstitutions.class);
+        replacements.registerSubstitutions(Long.class, LongSubstitutions.class);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotspotGuards.java	Wed Nov 19 13:32:05 2014 +0100
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012, 2014, 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 com.oracle.graal.hotspot.replacements;
+
+import com.oracle.graal.api.replacements.*;
+import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+
+public class HotspotGuards {
+    public static class CountLeadingZerosSupported implements SubstitutionGuard {
+        public boolean execute() {
+            return runtime().getConfig().useCountLeadingZerosInstruction;
+        }
+    }
+
+    public static class CountTrailingZerosSupported implements SubstitutionGuard {
+        public boolean execute() {
+            return runtime().getConfig().useCountTrailingZerosInstruction;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/IntegerSubstitutions.java	Wed Nov 19 13:32:05 2014 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, 2014, 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 com.oracle.graal.hotspot.replacements;
+
+import com.oracle.graal.api.replacements.*;
+
+@ClassSubstitution(Integer.class)
+public class IntegerSubstitutions {
+
+    @MethodSubstitution(guard = HotspotGuards.CountLeadingZerosSupported.class)
+    public static int numberOfLeadingZeros(int i) {
+        return CountLeadingZerosNode.count(i);
+    }
+
+    @MethodSubstitution(guard = HotspotGuards.CountTrailingZerosSupported.class)
+    public static int numberOfTrailingZeros(int i) {
+        return CountTrailingZerosNode.count(i);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/LongSubstitutions.java	Wed Nov 19 13:32:05 2014 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, 2014, 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 com.oracle.graal.hotspot.replacements;
+
+import com.oracle.graal.api.replacements.*;
+
+@ClassSubstitution(Long.class)
+public class LongSubstitutions {
+
+    @MethodSubstitution(guard = HotspotGuards.CountLeadingZerosSupported.class)
+    public static int numberOfLeadingZeros(long i) {
+        return CountLeadingZerosNode.count(i);
+    }
+
+    @MethodSubstitution(guard = HotspotGuards.CountTrailingZerosSupported.class)
+    public static int numberOfTrailingZeros(long i) {
+        return CountTrailingZerosNode.count(i);
+    }
+}
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64BitManipulationOp.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64BitManipulationOp.java	Wed Nov 19 13:32:05 2014 +0100
@@ -35,7 +35,11 @@
         LPOPCNT,
         IBSR,
         LBSR,
-        BSF;
+        BSF,
+        ILZCNT,
+        LLZCNT,
+        ITZCNT,
+        LTZCNT
     }
 
     @Opcode private final IntrinsicOpcode opcode;
@@ -69,6 +73,18 @@
                 case LBSR:
                     masm.bsrq(dst, src);
                     break;
+                case ILZCNT:
+                    masm.lzcntl(dst, src);
+                    break;
+                case LLZCNT:
+                    masm.lzcntq(dst, src);
+                    break;
+                case ITZCNT:
+                    masm.tzcntl(dst, src);
+                    break;
+                case LTZCNT:
+                    masm.tzcntq(dst, src);
+                    break;
             }
         } else {
             AMD64Address src = (AMD64Address) crb.asAddress(input);
@@ -88,6 +104,18 @@
                 case LBSR:
                     masm.bsrq(dst, src);
                     break;
+                case ILZCNT:
+                    masm.lzcntl(dst, src);
+                    break;
+                case LLZCNT:
+                    masm.lzcntq(dst, src);
+                    break;
+                case ITZCNT:
+                    masm.tzcntl(dst, src);
+                    break;
+                case LTZCNT:
+                    masm.tzcntq(dst, src);
+                    break;
             }
         }
     }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java	Wed Nov 19 13:32:05 2014 +0100
@@ -189,4 +189,14 @@
 
     Value emitArrayEquals(Kind kind, Value array1, Value array2, Value length);
 
+    @SuppressWarnings("unused")
+    default Value emitCountLeadingZeros(Value value) {
+        throw GraalInternalError.unimplemented();
+    }
+
+    @SuppressWarnings("unused")
+    default Value emitCountTrailingZeros(Value value) {
+        throw GraalInternalError.unimplemented();
+    }
+
 }
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/BitOpNodesTest.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/BitOpNodesTest.java	Wed Nov 19 13:32:05 2014 +0100
@@ -164,7 +164,7 @@
     @Test
     public void testScanForwardLongEmpty() {
         ValueNode result = parseAndInline("scanForwardLongEmptySnippet");
-        Assert.assertEquals(StampFactory.forInteger(Kind.Int, -1, 64), result.stamp());
+        Assert.assertEquals(StampFactory.forInteger(Kind.Int, 24, 64), result.stamp());
     }
 
     /*
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BitScanForwardNode.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BitScanForwardNode.java	Wed Nov 19 13:32:05 2014 +0100
@@ -54,12 +54,12 @@
         int max;
         long mask = CodeUtil.mask(valueStamp.getBits());
         int firstAlwaysSetBit = scan(valueStamp.downMask() & mask);
+        int firstMaybeSetBit = scan(valueStamp.upMask() & mask);
         if (firstAlwaysSetBit == -1) {
             int lastMaybeSetBit = BitScanReverseNode.scan(valueStamp.upMask() & mask);
-            min = -1;
+            min = firstMaybeSetBit;
             max = lastMaybeSetBit;
         } else {
-            int firstMaybeSetBit = scan(valueStamp.upMask() & mask);
             min = firstMaybeSetBit;
             max = firstAlwaysSetBit;
         }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BitScanReverseNode.java	Wed Nov 19 13:10:24 2014 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BitScanReverseNode.java	Wed Nov 19 13:32:05 2014 +0100
@@ -55,7 +55,8 @@
         long mask = CodeUtil.mask(valueStamp.getBits());
         int lastAlwaysSetBit = scan(valueStamp.downMask() & mask);
         if (lastAlwaysSetBit == -1) {
-            min = -1;
+            int firstMaybeSetBit = BitScanForwardNode.scan(valueStamp.upMask() & mask);
+            min = firstMaybeSetBit;
         } else {
             min = lastAlwaysSetBit;
         }
--- a/src/cpu/x86/vm/vmStructs_x86.hpp	Wed Nov 19 13:10:24 2014 +0100
+++ b/src/cpu/x86/vm/vmStructs_x86.hpp	Wed Nov 19 13:32:05 2014 +0100
@@ -70,7 +70,9 @@
   declare_constant(VM_Version::CPU_AVX2)                            \
   declare_constant(VM_Version::CPU_AES)                             \
   declare_constant(VM_Version::CPU_ERMS)                            \
-  declare_constant(VM_Version::CPU_CLMUL)
+  declare_constant(VM_Version::CPU_CLMUL)                           \
+  declare_constant(VM_Version::CPU_BMI1)                            \
+  declare_constant(VM_Version::CPU_BMI2)
 
 #define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant)