SFML编译和安装
首先需要下载SFML的源代码。从https://www.sfml-dev.org/files/SFML-2.5.1-sources.zip下载LLVM的源代码,并解压。或者直接从github上面克隆。
1git clone --config core.autocrlf=false https://github.com/SFML/SFML.git
选择合适的编译器,在windows系统下可以选择Visual C++,在linux上可以选择GNU编译器。这里以windows为例进行演示。为了使用Visual C++编译器,需要打开developer powershell for VS 2022作为终端,使用该终端的理由是该终端有相关的Visual C++环境变量。
进入源代码目录,进行cmake相关配置。
1cmake -GNinja -Bbuild
然后进入新建的build目录,进行构建。
1cmake --build .
构建成功之后,使用管理员方式重新打开developer powershell for VS 2022,进行安装。
1ninja install
默认会安装在C:\Pr ...
探索 Rust 中的动态调度
本文档翻译自博客Exploring Dynamic Dispatch in Rust
我是 rust 世界的新手(尽管到目前为止我很喜欢 rust ),所以如果我犯了技术错误,请告诉我,我会尽力纠正它们。 现在让我们开始吧。
我研究动态调度的真正动机可以在下面的代码片段中看到。 假设我想创建一个包含特征对象向量的结构 CloningLab(在本例中为 Mammal):
1234567891011121314151617181920212223struct CloningLab { subjects: Vec<Box<Mammal>>,}trait Mammal { fn walk(&self); fn run(&self);}#[derive(Clone)]struct Cat { meow_factor: u8, purr_factor: u8}impl Mammal for Cat { fn walk(&self) { ...
LLVM如何实现字符串类型
LLVM中有两种方式实现字符串:
直接使用LLVM IR实现
使用高级语言实现并生成LLVM IR
我个人更喜欢使用第二种方法,但为了示例,我将继续说明 LLVM IR 中一个简单但有用的字符串类型。 它采用 32 位架构,因此如果您的目标是 64 位架构,请将所有出现的 i32 替换为 i64。
我们将创建一个动态的、可变的字符串类型,它可以附加内容,也可以插入内容,转换大小写等等,这取决于定义了哪些支持函数来操作字符串类型。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103; The actual type definition for our 'String' type.%String = type ...
结语
请记住,通过使用 clang/clang++ 编译器的 -emit-llvm 选项可以学到很多东西。 这使您有机会看到实时生产编译器的运行情况以及它如何精确地执行操作。
如果您通过这种方式发现了一些新内容,或者本文档中有任何错误,或者您需要比此处提供的更多信息,请在 Github 上创建 issue。
延申阅读
本章列出了一些读者可能感兴趣的资源:
LLVM documentation http://llvm.org/docs/
Modern Compiler Implementation in Java, 2nd Edition.
Eli Bendersky’s collection of code examples for using LLVM/clang, https://github.com/eliben/llvm-clang-samples
“How Clang Compiles a Function” by John Regehr, June 2018, https://blog.regehr.org/archives/1605
贡献
如果您想为本文档做出贡献,您可以: ...
操作系统接口
POSIX操作系统接口
在 POSIX 上,C 运行时库的存在是不可避免的事实,因此直接调用此类 C 运行时函数非常有意义。
在 POSIX 上,创建 Hello world 程序真的很容易:
123456789declare i32 @puts(i8* nocapture) nounwind@.hello = private unnamed_addr constant [13 x i8] c"hello world\0A\00"define i32 @main(i32 %argc, i8** %argv) { %1 = getelementptr [13 x i8], [13 x i8]* @.hello, i32 0, i32 0 call i32 @puts(i8* %1) ret i32 0}
windows操作系统接口
在 Windows 上,C 运行时库主要被认为仅与 C 和 C++ 语言相关,因此您拥有任何客户端应用程序都可以使用的过多(数千个)标准系统接口。
Windows 上的 Hello world 远没有 ...
与运行时库交互
提供一组运行时支持函数是很常见的,这些函数是用 LLVM IR 之外的另一种语言编写的,并且与这样的运行时库进行接口非常容易。 本文档的示例中使用 malloc 和 free 作为外部定义的运行时函数的示例。
自定义的非 IR 运行时库函数的优点是它可以手动优化,以在特定标准下提供最佳性能。 此外,自定义的非 IR 运行时库函数可以显式使用 LLVM 基础架构之外的本机指令。
IR 运行时库函数的优点是它们可以通过优化器进行优化,因此也可以自动内联。
高级结构
在本章中,我们将研究各种非常有用且使用越来越广泛的非 OOP 结构。
λ函数
lambda 函数是一个匿名函数,它可以自由引用包含它的函数中的局部变量(包括参数变量)。 除了编译器负责为 lambda 函数生成内部名称以外,Lambda 的实现与 Pascal 的嵌套函数一样。 有几种不同的方法可以实现 lambda 函数(更多相关信息,请参阅 Wikipedia on Nested Functions)。
12345int foo(int a){ auto function = [a](int x) { return x + a; }; return function(10);}
这里的问题是 lambda 函数引用了调用者的一个局部变量 a。 这可以通过将局部变量作为隐式参数传递给 lambda 函数来轻松解决:
123456789define internal i32 @lambda(i32 %a, i32 %x) { %1 = add i32 %a, %x ret i32 %1}define i32 @foo(i3 ...
面向对象的结构
在本章中,我们将研究各种面向对象的结构,并了解它们如何映射到 LLVM IR。
类
一个类只不过是一个结构,它具有一组相关的函数,这些函数接受一个隐式的第一个参数,即指向该结构的指针。 因此,将一个类映射到 LLVM IR 非常简单:
1234567891011121314151617181920212223#include <stddef.h>class Foo{public: Foo() { _length = 0; } size_t GetLength() const { return _length; } void SetLength(size_t value) { _length = value; }private: size_t _length;};
我们首先将这段代码转换成两个独立的部分:
结构定义
一系列的方法定义,包括构造函数
12345678910111213141516 ...
控制流结构
与低级汇编语言类似,LLVM 的 IR 由按顺序执行的指令序列组成。 这些指令组合在一起形成基本块。 每个基本块都以改变程序控制流的指令结束。
if-else分支结构
首先让我们看一个非常简单的函数,它计算两个整数的最大值。 这是使用单个 if 控制语句实现的。
1234567int max(int a, int b) { if (a > b) { return a; } else { return b; }}
请记住,在 LLVM 中,IR 控制流是通过在基本块之间跳转来实现的,这些基本块包含不改变控制流的指令序列。 每个基本块都以改变控制流的指令结束。 最常见的分支指令是 br (参见LangRef: br)。 br 以一个布尔条件标志和两个基本块标签作为参数。
1br i1 %cond, label %iftrue, label %iffalse
br也可以用于无条件跳转。
1br label %dest
123456789101112131415161718define i32 @max(i32 ...