void untwist(unsigned char *buf, int len) {
  int i;
  static const int sub[16] = {2, 10, 13, 1, 11, 6, 3, 15,
                              5, 12, 8,  0, 14, 4, 7, 9};
  for (i = 0; i < len; i++) {
    buf[i] = ((sub[buf[i] >> 4]) << 4) | (sub[buf[i] & 0x0F]);
  }
}


x86 assembly (clang, -O0):
<untwist>:
push   %rbp
mov    %rsp,%rbp
mov    %rdi,-0x8(%rbp)
mov    %esi,-0xc(%rbp)
movl   $0x0,-0x10(%rbp)
mov    -0x10(%rbp),%eax
cmp    -0xc(%rbp),%eax
jge    1174 <untwist+0x74>
mov    -0x8(%rbp),%rax
movslq -0x10(%rbp),%rcx
movzbl (%rax,%rcx,1),%eax
sar    $0x4,%eax
movslq %eax,%rcx
lea    0xec9(%rip),%rax
mov    (%rax,%rcx,4),%eax
shl    $0x4,%eax
mov    -0x8(%rbp),%rcx
movslq -0x10(%rbp),%rdx
movzbl (%rcx,%rdx,1),%ecx
and    $0xf,%ecx
movslq %ecx,%rdx
lea    0xeaa(%rip),%rcx
or     (%rcx,%rdx,4),%eax
mov    %al,%dl
mov    -0x8(%rbp),%rax
movslq -0x10(%rbp),%rcx
mov    %dl,(%rax,%rcx,1)
mov    -0x10(%rbp),%eax
add    $0x1,%eax
mov    %eax,-0x10(%rbp)
jmp    1112 <untwist+0x12>
pop    %rbp
ret

x86 assembly (clang, -O2):
<untwist>:
test   %esi,%esi
jle    1196 <untwist+0x96>
mov    %esi,%r8d
cmp    $0x1,%esi
jne    1114 <untwist+0x14>
xor    %eax,%eax
jmp    116f <untwist+0x6f>
mov    %r8d,%r9d
and    $0xfffffffe,%r9d
xor    %eax,%eax
lea    0xedc(%rip),%rsi
cs nopw 0x0(%rax,%rax,1)
xchg   %ax,%ax
movzbl (%rdi,%rax,1),%edx
mov    %rdx,%rcx
shr    $0x4,%rcx
mov    (%rsi,%rcx,4),%ecx
shl    $0x4,%ecx
and    $0xf,%edx
or     (%rsi,%rdx,4),%ecx
mov    %cl,(%rdi,%rax,1)
movzbl 0x1(%rdi,%rax,1),%ecx
mov    %rcx,%rdx
shr    $0x4,%rdx
mov    (%rsi,%rdx,4),%edx
shl    $0x4,%edx
and    $0xf,%ecx
or     (%rsi,%rcx,4),%edx
mov    %dl,0x1(%rdi,%rax,1)
add    $0x2,%rax
cmp    %rax,%r9
jne    1130 <untwist+0x30>
test   $0x1,%r8b
je     1196 <untwist+0x96>
movzbl (%rdi,%rax,1),%ecx
mov    %rcx,%rdx
shr    $0x4,%rdx
lea    0xe79(%rip),%rsi
mov    (%rsi,%rdx,4),%edx
shl    $0x4,%edx
and    $0xf,%ecx
or     (%rsi,%rcx,4),%edx
mov    %dl,(%rdi,%rax,1)
ret

LLVM IR (-O0):
@untwist.sub = internal constant [16 x i32] [i32 2, i32 10, i32 13, i32 1, i32 11, i32 6, i32 3, i32 15, i32 5, i32 12, i32 8, i32 0, i32 14, i32 4, i32 7, i32 9]
define void @untwist(i8* %0, i32 %1) {
%3 = alloca i8*
%4 = alloca i32
%5 = alloca i32
store i8* %0, i8** %3
store i32 %1, i32* %4
store i32 0, i32* %5
br label %6
6: ; preds = %38, %2
%7 = load i32, i32* %5
%8 = load i32, i32* %4
%9 = icmp slt i32 %7, %8
br i1 %9, label %10, label %41
10: ; preds = %6
%11 = load i8*, i8** %3
%12 = load i32, i32* %5
%13 = sext i32 %12 to i64
%14 = getelementptr inbounds i8, i8* %11, i64 %13
%15 = load i8, i8* %14
%16 = zext i8 %15 to i32
%17 = ashr i32 %16, 4
%18 = sext i32 %17 to i64
%19 = getelementptr inbounds [16 x i32], [16 x i32]* @untwist.sub, i64 0, i64 %18
%20 = load i32, i32* %19
%21 = shl i32 %20, 4
%22 = load i8*, i8** %3
%23 = load i32, i32* %5
%24 = sext i32 %23 to i64
%25 = getelementptr inbounds i8, i8* %22, i64 %24
%26 = load i8, i8* %25
%27 = zext i8 %26 to i32
%28 = and i32 %27, 15
%29 = sext i32 %28 to i64
%30 = getelementptr inbounds [16 x i32], [16 x i32]* @untwist.sub, i64 0, i64 %29
%31 = load i32, i32* %30
%32 = or i32 %21, %31
%33 = trunc i32 %32 to i8
%34 = load i8*, i8** %3
%35 = load i32, i32* %5
%36 = sext i32 %35 to i64
%37 = getelementptr inbounds i8, i8* %34, i64 %36
store i8 %33, i8* %37
br label %38
38: ; preds = %10
%39 = load i32, i32* %5
%40 = add nsw i32 %39, 1
store i32 %40, i32* %5
br label %6
41: ; preds = %6
ret void
}

