char *ultoa(unsigned long val, char *s, int radix) {
  char *_s = s;
  if (radix >= 2 && radix <= 36) {
    char buf[sizeof(val) * 8];
    int i = 0;
    do {
      int mod = val % radix;
      buf[i++] = (mod < 10) ? '0' + mod : 'a' + mod - 10;
      val /= radix;
    } while (val > 0);
    do {
      *_s++ = buf[--i];
    } while (i > 0);
  }
  *_s = '\0';
  return s;
}


x86 assembly (clang, -O0):
<ultoa>:
push   %rbp
mov    %rsp,%rbp
mov    %rdi,-0x8(%rbp)
mov    %rsi,-0x10(%rbp)
mov    %edx,-0x14(%rbp)
mov    -0x10(%rbp),%rax
mov    %rax,-0x20(%rbp)
cmpl   $0x2,-0x14(%rbp)
jl     11ce <ultoa+0xce>
cmpl   $0x24,-0x14(%rbp)
jg     11ce <ultoa+0xce>
movl   $0x0,-0x64(%rbp)
mov    -0x8(%rbp),%rax
movslq -0x14(%rbp),%rcx
xor    %edx,%edx
div    %rcx
mov    %edx,%eax
mov    %eax,-0x68(%rbp)
cmpl   $0xa,-0x68(%rbp)
jge    115c <ultoa+0x5c>
mov    -0x68(%rbp),%eax
add    $0x30,%eax
mov    %eax,-0x6c(%rbp)
jmp    1168 <ultoa+0x68>
mov    -0x68(%rbp),%eax
add    $0x61,%eax
sub    $0xa,%eax
mov    %eax,-0x6c(%rbp)
mov    -0x6c(%rbp),%eax
mov    %al,%cl
mov    -0x64(%rbp),%eax
mov    %eax,%edx
add    $0x1,%edx
mov    %edx,-0x64(%rbp)
cltq
mov    %cl,-0x60(%rbp,%rax,1)
movslq -0x14(%rbp),%rcx
mov    -0x8(%rbp),%rax
xor    %edx,%edx
div    %rcx
mov    %rax,-0x8(%rbp)
cmpq   $0x0,-0x8(%rbp)
ja     1132 <ultoa+0x32>
jmp    119f <ultoa+0x9f>
mov    -0x64(%rbp),%eax
add    $0xffffffff,%eax
mov    %eax,-0x64(%rbp)
cltq
mov    -0x60(%rbp,%rax,1),%cl
mov    -0x20(%rbp),%rax
mov    %rax,%rdx
add    $0x1,%rdx
mov    %rdx,-0x20(%rbp)
mov    %cl,(%rax)
cmpl   $0x0,-0x64(%rbp)
jg     119f <ultoa+0x9f>
jmp    11ce <ultoa+0xce>
mov    -0x20(%rbp),%rax
movb   $0x0,(%rax)
mov    -0x10(%rbp),%rax
pop    %rbp
ret

