int tohex(const char *input, const int len_input, char *output,
          const int len_output) {
  if (len_output < len_input * 2)
    return -1;
  char low;
  char high;
  int i;
  for (i = 0; i < len_input; i++) {
    high = 0xf & input[i];
    low = (0xf0 & input[i]) >> 4;
    if (low >= 10)
      output[i * 2] = 'a' + low - 10;
    else
      output[i * 2] = '0' + low;
    if (high >= 10)
      output[i * 2 + 1] = 'a' + high - 10;
    else
      output[i * 2 + 1] = '0' + high;
  }
  return 0;
}


x86 assembly (clang, -O0):
<tohex>:
push   %rbp
mov    %rsp,%rbp
mov    %rdi,-0x10(%rbp)
mov    %esi,-0x14(%rbp)
mov    %rdx,-0x20(%rbp)
mov    %ecx,-0x24(%rbp)
mov    -0x24(%rbp),%eax
mov    -0x14(%rbp),%ecx
shl    $0x1,%ecx
cmp    %ecx,%eax
jge    112f <tohex+0x2f>
movl   $0xffffffff,-0x4(%rbp)
jmp    1219 <tohex+0x119>
movl   $0x0,-0x2c(%rbp)
mov    -0x2c(%rbp),%eax
cmp    -0x14(%rbp),%eax
jge    1212 <tohex+0x112>
mov    -0x10(%rbp),%rax
movslq -0x2c(%rbp),%rcx
movsbl (%rax,%rcx,1),%eax
and    $0xf,%eax
mov    %al,-0x26(%rbp)
mov    -0x10(%rbp),%rax
movslq -0x2c(%rbp),%rcx
movsbl (%rax,%rcx,1),%eax
and    $0xf0,%eax
sar    $0x4,%eax
mov    %al,-0x25(%rbp)
movsbl -0x25(%rbp),%eax
cmp    $0xa,%eax
jl     1199 <tohex+0x99>
movsbl -0x25(%rbp),%eax
add    $0x61,%eax
sub    $0xa,%eax
mov    %al,%dl
mov    -0x20(%rbp),%rax
mov    -0x2c(%rbp),%ecx
shl    $0x1,%ecx
movslq %ecx,%rcx
mov    %dl,(%rax,%rcx,1)
jmp    11b2 <tohex+0xb2>
movsbl -0x25(%rbp),%eax
add    $0x30,%eax
mov    %al,%dl
mov    -0x20(%rbp),%rax
mov    -0x2c(%rbp),%ecx
shl    $0x1,%ecx
movslq %ecx,%rcx
mov    %dl,(%rax,%rcx,1)
movsbl -0x26(%rbp),%eax
cmp    $0xa,%eax
jl     11e3 <tohex+0xe3>
movsbl -0x26(%rbp),%eax
add    $0x61,%eax
sub    $0xa,%eax
mov    %al,%dl
mov    -0x20(%rbp),%rax
mov    -0x2c(%rbp),%ecx
shl    $0x1,%ecx
add    $0x1,%ecx
movslq %ecx,%rcx
mov    %dl,(%rax,%rcx,1)
jmp    11ff <tohex+0xff>
movsbl -0x26(%rbp),%eax
add    $0x30,%eax
mov    %al,%dl
mov    -0x20(%rbp),%rax
mov    -0x2c(%rbp),%ecx
shl    $0x1,%ecx
add    $0x1,%ecx
movslq %ecx,%rcx
mov    %dl,(%rax,%rcx,1)
jmp    1204 <tohex+0x104>
mov    -0x2c(%rbp),%eax
add    $0x1,%eax
mov    %eax,-0x2c(%rbp)
jmp    1136 <tohex+0x36>
movl   $0x0,-0x4(%rbp)
mov    -0x4(%rbp),%eax
pop    %rbp
ret

x86 assembly (clang, -O2):
<tohex>:
lea    (%rsi,%rsi,1),%r8d
mov    $0xffffffff,%eax
cmp    %ecx,%r8d
jg     124b <tohex+0x14b>
test   %esi,%esi
jle    1249 <tohex+0x149>
mov    %esi,%r8d
cmp    $0x8,%esi
jae    112a <tohex+0x2a>
xor    %r9d,%r9d
jmp    1200 <tohex+0x100>
lea    (%rdi,%r8,1),%rax
cmp    %rdx,%rax
jbe    1144 <tohex+0x44>
lea    (%rdx,%r8,2),%rax
cmp    %rdi,%rax
jbe    1144 <tohex+0x44>
xor    %r9d,%r9d
jmp    1200 <tohex+0x100>
mov    %r8d,%r9d
and    $0xfffffff8,%r9d
xor    %eax,%eax
movdqa 0xeaa(%rip),%xmm8
movdqa 0xeb1(%rip),%xmm9
movdqa 0xeb9(%rip),%xmm2
movdqa 0xec1(%rip),%xmm3
movdqa 0xec9(%rip),%xmm4
movdqa 0xed1(%rip),%xmm5
nop
movq   (%rdi,%rax,1),%xmm0
movdqa %xmm0,%xmm7
movdqa %xmm0,%xmm6
pminub %xmm2,%xmm6
pcmpeqb %xmm0,%xmm6
pand   %xmm8,%xmm0
psrlw  $0x4,%xmm7
pand   %xmm9,%xmm7
movdqa %xmm7,%xmm1
por    %xmm3,%xmm1
paddb  %xmm4,%xmm7
pand   %xmm6,%xmm1
pandn  %xmm7,%xmm6
por    %xmm1,%xmm6
movdqa %xmm0,%xmm1
pminub %xmm5,%xmm1
pcmpeqb %xmm0,%xmm1
movdqa %xmm0,%xmm7
paddb  %xmm4,%xmm7
por    %xmm3,%xmm0
pand   %xmm1,%xmm0
pandn  %xmm7,%xmm1
por    %xmm0,%xmm1
punpcklbw %xmm1,%xmm6
movdqu %xmm6,(%rdx,%rax,2)
add    $0x8,%rax
cmp    %rax,%r9
jne    1180 <tohex+0x80>
xor    %eax,%eax
cmp    %r8,%r9
je     124b <tohex+0x14b>
nopl   0x0(%rax)
movzbl (%rdi,%r9,1),%eax
mov    %eax,%ecx
and    $0xf,%cl
mov    %eax,%esi
shr    $0x4,%sil
lea    0x30(%rsi),%r10d
add    $0x57,%sil
cmp    $0xa0,%al
movzbl %sil,%eax
movzbl %r10b,%esi
cmovae %eax,%esi
lea    0x57(%rcx),%eax
cmp    $0xa,%cl
lea    0x30(%rcx),%ecx
movzbl %al,%eax
movzbl %cl,%ecx
cmovae %eax,%ecx
mov    %sil,(%rdx,%r9,2)
mov    %cl,0x1(%rdx,%r9,2)
add    $0x1,%r9
cmp    %r9,%r8
jne    1200 <tohex+0x100>
xor    %eax,%eax
ret

