基于LLVM的编译原理简明教程 (1)

基于LLVM的编译原理简明教程 (1) - 写编译器越来越容易了

字数2507 阅读192 评论0 喜欢3

收藏文章 分享

基于LLVM的编译原理简明教程 (1) - 写编译器越来越容易了

进入21世纪,新的编程语言如雨后春笋一样不停地冒出来。需求当然是重要的驱动力量,但是在其中起了重要作用的就是工具链的改善。

2000年,UIUC的Chris Lattner主持开发了一套称为LLVM(Low Level Virtual Machine)的编译器工具库套件。后来,LLVM的scope越来越大,Low Level Virtual Machine已经不足以表示LLVM的全部,于是,LLVM就变成了正式的名字。LLVM可以用于常规编译器,JIT编译器,汇编器,调试器,静态分析工具等一系列跟编程语言相关的工作。

后来,Chris Lattner又主持开发了Clang,针对C/C++/Objective-C的前端。这个编译器直接挑战了GCC的统治地位。成为Apple系统的主要编译器,在Android中,指名使用Clang的模块也越来越多。

2012年,LLVM荣获美国计算机学会ACM的软件系统大奖,跟UNIX,WWW,TCP/IP,TeX,Java等经典系统作伴。

ACM系统奖完全名单

另外再八卦几句LLVM的主要作者和架构师Chris Lattner。这哥们生于1978年。

2005年,Chris Lattner加入Apple。因为Apple对于GCC支持Objective-C不力的不满,LLVM和Clang成为Apple替代GCC的杀手级武器。

2010年,Chris Lattner又开始主持开发Swift语言。

好了,言归正传。首先我们想说明的是,跟学院派的厚书给大家的印象不同,其实用LLVM写个简单的编译器是件容易的事情,因为大部分事情LLVM都替我们做了。

用LLVM做个简单的编译器很容易

我们先看一个使用LLVM工具之后,实现一门编程语言的简图:

编译器简图

完全需要我们手工,或者依靠其他工具如lex, yacc来做的事情,是从源代码到token的词法分析和从token到AST的语法分析。也就是前端的主要部分需要我们来实现,毕竟我们是这门语言的定义者。在介绍LLVM的书里,讲前端的部分都是只占很小的篇幅的,所以大家可以take it easy.

在LLVM的万花筒语言例子里,带有注释的词法分析和语法分析也不过400行。大家如果觉得还复杂,后面我会带大家做一些更简单的,先完成一小部分功能,然后迭代式开发。区区百余行代码,不需要学习编译原理。

比如Clang就是一个实现了C/C++/Objective-C的前端。

从AST转LLVM开始,LLVM就开始提供一系列的工具帮助我们快速开发。从IR(中间指令代码)到DAG(有向无环图)再到机器指令,针对常用的平台,LLVM有完善的后端。也就是说,我们只要完成了到IR这一步,后面的工作我们就享有和Clang一样的先进生产力了。

口说无凭,有例子为证,这是将二元表达式AST转成IR的函数:

