Re: [PATCH v7 2/5] x86/asm: add volatile, cl obbers and zero-length check in inline memcmp

"H. Peter Anvin" <[email protected]>
Newsgroups org.xenproject.lists.xen-devel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On July 23, 2026 5:35:02 PM PDT, Mauricio Faria de Oliveira <[email protected]> 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/[email protected]/
>[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.