unsigned long nibble_sort_word(unsigned long word) {
  char nibble_counts[16] = {0};
  unsigned long result = 0;
  unsigned char nibble_index = 15;
  for (int i = 0; i < sizeof(word) * 2; ++i) {
    nibble_counts[word & 0x0F]++;
    word >>= 4;
  }
  for (unsigned long int i = 15; i > 0; --i) {
    while (nibble_counts[i]--) {
      result |= (i << nibble_index * 4);
      nibble_index--;
    }
  }
  return result;
}


x86 assembly (clang, -O0):
<nibble_sort_word>:
push   %rbp
mov    %rsp,%rbp
sub    $0x40,%rsp
mov    %rdi,-0x8(%rbp)
lea    -0x20(%rbp),%rdi
xor    %esi,%esi
mov    $0x10,%edx
call   1030 <memset@plt>
movq   $0x0,-0x28(%rbp)
movb   $0xf,-0x29(%rbp)
movl   $0x0,-0x30(%rbp)
movslq -0x30(%rbp),%rax
cmp    $0x10,%rax
jae    117a <nibble_sort_word+0x6a>
mov    -0x8(%rbp),%rax
and    $0xf,%rax
mov    -0x20(%rbp,%rax,1),%cl
add    $0x1,%cl
mov    %cl,-0x20(%rbp,%rax,1)
mov    -0x8(%rbp),%rax
shr    $0x4,%rax
mov    %rax,-0x8(%rbp)
mov    -0x30(%rbp),%eax
add    $0x1,%eax
mov    %eax,-0x30(%rbp)
jmp    113f <nibble_sort_word+0x2f>
movq   $0xf,-0x38(%rbp)
cmpq   $0x0,-0x38(%rbp)
jbe    11e6 <nibble_sort_word+0xd6>
jmp    1192 <nibble_sort_word+0x82>
mov    -0x38(%rbp),%rcx
mov    -0x20(%rbp,%rcx,1),%al
mov    %al,%dl
add    $0xff,%dl
mov    %dl,-0x20(%rbp,%rcx,1)
cmp    $0x0,%al
je     11d0 <nibble_sort_word+0xc0>
mov    -0x38(%rbp),%rax
movzbl -0x29(%rbp),%ecx
shl    $0x2,%ecx
mov    %ecx,%ecx
shl    %cl,%rax
or     -0x28(%rbp),%rax
mov    %rax,-0x28(%rbp)
mov    -0x29(%rbp),%al
add    $0xff,%al
mov    %al,-0x29(%rbp)
jmp    1192 <nibble_sort_word+0x82>
jmp    11d5 <nibble_sort_word+0xc5>
mov    -0x38(%rbp),%rax
add    $0xffffffffffffffff,%rax
mov    %rax,-0x38(%rbp)
jmp    1182 <nibble_sort_word+0x72>
mov    -0x28(%rbp),%rax
add    $0x40,%rsp
pop    %rbp
ret