LLVM IR (-O2):
@untwist.sub = internal unnamed_addr constant [16 x i32] [i32 2, i32 10, i32 13, i32 1, i32 11, i32 6, i32 3, i32 15, i32 5, i32 12, i32 8, i32 0, i32 14, i32 4, i32 7, i32 9]
define void @untwist(i8* nocapture %0, i32 %1) {
%3 = icmp sgt i32 %1, 0
br i1 %3, label %4, label %63
4: ; preds = %2
%5 = zext i32 %1 to i64
%6 = and i64 %5, 1
%7 = icmp eq i32 %1, 1
br i1 %7, label %45, label %8
8: ; preds = %4
%9 = and i64 %5, 4294967294
br label %10
10: ; preds = %10, %8
%11 = phi i64 [ 0, %8 ], [ %42, %10 ]
%12 = phi i64 [ 0, %8 ], [ %43, %10 ]
%13 = getelementptr inbounds i8, i8* %0, i64 %11
%14 = load i8, i8* %13
%15 = zext i8 %14 to i32
%16 = lshr i32 %15, 4
%17 = zext i32 %16 to i64
%18 = getelementptr inbounds [16 x i32], [16 x i32]* @untwist.sub, i64 0, i64 %17
%19 = load i32, i32* %18
%20 = shl i32 %19, 4
%21 = and i32 %15, 15
%22 = zext i32 %21 to i64
%23 = getelementptr inbounds [16 x i32], [16 x i32]* @untwist.sub, i64 0, i64 %22
%24 = load i32, i32* %23
%25 = or i32 %20, %24
%26 = trunc i32 %25 to i8
store i8 %26, i8* %13
%27 = or i64 %11, 1
%28 = getelementptr inbounds i8, i8* %0, i64 %27
%29 = load i8, i8* %28
%30 = zext i8 %29 to i32
%31 = lshr i32 %30, 4
%32 = zext i32 %31 to i64
%33 = getelementptr inbounds [16 x i32], [16 x i32]* @untwist.sub, i64 0, i64 %32
%34 = load i32, i32* %33
%35 = shl i32 %34, 4
%36 = and i32 %30, 15
%37 = zext i32 %36 to i64
%38 = getelementptr inbounds [16 x i32], [16 x i32]* @untwist.sub, i64 0, i64 %37
%39 = load i32, i32* %38
%40 = or i32 %35, %39
%41 = trunc i32 %40 to i8
store i8 %41, i8* %28
%42 = add nuw nsw i64 %11, 2
%43 = add i64 %12, 2
%44 = icmp eq i64 %43, %9
br i1 %44, label %45, label %10
45: ; preds = %10, %4
%46 = phi i64 [ 0, %4 ], [ %42, %10 ]
%47 = icmp eq i64 %6, 0
br i1 %47, label %63, label %48
48: ; preds = %45
%49 = getelementptr inbounds i8, i8* %0, i64 %46
%50 = load i8, i8* %49
%51 = zext i8 %50 to i32
%52 = lshr i32 %51, 4
%53 = zext i32 %52 to i64
%54 = getelementptr inbounds [16 x i32], [16 x i32]* @untwist.sub, i64 0, i64 %53
%55 = load i32, i32* %54
%56 = shl i32 %55, 4
%57 = and i32 %51, 15
%58 = zext i32 %57 to i64
%59 = getelementptr inbounds [16 x i32], [16 x i32]* @untwist.sub, i64 0, i64 %58
%60 = load i32, i32* %59
%61 = or i32 %56, %60
%62 = trunc i32 %61 to i8
store i8 %62, i8* %49
br label %63
63: ; preds = %48, %45, %2
ret void
}