int utf8_next_codepoint(const char **msg) {
  int code;
  const unsigned char *s = (const unsigned char *)*msg;
  if (*s <= 0x7f) {
    code = *s++;
  } else if (*s <= 0xbf) {
    code = '?';
    s++;
  } else if (*s <= 0xdf) {
    code = (s[0] & 0x1f) << 6 | (s[1] & 0x3f);
    s += 2;
  } else if (*s <= 0xef) {
    code = (s[0] & 0xf) << 12 | (s[1] & 0x3f) << 6 | (s[2] & 0x3f);
    s += 3;
  } else if (*s <= 0xf7) {
    code = (s[0] & 0x7) << 18 | (s[1] & 0x3f) << 12 | (s[2] & 0x3f) << 6 |
           (s[3] & 0x3f);
    s += 4;
  } else {
    code = 0xfffd;
    s++;
    while (0x80 <= *s && *s <= 0xbf)
      s++;
  }
  *msg = (const char *)s;
  return code;
}


x86 assembly (clang, -O0):
<utf8_next_codepoint>:
push   %rbp
mov    %rsp,%rbp
mov    %rdi,-0x8(%rbp)
mov    -0x8(%rbp),%rax
mov    (%rax),%rax
mov    %rax,-0x18(%rbp)
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
cmp    $0x7f,%eax
jg     113d <utf8_next_codepoint+0x3d>
mov    -0x18(%rbp),%rax
mov    %rax,%rcx
add    $0x1,%rcx
mov    %rcx,-0x18(%rbp)
movzbl (%rax),%eax
mov    %eax,-0xc(%rbp)
jmp    12cf <utf8_next_codepoint+0x1cf>
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
cmp    $0xbf,%eax
jg     1167 <utf8_next_codepoint+0x67>
movl   $0x3f,-0xc(%rbp)
mov    -0x18(%rbp),%rax
add    $0x1,%rax
mov    %rax,-0x18(%rbp)
jmp    12ca <utf8_next_codepoint+0x1ca>
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
cmp    $0xdf,%eax
jg     11a7 <utf8_next_codepoint+0xa7>
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
and    $0x1f,%eax
shl    $0x6,%eax
mov    -0x18(%rbp),%rcx
movzbl 0x1(%rcx),%ecx
and    $0x3f,%ecx
or     %ecx,%eax
mov    %eax,-0xc(%rbp)
mov    -0x18(%rbp),%rax
add    $0x2,%rax
mov    %rax,-0x18(%rbp)
jmp    12c5 <utf8_next_codepoint+0x1c5>
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
cmp    $0xef,%eax
jg     11f7 <utf8_next_codepoint+0xf7>
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
and    $0xf,%eax
shl    $0xc,%eax
mov    -0x18(%rbp),%rcx
movzbl 0x1(%rcx),%ecx
and    $0x3f,%ecx
shl    $0x6,%ecx
or     %ecx,%eax
mov    -0x18(%rbp),%rcx
movzbl 0x2(%rcx),%ecx
and    $0x3f,%ecx
or     %ecx,%eax
mov    %eax,-0xc(%rbp)
mov    -0x18(%rbp),%rax
add    $0x3,%rax
mov    %rax,-0x18(%rbp)
jmp    12c0 <utf8_next_codepoint+0x1c0>
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
cmp    $0xf7,%eax
jg     1257 <utf8_next_codepoint+0x157>
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
and    $0x7,%eax
shl    $0x12,%eax
mov    -0x18(%rbp),%rcx
movzbl 0x1(%rcx),%ecx
and    $0x3f,%ecx
shl    $0xc,%ecx
or     %ecx,%eax
mov    -0x18(%rbp),%rcx
movzbl 0x2(%rcx),%ecx
and    $0x3f,%ecx
shl    $0x6,%ecx
or     %ecx,%eax
mov    -0x18(%rbp),%rcx
movzbl 0x3(%rcx),%ecx
and    $0x3f,%ecx
or     %ecx,%eax
mov    %eax,-0xc(%rbp)
mov    -0x18(%rbp),%rax
add    $0x4,%rax
mov    %rax,-0x18(%rbp)
jmp    12bb <utf8_next_codepoint+0x1bb>
movl   $0xfffd,-0xc(%rbp)
mov    -0x18(%rbp),%rax
add    $0x1,%rax
mov    %rax,-0x18(%rbp)
mov    -0x18(%rbp),%rax
movzbl (%rax),%edx
xor    %eax,%eax
mov    $0x80,%ecx
cmp    %edx,%ecx
mov    %al,-0x19(%rbp)
jg     1295 <utf8_next_codepoint+0x195>
mov    -0x18(%rbp),%rax
movzbl (%rax),%eax
cmp    $0xbf,%eax
setle  %al
mov    %al,-0x19(%rbp)
mov    -0x19(%rbp),%al
test   $0x1,%al
jne    12a5 <utf8_next_codepoint+0x1a5>
jmp    12b6 <utf8_next_codepoint+0x1b6>
mov    -0x18(%rbp),%rax
add    $0x1,%rax
mov    %rax,-0x18(%rbp)
jmp    126a <utf8_next_codepoint+0x16a>
jmp    12bb <utf8_next_codepoint+0x1bb>
jmp    12c0 <utf8_next_codepoint+0x1c0>
jmp    12c5 <utf8_next_codepoint+0x1c5>
jmp    12ca <utf8_next_codepoint+0x1ca>
jmp    12cf <utf8_next_codepoint+0x1cf>
mov    -0x18(%rbp),%rcx
mov    -0x8(%rbp),%rax
mov    %rcx,(%rax)
mov    -0xc(%rbp),%eax
pop    %rbp
ret

