Hello everyone,
I'm new to LLVM (which is a really great project by the way) and I hope this is the right place for my question.
In the LLVM Language Reference Manual I found the "jumptable" function attribute, which seems to be exactly what I need for my project (where I want to add one level of indirection to every function call), but I have trouble figuring out, how to use it correctly.
I wrote my own transformation pass (using LLVM 8.0.1) to add this attribute as well as unnamed_addr (I'm aware that this might break some programs, but jumptable requires it) to every function definition:
// includes \.\.\.
using namespace llvm;
namespace \{
struct MyPass : public ModulePass \{
static char ID;
MyPass\(\) : ModulePass\(ID\) \{\}
bool runOnModule\(Module &M\) override \{
for \(Function &F : M\.getFunctionList\(\)\) \{
if \(\!F\.isDeclaration\(\)\) \{
F.setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
F.addFnAttr(Attribute::JumpTable);
}
}
return true;
}
};
}
char MyPass::ID = 0;
static RegisterPass<MyPass> X\("mypass", "My Pass"\);
And I wrote a program test.c:
\#include <stdio\.h>
void foo\(\) \{
puts\("Hello World"\);
\}
int main\(\) \{
foo\(\);
\}
I compiled it to test.ll and ran it through opt:
clang \-O0 \-emit\-llvm \-S \-o test\.ll test\.c
build/bin/opt \-load build/lib/LLVMMyPass\.so \-mypass \-S \-o test\.out\.ll test\.ll
The output (test.out.ll) was identical to test.ll except for the 3 occurrences of jumptable and the last 2 occurrences of unnamed_addr:
; ModuleID = 'test\.ll'
source\_filename = "test\.c"
target datalayout = "e\-m:e\-i64:64\-f80:128\-n8:16:32:64\-S128"
target triple = "x86\_64\-unknown\-linux\-gnu"
@\.str = private unnamed\_addr constant \[12 x i8\] c"Hello World\\00", align 1
; Function Attrs: jumptable noinline nounwind optnone uwtable
define dso\_local void @foo\(\) unnamed\_addr \#0 \{
%1 = call i32 @puts\(i8\* getelementptr inbounds \(\[12 x i8\], \[12 x i8\]\* @\.str, i32 0, i32 0\)\)
ret void
\}
declare dso\_local i32 @puts\(i8\*\) \#1
; Function Attrs: jumptable noinline nounwind optnone uwtable
define dso\_local i32 @main\(\) unnamed\_addr \#0 \{
call void @foo\(\)
ret i32 0
\}
attributes \#0 = \{ jumptable noinline nounwind optnone uwtable "no\-jump\-tables"="false"
; \.\.\. many more attributes \.\.\.
\}
attributes \#1 = \{
; \.\.\. many more attributes \.\.\.
\}
\!llvm\.module\.flags = \!\{\!0\}
\!llvm\.ident = \!\{\!1\}
\!0 = \!\{i32 1, \!"wchar\_size", i32 4\}
\!1 = \!\{\!"clang version 8\.0\.0 \(tags/RELEASE\_800/final\)"\}
Finally, I executed:
build/bin/llc \-o test\.out\.s test\.out\.ll
clang \-o test\.out\.native test\.out\.s
objdump \-\-disassemble\-all test\.out\.native
I hoped to see some kind of jump table being used for the call to foo (maybe even the plt mechanism the dynamic library calls use), but it is just a "normal" jump to foo's address:
0000000000400500 <foo>:
400500: 55 push %rbp
400501: 48 89 e5 mov %rsp,%rbp
400504: 48 bf c0 05 40 00 00 movabs $0x4005c0,%rdi
40050b: 00 00 00
40050e: e8 cd fe ff ff callq 4003e0 <puts@plt>
400513: 5d pop %rbp
400514: c3 retq
400515: 66 2e 0f 1f 84 00 00 nopw %cs:0x0\(%rax,%rax,1\)
40051c: 00 00 00
40051f: 90 nop
0000000000400520 <main>:
400520: 55 push %rbp
400521: 48 89 e5 mov %rsp,%rbp
400524: e8 d7 ff ff ff callq 400500 <foo>
400529: 31 c0 xor %eax,%eax
40052b: 5d pop %rbp
40052c: c3 retq
40052d: 0f 1f 00 nopl \(%rax\)
So, what am I doing wrong? The documentation indicates that the jump table is created at code-generation time. So I guess my pass works fine and I just need to get the code generator to process the jumptable attribute somehow? I found the createJumpInstrTablesPass in include/CodeGen/Passes.h, but it seems like it's never used.
Thanks in advance
Daniel