Java中的代码注入/汇编内联?

时间:2012-07-24 13:37:35

标签: java assembly inline-assembly

我知道Java是一种安全的语言,但是当需要进行矩阵计算时,我可以更快地尝试一下吗?

我在C ++,Digital-Mars编译器和FASM中学习__asm {}。我想在Java中做同样的事情。如何在函数中内联汇编代码?这甚至可能吗?

像这样的东西(使用AVX支持CPU将数组的所有元素钳制到没有分支的值的矢量化循环):

JavaAsmBlock(
   # get pointers into registers somehow
   # and tell Java which registers the asm clobbers somehow
     vbroadcastss  twenty_five(%rip), %ymm0
     xor   %edx,%edx
.Lloop:                            # do {
    vmovups   (%rsi, %rdx, 4), %ymm1
    vcmpltps   %ymm1, %ymm0, %ymm2
    vblendvps  %ymm2, %ymm0, %ymm1, %ymm1  # TODO: use vminps instead
    vmovups    %ymm1, (%rdi, %rdx, 4)
    # TODO: unroll the loop a bit, and maybe handle unaligned output specially if that's common
    add         $32, %rdx
    cmp         %rcx, %rdx
    jb     .Lloop                  # } while(idx < count)
    vzeroupper
);

System.out.println(var[0]);

我不想使用代码注入器。我想看看英特尔或AT&amp; T风格的x86指令。

6 个答案:

答案 0 :(得分:14)

在Java代码和底层硬件之间有一层抽象,原则上使这种事情变得不可能;从技术上讲,你不知道你的代码是如何在底层机器上表示的,因为相同的字节码可以在不同的处理器和不同的架构上运行。

您正式可以做的是使用Java Native Interface(JNI)从您的Java代码调用本机代码。调用开销很大,并且与Java共享数据相当昂贵,因此这应该只用于体面大小的本机代码块。

理论上,这种扩展应该是可能的。可以想象一个针对特定平台并允许程序集转义的Java编译器。编译器必须发布其ABI,因此您将了解调用约定。但是,我不知道有什么用。但是several compilers available可以直接将Java编译为本机代码;可能其中一个人在我不知情的情况下支持这样的事情,或者可以扩展到这样做。

最后,在一个不同的层面上,JVM有字节码汇编程序,如Jasmin.字节码汇编程序允许您编写直接面向JVM的“机器代码”,有时您可以编写比javac编译器可以生成。无论如何,玩它很有趣。

答案 1 :(得分:7)

您无法在Java代码中直接内联汇编。然而,与其他一些答案所声称的相反,可以方便地调用汇编而无需通过任何中间C(或C ++)层。

快速演练

考虑以下Java类:

public class MyJNIClass {

    public native void printVersion();

}

主要思想是使用JNI命名约定声明符号。在这种情况下,要在汇编代码中使用的错位名称为Java_MyJNIClass_printVersion。此符号必须在其他翻译单元中可见,例如可以使用FASM中的public指令或NASM中的global指令来实现。如果您使用的是macOS,请在名称前加一个下划线。

使用目标体系结构的调用约定编写汇编代码(参数可以在寄存器,堆栈,其他内存结构等中传递)。传递给汇编函数的第一个参数是指向JNIEnv的指针,它本身是指向JNI函数表的指针。用它来调用JNI函数。例如,使用NASM并定位x86_64:

global Java_MyJNIClass_printVersion

section .text

Java_MyJNIClass_printVersion:
    mov rax, [rdi]
    call [rax + 8*4]  ; pointer size in x86_64 * index of GetVersion
    ...

JNI函数的索引可以在Java documentation中找到。由于JNI函数表基本上是一个指针数组,不要忘记将这些索引乘以目标体系结构中指针的大小。

传递给汇编函数的第二个参数是对调用Java类或对象的引用。所有后续参数都是本机Java方法的参数。

最后,汇编代码以生成目标文件,然后从该目标文件创建共享库。 GCC和Clang可以使用类似于gcc/clang -shared -o ...的命令执行最后一步。

其他资源

this DZone article中提供了更全面的演练。我还创建了一个fully runnable example on GitHub,随时可以查看并使用它来更好地理解。

答案 2 :(得分:2)

可以使用Machine Level Java技术从Java调用程序集。它透明地将用Java编写的汇编代码打包到本机库中,但与最常用的汇编语法非常相似。接下来,您只需要调用一个本机方法,您可以在同一个类中定义程序集的编写位置。因此,您始终处于Java环境中,无需从Java IDE切换到某些组装工具,然后再切换回Java。

答案 3 :(得分:1)

您无法直接从Java调用程序集。但您可以通过JNI调用C代码,然后从那里调用程序集。

This article shows how.

答案 4 :(得分:1)

您使用JNI或JNA并从Java调用本机函数。或者作为替代方案,您将字节码作为InputStream并从中创建Java类。

答案 5 :(得分:1)

您可能还希望查看Aparapi