x86 assembly (clang, -O2):
<utf8_next_codepoint>:
mov    (%rdi),%rcx
movzbl (%rcx),%eax
test   %al,%al
js     1112 <utf8_next_codepoint+0x12>
add    $0x1,%rcx
mov    %rcx,(%rdi)
ret
cmp    $0xbf,%al
ja     1123 <utf8_next_codepoint+0x23>
add    $0x1,%rcx
mov    $0x3f,%eax
mov    %rcx,(%rdi)
ret
cmp    $0xdf,%al
ja     113e <utf8_next_codepoint+0x3e>
and    $0x1f,%eax
shl    $0x6,%eax
movzbl 0x1(%rcx),%edx
and    $0x3f,%edx
or     %edx,%eax
add    $0x2,%rcx
mov    %rcx,(%rdi)
ret
cmp    $0xef,%al
ja     1165 <utf8_next_codepoint+0x65>
shl    $0xc,%eax
movzwl %ax,%eax
movzbl 0x1(%rcx),%edx
and    $0x3f,%edx
shl    $0x6,%edx
or     %eax,%edx
movzbl 0x2(%rcx),%eax
and    $0x3f,%eax
or     %edx,%eax
add    $0x3,%rcx
mov    %rcx,(%rdi)
ret
cmp    $0xf8,%al
jae    1198 <utf8_next_codepoint+0x98>
and    $0x7,%eax
shl    $0x12,%eax
movzbl 0x1(%rcx),%edx
and    $0x3f,%edx
shl    $0xc,%edx
or     %eax,%edx
movzbl 0x2(%rcx),%esi
and    $0x3f,%esi
shl    $0x6,%esi
or     %edx,%esi
movzbl 0x3(%rcx),%eax
and    $0x3f,%eax
or     %esi,%eax
add    $0x4,%rcx
mov    %rcx,(%rdi)
ret
mov    $0xfffd,%eax
nopl   (%rax)
lea    0x1(%rcx),%rdx
cmpb   $0xc0,0x1(%rcx)
mov    %rdx,%rcx
jl     11a0 <utf8_next_codepoint+0xa0>
mov    %rdx,(%rdi)
ret

