C1's usage of 32-bit registers whose part of 64-bit registers on amd64
Christian Thalinger
christian.thalinger at oracle.com
Tue Mar 11 03:56:42 UTC 2014
On Mar 10, 2014, at 7:52 PM, Igor Veresov <igor.veresov at oracle.com> wrote:
> I think everything should be zero-extended by default on x64. The invariant should be supported by using only 32bit ops on 32bit arguments and using zero-extending loads. Not sure why we do sign extension in the element address formation, zero-extending would seem to be enough (which should be a no-op on x64).
I think the main reason C1 does a sign-extend on 64-bit is because pointers have the type T_LONG and we need the index register to be a T_LONG as well. Additionally to be able to reuse existing machinery we just do an I2L:
#ifdef _LP64
if (index_opr->type() == T_INT) {
LIR_Opr tmp = new_register(T_LONG);
__ convert(Bytecodes::_i2l, index_opr, tmp);
index_opr = tmp;
}
#endif
>
> igor
>
> On Mar 10, 2014, at 5:06 PM, Krystal Mok <rednaxelafx at gmail.com> wrote:
>
>> Hi all,
>>
>> I'd like to ask a couple of questions on C1's usage of 32-bit registers on amd64, when they're a part of the corresponding 64-bit register (e.g. ESI vs RSI).
>>
>> 1. Does C1 ensure the high 32 bits of a 64-bit register is cleared when using it as a 32-bit register? If so, where does C1 enforce that?
>>
>> I see that for array indexing, C1 generates code that uses 64-bit register whose actual value is only stored in the low 32-bit part, e.g.
>>
>> static int foo(int[] a, int i) {
>> return a[i];
>> }
>>
>> the actual load in C1 generated code would be (in AT&T syntax):
>>
>> mov 0x10(%rsi,%rax,4),%eax
>>
>> and there's an instruction prior to it that explicitly clears the high 32 bits,
>>
>> movslq %edx,%rax
>>
>> generated by LIRGenerator::emit_array_address().
>>
>> So it's an invariant property enforced throughout C1, right?
>>
>> 2. There a piece of code in C1's linear scan register allocator that removes useless moves:
>>
>> http://hg.openjdk.java.net/jdk9/hs-comp/hotspot/file/480b0109db65/src/share/vm/c1/c1_LinearScan.cpp#l2996
>>
>> // remove useless moves
>> if (op->code() == lir_move) {
>> assert(op->as_Op1() != NULL, "move must be LIR_Op1");
>> LIR_Op1* move = (LIR_Op1*)op;
>> LIR_Opr src = move->in_opr();
>> LIR_Opr dst = move->result_opr();
>> if (dst == src ||
>> !dst->is_pointer() && !src->is_pointer() &&
>> src->is_same_register(dst)) {
>> instructions->at_put(j, NULL);
>> has_dead = true;
>> }
>> }
>>
>> and I'd like to ask two questions about it:
>>
>> 2.1: On amd64, moving between a 32-bit register and themselves has the side effect of clearing the high 32 bits of the corresponding 64-bit register. So the code being removed isn't entirely side-effect free. It's only safe to remove them if there's an invariant from question 1 holds.
>>
>> 2.2 This piece of code explicitly checks !LIR_Opr::is_pointer(). Why is this check needed? Could anybody share the history behind it?
>> I thought LIR_Opr::is_same_register() checks LIR_Opr::is_register() which is stricter than !is_pointer(), which seems to make the !is_pointer() check redundant.
>>
>> Thanks,
>> Kris
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/attachments/20140310/a9c3c3e7/attachment.html>
More information about the hotspot-compiler-dev
mailing list