x86 assembly (clang, -O2):
<nibble_sort_word>:
pxor   %xmm13,%xmm13
movdqa %xmm13,-0x18(%rsp)
mov    %edi,%eax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %edi,%eax
shr    $0x4,%eax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %edi,%eax
shr    $0x8,%eax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %edi,%eax
shr    $0xc,%eax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %edi,%eax
shr    $0x10,%eax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %edi,%eax
shr    $0x14,%eax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %edi,%eax
shr    $0x18,%eax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %edi,%eax
shr    $0x1c,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %rdi,%rax
shr    $0x20,%rax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %rdi,%rax
shr    $0x24,%rax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %rdi,%rax
shr    $0x28,%rax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %rdi,%rax
shr    $0x2c,%rax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %rdi,%rax
shr    $0x30,%rax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %rdi,%rax
shr    $0x34,%rax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
mov    %rdi,%rax
shr    $0x38,%rax
and    $0xf,%eax
addb   $0x1,-0x18(%rsp,%rax,1)
shr    $0x3c,%rdi
addb   $0x1,-0x18(%rsp,%rdi,1)
mov    $0xf,%r8b
mov    $0xf,%r11d
xor    %eax,%eax
movdqa 0xe0c(%rip),%xmm8
movdqa 0xe13(%rip),%xmm12
movdqa 0xe1a(%rip),%xmm9
movdqa 0xe21(%rip),%xmm10
movdqa 0xe28(%rip),%xmm11
jmp    1233 <nibble_sort_word+0x133>
nopw   0x0(%rax,%rax,1)
sub    %r10b,%r8b
movb   $0xff,-0x18(%rsp,%r11,1)
add    $0xffffffffffffffff,%r11
je     1471 <nibble_sort_word+0x371>
mov    -0x18(%rsp,%r11,1),%r10b
test   %r10b,%r10b
je     1223 <nibble_sort_word+0x123>
lea    -0x1(%r10),%ecx
cmp    $0x3,%cl
jae    1260 <nibble_sort_word+0x160>
mov    %r10d,%edx
mov    %r8d,%edi
jmp    1440 <nibble_sort_word+0x340>
cs nopw 0x0(%rax,%rax,1)
nopl   0x0(%rax,%rax,1)
movzbl %cl,%r9d
add    $0x1,%r9d
mov    %r9d,%ecx
and    $0xfffffffc,%ecx
movzbl %r8b,%edx
movd   %edx,%xmm7
punpcklbw %xmm7,%xmm7
paddb  %xmm8,%xmm7
movq   %rax,%xmm6
movq   %r11,%xmm1
pshufd $0x44,%xmm1,%xmm2
lea    -0x4(%rcx),%eax
mov    %eax,%edx
shr    $0x2,%edx
add    $0x1,%edx
test   %eax,%eax
je     1462 <nibble_sort_word+0x362>
mov    %edx,%eax
and    $0xfffffffe,%eax
pxor   %xmm1,%xmm1
nopw   0x0(%rax,%rax,1)
movdqa %xmm7,%xmm4
paddb  %xmm12,%xmm4
movdqa %xmm7,%xmm3
punpcklbw %xmm13,%xmm3
punpcklwd %xmm13,%xmm3
punpckldq %xmm13,%xmm3
punpcklbw %xmm13,%xmm4
punpcklwd %xmm13,%xmm4
psllq  $0x2,%xmm3
movdqa %xmm2,%xmm5
psllq  %xmm3,%xmm5
punpckldq %xmm13,%xmm4
pshufd $0xee,%xmm3,%xmm0
movdqa %xmm2,%xmm3
psllq  %xmm0,%xmm3
psllq  $0x2,%xmm4
movsd  %xmm5,%xmm3
movdqa %xmm2,%xmm0
psllq  %xmm4,%xmm0
orpd   %xmm6,%xmm3
pshufd $0xee,%xmm4,%xmm5
movdqa %xmm2,%xmm4
psllq  %xmm5,%xmm4
movsd  %xmm0,%xmm4
orpd   %xmm1,%xmm4
movdqa %xmm7,%xmm0
paddb  %xmm9,%xmm0
movdqa %xmm7,%xmm1
paddb  %xmm10,%xmm1
punpcklbw %xmm13,%xmm0
punpcklwd %xmm13,%xmm0
punpckldq %xmm13,%xmm0
punpcklbw %xmm13,%xmm1
punpcklwd %xmm13,%xmm1
psllq  $0x2,%xmm0
movdqa %xmm2,%xmm5
psllq  %xmm0,%xmm5
punpckldq %xmm13,%xmm1
pshufd $0xee,%xmm0,%xmm0
movdqa %xmm2,%xmm6
psllq  %xmm0,%xmm6
psllq  $0x2,%xmm1
movsd  %xmm5,%xmm6
movdqa %xmm2,%xmm0
psllq  %xmm1,%xmm0
orpd   %xmm3,%xmm6
pshufd $0xee,%xmm1,%xmm3
movdqa %xmm2,%xmm1
psllq  %xmm3,%xmm1
movsd  %xmm0,%xmm1
orpd   %xmm4,%xmm1
paddb  %xmm11,%xmm7
add    $0xfffffffe,%eax
jne    12b0 <nibble_sort_word+0x1b0>
test   $0x1,%dl
je     140d <nibble_sort_word+0x30d>
movdqa %xmm7,%xmm0
paddb  %xmm12,%xmm0
punpcklbw %xmm13,%xmm7
punpcklwd %xmm13,%xmm7
punpckldq %xmm13,%xmm7
punpcklbw %xmm13,%xmm0
punpcklwd %xmm13,%xmm0
punpckldq %xmm13,%xmm0
psllq  $0x2,%xmm7
psllq  $0x2,%xmm0
movdqa %xmm2,%xmm3
psllq  %xmm7,%xmm3
pshufd $0xee,%xmm7,%xmm4
movdqa %xmm2,%xmm5
psllq  %xmm4,%xmm5
movsd  %xmm3,%xmm5
orpd   %xmm5,%xmm6
movdqa %xmm2,%xmm3
psllq  %xmm0,%xmm3
pshufd $0xee,%xmm0,%xmm0
psllq  %xmm0,%xmm2
movsd  %xmm3,%xmm2
orpd   %xmm2,%xmm1
orpd   %xmm1,%xmm6
pshufd $0xee,%xmm6,%xmm0
por    %xmm6,%xmm0
movq   %xmm0,%rax
cmp    %ecx,%r9d
je     1220 <nibble_sort_word+0x120>
mov    %r10d,%edx
sub    %cl,%dl
mov    %r8d,%edi
sub    %cl,%dil
cs nopw 0x0(%rax,%rax,1)
nopl   (%rax)
movzbl %dil,%edi
lea    0x0(,%rdi,4),%ecx
mov    %r11,%rsi
shl    %cl,%rsi
or     %rsi,%rax
add    $0xff,%dil
add    $0xff,%dl
jne    1440 <nibble_sort_word+0x340>
jmp    1220 <nibble_sort_word+0x120>
pxor   %xmm1,%xmm1
test   $0x1,%dl
jne    13a6 <nibble_sort_word+0x2a6>
jmp    140d <nibble_sort_word+0x30d>
ret

