unsigned char utf8_strlen(const char *s) {
  unsigned char len = 0;
  while (*s) {
    ++len;
    if (*s & 0x80) {
      if (!(*s & 0x20)) {
        s += 2;
      } else if (!(*s & 0x10)) {
        s += 3;
      } else if (!(*s & 0x08)) {
        s += 4;
      } else if (!(*s & 0x04)) {
        s += 5;
      } else if (!(*s & 0x02)) {
        s += 6;
      } else if (!(*s & 0x01)) {
        s += 7;
      }
    } else {
      ++s;
    }
  }
  return len;
}


x86 assembly (clang, -O0):
<utf8_strlen>:
push   %rbp
mov    %rsp,%rbp
mov    %rdi,-0x8(%rbp)
movb   $0x0,-0x9(%rbp)
mov    -0x8(%rbp),%rax
cmpb   $0x0,(%rax)
je     1238 <utf8_strlen+0x138>
mov    -0x9(%rbp),%al
add    $0x1,%al
mov    %al,-0x9(%rbp)
mov    -0x8(%rbp),%rax
movsbl (%rax),%eax
and    $0x80,%eax
cmp    $0x0,%eax
je     1227 <utf8_strlen+0x127>
mov    -0x8(%rbp),%rax
movsbl (%rax),%eax
and    $0x20,%eax
cmp    $0x0,%eax
jne    115a <utf8_strlen+0x5a>
mov    -0x8(%rbp),%rax
add    $0x2,%rax
mov    %rax,-0x8(%rbp)
jmp    1222 <utf8_strlen+0x122>
mov    -0x8(%rbp),%rax
movsbl (%rax),%eax
and    $0x10,%eax
cmp    $0x0,%eax
jne    117e <utf8_strlen+0x7e>
mov    -0x8(%rbp),%rax
add    $0x3,%rax
mov    %rax,-0x8(%rbp)
jmp    121d <utf8_strlen+0x11d>
mov    -0x8(%rbp),%rax
movsbl (%rax),%eax
and    $0x8,%eax
cmp    $0x0,%eax
jne    11a2 <utf8_strlen+0xa2>
mov    -0x8(%rbp),%rax
add    $0x4,%rax
mov    %rax,-0x8(%rbp)
jmp    1218 <utf8_strlen+0x118>
mov    -0x8(%rbp),%rax
movsbl (%rax),%eax
and    $0x4,%eax
cmp    $0x0,%eax
jne    11c6 <utf8_strlen+0xc6>
mov    -0x8(%rbp),%rax
add    $0x5,%rax
mov    %rax,-0x8(%rbp)
jmp    1213 <utf8_strlen+0x113>
mov    -0x8(%rbp),%rax
movsbl (%rax),%eax
and    $0x2,%eax
cmp    $0x0,%eax
jne    11ea <utf8_strlen+0xea>
mov    -0x8(%rbp),%rax
add    $0x6,%rax
mov    %rax,-0x8(%rbp)
jmp    120e <utf8_strlen+0x10e>
mov    -0x8(%rbp),%rax
movsbl (%rax),%eax
and    $0x1,%eax
cmp    $0x0,%eax
jne    1209 <utf8_strlen+0x109>
mov    -0x8(%rbp),%rax
add    $0x7,%rax
mov    %rax,-0x8(%rbp)
jmp    120e <utf8_strlen+0x10e>
jmp    1213 <utf8_strlen+0x113>
jmp    1218 <utf8_strlen+0x118>
jmp    121d <utf8_strlen+0x11d>
jmp    1222 <utf8_strlen+0x122>
jmp    1233 <utf8_strlen+0x133>
mov    -0x8(%rbp),%rax
add    $0x1,%rax
mov    %rax,-0x8(%rbp)
jmp    110c <utf8_strlen+0xc>
movzbl -0x9(%rbp),%eax
pop    %rbp
ret

x86 assembly (clang, -O2):
<utf8_strlen>:
mov    (%rdi),%cl
xor    %eax,%eax
jmp    1119 <utf8_strlen+0x19>
cs nopw 0x0(%rax,%rax,1)
movzbl (%rdi,%rdx,1),%ecx
add    %rdx,%rdi
add    $0x1,%al
test   %cl,%cl
je     1167 <utf8_strlen+0x67>
mov    $0x1,%edx
test   $0x80,%cl
je     1110 <utf8_strlen+0x10>
mov    $0x2,%edx
test   $0x20,%cl
je     1110 <utf8_strlen+0x10>
mov    $0x3,%edx
test   $0x10,%cl
je     1110 <utf8_strlen+0x10>
mov    $0x4,%edx
test   $0x8,%cl
je     1110 <utf8_strlen+0x10>
mov    $0x5,%edx
test   $0x4,%cl
je     1110 <utf8_strlen+0x10>
mov    $0x6,%edx
test   $0x2,%cl
je     1110 <utf8_strlen+0x10>
mov    $0x7,%edx
test   $0x1,%cl
je     1110 <utf8_strlen+0x10>
xor    %edx,%edx
jmp    1110 <utf8_strlen+0x10>
ret

