LLVM入门2:如何基于自己的代码生成IR-LLVM IR code generation实例介绍

2024-09-08 10:52

本文主要是介绍LLVM入门2:如何基于自己的代码生成IR-LLVM IR code generation实例介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

本节将通过一个简单的例子来介绍如何生成llvm IR,以Kaleidoscope IR中的例子为例,我们基于LLVM接口构建一个简单的编译器,实现简单的语句解析并转化为LLVM IR,生成对应的LLVM IR部分,代码如下,文件名为toy.cpp,先给出代码,后面会详细介绍每一步分代码:

#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <memory>
#include <string>
#include <vector>using namespace llvm;//===----------------------------------------------------------------------===//
// Lexer
//===----------------------------------------------------------------------===//// The lexer returns tokens [0-255] if it is an unknown character, otherwise one
// of these for known things.
enum Token {tok_eof = -1,// commandstok_def = -2,tok_extern = -3,// primarytok_identifier = -4,tok_number = -5
};static std::string IdentifierStr; // Filled in if tok_identifier
static 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;if (IdentifierStr == "def")return tok_def;if (IdentifierStr == "extern")return tok_extern;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;}if (LastChar == '#') {// Comment until end of line.doLastChar = getchar();while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');if (LastChar != EOF)return gettok();}// Check for end of file.  Don't eat the EOF.if (LastChar == EOF)return tok_eof;// Otherwise, just return the character as its ascii value.int ThisChar = LastChar;LastChar = getchar();return ThisChar;
}//===----------------------------------------------------------------------===//
// Abstract Syntax Tree (aka Parse Tree)
//===----------------------------------------------------------------------===//namespace {/// ExprAST - Base class for all expression nodes.
class ExprAST {
public:virtual ~ExprAST() = default;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;
};/// BinaryExprAST - Expression class for a binary operator.
class BinaryExprAST : public ExprAST {char Op;std::unique_ptr<ExprAST> LHS, RHS;public:BinaryExprAST(char Op, std::unique_ptr<ExprAST> LHS,std::unique_ptr<ExprAST> RHS): Op(Op), LHS(std::move(LHS)), RHS(std::move(RHS)) {}Value *codegen() override;
};/// CallExprAST - Expression class for function calls.
class CallExprAST : public ExprAST {std::string Callee;std::vector<std::unique_ptr<ExprAST>> Args;public:CallExprAST(const std::string &Callee,std::vector<std::unique_ptr<ExprAST>> Args): Callee(Callee), Args(std::move(Args)) {}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<std::string> Args;public:PrototypeAST(const std::string &Name, std::vector<std::string> Args): Name(Name), Args(std::move(Args)) {}Function *codegen();const std::string &getName() const { return Name; }
};/// FunctionAST - This class represents a function definition itself.
class FunctionAST {std::unique_ptr<PrototypeAST> Proto;std::unique_ptr<ExprAST> Body;public:FunctionAST(std::unique_ptr<PrototypeAST> Proto,std::unique_ptr<ExprAST> Body): Proto(std::move(Proto)), Body(std::move(Body)) {}Function *codegen();
};} // end anonymous namespace//===----------------------------------------------------------------------===//
// Parser
//===----------------------------------------------------------------------===///// CurTok/getNextToken - Provide a simple token buffer.  CurTok is the current
/// token the parser is looking at.  getNextToken reads another token from the
/// lexer and updates CurTok with its results.
static int CurTok;
static int getNextToken() { return CurTok = gettok(); }/// BinopPrecedence - This holds the precedence for each binary operator that is
/// defined.
static std::map<char, int> BinopPrecedence;/// GetTokPrecedence - Get the precedence of the pending binary operator token.
static int GetTokPrecedence() {if (!isascii(CurTok))return -1;// Make sure it's a declared binop.int TokPrec = BinopPrecedence[CurTok];if (TokPrec <= 0)return -1;return TokPrec;
}/// LogError* - These are little helper functions for error handling.
std::unique_ptr<ExprAST> LogError(const char *Str) {fprintf(stderr, "Error: %s\n", Str);return nullptr;
}std::unique_ptr<PrototypeAST> LogErrorP(const char *Str) {LogError(Str);return nullptr;
}static std::unique_ptr<ExprAST> ParseExpression();/// numberexpr ::= number
static std::unique_ptr<ExprAST> ParseNumberExpr() {auto Result = std::make_unique<NumberExprAST>(NumVal);getNextToken(); // consume the numberreturn std::move(Result);
}/// parenexpr ::= '(' expression ')'
static std::unique_ptr<ExprAST> ParseParenExpr() {getNextToken(); // eat (.auto V = ParseExpression();if (!V)return nullptr;if (CurTok != ')')return LogError("expected ')'");getNextToken(); // eat ).return V;
}/// identifierexpr
///   ::= identifier
///   ::= identifier '(' expression* ')'
static std::unique_ptr<ExprAST> ParseIdentifierExpr() {std::string IdName = IdentifierStr;getNextToken(); // eat identifier.if (CurTok != '(') // Simple variable ref.return std::make_unique<VariableExprAST>(IdName);// Call.getNextToken(); // eat (std::vector<std::unique_ptr<ExprAST>> Args;if (CurTok != ')') {while (true) {if (auto Arg = ParseExpression())Args.push_back(std::move(Arg));elsereturn nullptr;if (CurTok == ')')break;if (CurTok != ',')return LogError("Expected ')' or ',' in argument list");getNextToken();}}// Eat the ')'.getNextToken();return std::make_unique<CallExprAST>(IdName, std::move(Args));
}/// primary
///   ::= identifierexpr
///   ::= numberexpr
///   ::= parenexpr
static std::unique_ptr<ExprAST> ParsePrimary() {switch (CurTok) {default:return LogError("unknown token when expecting an expression");case tok_identifier:return ParseIdentifierExpr();case tok_number:return ParseNumberExpr();case '(':return ParseParenExpr();}
}/// binoprhs
///   ::= ('+' primary)*
static std::unique_ptr<ExprAST> ParseBinOpRHS(int ExprPrec,std::unique_ptr<ExprAST> L

这篇关于LLVM入门2:如何基于自己的代码生成IR-LLVM IR code generation实例介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1147905

相关文章

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Java中基于注解的代码生成工具MapStruct映射使用详解

《Java中基于注解的代码生成工具MapStruct映射使用详解》MapStruct作为一个基于注解的代码生成工具,为我们提供了一种更加优雅、高效的解决方案,本文主要为大家介绍了它的具体使用,感兴趣... 目录介绍优缺点优点缺点核心注解及详细使用语法说明@Mapper@Mapping@Mappings@Co

mysqld_multi在Linux服务器上运行多个MySQL实例

《mysqld_multi在Linux服务器上运行多个MySQL实例》在Linux系统上使用mysqld_multi来启动和管理多个MySQL实例是一种常见的做法,这种方式允许你在同一台机器上运行多个... 目录1. 安装mysql2. 配置文件示例配置文件3. 创建数据目录4. 启动和管理实例启动所有实例

Java function函数式接口的使用方法与实例

《Javafunction函数式接口的使用方法与实例》:本文主要介绍Javafunction函数式接口的使用方法与实例,函数式接口如一支未完成的诗篇,用Lambda表达式作韵脚,将代码的机械美感... 目录引言-当代码遇见诗性一、函数式接口的生物学解构1.1 函数式接口的基因密码1.2 六大核心接口的形态学

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如