user: Please show me the C implementation that matches the assembly code listed below:

```asm
<bn_mul_add_words>:
  endbr64
  mov    %rcx,%r9
  mov    %edx,%r8d
  xor    %ecx,%ecx
  test   %edx,%edx
  jle    L31
  test   $0xfffffffc,%edx
  je     L18
L50:
  mov    %r9,%rax
  sub    $0x4,%r8d
  add    $0x20,%rsi
  mulq   -0x20(%rsi)
  add    %rax,%rcx
  adc    $0x0,%rdx
  add    %rcx,(%rdi)
  adc    $0x0,%rdx
  mov    %rdx,%r10
  mov    %r9,%rax
  mov    %r10,%rcx
  mulq   -0x18(%rsi)
  add    %rax,%rcx
  adc    $0x0,%rdx
  mov    %r9,%rax
  add    %rcx,0x8(%rdi)
  adc    $0x0,%rdx
  mov    %rdx,%r10
  mulq   -0x10(%rsi)
  add    %rax,%r10
  adc    $0x0,%rdx
  mov    %r9,%rax
  add    %r10,0x10(%rdi)
  adc    $0x0,%rdx
  mov    %rdx,%rcx
  mulq   -0x8(%rsi)
  add    %rax,%rcx
  adc    $0x0,%rdx
  add    %rcx,0x18(%rdi)
  adc    $0x0,%rdx
  add    $0x20,%rdi
  mov    %rdx,%rcx
  test   $0xfffffffc,%r8d
  jne    L50
  test   %r8d,%r8d
  jne    L18
L31:
  mov    %rcx,%rax
  ret
  nopl   0x0(%rax)
L18:
  mov    %r9,%rax
  mulq   (%rsi)
  add    %rax,%rcx
  adc    $0x0,%rdx
  add    %rcx,(%rdi)
  adc    $0x0,%rdx
  mov    %rdx,%rcx
  cmp    $0x1,%r8d
  je     L31
  mov    %r9,%rax
  mulq   0x8(%rsi)
  add    %rax,%rcx
  adc    $0x0,%rdx
  add    %rcx,0x8(%rdi)
  adc    $0x0,%rdx
  mov    %rdx,%rcx
  cmp    $0x2,%r8d
  je     L31
  mov    %r9,%rax
  mulq   0x10(%rsi)
  add    %rax,%rcx
  adc    $0x0,%rdx
  add    %rcx,0x10(%rdi)
  adc    $0x0,%rdx
  mov    %rdx,%rcx
  mov    %rcx,%rax
  ret
```

assistant: ```c
unsigned long bn_mul_add_words(unsigned long *rp, const unsigned long *ap, int num, unsigned long w) {
  unsigned long c1 = 0;
  if (num <= 0)
    return (c1);
  while (num & ~3) {
    do {
      register unsigned long high, low;
      asm("mulq %3" : "=a"(low), "=d"(high) : "a"(w), "m"(ap[0]) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+r"(c1), "+d"(high) : "a"(low), "g"(0) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+m"(rp[0]), "+d"(high) : "r"(c1), "g"(0) : "cc");
      c1 = high;
    } while (0);
    do {
      register unsigned long high, low;
      asm("mulq %3" : "=a"(low), "=d"(high) : "a"(w), "m"(ap[1]) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+r"(c1), "+d"(high) : "a"(low), "g"(0) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+m"(rp[1]), "+d"(high) : "r"(c1), "g"(0) : "cc");
      c1 = high;
    } while (0);
    do {
      register unsigned long high, low;
      asm("mulq %3" : "=a"(low), "=d"(high) : "a"(w), "m"(ap[2]) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+r"(c1), "+d"(high) : "a"(low), "g"(0) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+m"(rp[2]), "+d"(high) : "r"(c1), "g"(0) : "cc");
      c1 = high;
    } while (0);
    do {
      register unsigned long high, low;
      asm("mulq %3" : "=a"(low), "=d"(high) : "a"(w), "m"(ap[3]) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+r"(c1), "+d"(high) : "a"(low), "g"(0) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+m"(rp[3]), "+d"(high) : "r"(c1), "g"(0) : "cc");
      c1 = high;
    } while (0);
    ap += 4;
    rp += 4;
    num -= 4;
  }
  if (num) {
    do {
      register unsigned long high, low;
      asm("mulq %3" : "=a"(low), "=d"(high) : "a"(w), "m"(ap[0]) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+r"(c1), "+d"(high) : "a"(low), "g"(0) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+m"(rp[0]), "+d"(high) : "r"(c1), "g"(0) : "cc");
      c1 = high;
    } while (0);
    if (--num == 0)
      return c1;
    do {
      register unsigned long high, low;
      asm("mulq %3" : "=a"(low), "=d"(high) : "a"(w), "m"(ap[1]) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+r"(c1), "+d"(high) : "a"(low), "g"(0) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+m"(rp[1]), "+d"(high) : "r"(c1), "g"(0) : "cc");
      c1 = high;
    } while (0);
    if (--num == 0)
      return c1;
    do {
      register unsigned long high, low;
      asm("mulq %3" : "=a"(low), "=d"(high) : "a"(w), "m"(ap[2]) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+r"(c1), "+d"(high) : "a"(low), "g"(0) : "cc");
      asm("addq %2,%0; adcq %3,%1" : "+m"(rp[2]), "+d"(high) : "r"(c1), "g"(0) : "cc");
      c1 = high;
    } while (0);
    return c1;
  }
  return (c1);
}
```