LLVM IR (-O0):
define i64 @nibble_sort_word(i64 %0) {
%2 = alloca i64
%3 = alloca [16 x i8]
%4 = alloca i64
%5 = alloca i8
%6 = alloca i32
%7 = alloca i64
store i64 %0, i64* %2
%8 = bitcast [16 x i8]* %3 to i8*
store i64 0, i64* %4
store i8 15, i8* %5
store i32 0, i32* %6
br label %9
9: ; preds = %21, %1
%10 = load i32, i32* %6
%11 = sext i32 %10 to i64
%12 = icmp ult i64 %11, 16
br i1 %12, label %13, label %24
13: ; preds = %9
%14 = load i64, i64* %2
%15 = and i64 %14, 15
%16 = getelementptr inbounds [16 x i8], [16 x i8]* %3, i64 0, i64 %15
%17 = load i8, i8* %16
%18 = add i8 %17, 1
store i8 %18, i8* %16
%19 = load i64, i64* %2
%20 = lshr i64 %19, 4
store i64 %20, i64* %2
br label %21
21: ; preds = %13
%22 = load i32, i32* %6
%23 = add nsw i32 %22, 1
store i32 %23, i32* %6
br label %9
24: ; preds = %9
store i64 15, i64* %7
br label %25
25: ; preds = %47, %24
%26 = load i64, i64* %7
%27 = icmp ugt i64 %26, 0
br i1 %27, label %28, label %50
28: ; preds = %25
br label %29
29: ; preds = %35, %28
%30 = load i64, i64* %7
%31 = getelementptr inbounds [16 x i8], [16 x i8]* %3, i64 0, i64 %30
%32 = load i8, i8* %31
%33 = add i8 %32, -1
store i8 %33, i8* %31
%34 = icmp ne i8 %32, 0
br i1 %34, label %35, label %46
35: ; preds = %29
%36 = load i64, i64* %7
%37 = load i8, i8* %5
%38 = zext i8 %37 to i32
%39 = mul nsw i32 %38, 4
%40 = zext i32 %39 to i64
%41 = shl i64 %36, %40
%42 = load i64, i64* %4
%43 = or i64 %42, %41
store i64 %43, i64* %4
%44 = load i8, i8* %5
%45 = add i8 %44, -1
store i8 %45, i8* %5
br label %29
46: ; preds = %29
br label %47
47: ; preds = %46
%48 = load i64, i64* %7
%49 = add i64 %48, -1
store i64 %49, i64* %7
br label %25
50: ; preds = %25
%51 = load i64, i64* %4
ret i64 %51
}

