int call_func(void *_func, unsigned nargs, char **args) {
  switch (nargs) {
  case 1: {
    int (*func)(char *a0) = _func;
    return func(args[0]);
  }
  case 2: {
    int (*func)(char *a0, char *a1) = _func;
    return func(args[0], args[1]);
  }
  case 3: {
    int (*func)(char *a0, char *a1, char *a2) = _func;
    return func(args[0], args[1], args[2]);
  }
  default:
    return -1;
  }
}


x86 assembly (clang, -O0):
<call_func>:
push   %rbp
mov    %rsp,%rbp
sub    $0x40,%rsp
mov    %rdi,-0x10(%rbp)
mov    %esi,-0x14(%rbp)
mov    %rdx,-0x20(%rbp)
mov    -0x14(%rbp),%eax
mov    %eax,-0x3c(%rbp)
sub    $0x1,%eax
je     1149 <call_func+0x49>
jmp    1127 <call_func+0x27>
mov    -0x3c(%rbp),%eax
sub    $0x2,%eax
je     1166 <call_func+0x66>
jmp    1138 <call_func+0x38>
mov    -0x3c(%rbp),%eax
sub    $0x3,%eax
je     118b <call_func+0x8b>
jmp    11b8 <call_func+0xb8>
mov    -0x10(%rbp),%rax
mov    %rax,-0x28(%rbp)
mov    -0x28(%rbp),%rax
mov    -0x20(%rbp),%rcx
mov    (%rcx),%rdi
call   *%rax
mov    %eax,-0x4(%rbp)
jmp    11bf <call_func+0xbf>
mov    -0x10(%rbp),%rax
mov    %rax,-0x30(%rbp)
mov    -0x30(%rbp),%rax
mov    -0x20(%rbp),%rcx
mov    (%rcx),%rdi
mov    -0x20(%rbp),%rcx
mov    0x8(%rcx),%rsi
call   *%rax
mov    %eax,-0x4(%rbp)
jmp    11bf <call_func+0xbf>
mov    -0x10(%rbp),%rax
mov    %rax,-0x38(%rbp)
mov    -0x38(%rbp),%rax
mov    -0x20(%rbp),%rcx
mov    (%rcx),%rdi
mov    -0x20(%rbp),%rcx
mov    0x8(%rcx),%rsi
mov    -0x20(%rbp),%rcx
mov    0x10(%rcx),%rdx
call   *%rax
mov    %eax,-0x4(%rbp)
jmp    11bf <call_func+0xbf>
movl   $0xffffffff,-0x4(%rbp)
mov    -0x4(%rbp),%eax
add    $0x40,%rsp
pop    %rbp
ret

x86 assembly (clang, -O2):
<call_func>:
mov    %rdi,%rax
cmp    $0x3,%esi
je     1117 <call_func+0x17>
cmp    $0x2,%esi
je     1124 <call_func+0x24>
cmp    $0x1,%esi
jne    112d <call_func+0x2d>
mov    (%rdx),%rdi
jmp    *%rax
mov    (%rdx),%rdi
mov    0x8(%rdx),%rsi
mov    0x10(%rdx),%rdx
jmp    *%rax
mov    (%rdx),%rdi
mov    0x8(%rdx),%rsi
jmp    *%rax
mov    $0xffffffff,%eax
ret

LLVM IR (-O0):
define i32 @call_func(i8* %0, i32 %1, i8** %2) {
%4 = alloca i32
%5 = alloca i8*
%6 = alloca i32
%7 = alloca i8**
%8 = alloca i32 (i8*)*
%9 = alloca i32 (i8*, i8*)*
%10 = alloca i32 (i8*, i8*, i8*)*
store i8* %0, i8** %5
store i32 %1, i32* %6
store i8** %2, i8*** %7
%11 = load i32, i32* %6
switch i32 %11, label %45 [
i32 1, label %12
i32 2, label %20
i32 3, label %31
]
12: ; preds = %3
%13 = load i8*, i8** %5
%14 = bitcast i8* %13 to i32 (i8*)*
store i32 (i8*)* %14, i32 (i8*)** %8
%15 = load i32 (i8*)*, i32 (i8*)** %8
%16 = load i8**, i8*** %7
%17 = getelementptr inbounds i8*, i8** %16, i64 0
%18 = load i8*, i8** %17
%19 = call i32 %15(i8* %18)
store i32 %19, i32* %4
br label %46
20: ; preds = %3
%21 = load i8*, i8** %5
%22 = bitcast i8* %21 to i32 (i8*, i8*)*
store i32 (i8*, i8*)* %22, i32 (i8*, i8*)** %9
%23 = load i32 (i8*, i8*)*, i32 (i8*, i8*)** %9
%24 = load i8**, i8*** %7
%25 = getelementptr inbounds i8*, i8** %24, i64 0
%26 = load i8*, i8** %25
%27 = load i8**, i8*** %7
%28 = getelementptr inbounds i8*, i8** %27, i64 1
%29 = load i8*, i8** %28
%30 = call i32 %23(i8* %26, i8* %29)
store i32 %30, i32* %4
br label %46
31: ; preds = %3
%32 = load i8*, i8** %5
%33 = bitcast i8* %32 to i32 (i8*, i8*, i8*)*
store i32 (i8*, i8*, i8*)* %33, i32 (i8*, i8*, i8*)** %10
%34 = load i32 (i8*, i8*, i8*)*, i32 (i8*, i8*, i8*)** %10
%35 = load i8**, i8*** %7
%36 = getelementptr inbounds i8*, i8** %35, i64 0
%37 = load i8*, i8** %36
%38 = load i8**, i8*** %7
%39 = getelementptr inbounds i8*, i8** %38, i64 1
%40 = load i8*, i8** %39
%41 = load i8**, i8*** %7
%42 = getelementptr inbounds i8*, i8** %41, i64 2
%43 = load i8*, i8** %42
%44 = call i32 %34(i8* %37, i8* %40, i8* %43)
store i32 %44, i32* %4
br label %46
45: ; preds = %3
store i32 -1, i32* %4
br label %46
46: ; preds = %45, %31, %20, %12
%47 = load i32, i32* %4
ret i32 %47
}

LLVM IR (-O2):
define i32 @call_func(i8* nocapture %0, i32 %1, i8** nocapture %2) {
switch i32 %1, label %22 [
i32 1, label %4
i32 2, label %8
i32 3, label %14
]
4: ; preds = %3
%5 = bitcast i8* %0 to i32 (i8*)*
%6 = load i8*, i8** %2
%7 = tail call i32 %5(i8* %6)
br label %22
8: ; preds = %3
%9 = bitcast i8* %0 to i32 (i8*, i8*)*
%10 = load i8*, i8** %2
%11 = getelementptr inbounds i8*, i8** %2, i64 1
%12 = load i8*, i8** %11
%13 = tail call i32 %9(i8* %10, i8* %12)
br label %22
14: ; preds = %3
%15 = bitcast i8* %0 to i32 (i8*, i8*, i8*)*
%16 = load i8*, i8** %2
%17 = getelementptr inbounds i8*, i8** %2, i64 1
%18 = load i8*, i8** %17
%19 = getelementptr inbounds i8*, i8** %2, i64 2
%20 = load i8*, i8** %19
%21 = tail call i32 %15(i8* %16, i8* %18, i8* %20)
br label %22
22: ; preds = %3, %14, %8, %4
%23 = phi i32 [ %21, %14 ], [ %13, %8 ], [ %7, %4 ], [ -1, %3 ]
ret i32 %23
}