跳到主要内容

JEP 165:编译器控制

概括

此 JEP 提出了一种改进的方法来控制 JVM 编译器。它启用运行时可管理的、方法相关的编译器标志。 (在编译期间不可变。)

目标

  • JVM 编译器(C1 和 C2)的细粒度和方法上下文相关控制

  • 能够在运行时更改 JVM 编译器控制选项

  • 无性能下降

动机

编译过程的方法上下文相关控制是一个强大的工具,用于编写小型包含的 JVM 编译器测试,这些测试可以在不重新启动整个 JVM 的情况下运行。它对于为 JVM 编译器中的错误创建解决方法也非常有用。编译器选项的良好封装也有利于卫生。

描述

指令

所有控制 JVM 编译器的选项都将被收集到一组选项中。一组带有值的选项称为编译器指令,是有关如何编译的指令。指令与方法匹配器一起提供给虚拟机,方法匹配器决定它适用于哪些方法。多个指令可以在运行时同时处于活动状态,但只有一个指令应用于特定编译。可以在运行时添加和删除指令。

指令格式

指令文件具有指定的标准化和人类可读的文件格式。指令文件可以通过命令行和诊断命令加载。指令文件具有一个或多个定义的指令。指令包含一个方法模式和许多带有值的选项。指令的顺序很重要。 compilerBroker 将应用第一个与编译模式匹配的指令。

指令文件格式将是 JSON 的子集,并添加了一些内容。该格式在以下方面与 JSON 有所不同:

  • 仅支持与命令行选项兼容的数字 - int 和 doubles。
  • 允许注释 - 以“//”开头的行
  • 数组和对象中允许有额外的尾随 '',"
  • 可能不允许转义字符(TBD)
  • 选项名称是字符串,但可以选择用引号引起来

该文件可以使用 JVM 规范支持的所有 UTF-8 字符。这些字符是为文件格式保留的:

{ - curly brace open
} - curly brace end
[ - square brace open
] - square brace end
" - quoutes
: - colon
, - comma

指令示例 1

[    // Start if array of directives
{ // Start of directive block
// Excactly one match with one or several patterns
// The array is not required with a single pattern
match: ["java*::*", "oracle*::*"],
// A directives block that only apply to a single compiler
c1: {
// A bool option. Extra trailing comma should not cause a parse error
PrintAssembly:true,
},
// Another compiler block
c2: {
// force inline patters prepended with +, prevent with -
inline: ["+vm*::*","-*::*" ]
},
// Options outside compiler block apply to all compilers
BreakAtExecute: true // enable break in compiled code
BreakAtCompile: true // enable break in compiler
},
{ // start of another directives block
// match ant method whose class end with 'Concurrent'
match: ["*Concurrent::*"],
c2: {
// disable compilation
Exclude:true,
}
// with the c1 directive unspecified the options remains default.
}
]

指令示例 2

[   
{
// pattern to match against class+method+signature
// leading and trailing wildcard (*) allowed
match: "apa.Dingo::*",

// override defaults for specified compiler
// the innermost option has the highest priority
c1: {
//override c1 presets
PrintInlining: false // Example - this option may not exist
}

c2: {
// control inlining of method
// + force inline, - dont inline
inline : [ "+java.util::*", "-com.sun::*"],
}

// directives outside a specific preset applies to all compilers
inline : [ "+java.util::*", "-com.sun::*"],
PrintAssembly: true
},
{
// matching several patterns require an array
match: ["steve::*","alex::*"]

c2: {
Enable: false, // Ignore this directive for c2.
BreakAtExecute: true // This will not be applied since Enable is false above
}

// applies to all compilers
// + force inline, - dont inline
inline : [ "+java.util::*", "-com.sun::*"],
PrintInlining: true
},
]

指令选项列表

第一个实现包含以下选项。所有选项之前都已在 CompileCommand 选项命令中使用。将添加更多选项。

常用标志:Enable、bool Exclude、bool BreakAtExecute、bool BreakAtCompile、bool Log、bool PrintAssembly、bool PrintInlined、bool PrintNMethods、bool ReplayInline、bool DumpReplay、bool DumpInline、bool CompilerDirectivesIgnoreCompileCommands、bool Inline、ccstr[]

