If you are willing to do some of LLVM's work manually, Rust's zero-cost abstractions shine right through the optimizer, and you can shave off even that single branch instruction: Compiler Explorer. The following Rust code:
use std::ops::BitOr;
fn bit_mask_for_chars(chars: &[u8]) -> (u64, u32, u32) {
let min = chars.iter().copied().min().unwrap();
let max = chars.iter().copied().max().unwrap();
let mask = chars.iter().map(|&c| 1_u64 << (c - min)).fold(0, BitOr::bitor);
(mask, min.into(), max.into())
}
fn is_one_of(c: char, chars: &[u8]) -> bool {
let c: u32 = c.into();
let (mask, min, max) = bit_mask_for_chars(chars);
let in_range = (min..=max).contains(&c);
let matches = mask & 1 << (c - min);
(matches != 0) & in_range
}
pub fn is_reserved_char_6(c: char) -> bool {
is_one_of(c, b"aeiou")
}
Compiles down to the following assembly:
example::is_reserved_char_6:
lea eax, [rdi - 97]
cmp eax, 21
setb cl
add dil, 31
movzx eax, dil
mov edx, 1065233
bt rdx, rax
setb al
and al, cl
ret