LLVM IR (-O0):
define i32 @utf8_next_codepoint(i8** %0) {
%2 = alloca i8**
%3 = alloca i32
%4 = alloca i8*
store i8** %0, i8*** %2
%5 = load i8**, i8*** %2
%6 = load i8*, i8** %5
store i8* %6, i8** %4
%7 = load i8*, i8** %4
%8 = load i8, i8* %7
%9 = zext i8 %8 to i32
%10 = icmp sle i32 %9, 127
br i1 %10, label %11, label %16
11: ; preds = %1
%12 = load i8*, i8** %4
%13 = getelementptr inbounds i8, i8* %12, i32 1
store i8* %13, i8** %4
%14 = load i8, i8* %12
%15 = zext i8 %14 to i32
store i32 %15, i32* %3
br label %128
16: ; preds = %1
%17 = load i8*, i8** %4
%18 = load i8, i8* %17
%19 = zext i8 %18 to i32
%20 = icmp sle i32 %19, 191
br i1 %20, label %21, label %24
21: ; preds = %16
store i32 63, i32* %3
%22 = load i8*, i8** %4
%23 = getelementptr inbounds i8, i8* %22, i32 1
store i8* %23, i8** %4
br label %127
24: ; preds = %16
%25 = load i8*, i8** %4
%26 = load i8, i8* %25
%27 = zext i8 %26 to i32
%28 = icmp sle i32 %27, 223
br i1 %28, label %29, label %44
29: ; preds = %24
%30 = load i8*, i8** %4
%31 = getelementptr inbounds i8, i8* %30, i64 0
%32 = load i8, i8* %31
%33 = zext i8 %32 to i32
%34 = and i32 %33, 31
%35 = shl i32 %34, 6
%36 = load i8*, i8** %4
%37 = getelementptr inbounds i8, i8* %36, i64 1
%38 = load i8, i8* %37
%39 = zext i8 %38 to i32
%40 = and i32 %39, 63
%41 = or i32 %35, %40
store i32 %41, i32* %3
%42 = load i8*, i8** %4
%43 = getelementptr inbounds i8, i8* %42, i64 2
store i8* %43, i8** %4
br label %126
44: ; preds = %24
%45 = load i8*, i8** %4
%46 = load i8, i8* %45
%47 = zext i8 %46 to i32
%48 = icmp sle i32 %47, 239
br i1 %48, label %49, label %71
49: ; preds = %44
%50 = load i8*, i8** %4
%51 = getelementptr inbounds i8, i8* %50, i64 0
%52 = load i8, i8* %51
%53 = zext i8 %52 to i32
%54 = and i32 %53, 15
%55 = shl i32 %54, 12
%56 = load i8*, i8** %4
%57 = getelementptr inbounds i8, i8* %56, i64 1
%58 = load i8, i8* %57
%59 = zext i8 %58 to i32
%60 = and i32 %59, 63
%61 = shl i32 %60, 6
%62 = or i32 %55, %61
%63 = load i8*, i8** %4
%64 = getelementptr inbounds i8, i8* %63, i64 2
%65 = load i8, i8* %64
%66 = zext i8 %65 to i32
%67 = and i32 %66, 63
%68 = or i32 %62, %67
store i32 %68, i32* %3
%69 = load i8*, i8** %4
%70 = getelementptr inbounds i8, i8* %69, i64 3
store i8* %70, i8** %4
br label %125
71: ; preds = %44
%72 = load i8*, i8** %4
%73 = load i8, i8* %72
%74 = zext i8 %73 to i32
%75 = icmp sle i32 %74, 247
br i1 %75, label %76, label %105
76: ; preds = %71
%77 = load i8*, i8** %4
%78 = getelementptr inbounds i8, i8* %77, i64 0
%79 = load i8, i8* %78
%80 = zext i8 %79 to i32
%81 = and i32 %80, 7
%82 = shl i32 %81, 18
%83 = load i8*, i8** %4
%84 = getelementptr inbounds i8, i8* %83, i64 1
%85 = load i8, i8* %84
%86 = zext i8 %85 to i32
%87 = and i32 %86, 63
%88 = shl i32 %87, 12
%89 = or i32 %82, %88
%90 = load i8*, i8** %4
%91 = getelementptr inbounds i8, i8* %90, i64 2
%92 = load i8, i8* %91
%93 = zext i8 %92 to i32
%94 = and i32 %93, 63
%95 = shl i32 %94, 6
%96 = or i32 %89, %95
%97 = load i8*, i8** %4
%98 = getelementptr inbounds i8, i8* %97, i64 3
%99 = load i8, i8* %98
%100 = zext i8 %99 to i32
%101 = and i32 %100, 63
%102 = or i32 %96, %101
store i32 %102, i32* %3
%103 = load i8*, i8** %4
%104 = getelementptr inbounds i8, i8* %103, i64 4
store i8* %104, i8** %4
br label %124
105: ; preds = %71
store i32 65533, i32* %3
%106 = load i8*, i8** %4
%107 = getelementptr inbounds i8, i8* %106, i32 1
store i8* %107, i8** %4
br label %108
108: ; preds = %120, %105
%109 = load i8*, i8** %4
%110 = load i8, i8* %109
%111 = zext i8 %110 to i32
%112 = icmp sle i32 128, %111
br i1 %112, label %113, label %118
113: ; preds = %108
%114 = load i8*, i8** %4
%115 = load i8, i8* %114
%116 = zext i8 %115 to i32
%117 = icmp sle i32 %116, 191
br label %118
118: ; preds = %113, %108
%119 = phi i1 [ false, %108 ], [ %117, %113 ]
br i1 %119, label %120, label %123
120: ; preds = %118
%121 = load i8*, i8** %4
%122 = getelementptr inbounds i8, i8* %121, i32 1
store i8* %122, i8** %4
br label %108
123: ; preds = %118
br label %124
124: ; preds = %123, %76
br label %125
125: ; preds = %124, %49
br label %126
126: ; preds = %125, %29
br label %127
127: ; preds = %126, %21
br label %128
128: ; preds = %127, %11
%129 = load i8*, i8** %4
%130 = load i8**, i8*** %2
store i8* %129, i8** %130
%131 = load i32, i32* %3
ret i32 %131
}

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