user: Converte this assembly snippet into its equivalent C code:

```asm
<qsort_helper>:
  endbr64
  push   %rbp
  mov    %rsp,%rbp
  sub    $0x30,%rsp
  mov    %rdi,-0x18(%rbp)
  mov    %esi,-0x1c(%rbp)
  mov    %edx,-0x20(%rbp)
  mov    %ecx,-0x24(%rbp)
  mov    %r8,-0x30(%rbp)
  mov    -0x1c(%rbp),%eax
  cmp    -0x20(%rbp),%eax
  jge    L29
  mov    -0x1c(%rbp),%eax
  imul   -0x24(%rbp),%eax
  movslq %eax,%rdx
  mov    -0x18(%rbp),%rax
  add    %rdx,%rax
  mov    %rax,-0x8(%rbp)
  mov    -0x1c(%rbp),%eax
  sub    $0x1,%eax
  mov    %eax,-0x10(%rbp)
  mov    -0x20(%rbp),%eax
  add    $0x1,%eax
  mov    %eax,-0xc(%rbp)
L68:
  addl   $0x1,-0x10(%rbp)
  mov    -0x10(%rbp),%eax
  imul   -0x24(%rbp),%eax
  movslq %eax,%rdx
  mov    -0x18(%rbp),%rax
  lea    (%rdx,%rax,1),%rcx
  mov    -0x8(%rbp),%rax
  mov    -0x30(%rbp),%rdx
  mov    %rax,%rsi
  mov    %rcx,%rdi
  call   *%rdx
  test   %eax,%eax
  jg     L3
  mov    -0x10(%rbp),%eax
  cmp    -0x20(%rbp),%eax
  jl     L68
L3:
  subl   $0x1,-0xc(%rbp)
  mov    -0xc(%rbp),%eax
  imul   -0x24(%rbp),%eax
  movslq %eax,%rdx
  mov    -0x18(%rbp),%rax
  lea    (%rdx,%rax,1),%rcx
  mov    -0x8(%rbp),%rax
  mov    -0x30(%rbp),%rdx
  mov    %rax,%rsi
  mov    %rcx,%rdi
  call   *%rdx
  test   %eax,%eax
  js     L10
  mov    -0xc(%rbp),%eax
  cmp    -0x1c(%rbp),%eax
  jg     L3
L10:
  mov    -0x10(%rbp),%eax
  cmp    -0xc(%rbp),%eax
  jge    L11
  mov    -0xc(%rbp),%eax
  imul   -0x24(%rbp),%eax
  movslq %eax,%rdx
  mov    -0x18(%rbp),%rax
  lea    (%rdx,%rax,1),%rsi
  mov    -0x10(%rbp),%eax
  imul   -0x24(%rbp),%eax
  movslq %eax,%rdx
  mov    -0x18(%rbp),%rax
  lea    (%rdx,%rax,1),%rcx
  mov    -0x24(%rbp),%eax
  mov    %eax,%edx
  mov    %rcx,%rdi
  call   <swap@plt>
  jmp    L68
L11:
  nop
  mov    -0xc(%rbp),%eax
  imul   -0x24(%rbp),%eax
  movslq %eax,%rdx
  mov    -0x18(%rbp),%rax
  lea    (%rdx,%rax,1),%rcx
  mov    -0x24(%rbp),%edx
  mov    -0x8(%rbp),%rax
  mov    %rax,%rsi
  mov    %rcx,%rdi
  call   <swap@plt>
  mov    -0xc(%rbp),%eax
  lea    -0x1(%rax),%edi
  mov    -0x30(%rbp),%rcx
  mov    -0x24(%rbp),%edx
  mov    -0x1c(%rbp),%esi
  mov    -0x18(%rbp),%rax
  mov    %rcx,%r8
  mov    %edx,%ecx
  mov    %edi,%edx
  mov    %rax,%rdi
  call   <qsort_helper>
  mov    -0xc(%rbp),%eax
  lea    0x1(%rax),%esi
  mov    -0x30(%rbp),%rdi
  mov    -0x24(%rbp),%ecx
  mov    -0x20(%rbp),%edx
  mov    -0x18(%rbp),%rax
  mov    %rdi,%r8
  mov    %rax,%rdi
  call   <qsort_helper>
L29:
  nop
  leave
  ret
```

assistant: ```c
void qsort_helper(void *base, int l, int r, int elem_size, int (*cmpfn)(void *, void *)) {
  if (l < r) {
    void *target = (char *)base + l * elem_size;
    int i = l - 1, j = r + 1;
    while (1) {
      do {
        i++;
      } while (cmpfn((char *)base + i * elem_size, target) <= 0 && i < r);
      do {
        j--;
      } while (cmpfn((char *)base + j * elem_size, target) >= 0 && j > l);
      if (i < j)
        swap((char *)base + i * elem_size, (char *)base + j * elem_size, elem_size);
      else
        break;
    }
    swap((char *)base + j * elem_size, target, elem_size);
    qsort_helper(base, l, j - 1, elem_size, cmpfn);
    qsort_helper(base, j + 1, r, elem_size, cmpfn);
  }
}
```