x86 assembly (clang, -O2):
<ultoa>:
push   %rbx
mov    %rsi,%r8
lea    -0x2(%rdx),%ecx
mov    %rsi,%rax
cmp    $0x22,%ecx
ja     1276 <ultoa+0x176>
mov    %edx,%r9d
xor    %r11d,%r11d
movabs $0x100000000,%r10
xor    %esi,%esi
jmp    1157 <ultoa+0x57>
nopw   0x0(%rax,%rax,1)
mov    %rdi,%rax
xor    %edx,%edx
div    %r9
lea    0x57(%rdx),%ecx
lea    0x30(%rdx),%ebx
cmp    $0xa,%edx
cmovge %ecx,%ebx
mov    %bl,-0x40(%rsp,%rsi,1)
add    $0x1,%rsi
add    %r10,%r11
cmp    %r9,%rdi
mov    %rax,%rdi
jb     1169 <ultoa+0x69>
mov    %rdi,%rax
shr    $0x20,%rax
jne    1130 <ultoa+0x30>
mov    %edi,%eax
xor    %edx,%edx
div    %r9d
jmp    1138 <ultoa+0x38>
sar    $0x20,%r11
test   %r11,%r11
mov    $0x1,%eax
cmovle %r11,%rax
mov    $0x1,%edx
neg    %rax
lea    (%r11,%rax,1),%r9
add    $0x1,%r9
cmp    $0x20,%r9
jae    1197 <ultoa+0x97>
mov    %r8,%rax
jmp    1254 <ultoa+0x154>
mov    %r9,%r10
and    $0xffffffffffffffe0,%r10
sub    %r10,%r11
lea    (%r8,%r10,1),%rax
movslq %esi,%rcx
lea    (%rsp,%rcx,1),%rsi
add    $0xffffffffffffffb0,%rsi
test   %rcx,%rcx
cmovle %rcx,%rdx
neg    %rdx
add    %rcx,%rdx
add    $0x1,%rdx
and    $0xffffffffffffffe0,%rdx
xor    %edi,%edi
pxor   %xmm0,%xmm0
nopl   0x0(%rax,%rax,1)
movdqu -0x10(%rsi),%xmm1
movdqu (%rsi),%xmm2
movdqa %xmm2,%xmm3
punpcklbw %xmm0,%xmm3
pshufd $0x4e,%xmm3,%xmm3
pshuflw $0x1b,%xmm3,%xmm3
pshufhw $0x1b,%xmm3,%xmm3
punpckhbw %xmm0,%xmm2
pshufd $0x4e,%xmm2,%xmm2
pshuflw $0x1b,%xmm2,%xmm2
pshufhw $0x1b,%xmm2,%xmm2
packuswb %xmm3,%xmm2
movdqa %xmm1,%xmm3
punpcklbw %xmm0,%xmm3
pshufd $0x4e,%xmm3,%xmm3
pshuflw $0x1b,%xmm3,%xmm3
pshufhw $0x1b,%xmm3,%xmm3
punpckhbw %xmm0,%xmm1
pshufd $0x4e,%xmm1,%xmm1
pshuflw $0x1b,%xmm1,%xmm1
pshufhw $0x1b,%xmm1,%xmm1
packuswb %xmm3,%xmm1
movdqu %xmm2,(%r8,%rdi,1)
movdqu %xmm1,0x10(%r8,%rdi,1)
add    $0x20,%rdi
add    $0xffffffffffffffe0,%rsi
cmp    %rdi,%rdx
jne    11d0 <ultoa+0xd0>
cmp    %r10,%r9
je     1276 <ultoa+0x176>
add    $0x1,%r11
nopl   0x0(%rax,%rax,1)
movzbl -0x42(%rsp,%r11,1),%ecx
mov    %cl,(%rax)
add    $0x1,%rax
add    $0xffffffffffffffff,%r11
cmp    $0x1,%r11
jg     1260 <ultoa+0x160>
movb   $0x0,(%rax)
mov    %r8,%rax
pop    %rbx
ret

LLVM IR (-O0):
define i8* @ultoa(i64 %0, i8* %1, i32 %2) {
%4 = alloca i64
%5 = alloca i8*
%6 = alloca i32
%7 = alloca i8*
%8 = alloca [64 x i8]
%9 = alloca i32
%10 = alloca i32
store i64 %0, i64* %4
store i8* %1, i8** %5
store i32 %2, i32* %6
%11 = load i8*, i8** %5
store i8* %11, i8** %7
%12 = load i32, i32* %6
%13 = icmp sge i32 %12, 2
br i1 %13, label %14, label %60
14: ; preds = %3
%15 = load i32, i32* %6
%16 = icmp sle i32 %15, 36
br i1 %16, label %17, label %60
17: ; preds = %14
store i32 0, i32* %9
br label %18
18: ; preds = %44, %17
%19 = load i64, i64* %4
%20 = load i32, i32* %6
%21 = sext i32 %20 to i64
%22 = urem i64 %19, %21
%23 = trunc i64 %22 to i32
store i32 %23, i32* %10
%24 = load i32, i32* %10
%25 = icmp slt i32 %24, 10
br i1 %25, label %26, label %29
26: ; preds = %18
%27 = load i32, i32* %10
%28 = add nsw i32 48, %27
br label %33
29: ; preds = %18
%30 = load i32, i32* %10
%31 = add nsw i32 97, %30
%32 = sub nsw i32 %31, 10
br label %33
33: ; preds = %29, %26
%34 = phi i32 [ %28, %26 ], [ %32, %29 ]
%35 = trunc i32 %34 to i8
%36 = load i32, i32* %9
%37 = add nsw i32 %36, 1
store i32 %37, i32* %9
%38 = sext i32 %36 to i64
%39 = getelementptr inbounds [64 x i8], [64 x i8]* %8, i64 0, i64 %38
store i8 %35, i8* %39
%40 = load i32, i32* %6
%41 = sext i32 %40 to i64
%42 = load i64, i64* %4
%43 = udiv i64 %42, %41
store i64 %43, i64* %4
br label %44
44: ; preds = %33
%45 = load i64, i64* %4
%46 = icmp ugt i64 %45, 0
br i1 %46, label %18, label %47
47: ; preds = %44
br label %48
48: ; preds = %56, %47
%49 = load i32, i32* %9
%50 = add nsw i32 %49, -1
store i32 %50, i32* %9
%51 = sext i32 %50 to i64
%52 = getelementptr inbounds [64 x i8], [64 x i8]* %8, i64 0, i64 %51
%53 = load i8, i8* %52
%54 = load i8*, i8** %7
%55 = getelementptr inbounds i8, i8* %54, i32 1
store i8* %55, i8** %7
store i8 %53, i8* %54
br label %56
56: ; preds = %48
%57 = load i32, i32* %9
%58 = icmp sgt i32 %57, 0
br i1 %58, label %48, label %59
59: ; preds = %56
br label %60
60: ; preds = %59, %14, %3
%61 = load i8*, i8** %7
store i8 0, i8* %61
%62 = load i8*, i8** %5
ret i8* %62
}

