阿萍的博客 - LLVM
type: channel
link: https://aping-dev.com/index.php/tag/LLVM/
items: 使用 Inkwell 生成 LLVM IR
items: 使用 LLVM 实现一门简单的语言

使用 Inkwell 生成 LLVM IR
type: item
date: 2023-06-14T06:31:00+00:00
description: 源码：https://github.com/felicityin/sysy-rs本文中的例子拷贝自：https://pku-minic.github.io/online-doc实现语言的过程大概为：词法分析、语法分析、语义分析、优化、代码生成。可以使用 larlpop 进行词法、语法解析，这部分的文档较为充足，既可以阅读官方的教程，也可以阅读该教程：https://michael-f-bryan.github.io/calc/book/html/intro.html。本文主要讲述在用 larlpop 生成 AST (Abstract Syntax Tree) 后，如何使用 inkwell 将其转为 LLVM IR，该过程会进行一些语义分析和优化。最后将 LLVM IR 交给 LLVM，LLVM 将其生成指定平台的目标代码。IR 指中间表达方式，介于高级语言和汇编语言之间。与高级语言相比，丢弃了语法和语义特征，比如作用域、面向对象等；与汇编语言相比，不会有硬件相关的细节，比如目标机器架构、操作系统等。本文不考虑浮点型，整型只考虑 i32。1 LLVM 数据结构Context: 拥有和管理 LLVM 核心基础设施的核心“全局”数据，包括类型和常量统一表。let context = Context::create(); let builder = context.create_module("module"); let module = context.create_builder(); context.f64_type().const_float(0.0) context.append_basic_block(function, "then");Module: 是所有其它 LLVM IR 对象的顶级容器。每个模块直接包含全局变量列表、函数列表、该模块所依赖的库（或其它模块）列表、符号表以及有关目标特性的各种数据。module.add_function(proto.name.as_str(), fn_type, None); module.add_global(llvm_type, None, var_name); module.get_first_global().unwrap() module.get_last_global().unwrap() let global_value = self.module.add_global(llvm_type, None, var_name); global_value.set_linkage(Linkage::Common); global_value.set_constant(true); global_value.set_initializer(&value_after_cast); global_value.set_initializer(&llvm_type.const_zero()); global_value.as_pointer_value()Builder: 生成 LLVM 指令。追踪当前指令要插入的位置，创建新指令。一个 basic block 中可包含多条指令，一个函数中可包含多个 basic block。builder.build_conditional_branch(cond, then_bb, else_bb); builder.position_at_end(then_bb); builder.build_phi(self.context.f64_type(), "iftmp"); builder.build_store(start_alloca, start); builder.build_load(start_alloca, var_name); builder.build_float_add(lhs, rhs, "tmpadd")), builder.build_call(fun, &[lhs.into(), rhs.into()], "tmpbin")2 表达式2.1 一元表达式变补 (取负数): 0 减去操作数.builder.build_int_neg(exp, "int_neg")逻辑取反: 操作数和 0 比较相等builder.build_int_compare( IntPredicate::EQ, compiler.int_type.const_int(0_u64, true), exp, "logical_not_result_int", )2.2 算术表达式builder.build_int_add(lhs, rhs, "int_add") builder.build_int_sub(lhs, rhs, "int_sub") builde…

使用 LLVM 实现一门简单的语言
type: item
date: 2022-03-01T11:09:00+00:00
description: 本文跟着 LLVM Tutorial 教程完成，加上了一些注释。本文中的代码并非工程最佳实践。1 目标用 LLVM 实现一门简单的语言 Kaleidoscope，实现对如下代码的编译运行：# 斐波那契数列函数定义 def fib(x) if x < 3 then 1 else fib(x - 1) + fib(x - 2) fib(40) # 函数声明 extern sin(arg) extern cos(arg) extern atan2(arg1 arg2) # 声明后的函数可调用 atan2(sin(.4), cos(42))为方便，Kaleidoscope 只支持 float64 数据类型。2 词法分析 Lexer词法分析是把程序分割成一个个 Token 的过程。Token 包括 if、else、int 等关键字，age 等标识符，+、-、= 等操作符，以及数字、字符串变量等。2.1 Token 种类// The lexer returns tokens [0-255] if it is an unknown character, otherwise one // of these for known things. enum Token { TOKEN_EOF = -1, // commands TOKEN_DEF = -2, TOKEN_EXTERN = -3, // primary TOKEN_IDENTIFIER = -4, TOKEN_NUMBER = -5, }; // 如果当前 Token 是 TOKEN_IDENTIFIER，记录变量 static std::string g_identifier_str; // 如果当前 Token 是 TOKEN_NUMBER，记录数值 static double g_num_val; 2.2 识别 Token/// 返回从标准输入中解析出的 token // TOKEN_EOF: 'def' // TOKEN_EXTERN: 'extern' // TOKEN_IDENTIFIER: [a-zA-Z][a-zA-Z0-9]* // TOKEN_NUMBER: [0-9.]+ static int GetToken() { static int last_char = ' '; // 跳过空格 while (isspace(last_char)) last_char = getchar(); // 识别关键字或变量 [a-zA-Z][a-zA-Z0-9]* if (isalpha(last_char)) { g_identifier_str = last_char; while (isalnum((last_char = getchar()))) g_identifier_str += last_char; if (g_identifier_str == "def") return TOEKN_DEF; if (g_identifier_str == "extern") return TOKEN_EXTERN; return TOKEN_IDENTIFIER; } // 识别数字: [0-9.]+ if (isdigit(last_char) || last_char == '.') { std::string num_str; do { num_str += last_char; last_char = getchar(); } while (isdigit(last_char) || last_char == '.'); g_num_val = strtod(num_str.c_str(), nullptr); return TOKEN_NUMBER; } // 跳过注释 if (last_char == '#') { // Comment until end of line. do last_char = getchar(); while (last_char != EOF && last_char != '\n' && last_char != '\r'); if (last_char != EOF) return GetToken(); } // 识别文件结束 if (last_char == EOF) return TOEKN_EOF; // 未知，直接返回 ascii 码 int this_char = last_char; last_char = getchar(); return this_char; }3 语法分析 Parser语法分析是在词法分析的基础上识别出程序的语法结构，这个结构是树状的，称为抽象语法树（Abstract Syntax Tr…