4 ms·
I see no reason why the same addressing mode could not be used in both versions. This just seems like an optimization that has been made under the assumption th
by sparkie 5y ago
I see no reason why the same addressing mode could not be used in both versions. This just seems like an optimization that has been made under the assumption that in the addressing mode
[reg1 + reg2 + disp]
reg2 refers to a signed integer (Which may result in the effective address being < reg1 if reg2 is negative).
Obviously you would not want to make the same assumption if reg2 refers to an unsigned integer, because if the most significant bit is set, it would result in an effective address below reg1, which is definitely not what we would expect from adding an unsigned integer.
But in this case, 64-bit addressing is being used, and the value of reg2 is a 32-bit integer which has been specifically zero-extended. We can make the assumption that [reg1 + reg2 + disp] can never result in an effective address below reg1 (assuming positive disp).
If you take the signed version, and replace the lines
movsxd rdi, edi
movsxd rcx, esi
with
movzx rdi, edi
movzx rcx, esi
I believe you will have something functionally equivalent to the unsigned version.
Of course, this same optimization could not apply if we were using uint64, but it could in the case of int64.
Either way, buggy code with no bounds checking is not a reliable method of determining what optimizations can be done in production.
- tom_ 5y agoThe code is compiled as it is in order to handle the case where the indexes need to wrap around. Suppose i1=0xffffffff on entry - you then want to access block[0x00000000ffffffff], block[0x0000000000000000], block[0x00000000000000001]. I've written out all 64 bits of the offset, in the hope this makes the problem clearer: there's no addressing mode for getting the 32 bit truncation. That's why there's an LEAs after each access. (Note that they write to the 32 bit part of each register.) In the int32_t case, suppose i1=-1. Then you want to access block[0xffffffffffffffff], block[0x0000000000000000], block[0x0000000000000001], and so on. You can do an initial sign extension of the indexes, and then the base64+index64+disp32 addressing mode gives you the right result. Make the unsigned version take uint64_t indices, and you get the same code for both. Or #include <stddef.h>, and make it take size_t - same thing. This is exactly the sort of thing that size_t is there for.