OOL(越线)代码
OOL (out of line) code
什么是 OOL(Out of line)代码?我在 ION 编译器中找到了它,但无法理解发生了什么。
bool CodeGeneratorShared::generateOutOfLineCode() {
for (size_t i = 0; i < outOfLineCode_.length(); i++) {
// Add native => bytecode mapping entries for OOL sites.
// Not enabled on asm.js yet since asm doesn't contain bytecode mappings.
if (!gen->compilingAsmJS()) {
if (!addNativeToBytecodeEntry(outOfLineCode_[i]->bytecodeSite()))
return false;
}
if (!gen->alloc().ensureBallast())
return false;
JitSpew(JitSpew_Codegen, "# Emitting out of line code");
masm.setFramePushed(outOfLineCode_[i]->framePushed());
lastPC_ = outOfLineCode_[i]->pc();
outOfLineCode_[i]->bind(&masm);
outOfLineCode_[i]->generate(this);
}
return !masm.oom();
}
我曾尝试使用 google 查找有关它的信息,但没有成功。也许你可以告诉我它是什么?谢谢:)
我查了下源码,这里的"out of line"好像是正常code/function.
后生成的代码
参见例如CodeGenerator::generate
基本上是这样的:
generateProlog();
generateBody();
generateEpilog();
generateOutOfLineCode();
所以代码结束后会产生越界代码。这通常用于异常控制流,并将调用去优化、抛出异常等的代码保留在指令缓存和 "normal" 程序代码之外。
假设我们有一个函数 int f(int a, int b) { return a / b; }
并且语言语义强制我们在除数为 0 时抛出异常。这是伪汇编中的代码:
cmp b, 0
jump-if-not-zero lbl1
call throw_exception
lbl1:
div c, a, b
ret c
可以看到正常的程序流程需要跳过抛出异常的代码。通常 b 在几乎所有情况下都是非零的,所以这看起来有点浪费。使用越界代码,我们可以生成更高效的代码:
cmp b, 0
jump-if-zero out-of-line1
div c, a, b
ret c
out-of-line1:
call throw_exception
这里我们只跳零值,这应该很少见。 cmp
和 div
指令也更接近彼此,这有利于指令缓存的使用。
在我的 JIT 中,我正在为空指针异常抛出、失败断言等生成行外代码。JS 和 IonMonkey 可能将它用于不同的操作。我发现的越界代码的一个示例是 WASM 的 class OutOfLineTruncateF32OrF64ToI32
,它扩展了 OutOfLineCode
所有越界代码的基础 class。
还有一个好处是 IonMonkey 中的越界代码可以使用字段 rejoin
跳回到正常的代码流。
什么是 OOL(Out of line)代码?我在 ION 编译器中找到了它,但无法理解发生了什么。
bool CodeGeneratorShared::generateOutOfLineCode() {
for (size_t i = 0; i < outOfLineCode_.length(); i++) {
// Add native => bytecode mapping entries for OOL sites.
// Not enabled on asm.js yet since asm doesn't contain bytecode mappings.
if (!gen->compilingAsmJS()) {
if (!addNativeToBytecodeEntry(outOfLineCode_[i]->bytecodeSite()))
return false;
}
if (!gen->alloc().ensureBallast())
return false;
JitSpew(JitSpew_Codegen, "# Emitting out of line code");
masm.setFramePushed(outOfLineCode_[i]->framePushed());
lastPC_ = outOfLineCode_[i]->pc();
outOfLineCode_[i]->bind(&masm);
outOfLineCode_[i]->generate(this);
}
return !masm.oom();
}
我曾尝试使用 google 查找有关它的信息,但没有成功。也许你可以告诉我它是什么?谢谢:)
我查了下源码,这里的"out of line"好像是正常code/function.
后生成的代码参见例如CodeGenerator::generate
基本上是这样的:
generateProlog();
generateBody();
generateEpilog();
generateOutOfLineCode();
所以代码结束后会产生越界代码。这通常用于异常控制流,并将调用去优化、抛出异常等的代码保留在指令缓存和 "normal" 程序代码之外。
假设我们有一个函数 int f(int a, int b) { return a / b; }
并且语言语义强制我们在除数为 0 时抛出异常。这是伪汇编中的代码:
cmp b, 0
jump-if-not-zero lbl1
call throw_exception
lbl1:
div c, a, b
ret c
可以看到正常的程序流程需要跳过抛出异常的代码。通常 b 在几乎所有情况下都是非零的,所以这看起来有点浪费。使用越界代码,我们可以生成更高效的代码:
cmp b, 0
jump-if-zero out-of-line1
div c, a, b
ret c
out-of-line1:
call throw_exception
这里我们只跳零值,这应该很少见。 cmp
和 div
指令也更接近彼此,这有利于指令缓存的使用。
在我的 JIT 中,我正在为空指针异常抛出、失败断言等生成行外代码。JS 和 IonMonkey 可能将它用于不同的操作。我发现的越界代码的一个示例是 WASM 的 class OutOfLineTruncateF32OrF64ToI32
,它扩展了 OutOfLineCode
所有越界代码的基础 class。
还有一个好处是 IonMonkey 中的越界代码可以使用字段 rejoin
跳回到正常的代码流。