LLVM IR为本机代码生成代码

时间:2015-02-26 03:28:45

标签: c++ compiler-construction llvm-ir

我正在学习编译器的工作原理。为了学习,我使用了几本书和教程,在某些时候偶然发现了我无法解决的问题。

我所遵循的完整教程代码可以在Github repository

找到

此代码生成IR代码并成功执行它。但是,如果我尝试将代码保存为example.ll文件并且(使用llc)编译为本机程序集,则此程序集无法编译为本机可执行文件(使用nasm和ld)。我还尝试将IR编译为本机对象文件,然后使用g ++编译它(与教程的make文件中编译的解析器相同),这也失败了。我想找到一种方法将我生成的IR代码实际编译成可执行的二进制文件(至少对于elf64)。

生成的IR代码[example.ll]:

; ModuleID = 'main'

@.str = private constant [4 x i8] c"%d\0A\00"

declare i32 @printf(i8*, ...)

define internal void @echo(i64 %toPrint) {
entry:
  %0 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i64 %toPrint)
  ret void
}

define internal void @main() {
entry:
  %0 = call i64 @do_math(i64 11)
  call void @echo(i64 %0)
  %1 = call i64 @do_math(i64 12)
  call void @echo(i64 %1)
  call void @printi(i64 10)
  ret void
}

declare void @printi(i64)

define internal i64 @do_math(i64 %a1) {
entry:
  %a = alloca i64
  store i64 %a1, i64* %a
  %x = alloca i64
  %0 = load i64* %a
  %1 = mul i64 %0, 5
  store i64 %1, i64* %x
  %2 = load i64* %x
  %3 = add i64 %2, 3
  ret i64 %3
}

然后通过asm:

$ llc-3.5 -filetype=asm -x86-asm-syntax=intel -o example.asm example.ll
$ nasm example.asm
example.asm:1: error: attempt to define a local label before any non-local labels
example.asm:2: error: attempt to define a local label before any non-local labels
example.asm:2: error: parser: instruction expected
example.asm:3: error: attempt to define a local label before any non-local labels
example.asm:3: error: parser: instruction expected
example.asm:4: error: attempt to define a local label before any non-local labels
example.asm:4: error: parser: instruction expected
example.asm:5: error: parser: instruction expected
BB#0: # %entry:3: error: parser: instruction expected
BB#0: # %entry:12: error: parser: instruction expected
...
...
<many similar errors here>

或通过海湾合作委员会:

$ llc-3.5 -filetype=obj -o example.o example.ll
$ g++ native.o example.o 
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status

PS:对存储库的贡献相应地修改代码(使其工作)将非常受欢迎!

UPD :根据要求

asm代码

    .text
    .file   "example.ll"
    .align  16, 0x90
    .type   echo,@function
echo:                                   # @echo
    .cfi_startproc
# BB#0:                                 # %entry
    pushq   %rax
.Ltmp0:
    .cfi_def_cfa_offset 16
    movq    %rdi, %rcx
    movl    $.L.str, %edi
    xorl    %eax, %eax
    movq    %rcx, %rsi
    callq   printf
    popq    %rax
    retq
.Ltmp1:
    .size   echo, .Ltmp1-echo
    .cfi_endproc

    .align  16, 0x90
    .type   main,@function
main:                                   # @main
    .cfi_startproc
# BB#0:                                 # %entry
    pushq   %rax
.Ltmp2:
    .cfi_def_cfa_offset 16
    movl    $11, %edi
    callq   do_math
    movq    %rax, %rdi
    callq   echo
    movl    $12, %edi
    callq   do_math
    movq    %rax, %rdi
    callq   echo
    movl    $10, %edi
    callq   printi
    popq    %rax
    retq
.Ltmp3:
    .size   main, .Ltmp3-main
    .cfi_endproc

    .align  16, 0x90
    .type   do_math,@function
do_math:                                # @do_math
    .cfi_startproc
# BB#0:                                 # %entry
    movq    %rdi, -8(%rsp)
    leaq    (%rdi,%rdi,4), %rax
    movq    %rax, -16(%rsp)
    leaq    3(%rdi,%rdi,4), %rax
    retq
.Ltmp4:
    .size   do_math, .Ltmp4-do_math
    .cfi_endproc

    .type   .L.str,@object          # @.str
    .section    .rodata,"a",@progbits
.L.str:
    .asciz  "%d\n"
    .size   .L.str, 4


    .section    ".note.GNU-stack","",@progbits

输出nm

$ nm example.o 
0000000000000060 t do_math
0000000000000000 t echo
0000000000000020 t main
                 U printf
                 U printi

1 个答案:

答案 0 :(得分:2)

汇编文件

您无法汇编example.asm的原因可能是它采用AT&amp; T语法,而nasm则需要英特尔语法。您似乎已经要求llc输出英特尔语法,但您的标志错误了。根据{{​​3}},它--x86-asm-syntax(注意双击)。

我怀疑你可能会更好地与as(GNU汇编程序)组装,因为有很多互不兼容的英特尔语法方言;我不确定哪一个LLVM会说话。为此,请使用命令:

$ as example.asm -o example.o

目标文件

您无法将目标文件与C库关联的原因是您已宣布main函数有this manualdefine internal) 。与C中的static关键字一样,它使符号&#34;不可见&#34;在目标文件之外,由小写字母&#39; t&#39;在nm输出中。

main创建LLVM函数对象时,应将其链接类型设置为llvm::GlobalValue::ExternalLinkage

同样的问题出现在汇编文件中,当然 - 缺少.global main


不要认为你应该提供所有功能的外部链接;如果函数仅用于定义它的翻译单元,它实际上应该有内部链接。你无法为main执行此操作。