C1's usage of 32-bit registers whose part of 64-bit registers on amd64

Igor Veresov igor.veresov at oracle.com
Tue Mar 11 07:20:50 UTC 2014


No, it’s quite the opposite. Upper 32bits should be clear (zeros) for 32bit values on x64. Moreover, C2 relies on the fact the on x64 32bit ints have upper word with zeros. So if you plan to call C2-compiled methods this must hold. Addressing requires that you use full 64-bit registers for the base and index, so if your index is 32bit, you must make it 64-bit one way on another.

On SPARC however it’s another story, so you can’t rely on this in platform-independent way.

igor

On Mar 10, 2014, at 11:38 PM, Krystal Mok <rednaxelafx at gmail.com> wrote:

> Hi Igor and Christian,
> 
> Thanks a lot for your replies. I think my first question about the invariant boils down to these:
> 
> 1. I can't trust any 64-bit register used as a 32-bit int to have its high 32 bits cleared, so: I have to always use 32-bit ops when possible; when having to use it in addressing, explicitly clear the high 32 bits.
> 
> 2. The only special case of having to explicitly clear the high 32 bits is array addressing.
> 
> Are these statements correct?
> 
> Also, any thoughts on the second question on removing useless moves?
> 
> Thanks,
> Kris
> 
> 
> On Mon, Mar 10, 2014 at 8:56 PM, Christian Thalinger <christian.thalinger at oracle.com> wrote:
> 
> 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/20140311/752561c0/attachment.html>


More information about the hotspot-compiler-dev mailing list