仅 C2:BlockLayoutByFrequency、bool PrintOptoAssembly、bool PrintIntrinsics、bool raceOptoPipelined、bool TraceOptoOutput、bool TraceSpilling、bool Vectorize、bool VectorizeDebug、bool CloneMapDebug、bool IGVPrintLevel、intx MaxNodeLimit、intx DisableIntrinsics、ccstr

inline:<one pattern or an array of string patterns>
The pattern is a string that matches a method name in the same way as directives are matched.
A '+' prepended to the pattern signifies that methods matching should be force-inlined.
A '-' that it should be prevented from inlining.
The command from the first pattern that matches is used.
Example1: inline:["+java.lang.*::*", -"sun*::*"]
Example2: inline:"+java.lang.*::*"

指令模式

“match”和“inline”选项中使用的方法模式具有以下模式: Class.method(signature)

类包含由 / 分隔的包名称 类和方法可以使用前导和尾随 * 进行通配,或替换为 * 如果省略签名,则默认为 *

这些是有效的模式: "java.lang.String::indexOf" "java/lang/String.indexOf" " .lang.String::indexOf(I)" "java/lang/String. (I)" "java/ lang/String. ( )" " " :: " " java.lang .

指令解析器

指令解析器负责解析指令文件并将信息添加到 VM 内部格式。

如果在命令行上指定了格式错误的指令文件,VM 将打印错误并退出。如果通过诊断命令添加格式错误的指令文件,它将被忽略并打印正确的警告。

解析器将验证所有选项是否有效。平台相关选项将在不支持它们的平台上打印警告。基本原理是,无论部署在哪个平台上,都应该可以使用相同的指令文件。

未指定的选项将使用默认值。如果指定了命令行选项,则该选项将成为默认值。方法模式的默认值为“ . ”(匹配所有方法)。

编译器代理

compilerBroker 有一个指令堆栈,其中包含所有应用的指令。底部指令是默认设置,永远无法删除。当文件加载附加指令时,它们将以相反的顺序添加,文件中的第一个指令最终位于堆栈顶部。这是一个可用性功能。

当提交方法进行编译时,compilerBroker 将选择第一个匹配的指令并将其传递给编译器。 compilerBroker 和编译器将忽略会创建错误代码的选项(例如,在不支持的平台上强制执行硬件指令),并且会发出适当的警告。指令选项与普通命令行标志具有相同的限制 - 例如,只有当 IR 不变得很大时,才会考虑强制内联。

命令行界面

可以在命令行中添加指令文件。如果标志错误(正常命令行解析)、文件丢失或文件内容格式错误,VM 将退出并显示错误消息。

-XX:CompilerDirectivesFile=<file>

诊断命令接口

这些是将与编译器控制一起使用的诊断命令:

jcmd <pid> Compiler.add_directives <file>   
Add additional directives from the file. The new directives will be added on top of the old, with the first directive in the file ending up on the top of the directives stack.

jcmd <pid> Compiler.list_directives
List all directives on the directives stack from top to bottom.

jcmd <pid> Compiler.clear_directives
Clear the directives stack

jcmd <pid> Compiler.remove_directives
Remove the top element from the directives stack

CompileCommand 和向后兼容性

在所有用例中,CompilerControl 应该取代 CompileCommand。 CompileCommand 将被保留以实现向后兼容性,目标是尽可能地保留行为。

可以应用四层控制。编译器控制将具有最高优先级并覆盖任何其他标志或命令。第二个是 CompileCommand,第三个是任何命令行标志,第四个是默认标志值。如果同时使用 Compiler 控件和 CompileCommand,Compiler 控件将认为 CompileCommand 覆盖默认值。

如果同时使用 CompileCommand 和编译器指令,JVM 应该打印警告。

方法模式

编译器控件将使用与 CompileCommand 相同的方法模式格式。该模式由三部分组成:包&类名、方法名和签名。这三个中的任何一个都可以是带有前导或尾随 * 的通配符。任意部分的默认值为*。

例子:

java/example/Test.split

由三部分组成

java/example/Test + split + (Ljava/lang/String;)Ljava/lang/String;

风险和假设

编译器选项的绝对数量将限制我们最初只关注一个子集。我们将专注于一个子集并从那里扩展。

依赖关系

  • 诊断命令 - 已就位
  • 使用完整的 JDK - 已经就位

影响

  • 文档:标志和 API
  • CCC:指令格式、JVM 编译器标志更改和 API 需要 CCC 请求。
  • 性能:标准回归测试