LLVM IR (-O0):
define zeroext i8 @utf8_strlen(i8* %0) {
%2 = alloca i8*
%3 = alloca i8
store i8* %0, i8** %2
store i8 0, i8* %3
br label %4
4: ; preds = %79, %1
%5 = load i8*, i8** %2
%6 = load i8, i8* %5
%7 = icmp ne i8 %6, 0
br i1 %7, label %8, label %80
8: ; preds = %4
%9 = load i8, i8* %3
%10 = add i8 %9, 1
store i8 %10, i8* %3
%11 = load i8*, i8** %2
%12 = load i8, i8* %11
%13 = sext i8 %12 to i32
%14 = and i32 %13, 128
%15 = icmp ne i32 %14, 0
br i1 %15, label %16, label %76
16: ; preds = %8
%17 = load i8*, i8** %2
%18 = load i8, i8* %17
%19 = sext i8 %18 to i32
%20 = and i32 %19, 32
%21 = icmp ne i32 %20, 0
br i1 %21, label %25, label %22
22: ; preds = %16
%23 = load i8*, i8** %2
%24 = getelementptr inbounds i8, i8* %23, i64 2
store i8* %24, i8** %2
br label %75
25: ; preds = %16
%26 = load i8*, i8** %2
%27 = load i8, i8* %26
%28 = sext i8 %27 to i32
%29 = and i32 %28, 16
%30 = icmp ne i32 %29, 0
br i1 %30, label %34, label %31
31: ; preds = %25
%32 = load i8*, i8** %2
%33 = getelementptr inbounds i8, i8* %32, i64 3
store i8* %33, i8** %2
br label %74
34: ; preds = %25
%35 = load i8*, i8** %2
%36 = load i8, i8* %35
%37 = sext i8 %36 to i32
%38 = and i32 %37, 8
%39 = icmp ne i32 %38, 0
br i1 %39, label %43, label %40
40: ; preds = %34
%41 = load i8*, i8** %2
%42 = getelementptr inbounds i8, i8* %41, i64 4
store i8* %42, i8** %2
br label %73
43: ; preds = %34
%44 = load i8*, i8** %2
%45 = load i8, i8* %44
%46 = sext i8 %45 to i32
%47 = and i32 %46, 4
%48 = icmp ne i32 %47, 0
br i1 %48, label %52, label %49
49: ; preds = %43
%50 = load i8*, i8** %2
%51 = getelementptr inbounds i8, i8* %50, i64 5
store i8* %51, i8** %2
br label %72
52: ; preds = %43
%53 = load i8*, i8** %2
%54 = load i8, i8* %53
%55 = sext i8 %54 to i32
%56 = and i32 %55, 2
%57 = icmp ne i32 %56, 0
br i1 %57, label %61, label %58
58: ; preds = %52
%59 = load i8*, i8** %2
%60 = getelementptr inbounds i8, i8* %59, i64 6
store i8* %60, i8** %2
br label %71
61: ; preds = %52
%62 = load i8*, i8** %2
%63 = load i8, i8* %62
%64 = sext i8 %63 to i32
%65 = and i32 %64, 1
%66 = icmp ne i32 %65, 0
br i1 %66, label %70, label %67
67: ; preds = %61
%68 = load i8*, i8** %2
%69 = getelementptr inbounds i8, i8* %68, i64 7
store i8* %69, i8** %2
br label %70
70: ; preds = %67, %61
br label %71
71: ; preds = %70, %58
br label %72
72: ; preds = %71, %49
br label %73
73: ; preds = %72, %40
br label %74
74: ; preds = %73, %31
br label %75
75: ; preds = %74, %22
br label %79
76: ; preds = %8
%77 = load i8*, i8** %2
%78 = getelementptr inbounds i8, i8* %77, i32 1
store i8* %78, i8** %2
br label %79
79: ; preds = %76, %75
br label %4
80: ; preds = %4
%81 = load i8, i8* %3
ret i8 %81
}

LLVM IR (-O2):
define zeroext i8 @utf8_strlen(i8* nocapture %0) {
%2 = load i8, i8* %0
%3 = icmp eq i8 %2, 0
br i1 %3, label %36, label %4
4: ; preds = %1, %31
%5 = phi i8 [ %34, %31 ], [ %2, %1 ]
%6 = phi i8 [ %8, %31 ], [ 0, %1 ]
%7 = phi i8* [ %33, %31 ], [ %0, %1 ]
%8 = add i8 %6, 1
%9 = zext i8 %5 to i32
%10 = and i32 %9, 128
%11 = icmp eq i32 %10, 0
br i1 %11, label %31, label %12
12: ; preds = %4
%13 = and i32 %9, 32
%14 = icmp eq i32 %13, 0
br i1 %14, label %31, label %15
15: ; preds = %12
%16 = and i32 %9, 16
%17 = icmp eq i32 %16, 0
br i1 %17, label %31, label %18
18: ; preds = %15
%19 = and i32 %9, 8
%20 = icmp eq i32 %19, 0
br i1 %20, label %31, label %21
21: ; preds = %18
%22 = and i32 %9, 4
%23 = icmp eq i32 %22, 0
br i1 %23, label %31, label %24
24: ; preds = %21
%25 = and i32 %9, 2
%26 = icmp eq i32 %25, 0
br i1 %26, label %31, label %27
27: ; preds = %24
%28 = and i32 %9, 1
%29 = icmp eq i32 %28, 0
%30 = select i1 %29, i64 7, i64 0
br label %31
31: ; preds = %4, %24, %21, %18, %15, %12, %27
%32 = phi i64 [ %30, %27 ], [ 2, %12 ], [ 3, %15 ], [ 4, %18 ], [ 5, %21 ], [ 6, %24 ], [ 1, %4 ]
%33 = getelementptr i8, i8* %7, i64 %32
%34 = load i8, i8* %33
%35 = icmp eq i8 %34, 0
br i1 %35, label %36, label %4
36: ; preds = %31, %1
%37 = phi i8 [ 0, %1 ], [ %8, %31 ]
ret i8 %37
}