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

相关文章

MySQL中慢SQL优化的不同方式介绍

《MySQL中慢SQL优化的不同方式介绍》慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化,下面小编就来给大家介绍一下有哪些方式可以优化慢SQL吧... 目录避免不必要的列分页优化索引优化JOIN 的优化排序优化UNION 优化慢 SQL 的优化,主要从两个方面考虑,SQL 语

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

Python实现html转png的完美方案介绍

《Python实现html转png的完美方案介绍》这篇文章主要为大家详细介绍了如何使用Python实现html转png功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 1.增强稳定性与错误处理建议使用三层异常捕获结构:try: with sync_playwright(

Java使用多线程处理未知任务数的方案介绍

《Java使用多线程处理未知任务数的方案介绍》这篇文章主要为大家详细介绍了Java如何使用多线程实现处理未知任务数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 知道任务个数,你可以定义好线程数规则,生成线程数去跑代码说明:1.虚拟线程池:使用 Executors.newVir

Spring 中使用反射创建 Bean 实例的几种方式

《Spring中使用反射创建Bean实例的几种方式》文章介绍了在Spring框架中如何使用反射来创建Bean实例,包括使用Class.newInstance()、Constructor.newI... 目录1. 使用 Class.newInstance() (仅限无参构造函数):2. 使用 Construc

MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析

《MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析》本文将详细讲解MyBatis-Plus中的lambdaUpdate用法,并提供丰富的案例来帮助读者更好地理解和应... 目录深入探索MyBATis-Plus中Service接口的lambdaUpdate用法及示例案例背景

MyBatis-Plus中静态工具Db的多种用法及实例分析

《MyBatis-Plus中静态工具Db的多种用法及实例分析》本文将详细讲解MyBatis-Plus中静态工具Db的各种用法,并结合具体案例进行演示和说明,具有很好的参考价值,希望对大家有所帮助,如有... 目录MyBATis-Plus中静态工具Db的多种用法及实例案例背景使用静态工具Db进行数据库操作插入

Python FastAPI入门安装使用

《PythonFastAPI入门安装使用》FastAPI是一个现代、快速的PythonWeb框架,用于构建API,它基于Python3.6+的类型提示特性,使得代码更加简洁且易于绶护,这篇文章主要介... 目录第一节:FastAPI入门一、FastAPI框架介绍什么是ASGI服务(WSGI)二、FastAP

JAVA SE包装类和泛型详细介绍及说明方法

《JAVASE包装类和泛型详细介绍及说明方法》:本文主要介绍JAVASE包装类和泛型的相关资料,包括基本数据类型与包装类的对应关系,以及装箱和拆箱的概念,并重点讲解了自动装箱和自动拆箱的机制,文... 目录1. 包装类1.1 基本数据类型和对应的包装类1.2 装箱和拆箱1.3 自动装箱和自动拆箱2. 泛型2

Spring中@Lazy注解的使用技巧与实例解析

《Spring中@Lazy注解的使用技巧与实例解析》@Lazy注解在Spring框架中用于延迟Bean的初始化,优化应用启动性能,它不仅适用于@Bean和@Component,还可以用于注入点,通过将... 目录一、@Lazy注解的作用(一)延迟Bean的初始化(二)与@Autowired结合使用二、实例解