stdlib: Avoid element self-comparisons in qsort

Message ID 87leb9bl3k.fsf@oldenburg.str.redhat.com
State Committed
Commit f8cfb6836e8d91bb789b2e7fd65338d6f5bd459c
Headers
Series stdlib: Avoid element self-comparisons in qsort |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Testing passed

Commit Message

Florian Weimer Nov. 7, 2023, 4:10 p.m. UTC
  This improves compatibility with applications which assume that qsort
does not invoke the comparison function with equal pointer arguments.

The newly introduced branches should be predictable, as leading to a
call to the comparison function.  If the prediction fails, we avoid
calling the function.

Tested on x86_64-linux-gnu.  I don't know if it papers over the LLVM
problem, but the line number in the posted backtrace for the assertion
failure points to the first while statement that the patch changes.

Thanks,
Florian

---
 stdlib/qsort.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)


base-commit: 5dd3bda59c2d9da138f0d98808d087cdb95cdc17
  

Comments

Adhemerval Zanella Netto Nov. 8, 2023, 1:10 p.m. UTC | #1
On 07/11/23 13:10, Florian Weimer wrote:
> This improves compatibility with applications which assume that qsort
> does not invoke the comparison function with equal pointer arguments.
> 
> The newly introduced branches should be predictable, as leading to a
> call to the comparison function.  If the prediction fails, we avoid
> calling the function.
> 
> Tested on x86_64-linux-gnu.  I don't know if it papers over the LLVM
> problem, but the line number in the posted backtrace for the assertion
> failure points to the first while statement that the patch changes.
> 
> Thanks,
> Florian
> 

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  stdlib/qsort.c | 8 +++++---
>  1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/stdlib/qsort.c b/stdlib/qsort.c
> index fd32a165e7..ad110e8a89 100644
> --- a/stdlib/qsort.c
> +++ b/stdlib/qsort.c
> @@ -136,7 +136,7 @@ siftdown (void *base, size_t size, size_t k, size_t n,
>        if (j < n && cmp (base + (j * size), base + ((j + 1) * size), arg) < 0)
>  	j++;
>  
> -      if (cmp (base + (k * size), base + (j * size), arg) >= 0)
> +      if (j == k || cmp (base + (k * size), base + (j * size), arg) >= 0)
>  	break;
>  
>        do_swap (base + (size * j), base + (k * size), size, swap_type);
> @@ -332,10 +332,12 @@ __qsort_r (void *const pbase, size_t total_elems, size_t size,
>  	     that this algorithm runs much faster than others. */
>  	  do
>  	    {
> -	      while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
> +	      while (left_ptr != mid
> +		     && (*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
>  		left_ptr += size;
>  
> -	      while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
> +	      while (right_ptr != mid
> +		     && (*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
>  		right_ptr -= size;
>  
>  	      if (left_ptr < right_ptr)
> 
> base-commit: 5dd3bda59c2d9da138f0d98808d087cdb95cdc17
>
  

Patch

diff --git a/stdlib/qsort.c b/stdlib/qsort.c
index fd32a165e7..ad110e8a89 100644
--- a/stdlib/qsort.c
+++ b/stdlib/qsort.c
@@ -136,7 +136,7 @@  siftdown (void *base, size_t size, size_t k, size_t n,
       if (j < n && cmp (base + (j * size), base + ((j + 1) * size), arg) < 0)
 	j++;
 
-      if (cmp (base + (k * size), base + (j * size), arg) >= 0)
+      if (j == k || cmp (base + (k * size), base + (j * size), arg) >= 0)
 	break;
 
       do_swap (base + (size * j), base + (k * size), size, swap_type);
@@ -332,10 +332,12 @@  __qsort_r (void *const pbase, size_t total_elems, size_t size,
 	     that this algorithm runs much faster than others. */
 	  do
 	    {
-	      while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
+	      while (left_ptr != mid
+		     && (*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
 		left_ptr += size;
 
-	      while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
+	      while (right_ptr != mid
+		     && (*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
 		right_ptr -= size;
 
 	      if (left_ptr < right_ptr)