LLVM IR (-O2):
define i64 @nibble_sort_word(i64 %0) {
%2 = alloca [16 x i8]
%3 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 0
%4 = and i64 %0, 15
%5 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %4
%6 = load i8, i8* %5
%7 = add i8 %6, 1
store i8 %7, i8* %5
%8 = lshr i64 %0, 4
%9 = and i64 %8, 15
%10 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %9
%11 = load i8, i8* %10
%12 = add i8 %11, 1
store i8 %12, i8* %10
%13 = lshr i64 %0, 8
%14 = and i64 %13, 15
%15 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %14
%16 = load i8, i8* %15
%17 = add i8 %16, 1
store i8 %17, i8* %15
%18 = lshr i64 %0, 12
%19 = and i64 %18, 15
%20 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %19
%21 = load i8, i8* %20
%22 = add i8 %21, 1
store i8 %22, i8* %20
%23 = lshr i64 %0, 16
%24 = and i64 %23, 15
%25 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %24
%26 = load i8, i8* %25
%27 = add i8 %26, 1
store i8 %27, i8* %25
%28 = lshr i64 %0, 20
%29 = and i64 %28, 15
%30 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %29
%31 = load i8, i8* %30
%32 = add i8 %31, 1
store i8 %32, i8* %30
%33 = lshr i64 %0, 24
%34 = and i64 %33, 15
%35 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %34
%36 = load i8, i8* %35
%37 = add i8 %36, 1
store i8 %37, i8* %35
%38 = lshr i64 %0, 28
%39 = and i64 %38, 15
%40 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %39
%41 = load i8, i8* %40
%42 = add i8 %41, 1
store i8 %42, i8* %40
%43 = lshr i64 %0, 32
%44 = and i64 %43, 15
%45 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %44
%46 = load i8, i8* %45
%47 = add i8 %46, 1
store i8 %47, i8* %45
%48 = lshr i64 %0, 36
%49 = and i64 %48, 15
%50 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %49
%51 = load i8, i8* %50
%52 = add i8 %51, 1
store i8 %52, i8* %50
%53 = lshr i64 %0, 40
%54 = and i64 %53, 15
%55 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %54
%56 = load i8, i8* %55
%57 = add i8 %56, 1
store i8 %57, i8* %55
%58 = lshr i64 %0, 44
%59 = and i64 %58, 15
%60 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %59
%61 = load i8, i8* %60
%62 = add i8 %61, 1
store i8 %62, i8* %60
%63 = lshr i64 %0, 48
%64 = and i64 %63, 15
%65 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %64
%66 = load i8, i8* %65
%67 = add i8 %66, 1
store i8 %67, i8* %65
%68 = lshr i64 %0, 52
%69 = and i64 %68, 15
%70 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %69
%71 = load i8, i8* %70
%72 = add i8 %71, 1
store i8 %72, i8* %70
%73 = lshr i64 %0, 56
%74 = and i64 %73, 15
%75 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %74
%76 = load i8, i8* %75
%77 = add i8 %76, 1
store i8 %77, i8* %75
%78 = lshr i64 %0, 60
%79 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %78
%80 = load i8, i8* %79
%81 = add i8 %80, 1
store i8 %81, i8* %79
br label %82
82: ; preds = %1, %184
%83 = phi i64 [ %187, %184 ], [ 15, %1 ]
%84 = phi i8 [ %186, %184 ], [ 15, %1 ]
%85 = phi i64 [ %185, %184 ], [ 0, %1 ]
%86 = getelementptr inbounds [16 x i8], [16 x i8]* %2, i64 0, i64 %83
%87 = load i8, i8* %86
%88 = icmp eq i8 %87, 0
br i1 %88, label %184, label %89
89: ; preds = %82
%90 = add i8 %87, -1
%91 = zext i8 %90 to i32
%92 = add nuw nsw i32 %91, 1
%93 = icmp ult i8 %90, 3
br i1 %93, label %165, label %94
94: ; preds = %89
%95 = and i32 %92, 508
%96 = trunc i32 %95 to i8
%97 = sub i8 %87, %96
%98 = trunc i32 %95 to i8
%99 = sub i8 %84, %98
%100 = insertelement <2 x i8> poison, i8 %84, i64 0
%101 = shufflevector <2 x i8> %100, <2 x i8> poison, <2 x i32> zeroinitializer
%102 = add <2 x i8> %101, <i8 0, i8 -1>
%103 = insertelement <2 x i64> <i64 poison, i64 0>, i64 %85, i64 0
%104 = insertelement <2 x i64> poison, i64 %83, i64 0
%105 = shufflevector <2 x i64> %104, <2 x i64> poison, <2 x i32> zeroinitializer
%106 = insertelement <2 x i64> poison, i64 %83, i64 0
%107 = shufflevector <2 x i64> %106, <2 x i64> poison, <2 x i32> zeroinitializer
%108 = add nsw i32 %95, -4
%109 = lshr exact i32 %108, 2
%110 = add nuw nsw i32 %109, 1
%111 = and i32 %110, 1
%112 = icmp eq i32 %108, 0
br i1 %112, label %142, label %113
113: ; preds = %94
%114 = and i32 %110, 2147483646
br label %115
115: ; preds = %115, %113
%116 = phi <2 x i8> [ %102, %113 ], [ %139, %115 ]
%117 = phi <2 x i64> [ %103, %113 ], [ %137, %115 ]
%118 = phi <2 x i64> [ zeroinitializer, %113 ], [ %138, %115 ]
%119 = phi i32 [ 0, %113 ], [ %140, %115 ]
%120 = add <2 x i8> %116, <i8 -2, i8 -2>
%121 = zext <2 x i8> %116 to <2 x i64>
%122 = zext <2 x i8> %120 to <2 x i64>
%123 = shl nuw nsw <2 x i64> %121, <i64 2, i64 2>
%124 = shl nuw nsw <2 x i64> %122, <i64 2, i64 2>
%125 = shl <2 x i64> %105, %123
%126 = shl <2 x i64> %107, %124
%127 = or <2 x i64> %125, %117
%128 = or <2 x i64> %126, %118
%129 = add <2 x i8> %116, <i8 -4, i8 -4>
%130 = add <2 x i8> %116, <i8 -6, i8 -6>
%131 = zext <2 x i8> %129 to <2 x i64>
%132 = zext <2 x i8> %130 to <2 x i64>
%133 = shl nuw nsw <2 x i64> %131, <i64 2, i64 2>
%134 = shl nuw nsw <2 x i64> %132, <i64 2, i64 2>
%135 = shl <2 x i64> %105, %133
%136 = shl <2 x i64> %107, %134
%137 = or <2 x i64> %135, %127
%138 = or <2 x i64> %136, %128
%139 = add <2 x i8> %116, <i8 -8, i8 -8>
%140 = add i32 %119, 2
%141 = icmp eq i32 %140, %114
br i1 %141, label %142, label %115
142: ; preds = %115, %94
%143 = phi <2 x i64> [ undef, %94 ], [ %137, %115 ]
%144 = phi <2 x i64> [ undef, %94 ], [ %138, %115 ]
%145 = phi <2 x i8> [ %102, %94 ], [ %139, %115 ]
%146 = phi <2 x i64> [ %103, %94 ], [ %137, %115 ]
%147 = phi <2 x i64> [ zeroinitializer, %94 ], [ %138, %115 ]
%148 = icmp eq i32 %111, 0
br i1 %148, label %159, label %149
149: ; preds = %142
%150 = add <2 x i8> %145, <i8 -2, i8 -2>
%151 = zext <2 x i8> %145 to <2 x i64>
%152 = zext <2 x i8> %150 to <2 x i64>
%153 = shl nuw nsw <2 x i64> %151, <i64 2, i64 2>
%154 = shl nuw nsw <2 x i64> %152, <i64 2, i64 2>
%155 = shl <2 x i64> %105, %153
%156 = shl <2 x i64> %107, %154
%157 = or <2 x i64> %155, %146
%158 = or <2 x i64> %156, %147
br label %159
159: ; preds = %142, %149
%160 = phi <2 x i64> [ %143, %142 ], [ %157, %149 ]
%161 = phi <2 x i64> [ %144, %142 ], [ %158, %149 ]
%162 = or <2 x i64> %161, %160
%163 = call i64 @llvm.vector.reduce.or.v2i64(<2 x i64> %162)
%164 = icmp eq i32 %92, %95
br i1 %164, label %181, label %165
165: ; preds = %89, %159
%166 = phi i8 [ %87, %89 ], [ %97, %159 ]
%167 = phi i8 [ %84, %89 ], [ %99, %159 ]
%168 = phi i64 [ %85, %89 ], [ %163, %159 ]
br label %170
169: ; preds = %184
ret i64 %185
170: ; preds = %165, %170
%171 = phi i8 [ %174, %170 ], [ %166, %165 ]
%172 = phi i8 [ %179, %170 ], [ %167, %165 ]
%173 = phi i64 [ %178, %170 ], [ %168, %165 ]
%174 = add i8 %171, -1
%175 = zext i8 %172 to i64
%176 = shl nuw nsw i64 %175, 2
%177 = shl i64 %83, %176
%178 = or i64 %177, %173
%179 = add i8 %172, -1
%180 = icmp eq i8 %174, 0
br i1 %180, label %181, label %170
181: ; preds = %170, %159
%182 = phi i64 [ %163, %159 ], [ %178, %170 ]
%183 = sub i8 %84, %87
br label %184
184: ; preds = %181, %82
%185 = phi i64 [ %85, %82 ], [ %182, %181 ]
%186 = phi i8 [ %84, %82 ], [ %183, %181 ]
store i8 -1, i8* %86
%187 = add nsw i64 %83, -1
%188 = icmp eq i64 %187, 0
br i1 %188, label %169, label %82
}