LLVM IR (-O2):
define i8* @ultoa(i64 %0, i8* returned writeonly %1, i32 %2) {
%4 = alloca [64 x i8]
%5 = add i32 %2, -2
%6 = icmp ult i32 %5, 35
br i1 %6, label %7, label %68
7: ; preds = %3
%8 = getelementptr inbounds [64 x i8], [64 x i8]* %4, i64 0, i64 0
%9 = zext i32 %2 to i64
br label %10
10: ; preds = %10, %7
%11 = phi i64 [ %19, %10 ], [ 0, %7 ]
%12 = phi i64 [ %21, %10 ], [ %0, %7 ]
%13 = urem i64 %12, %9
%14 = trunc i64 %13 to i32
%15 = icmp slt i32 %14, 10
%16 = select i1 %15, i32 48, i32 87
%17 = add nsw i32 %16, %14
%18 = trunc i32 %17 to i8
%19 = add nuw i64 %11, 1
%20 = getelementptr inbounds [64 x i8], [64 x i8]* %4, i64 0, i64 %11
store i8 %18, i8* %20
%21 = udiv i64 %12, %9
%22 = icmp ult i64 %12, %9
br i1 %22, label %23, label %10
23: ; preds = %10
%24 = shl i64 %19, 32
%25 = ashr exact i64 %24, 32
%26 = add nsw i64 %25, 1
%27 = call i64 @llvm.smin.i64(i64 %25, i64 1)
%28 = sub i64 %26, %27
%29 = icmp ult i64 %28, 32
br i1 %29, label %55, label %30
30: ; preds = %23
%31 = and i64 %28, -32
%32 = sub i64 %25, %31
%33 = getelementptr i8, i8* %1, i64 %31
br label %34
34: ; preds = %34, %30
%35 = phi i64 [ 0, %30 ], [ %51, %34 ]
%36 = getelementptr i8, i8* %1, i64 %35
%37 = xor i64 %35, -1
%38 = add i64 %25, %37
%39 = getelementptr inbounds [64 x i8], [64 x i8]* %4, i64 0, i64 %38
%40 = getelementptr inbounds i8, i8* %39, i64 -15
%41 = bitcast i8* %40 to <16 x i8>*
%42 = load <16 x i8>, <16 x i8>* %41
%43 = shufflevector <16 x i8> %42, <16 x i8> poison, <16 x i32> <i32 15, i32 14, i32 13, i32 12, i32 11, i32 10, i32 9, i32 8, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0>
%44 = getelementptr inbounds i8, i8* %39, i64 -31
%45 = bitcast i8* %44 to <16 x i8>*
%46 = load <16 x i8>, <16 x i8>* %45
%47 = shufflevector <16 x i8> %46, <16 x i8> poison, <16 x i32> <i32 15, i32 14, i32 13, i32 12, i32 11, i32 10, i32 9, i32 8, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0>
%48 = bitcast i8* %36 to <16 x i8>*
store <16 x i8> %43, <16 x i8>* %48
%49 = getelementptr i8, i8* %36, i64 16
%50 = bitcast i8* %49 to <16 x i8>*
store <16 x i8> %47, <16 x i8>* %50
%51 = add nuw i64 %35, 32
%52 = icmp eq i64 %51, %31
br i1 %52, label %53, label %34
53: ; preds = %34
%54 = icmp eq i64 %28, %31
br i1 %54, label %66, label %55
55: ; preds = %23, %53
%56 = phi i64 [ %25, %23 ], [ %32, %53 ]
%57 = phi i8* [ %1, %23 ], [ %33, %53 ]
br label %58
58: ; preds = %55, %58
%59 = phi i64 [ %61, %58 ], [ %56, %55 ]
%60 = phi i8* [ %64, %58 ], [ %57, %55 ]
%61 = add nsw i64 %59, -1
%62 = getelementptr inbounds [64 x i8], [64 x i8]* %4, i64 0, i64 %61
%63 = load i8, i8* %62
%64 = getelementptr inbounds i8, i8* %60, i64 1
store i8 %63, i8* %60
%65 = icmp sgt i64 %59, 1
br i1 %65, label %58, label %66
66: ; preds = %58, %53
%67 = phi i8* [ %33, %53 ], [ %64, %58 ]
br label %68
68: ; preds = %66, %3
%69 = phi i8* [ %67, %66 ], [ %1, %3 ]
store i8 0, i8* %69
ret i8* %1
}