Value *BinaryExprAST::codegen() {... switch (Op) { case '+': return Builder.CreateFAdd(L, R, "addtmp"); case '-': return Builder.CreateFSub(L, R, "subtmp"); case '*': return Builder.CreateFMul(L, R, "multmp"); case '

如何生成加减乘除的IR,在这个阶段完全不用关心,LLVM会帮我们生成相应的代码。

下面我们再看一个声明函数原型的:

Function *PrototypeAST::codegen() { // Make the function type: double(double,double) etc. std::vector Doubles(Args.size(), Type::getDoubleTy(TheContext)); FunctionType *FT = FunctionType::get(Type::getDoubleTy(TheContext), Doubles, false); Function *F = Function::Create(FT, Function::ExternalLinkage, Name, TheModule.get()); // Set names for all arguments. unsigned Idx = 0; for (auto &Arg : F->args()) Arg.setName(Args[Idx++]); return F;}词法分析

在正则表达式已经成为基本技能的今天,词法分析完全无门槛啊。正常情况下,我们只要写一组正则表达式,或者写个简单的状态机就可以了。

词法分析的输出是将源代码解析成一个个的token。这些token就是有类型和值的一些小单元,比如是关键字,还是数字,还是标识符等等。这个阶段不用管它们是如何组合的,都是干嘛的。

比如一个token类型是数值,值是3. 这个信息就已经足够了,至于这个3干嘛用,后面整理AST的时候再放到合适的位置上去。

至于什么时上下文无关语言,什么是确定有穷自动机,非确定有穷自动机等等这些,暂时都不需要了解。

语法分析

语法分析诚然是比词法分析要复杂一些。但是幸运的是,对于绝大多数语句和表达式来讲,并不需要高深的知识,“移进-归约”是个好方法,但是在我们学习的相当长的一段时期内都用不上。

语法分析的输出是抽象语法树AST,既然是棵树,自然构造时需要递归。所以在大部分的语句中,我们只按递归下降的方法就足够了。

对于表达式,递归下降还不够用,至少运算符还有优先级啊。所以针对表达式,我们还需要运算符优先分析法。SLR,LALR和LR暂时还用不上。

语法制导翻译和中间代码生成

从前面的简单例子中我们已经看到了,这部分大部分调用LLVM为我们提供的IR构造工具就可以了。入门阶段我们能想到的,如代码块,函数调用,控制结构等,LLVM都为我们准备好了。

优化

LLVM主我们提供了大量的优化Pass供我们选择和组合。在IR阶段和机器码阶段,我们都将花大量的篇幅来讨论优化。这可能也是我们真正感兴趣的部分。

词法分析很简单

我们看一个官方的例子,首先定义token的类型,有一种算一种吧。将来扩展都是体力活。

enum Token { tok_eof = -1,... // primary tok_identifier = -4, tok_number = -5};

然后就是解析正则表达式,一点技术含量也没有,哈哈~

我对官方的版本做了一点删节,看起来可以更清楚一些:

static std::string IdentifierStr; // Filled in if tok_identifierstatic double NumVal; // Filled in if tok_number/// gettok - Return the next token from standard input.static int gettok() { static int LastChar = ' '; // Skip any whitespace. while (isspace(LastChar)) LastChar = getchar(); if (isalpha(LastChar)) { // identifier: [a-zA-Z][a-zA-Z0-9]* IdentifierStr = LastChar; while (isalnum((LastChar = getchar()))) IdentifierStr += LastChar;... return tok_identifier; } if (isdigit(LastChar) || LastChar == '.') { // Number: [0-9.]+ std::string NumStr; do { NumStr += LastChar; LastChar = getchar(); } while (isdigit(LastChar) || LastChar == '.'); NumVal = strtod(NumStr.c_str(), nullptr); return tok_number; } ... // Check for end of file. Don't eat the EOF. if (LastChar == EOF) return tok_eof;...}

如果不想手写的话,lex, flex之类的工具很多,就是根据正则表达式来决定token类型,根据类型存一下对应的值。

如果token的类型多,就是搭积木,写正则。都是体力活~

够用的语法分析其实也很简单

上面介绍了,我们自顶向下,构造抽象语法树。

先定义个根类型吧:

/// ExprAST - Base class for all expression nodes.class ExprAST {public: virtual ~ExprAST() {} virtual Value *codegen() = 0;};

我们先来个简单的,就表示一个数字。这个好办,就一个节点,存个数值。

/// NumberExprAST - Expression class for numeric literals like "1.0".class NumberExprAST : public ExprAST { double Val;public: NumberExprAST(double Val) : Val(Val) {} Value *codegen() override;};

再来一个例子,变量,就是一个变量名么。赋值是下一步的事情了。

/// VariableExprAST - Expression class for referencing a variable, like "a".class VariableExprAST : public ExprAST { std::string Name;public: VariableExprAST(const std::string &Name) : Name(Name) {} Value *codegen() override;};

函数原型:

/// PrototypeAST - This class represents the "prototype" for a function,/// which captures its name, and its argument names (thus implicitly the number/// of arguments the function takes).class PrototypeAST { std::string Name; std::vector Args;public: PrototypeAST(const std::string &Name, std::vector Args) : Name(Name), Args(std::move(Args)) {} Function *codegen(); const std::string &getName() const { return Name; }};

然后我们再看看如何通过token去构造一个数值的AST:

词法分析时,已经把这个数值暂存了,我们把它拿来用就是了。

/// numberexpr ::= numberstatic std::unique_ptr ParseNumberExpr() { auto Result = llvm::make_unique(NumVal); getNextToken(); // consume the number return std::move(Result);}

再看看函数声明的:

/// prototype/// ::= id '(' id* ')'static std::unique_ptr ParsePrototype() { if (CurTok != tok_identifier) return LogErrorP("Expected function name in prototype"); std::string FnName = IdentifierStr; getNextToken(); if (CurTok != '(') return LogErrorP("Expected '(' in prototype"); std::vector ArgNames; while (getNextToken() == tok_identifier) ArgNames.push_back(IdentifierStr); if (CurTok != ')') return LogErrorP("Expected ')' in prototype"); // success. getNextToken(); // eat ')'. return llvm::make_unique(FnName, std::move(ArgNames));}

先读函数名,再找左括号,然后是参数列表,最后是处理右括号。什么嘛,一点技术含量也没有。。。

上面例子这些,都是没有嵌套的,也不需要递归下降和算符优先。这些是处理比如二元表达式的时候才会遇到的。我们可以先学习容易的,先能把这些容易的组件组成一门虽然语言功能不全,但是真正实现了从源码到机器指令的编译器。

上面的例子都来自官方的例子万花筒(Keleidoscope)语言的片段。官方教程当然写得已经足够好,但是还是稍嫌复杂了点,能生成一个可玩的编译器的速度还是有点慢。我打算把学习曲线再降低一下,通过不断地迭代,一点一点搭起可玩的编译器,然后慢慢扩充功能。

Hello,LLVM

LLVM的下载

先下载LLVMsvn co http://llvm.org/svn/llvm-project/llvm/trunk llvm

在LLVM的tools目录下,下载Clang(可选,但是建议):cd llvm/toolssvn co http://llvm.org/svn/llvm-project/cfe/trunk clang

在LLVM的projects目录下,可选下载compiler-rt,Libomp,libcxx,libcxxabi。反正我都下载了svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rtsvn co http://llvm.org/svn/llvm-project/openmp/trunk openmpsvn co http://llvm.org/svn/llvm-project/libcxx/trunk libcxxsvn co http://llvm.org/svn/llvm-project/libcxxabi/trunk libcxxabi

LLVM的编译

既然官方说大部分LLVM的开发者都使用Ninja,我们也就follow他们吧。

我在Mac下,所以使用Homebrew来安装CMake和Ninja。Linux与些类似,GCC版本太旧之类的请自助。Windows我还没试过,后面更新一下吧。

在LLVM目录下创建build目录

cd build

cmake -G Ninja

Ninja

写个LLVM上的Hello,World程序吧

从AST转IR开始,我们都要用到LLVM的工具啦。先写个小程序学习一下LLVM的程序是如何编译的吧:

#include "llvm/IR/Module.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include #include #include #include #include static llvm::LLVMContext TheContext;static llvm::IRBuilder Builder(TheContext);static std::unique_ptr TheModule;static std::map NameValues;int main(){ TheModule = llvm::make_unique("hello,llvm",TheContext); TheModule -> dump(); return 0;}

输出结果如下:

; ModuleID = 'hello,llvm'source_filename = "hello,llvm"如何链接LLVM的库

使用LLVM库的话,需要一大堆参数.

下面是在我的电脑上的参数:

-I/Users/ziyingliuziying/lusing/llvm/llvm/include -I/Users/ziyingliuziying/lusing/llvm/llvm/build/include -fPIC -fvisibility-inlines-hidden -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wcovered-switch-default -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Werror=date-time -std=c++11 -fcolor-diagnostics -fno-exceptions -fno-rtti -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS-L/Users/ziyingliuziying/lusing/llvm/llvm/build/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names-lLLVMCore -lLLVMSupport-lcurses -lz -lm

每次都这么写吓死人了啊。于是LLVM为我们提供了llvm-config工具。刚才我那一大串,是用下面的命令行生成的:

llvm-config --cxxflags --ldflags --system-libs --libs core

完整的编译命令可以这么写:

clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core` -o toy

推荐拓展阅读

著作权归作者所有

如果觉得我的文

基于LLVM的编译原理简明教程 (1) - 写编译器越来越容易了

字数2507 阅读192 评论0 喜欢3

收藏文章 分享

基于LLVM的编译原理简明教程 (1) - 写编译器越来越容易了

进入21世纪,新的编程语言如雨后春笋一样不停地冒出来。需求当然是重要的驱动力量,但是在其中起了重要作用的就是工具链的改善。

2000年,UIUC的Chris Lattner主持开发了一套称为LLVM(Low Level Virtual Machine)的编译器工具库套件。后来,LLVM的scope越来越大,Low Level Virtual Machine已经不足以表示LLVM的全部,于是,LLVM就变成了正式的名字。LLVM可以用于常规编译器,JIT编译器,汇编器,调试器,静态分析工具等一系列跟编程语言相关的工作。

后来,Chris Lattner又主持开发了Clang,针对C/C++/Objective-C的前端。这个编译器直接挑战了GCC的统治地位。成为Apple系统的主要编译器,在Android中,指名使用Clang的模块也越来越多。

2012年,LLVM荣获美国计算机学会ACM的软件系统大奖,跟UNIX,WWW,TCP/IP,TeX,Java等经典系统作伴。

ACM系统奖完全名单

另外再八卦几句LLVM的主要作者和架构师Chris Lattner。这哥们生于1978年。

2005年,Chris Lattner加入Apple。因为Apple对于GCC支持Objective-C不力的不满,LLVM和Clang成为Apple替代GCC的杀手级武器。

2010年,Chris Lattner又开始主持开发Swift语言。

好了,言归正传。首先我们想说明的是,跟学院派的厚书给大家的印象不同,其实用LLVM写个简单的编译器是件容易的事情,因为大部分事情LLVM都替我们做了。

用LLVM做个简单的编译器很容易

我们先看一个使用LLVM工具之后,实现一门编程语言的简图:

编译器简图

完全需要我们手工,或者依靠其他工具如lex, yacc来做的事情,是从源代码到token的词法分析和从token到AST的语法分析。也就是前端的主要部分需要我们来实现,毕竟我们是这门语言的定义者。在介绍LLVM的书里,讲前端的部分都是只占很小的篇幅的,所以大家可以take it easy.

在LLVM的万花筒语言例子里,带有注释的词法分析和语法分析也不过400行。大家如果觉得还复杂,后面我会带大家做一些更简单的,先完成一小部分功能,然后迭代式开发。区区百余行代码,不需要学习编译原理。

比如Clang就是一个实现了C/C++/Objective-C的前端。

从AST转LLVM开始,LLVM就开始提供一系列的工具帮助我们快速开发。从IR(中间指令代码)到DAG(有向无环图)再到机器指令,针对常用的平台,LLVM有完善的后端。也就是说,我们只要完成了到IR这一步,后面的工作我们就享有和Clang一样的先进生产力了。

口说无凭,有例子为证,这是将二元表达式AST转成IR的函数:

Value *BinaryExprAST::codegen() {... switch (Op) { case '+': return Builder.CreateFAdd(L, R, "addtmp"); case '-': return Builder.CreateFSub(L, R, "subtmp"); case '*': return Builder.CreateFMul(L, R, "multmp"); case '

如何生成加减乘除的IR,在这个阶段完全不用关心,LLVM会帮我们生成相应的代码。

下面我们再看一个声明函数原型的:

Function *PrototypeAST::codegen() { // Make the function type: double(double,double) etc. std::vector Doubles(Args.size(), Type::getDoubleTy(TheContext)); FunctionType *FT = FunctionType::get(Type::getDoubleTy(TheContext), Doubles, false); Function *F = Function::Create(FT, Function::ExternalLinkage, Name, TheModule.get()); // Set names for all arguments. unsigned Idx = 0; for (auto &Arg : F->args()) Arg.setName(Args[Idx++]); return F;}词法分析

在正则表达式已经成为基本技能的今天,词法分析完全无门槛啊。正常情况下,我们只要写一组正则表达式,或者写个简单的状态机就可以了。

词法分析的输出是将源代码解析成一个个的token。这些token就是有类型和值的一些小单元,比如是关键字,还是数字,还是标识符等等。这个阶段不用管它们是如何组合的,都是干嘛的。

比如一个token类型是数值,值是3. 这个信息就已经足够了,至于这个3干嘛用,后面整理AST的时候再放到合适的位置上去。

至于什么时上下文无关语言,什么是确定有穷自动机,非确定有穷自动机等等这些,暂时都不需要了解。

语法分析

语法分析诚然是比词法分析要复杂一些。但是幸运的是,对于绝大多数语句和表达式来讲,并不需要高深的知识,“移进-归约”是个好方法,但是在我们学习的相当长的一段时期内都用不上。

语法分析的输出是抽象语法树AST,既然是棵树,自然构造时需要递归。所以在大部分的语句中,我们只按递归下降的方法就足够了。

对于表达式,递归下降还不够用,至少运算符还有优先级啊。所以针对表达式,我们还需要运算符优先分析法。SLR,LALR和LR暂时还用不上。

语法制导翻译和中间代码生成

从前面的简单例子中我们已经看到了,这部分大部分调用LLVM为我们提供的IR构造工具就可以了。入门阶段我们能想到的,如代码块,函数调用,控制结构等,LLVM都为我们准备好了。

优化

LLVM主我们提供了大量的优化Pass供我们选择和组合。在IR阶段和机器码阶段,我们都将花大量的篇幅来讨论优化。这可能也是我们真正感兴趣的部分。

词法分析很简单

我们看一个官方的例子,首先定义token的类型,有一种算一种吧。将来扩展都是体力活。

enum Token { tok_eof = -1,... // primary tok_identifier = -4, tok_number = -5};

然后就是解析正则表达式,一点技术含量也没有,哈哈~

我对官方的版本做了一点删节,看起来可以更清楚一些:

static std::string IdentifierStr; // Filled in if tok_identifierstatic double NumVal; // Filled in if tok_number/// gettok - Return the next token from standard input.static int gettok() { static int LastChar = ' '; // Skip any whitespace. while (isspace(LastChar)) LastChar = getchar(); if (isalpha(LastChar)) { // identifier: [a-zA-Z][a-zA-Z0-9]* IdentifierStr = LastChar; while (isalnum((LastChar = getchar()))) IdentifierStr += LastChar;... return tok_identifier; } if (isdigit(LastChar) || LastChar == '.') { // Number: [0-9.]+ std::string NumStr; do { NumStr += LastChar; LastChar = getchar(); } while (isdigit(LastChar) || LastChar == '.'); NumVal = strtod(NumStr.c_str(), nullptr); return tok_number; } ... // Check for end of file. Don't eat the EOF. if (LastChar == EOF) return tok_eof;...}

如果不想手写的话,lex, flex之类的工具很多,就是根据正则表达式来决定token类型,根据类型存一下对应的值。

如果token的类型多,就是搭积木,写正则。都是体力活~

够用的语法分析其实也很简单

上面介绍了,我们自顶向下,构造抽象语法树。

先定义个根类型吧:

/// ExprAST - Base class for all expression nodes.class ExprAST {public: virtual ~ExprAST() {} virtual Value *codegen() = 0;};

我们先来个简单的,就表示一个数字。这个好办,就一个节点,存个数值。

/// NumberExprAST - Expression class for numeric literals like "1.0".class NumberExprAST : public ExprAST { double Val;public: NumberExprAST(double Val) : Val(Val) {} Value *codegen() override;};

再来一个例子,变量,就是一个变量名么。赋值是下一步的事情了。

/// VariableExprAST - Expression class for referencing a variable, like "a".class VariableExprAST : public ExprAST { std::string Name;public: VariableExprAST(const std::string &Name) : Name(Name) {} Value *codegen() override;};

函数原型:

/// PrototypeAST - This class represents the "prototype" for a function,/// which captures its name, and its argument names (thus implicitly the number/// of arguments the function takes).class PrototypeAST { std::string Name; std::vector Args;public: PrototypeAST(const std::string &Name, std::vector Args) : Name(Name), Args(std::move(Args)) {} Function *codegen(); const std::string &getName() const { return Name; }};

然后我们再看看如何通过token去构造一个数值的AST:

词法分析时,已经把这个数值暂存了,我们把它拿来用就是了。

/// numberexpr ::= numberstatic std::unique_ptr ParseNumberExpr() { auto Result = llvm::make_unique(NumVal); getNextToken(); // consume the number return std::move(Result);}

再看看函数声明的:

/// prototype/// ::= id '(' id* ')'static std::unique_ptr ParsePrototype() { if (CurTok != tok_identifier) return LogErrorP("Expected function name in prototype"); std::string FnName = IdentifierStr; getNextToken(); if (CurTok != '(') return LogErrorP("Expected '(' in prototype"); std::vector ArgNames; while (getNextToken() == tok_identifier) ArgNames.push_back(IdentifierStr); if (CurTok != ')') return LogErrorP("Expected ')' in prototype"); // success. getNextToken(); // eat ')'. return llvm::make_unique(FnName, std::move(ArgNames));}

先读函数名,再找左括号,然后是参数列表,最后是处理右括号。什么嘛,一点技术含量也没有。。。

上面例子这些,都是没有嵌套的,也不需要递归下降和算符优先。这些是处理比如二元表达式的时候才会遇到的。我们可以先学习容易的,先能把这些容易的组件组成一门虽然语言功能不全,但是真正实现了从源码到机器指令的编译器。

上面的例子都来自官方的例子万花筒(Keleidoscope)语言的片段。官方教程当然写得已经足够好,但是还是稍嫌复杂了点,能生成一个可玩的编译器的速度还是有点慢。我打算把学习曲线再降低一下,通过不断地迭代,一点一点搭起可玩的编译器,然后慢慢扩充功能。

Hello,LLVM

LLVM的下载

先下载LLVMsvn co http://llvm.org/svn/llvm-project/llvm/trunk llvm

在LLVM的tools目录下,下载Clang(可选,但是建议):cd llvm/toolssvn co http://llvm.org/svn/llvm-project/cfe/trunk clang

在LLVM的projects目录下,可选下载compiler-rt,Libomp,libcxx,libcxxabi。反正我都下载了svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rtsvn co http://llvm.org/svn/llvm-project/openmp/trunk openmpsvn co http://llvm.org/svn/llvm-project/libcxx/trunk libcxxsvn co http://llvm.org/svn/llvm-project/libcxxabi/trunk libcxxabi

LLVM的编译

既然官方说大部分LLVM的开发者都使用Ninja,我们也就follow他们吧。

我在Mac下,所以使用Homebrew来安装CMake和Ninja。Linux与些类似,GCC版本太旧之类的请自助。Windows我还没试过,后面更新一下吧。

在LLVM目录下创建build目录

cd build

cmake -G Ninja

Ninja

写个LLVM上的Hello,World程序吧

从AST转IR开始,我们都要用到LLVM的工具啦。先写个小程序学习一下LLVM的程序是如何编译的吧:

#include "llvm/IR/Module.h"#include "llvm/IR/IRBuilder.h"#include "llvm/IR/LLVMContext.h"#include #include #include #include #include static llvm::LLVMContext TheContext;static llvm::IRBuilder Builder(TheContext);static std::unique_ptr TheModule;static std::map NameValues;int main(){ TheModule = llvm::make_unique("hello,llvm",TheContext); TheModule -> dump(); return 0;}

输出结果如下:

; ModuleID = 'hello,llvm'source_filename = "hello,llvm"如何链接LLVM的库

使用LLVM库的话,需要一大堆参数.

下面是在我的电脑上的参数:

-I/Users/ziyingliuziying/lusing/llvm/llvm/include -I/Users/ziyingliuziying/lusing/llvm/llvm/build/include -fPIC -fvisibility-inlines-hidden -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wcovered-switch-default -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Werror=date-time -std=c++11 -fcolor-diagnostics -fno-exceptions -fno-rtti -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS-L/Users/ziyingliuziying/lusing/llvm/llvm/build/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names-lLLVMCore -lLLVMSupport-lcurses -lz -lm

每次都这么写吓死人了啊。于是LLVM为我们提供了llvm-config工具。刚才我那一大串,是用下面的命令行生成的:

llvm-config --cxxflags --ldflags --system-libs --libs core

完整的编译命令可以这么写:

clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core` -o toy

推荐拓展阅读

著作权归作者所有

如果觉得我的文


相关内容

  • 信息安全专业课程书籍推荐
  • [转载]武大信息安全专业课程 简介(四) (2011-07-10 01:16:12) 转载原文标签: 转载原文地址:武大信息安全专业课程简介(四)作者:柏拉图的思念 1模式识别Pattern Recognition7. 课程简介 本课程是信息安全专业的专业选修课.模式识别是一门理论与应用并重的技术科 ...

  • ISPLEVER简明教程
  • ispLEVER2.0培训教程 2002年12月 目录 第一节 ispLEVER2.0简介 第二节 ispLEVER2.0安装 第三节 ispLEVER2.0的原理图输入 第四节 设计的编译与仿真 第五节 ABEL 语言和原理图混合输入 第六节 ispLEVER2.0中VHDL 和Verilog 语 ...

  • 数字闹钟的设计
  • 基于VHDL 的数字闹钟设计 基于VHDL 的数字闹钟设计 摘要 随着EDA 技术的发展和应用领域的扩大与深入,EDA 技术在电子信息.通信.自动控制及计算机应用领域的重要性日益突出.EDA 技术就是以计算机为工具,设计者在EDA 软件平台上,用硬件描述语言HDL 完成设计文件,然后由计算机自动地完 ...

  • 支撑材料目录
  • 支撑材料目录 附件一.编写的主要教材情况 1.<C 语言程序设计教程(第二版)>封面与扉页 2.<C 语言程序设计教程习题与上机指导(第二版)>封面与扉页 3.<计算机应用基础(第五版)>封面与扉页 4.<计算机应用基础习题与上机指导(第五版)>封面与 ...

  • 直流电路放大的反相电路的设计
  • 1. 概述 为了更好地实践已学习到的理论知识,在老师的指导和帮助下,我利用两个星期的时间,设计了一个反相输入放大电路的设计.这次设计我采用的Protel DXP是深受广大电路设计者喜爱并现今比较流行的设计工具.它们使绘制原理图.PCB板布局线等功能更加完备.在这个设计中,我用Protel DXP绘制 ...

  • 数字电压表设计
  • 东北石油大学 课 程 设 计 2 东北石油大学课程设计成绩评价表 东北石油大学课程设计任务书 课程 硬件课程设计 题目 数字电压表设计 专业 计 主要内容.基本要求等 一.主要内容: 利用EL 教学实验箱.微机和Quartus Ⅱ软件系统,使用VHDL 语言输入方法设计数字钟.可以利用层次设计方法和 ...

  • 进程调度模拟实验说明书
  • ******************* 实践教学 ******************* 兰州理工大学 计算机与通信学院 2012年秋季学期 题 目: 进程调度模拟实验 专业班级:计算机科学与技术(3)班 姓 名: 刘永勤 学 号: 指导教师: 成 绩: 目 录 摘 要 .............. ...

  • 汉明码论文
  • 基于C51的汉明码编码器和译码器的设计 摘要:数字信号在传输过程中,由于受到干扰的影响,码元波形将变坏.接收端收到后可能发生错误判决.由乘性干扰引起的码间串扰,可以采用均衡的办法纠正,而加性干扰的影响可以采用差错控制的措施了.汉明码就是一种能纠正一位错码且编码效率较高的线性分组码,它能纠正一个错码或 ...

  • SignalTapⅡ逻辑分析仪教程20150528
  • 逻辑分析仪图文教程 专业班级:电力姓 名:学 号:时 间: 13级2班 郑竣杰 3113001333 2015年5月27日 摘要 对于数电实验三(基于FPGA 设计的十进制加减计数器),我们同学有必要通过逻辑分析仪获取相应的输入输出信息,即在已经将十进制加减计数器设计完毕的基础上,为所设计的十进制加 ...