Open3
llvm チュートリアル

llvm の使い方を学ぶ
チュートリアルは以下のサイトから
躓いたところや元のチュートリアルから変更した箇所をメモしていく.
最終的には rust を使って仮想言語のコンパイラを作りたい

cmake で llvm をリンクする
ビルド環境は M2 macbook
brew install llvm
でllvm をインストールして path を .zshrc に追加
path は以下のような感じ
export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"
export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
以下の様なcmakeファイルでビルドができた
cmake_minimum_required(VERSION 3.28)
project(Kaleidoscope)
set(CMAKE_CXX_STANDARD 23)
add_executable(Kaleidoscope main.cpp)
# llvm
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
# Set your project compile flags.
# E.g. if using the C++ header files
# you will need to enable C++11 support
# for your compiler.
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
# Find the libraries that correspond to the LLVM components
# that we wish to use
llvm_map_components_to_libnames(llvm_libs support core irreader)
# Link against LLVM libraries
target_link_libraries(Kaleidoscope ${llvm_libs})

rust のラッパーである inkwell を使って hello world を表示するコード
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::values::{FunctionValue, PointerValue};
use inkwell::OptimizationLevel;
use std::error::Error;
use std::path::Path;
struct CodeGen<'ctx> {
context: &'ctx Context,
module: Module<'ctx>,
builder: Builder<'ctx>,
}
impl<'ctx> CodeGen<'ctx> {
fn jit_compile_helloworld(&self) -> FunctionValue<'ctx> {
let void_type = self.context.void_type();
let i8_ptr_type = self.context.i8_type().ptr_type(inkwell::AddressSpace::from(0));
// printf を宣言
let printf_type = i8_ptr_type.fn_type(&[i8_ptr_type.into()], /* is_var_arg */ true);
let printf = self.module.add_function("printf", printf_type, None);
// hello_world 関数を定義
let function_type = void_type.fn_type(&[], false);
let function = self.module.add_function("hello_world", function_type, None);
let basic_block = self.context.append_basic_block(function, "entry");
self.builder.position_at_end(basic_block);
let hello_str = self.builder.build_global_string_ptr("hello, world\n", "hello_str");
// printf 呼び出し
self.builder.build_call(
printf,
&[hello_str.expect("REASON").as_pointer_value().into()],
"printf_call",
);
self.builder.build_return(None);
function
}
fn add_main_function(&self, hello_world_func: FunctionValue<'ctx>) {
let void_type = self.context.void_type();
// main 関数を定義
let function_type = void_type.fn_type(&[], false);
let function = self.module.add_function("main", function_type, None);
let basic_block = self.context.append_basic_block(function, "entry");
self.builder.position_at_end(basic_block);
// hello_world 関数を呼び出し
self.builder.build_call(hello_world_func, &[], "call_hello_world");
// main を終了
self.builder.build_return(None);
}
}
fn main() -> Result<(), Box<dyn Error>> {
let context = Context::create();
let module = context.create_module("hello_world");
let builder = context.create_builder();
let codegen = CodeGen {
context: &context,
module,
builder,
};
// hello_world 関数を生成
let hello_world_func = codegen.jit_compile_helloworld();
// main 関数を追加
codegen.add_main_function(hello_world_func);
// ビットコードをファイルに保存
let path = Path::new("output.bc");
codegen
.module
.write_bitcode_to_path(path);
println!("LLVM bitcode written to {:?}", path);
Ok(())
}
実行方法
clang output.bc -o output
./output