Open3

llvm チュートリアル

kaitokaito

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})
kaitokaito

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