LLVM IR (-O0):
define i32 @tohex(i8* %0, i32 %1, i8* %2, i32 %3) {
%5 = alloca i32
%6 = alloca i8*
%7 = alloca i32
%8 = alloca i8*
%9 = alloca i32
%10 = alloca i8
%11 = alloca i8
%12 = alloca i32
store i8* %0, i8** %6
store i32 %1, i32* %7
store i8* %2, i8** %8
store i32 %3, i32* %9
%13 = load i32, i32* %9
%14 = load i32, i32* %7
%15 = mul nsw i32 %14, 2
%16 = icmp slt i32 %13, %15
br i1 %16, label %17, label %18
17: ; preds = %4
store i32 -1, i32* %5
br label %97
18: ; preds = %4
store i32 0, i32* %12
br label %19
19: ; preds = %93, %18
%20 = load i32, i32* %12
%21 = load i32, i32* %7
%22 = icmp slt i32 %20, %21
br i1 %22, label %23, label %96
23: ; preds = %19
%24 = load i8*, i8** %6
%25 = load i32, i32* %12
%26 = sext i32 %25 to i64
%27 = getelementptr inbounds i8, i8* %24, i64 %26
%28 = load i8, i8* %27
%29 = sext i8 %28 to i32
%30 = and i32 15, %29
%31 = trunc i32 %30 to i8
store i8 %31, i8* %11
%32 = load i8*, i8** %6
%33 = load i32, i32* %12
%34 = sext i32 %33 to i64
%35 = getelementptr inbounds i8, i8* %32, i64 %34
%36 = load i8, i8* %35
%37 = sext i8 %36 to i32
%38 = and i32 240, %37
%39 = ashr i32 %38, 4
%40 = trunc i32 %39 to i8
store i8 %40, i8* %10
%41 = load i8, i8* %10
%42 = sext i8 %41 to i32
%43 = icmp sge i32 %42, 10
br i1 %43, label %44, label %55
44: ; preds = %23
%45 = load i8, i8* %10
%46 = sext i8 %45 to i32
%47 = add nsw i32 97, %46
%48 = sub nsw i32 %47, 10
%49 = trunc i32 %48 to i8
%50 = load i8*, i8** %8
%51 = load i32, i32* %12
%52 = mul nsw i32 %51, 2
%53 = sext i32 %52 to i64
%54 = getelementptr inbounds i8, i8* %50, i64 %53
store i8 %49, i8* %54
br label %65
55: ; preds = %23
%56 = load i8, i8* %10
%57 = sext i8 %56 to i32
%58 = add nsw i32 48, %57
%59 = trunc i32 %58 to i8
%60 = load i8*, i8** %8
%61 = load i32, i32* %12
%62 = mul nsw i32 %61, 2
%63 = sext i32 %62 to i64
%64 = getelementptr inbounds i8, i8* %60, i64 %63
store i8 %59, i8* %64
br label %65
65: ; preds = %55, %44
%66 = load i8, i8* %11
%67 = sext i8 %66 to i32
%68 = icmp sge i32 %67, 10
br i1 %68, label %69, label %81
69: ; preds = %65
%70 = load i8, i8* %11
%71 = sext i8 %70 to i32
%72 = add nsw i32 97, %71
%73 = sub nsw i32 %72, 10
%74 = trunc i32 %73 to i8
%75 = load i8*, i8** %8
%76 = load i32, i32* %12
%77 = mul nsw i32 %76, 2
%78 = add nsw i32 %77, 1
%79 = sext i32 %78 to i64
%80 = getelementptr inbounds i8, i8* %75, i64 %79
store i8 %74, i8* %80
br label %92
81: ; preds = %65
%82 = load i8, i8* %11
%83 = sext i8 %82 to i32
%84 = add nsw i32 48, %83
%85 = trunc i32 %84 to i8
%86 = load i8*, i8** %8
%87 = load i32, i32* %12
%88 = mul nsw i32 %87, 2
%89 = add nsw i32 %88, 1
%90 = sext i32 %89 to i64
%91 = getelementptr inbounds i8, i8* %86, i64 %90
store i8 %85, i8* %91
br label %92
92: ; preds = %81, %69
br label %93
93: ; preds = %92
%94 = load i32, i32* %12
%95 = add nsw i32 %94, 1
store i32 %95, i32* %12
br label %19
96: ; preds = %19
store i32 0, i32* %5
br label %97
97: ; preds = %96, %17
%98 = load i32, i32* %5
ret i32 %98
}

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