LLVM平臺,短短幾年間,改變了眾多編程語言的走向,也催生了一大批具有特色的編程語言的出現,不愧為編譯器架構的王者,也榮獲2012年ACM軟件系統獎 —— 題記
版權聲明:本文為 西風逍遙游 原創文章,轉載請注明出處 西風世界 [http://blog.csdn.net/xfxyy_sxfancy](http://blog.csdn.net/xfxyy_sxfancy)
### 開發LLVM項目
介紹了LLVM這么多,那么我們能用LLVM做一款自己的編程語言么?答案是,有點難度,但不是不可行。只要你熟悉C++編程,而且有足夠的熱情,那么就沒有什么能阻止你了。
下面我就來介紹一下,LLVM項目的基本方法。
需要的東西: LLVM平臺庫,文檔,CMAKE,C++編譯器
### 環境搭建
首先我的系統是Ubuntu14.04,我就介紹Ubuntu下的配置方法了,用Windows的朋友就不好意思了。
安裝llvm-3.6及其開發包:
~~~
sudo apt-get install llvm-3.6*
~~~
一般是推薦將文檔和示例都下載下來的,因為比較這些對應版本的參考很重要,很多網上的代碼,都是特定版本有效,后來就有API變更的情況。
所以大家一定注意版本問題,我開發的時候,源里面的版本最高就3.6,我也不追求什么最新版本,新特性什么的,所以聲明一下,本系列教程的LLVM版本均為3.6版,文檔參考也為3.6版。
~~~
sudo apt-get install clang cmake
~~~
clang編譯器,我個人感覺比gcc好用許多倍,而且這個編譯器就是用llvm作為后端,能夠幫助我們編譯一些C代碼到LLVM中間碼,方便我們有個正確的中間碼參考。
### CMAKE管理項目
CMake作為C++項目管理的利器,也是非常好用的一個工具,這樣我們就不用自己很煩的寫Makefile了,
下面是一個CMake示例,同時還帶有FLex和Bison的配置:
~~~
cmake_minimum_required(VERSION 2.8)
project(RedApple)
set(LLVM_TARGETS_TO_BUILD X86)
set(LLVM_BUILD_RUNTIME OFF)
set(LLVM_BUILD_TOOLS OFF)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
find_package(BISON)
find_package(FLEX)
SET (CMAKE_CXX_COMPILER_ENV_VAR "clang++")
SET (CMAKE_CXX_FLAGS "-std=c++11")
SET (CMAKE_CXX_FLAGS_DEBUG "-g")
SET (CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
FLEX_TARGET(MyScanner ${CMAKE_CURRENT_SOURCE_DIR}/src/redapple_lex.l
${CMAKE_CURRENT_BINARY_DIR}/redapple_lex.cpp COMPILE_FLAGS -w)
BISON_TARGET(MyParser ${CMAKE_CURRENT_SOURCE_DIR}/src/redapple_parser.y
${CMAKE_CURRENT_BINARY_DIR}/redapple_parser.cpp)
ADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser)
include_directories(Debug Release build include src src/Model src/Utils)
file(GLOB_RECURSE source_files ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Model/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Macro/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Utils/*.cpp)
add_executable(redapple ${source_files}
${BISON_MyParser_OUTPUTS}
${FLEX_MyScanner_OUTPUTS})
install(TARGETS redapple RUNTIME DESTINATION bin)
# Find the libraries that correspond to the LLVM components
# that we wish to use
llvm_map_components_to_libnames(llvm_libs
support core irreader executionengine interpreter
mc mcjit bitwriter x86codegen target)
# Link against LLVM libraries
target_link_libraries(redapple ${llvm_libs})
~~~
Ubuntu的默認安裝,有時LLVM會出bug,cmake找不到許多配置文件,我仔細查看了它的CMake配置,發現有一行腳本路徑寫錯了:
/usr/share/llvm-3.6/cmake/ 是llvm的cmake配置路徑
其中的LLVMConfig.cmake第48行,它原來的路徑是這樣的:
~~~
set(LLVM_CMAKE_DIR "/usr/share/llvm-3.6/share/llvm/cmake")
~~~
應該改成:
~~~
set(LLVM_CMAKE_DIR "/usr/share/llvm-3.6/cmake")
~~~
Ubuntu下的llvm文檔和示例都在如下目錄:
/usr/share/doc/llvm-3.6-doc
/usr/share/doc/llvm-3.6-examples
我們將example下的HowToUseJIT復制到工作目錄中,測試編譯一下,懶得找的可以粘我后面附錄給的內容。
然后再用簡單修改后的CMake測試編譯一下。
項目結構是這樣的:
~~~
HowToUseJIT -- src
+ --- HowToUseJIT.cpp
+ --- CMakeLists.txt
+ --- build
~~~
在項目根目錄執行如下指令:
~~~
cd build
cmake ..
make
~~~
如果編譯通過了,那么恭喜你,你已經會構建LLVM項目了
### 附: CMakeLists.txt 和 HowToUseJIT.cpp
CMakeLists.txt
~~~
cmake_minimum_required(VERSION 2.8)
project(llvm_test)
set(LLVM_TARGETS_TO_BUILD X86)
set(LLVM_BUILD_RUNTIME OFF)
set(LLVM_BUILD_TOOLS OFF)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
SET (CMAKE_CXX_COMPILER_ENV_VAR "clang++")
SET (CMAKE_CXX_FLAGS "-std=c++11")
SET (CMAKE_CXX_FLAGS_DEBUG "-g")
SET (CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
file(GLOB_RECURSE source_files "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_executable(llvm_test ${source_files})
install(TARGETS llvm_test RUNTIME DESTINATION bin)
# Find the libraries that correspond to the LLVM components
# that we wish to use
llvm_map_components_to_libnames(llvm_libs
Core
ExecutionEngine
Interpreter
MC
Support
nativecodegen)
# Link against LLVM libraries
target_link_libraries(llvm_test ${llvm_libs})
~~~
HowToUseJIT.cpp
~~~
//===-- examples/HowToUseJIT/HowToUseJIT.cpp - An example use of the JIT --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This small program provides an example of how to quickly build a small
// module with two functions and execute it with the JIT.
//
// Goal:
// The goal of this snippet is to create in the memory
// the LLVM module consisting of two functions as follow:
//
// int add1(int x) {
// return x+1;
// }
//
// int foo() {
// return add1(10);
// }
//
// then compile the module via JIT, then execute the `foo'
// function and return result to a driver, i.e. to a "host program".
//
// Some remarks and questions:
//
// - could we invoke some code using noname functions too?
// e.g. evaluate "foo()+foo()" without fears to introduce
// conflict of temporary function name with some real
// existing function name?
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/ExecutionEngine/Interpreter.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
int main() {
InitializeNativeTarget();
LLVMContext Context;
// Create some module to put our function into it.
std::unique_ptr<Module> Owner = make_unique<Module>("test", Context);
Module *M = Owner.get();
// Create the add1 function entry and insert this entry into module M. The
// function will have a return type of "int" and take an argument of "int".
// The '0' terminates the list of argument types.
Function *Add1F =
cast<Function>(M->getOrInsertFunction("add1", Type::getInt32Ty(Context),
Type::getInt32Ty(Context),
(Type *)0));
// Add a basic block to the function. As before, it automatically inserts
// because of the last argument.
BasicBlock *BB = BasicBlock::Create(Context, "EntryBlock", Add1F);
// Create a basic block builder with default parameters. The builder will
// automatically append instructions to the basic block `BB'.
IRBuilder<> builder(BB);
// Get pointers to the constant `1'.
Value *One = builder.getInt32(1);
// Get pointers to the integer argument of the add1 function...
assert(Add1F->arg_begin() != Add1F->arg_end()); // Make sure there's an arg
Argument *ArgX = Add1F->arg_begin(); // Get the arg
ArgX->setName("AnArg"); // Give it a nice symbolic name for fun.
// Create the add instruction, inserting it into the end of BB.
Value *Add = builder.CreateAdd(One, ArgX);
// Create the return instruction and add it to the basic block
builder.CreateRet(Add);
// Now, function add1 is ready.
// Now we're going to create function `foo', which returns an int and takes no
// arguments.
Function *FooF =
cast<Function>(M->getOrInsertFunction("foo", Type::getInt32Ty(Context),
(Type *)0));
// Add a basic block to the FooF function.
BB = BasicBlock::Create(Context, "EntryBlock", FooF);
// Tell the basic block builder to attach itself to the new basic block
builder.SetInsertPoint(BB);
// Get pointer to the constant `10'.
Value *Ten = builder.getInt32(10);
// Pass Ten to the call to Add1F
CallInst *Add1CallRes = builder.CreateCall(Add1F, Ten);
Add1CallRes->setTailCall(true);
// Create the return instruction and add it to the basic block.
builder.CreateRet(Add1CallRes);
// Now we create the JIT.
ExecutionEngine* EE = EngineBuilder(std::move(Owner)).create();
outs() << "We just constructed this LLVM module:\n\n" << *M;
outs() << "\n\nRunning foo: ";
outs().flush();
// Call the `foo' function with no arguments:
std::vector<GenericValue> noargs;
GenericValue gv = EE->runFunction(FooF, noargs);
// Import result of execution:
outs() << "Result: " << gv.IntVal << "\n";
delete EE;
llvm_shutdown();
return 0;
}
~~~