pinned instruction
Instructions that must be executed in the original order of the bytecodes are marked as pinned, printed out as “.” at the beginning of the instruction line. Examples for pinned instructions are loads and stores of fields, because they might have data dependencies. Additionally, instructions that do not compute a result or that are used across block boundaries are pinned for technical reasons.
被pin住的指令必须以原来字节码的顺序执行,比如load field和store field,因为他们可能有数据依赖。除此之外,出于一些原因,不计算结果的/跨越基本块使用的指令,也被pin住。
LIR的do_constant会注意到pinn指令:
// Code for a constant is generated lazily unless the constant is frequently used and can't be inlined.
void LIRGenerator::do_Constant(Constant* x) {
if (x->state_before() != NULL) {
// Any constant with a ValueStack requires patching so emit the patch here
LIR_Opr reg = rlock_result(x);
CodeEmitInfo* info = state_for(x, x->state_before());
__ oop2reg_patch(NULL, reg, info);
} else if (x->use_count() > 1 && !can_inline_as_constant(x)) {
if (!x->is_pinned()) {
// unpinned constants are handled specially so that they can be
// put into registers when they are used multiple times within a
// block. After the block completes their operand will be
// cleared so that other blocks can't refer to that register.
set_result(x, load_constant(x));
} else {
LIR_Opr res = x->operand();
if (!res->is_valid()) {
res = LIR_OprFact::value_type(x->type());
}
if (res->is_constant()) {
LIR_Opr reg = rlock_result(x);
__ move(res, reg);
} else {
set_result(x, res);
}
}
} else {
set_result(x, LIR_OprFact::value_type(x->type()));
}
}
假如指令没有pin住,它可能可以复用当前基本块中已有的常量(即_contants):
LIR_Opr LIRGenerator::load_constant(Constant* x) {
assert(!x->is_pinned(), "only for unpinned constants");
_unpinned_constants.append(x);
return load_constant(LIR_OprFact::value_type(x->type())->as_constant_ptr());
}
LIR_Opr LIRGenerator::load_constant(LIR_Const* c) {
BasicType t = c->type();
for (int i = 0; i < _constants.length(); i++) {
LIR_Const* other = _constants.at(i);
if (t == other->type()) {
switch (t) {
case T_INT:
case T_FLOAT:
if (c->as_jint_bits() != other->as_jint_bits()) continue;
break;
case T_LONG:
case T_DOUBLE:
if (c->as_jint_hi_bits() != other->as_jint_hi_bits()) continue;
if (c->as_jint_lo_bits() != other->as_jint_lo_bits()) continue;
break;
case T_OBJECT:
if (c->as_jobject() != other->as_jobject()) continue;
break;
default:
break;
}
return _reg_for_constants.at(i);
}
}
LIR_Opr result = new_register(t);
__ move((LIR_Opr)c, result);
if (!in_conditional_code()) {
_constants.append(c);
_reg_for_constants.append(result);
}
return result;
}
假如常量多次使用,且指令被pinned了,就只能老老实实将常量值加载到寄存器。
fixed reg
LIR generator里面默认都是分配虚拟寄存器来储存值,但是有些指令要求特定的寄存器,比如x64的calling convention要求假如返回一个int值,这个值必须放到eax,这时候就不能为返回值分配虚拟寄存器。所以在LIRGenerator里面必须要指明这一点,让后面的线性分配器懂得如何分配:
void LIRGenerator::do_Return(Return* x) {
...
if (x->type()->is_void()) {
__ return_op(LIR_OprFact::illegalOpr);
} else {
LIR_Opr reg = result_register_for(x->type(), /*callee=*/true);
LIRItem result(x->result(), this);
result.load_item_force(reg);
__ return_op(result.result());
}
set_no_result(x);
}
首先根据值x的类型选择寄存器reg,然后load_item_force强制用这个reg储存这个LIR的result。,result_register_for实现如下:
LIR_Opr LIRGenerator::result_register_for(ValueType* type, bool callee) {
LIR_Opr opr;
switch (type->tag()) {
case intTag: opr = FrameMap::rax_opr; break;
case objectTag: opr = FrameMap::rax_oop_opr; break;
case longTag: opr = FrameMap::long0_opr; break;
#ifdef _LP64
case floatTag: opr = FrameMap::xmm0_float_opr; break;
case doubleTag: opr = FrameMap::xmm0_double_opr; break;
#else
case floatTag: opr = UseSSE >= 1 ? FrameMap::xmm0_float_opr : FrameMap::fpu0_float_opr; break;
case doubleTag: opr = UseSSE >= 2 ? FrameMap::xmm0_double_opr : FrameMap::fpu0_double_opr; break;
#endif // _LP64
case addressTag:
default: ShouldNotReachHere(); return LIR_OprFact::illegalOpr;
}
assert(opr->type_field() == as_OprType(as_BasicType(type)), "type mismatch");
return opr;
}
可以看到,如果是int类型,就用rax,如果是float类型,就用xmm0。