|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v7 2/5] x86/asm: add volatile, clobbers and zero-length check in inline memcmp
On July 23, 2026 5:35:02 PM PDT, Mauricio Faria de Oliveira <mfo@xxxxxxxxxx>
wrote:
>On 2026-07-23 20:12, H. Peter Anvin wrote:
>> On 2026-07-22 23:59, Jan Beulich wrote:
>>>>
>>>> Also, this is silly. Instead of adding a whole separate test, just do
>>>> "test %3,%3" before the repe to set ZF and let the REPE skip.
>>>
>>> Besides this, isn't the function effectively returning bool wrong anyway?
>>> This
>>> way you can use it for equal / not-equal comparisons, but not for sorting
>>> and
>>> alike.
>>>
>> There is no use case in the early code for sorting, and it seems rather
>> broken
>> to burden the code with that.
>
>If this is merged, other users not in early code might eventually
>appear, which might introduce the use case for sorting. Even though that
>is unlikely, as such users could probably use regular memcmp() instead,
>consider that just in case, so I can ask for your input/advice on this:
>
>> That being said it probably should return bool explicitly (it makes no sense
>> for the prototype to be different than the internal variable.)
>>
>> We could call it memneq() if someone really, really cares, I guess.
>>
>> The early code is very size-sensitive, so I'm really not fond of the idea of
>> burdening it further. Perhaps something like:
>
>Do you think this implementation (returns -1/0/+1) is reasonable,
>size-wise, for early code?
>
>I considered submitting it eventually, once the return value difference
>to regular memcmp() was called out [0], as an improvement, if there's
>agreement this would be a good idea considering the scenario above.
>
>static __always_inline int __inline_memcmp(const void *s1, const void
>*s2, size_t len)
>{
> int above, below;
>
> asm volatile("test %2, %2\n\t"
> "repe cmpsb"
> : "+S" (s1), "+D" (s2), "+c" (len),
> "=@cca" (above), "=@ccb" (below)
> : : "memory");
>
> return above - below;
>}
>
>@ arch/x86/boot/string.o
>00000028 <memcmp>:
> 28: 66 57 push %di
> 2a: 66 56 push %si
> 2c: 66 89 c6 mov %ax,%si
> 2f: 66 89 d7 mov %dx,%di
>
> 32: 66 85 c9 test %cx,%cx
> 35: f3 a6 repz cmpsb %es:(%edi),%ds:(%esi)
> 37: 0f 97 c0 seta %al
> 3a: 66 0f b6 c0 movzbw %al,%ax
> 3e: 0f 92 c2 setb %dl
> 41: 66 0f b6 d2 movzbw %dl,%dx
> 45: 66 29 d0 sub %dx,%ax
>
> 48: 66 5e pop %si
> 4a: 66 5f pop %di
> 4c: 66 c3 retw
>
>> A memory clobber is ugly here since no memory is actually modified, although
>
>I'm definitely not an expert, but IIUIC, this memory clobber is for
>reads, not writes? Say, a caller/optimized code that writes to the
>buffer(s) prior to __inline_memcmp() and data might still reside in
>registers; even if theoretical/unlikely.
>
>As in [1]:
>
> The "memory" clobber tells the compiler that the assembly code
>performs memory reads or writes [...] (for example, accessing the memory
>pointed to [...]). To ensure memory contains correct values, GCC may
>need to flush specific register values to memory before executing the
>asm.
>
>> it probably doesn't affect code; "cc" is completely redundant with condition
>> code output operand.
>
>Thanks for explaining. I'll remove that in the next version.
>
>[0]
>https://lore.kernel.org/all/324ef97b16f52e0ccc72f6381d1b5dd2@xxxxxxxxxx/
>[1]
>https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clobbers-and-Scratch-Registers-1
>
>cheers,
>
>> static __always_inline bool
>> __inline_memcmp(const void *s1, const void *s2, size_t len)
>> {
>> bool diff;
>>
>> if (__builtin_constant_p(len == 0)) {
>> if (!len)
>> return false;
>> asm volatile("repe cmpsb"
>> : "=@ccnz" (diff),
>> "+D" (s1), "+S" (s2), "+c" (len)
>> : : "memory");
>> } else {
>> /* Clear ZF beforehand in case len == 0 */
>> asm volatile("test %3,%3; repe cmpsb"
>> : "=@ccnz" (diff),
>> "+D" (s1), "+S" (s2), "+c" (len)
>> : : "memory");
>> }
>> return diff;
>> }
>
>
It's pretty bloaty; I don't know exactly how much the rest of the code bloats.
I would be fine with having mem[n]eq() though as an explicit API. For the
out-of-line version used for compiler-generated memcmp() calls it doesn't
matter.
Here is a very compact way the out-of-line version can be written (showing the
64-bit version; formatting apologies, on a phone):
xor %eax,%eax /* Sets ZF */
repe cmpsb
jr z,1f
movzbl -1(%rdi),%edi
mov -1(%rsi),%al
sub %edi,%eax
1: ret
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |