即时编译jit和xbyak的基本使用介绍

news2024/12/23 22:36:41

一般来说,解释型编程语言都是依靠自身运行的虚拟机,在解释程序。有时候语言为了提高运行速度,不会去直接解释程序文本,而是模拟cpu执行方式,将文本代码执行一次翻译,翻译为类似cpu执行的汇编语言去执行。有些语言会模拟出几个CPU的寄存器,有些则使用栈的方式模拟寄存器。无论采用什么方式,基本都是循环读取文件的内容,按照一定规则去解释。这也就导致了,运行速度会慢一点,因为cpu最终执行的是虚拟机,而不是编写的语言。

通常,一个解释型编程语言如果想要运行速度提升,除了对自身代码的优化和对虚拟机的优化外,就是要让代码本身可以让CPU直接能执行语言,也就是说,将编写的语言,经过几次翻译后,变为可以直接在cpu中执行的代码,这个就是jit

实现JIT最重要的一步就是动态执行被翻译成汇编的代码,下面是一个最简单的动态执行汇编的方式

1.动态执行

1.1在linux相关系统中

注:以下代码在cygwin gcc 11.3中调试通过
有如下代码

int func()
{
    return 19;
}
int main(int, char**) {
	printf("%d",func());
}

以上代码很简单,执行func函数返回19.然后打印出来

不过,如果func函数没有参与编译,是在程序运行时产生的,比如是从文件读取,或者人工输入,或者源文件在运行时,才被编译为字节码时,这就需要用下面的方式执行了

int main(int, char**) 
{
    unsigned char code[] = { 0xB8,0x13,0x00,0x00,0x00,0xC3 }; 
    void *mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC,MAP_ANON | MAP_PRIVATE, -1, 0);
    memcpy(mem, code, sizeof(code));
    int(*func)() = (int(*)())mem;
    printf("%d",func());
    munmap(mem,sizeof(code));
    return 0;
}

逐行解释一下

 { 0xB8,0x13,0x00,0x00,0x00,0xC3 }; 

就是return 19 翻译为汇编后的字节码,B8 是mov,C3是return

mmap是内存映射函数,它的作用就是给开拓一块大小为sizeof(code)的空间。但是这一步还不能用其他分配内存函数实现,比如,如果mmap改成malloc就不行,执行会报错,应为mmap分配的空间,是可以为这个空间设置属性的,比如可以写入PROT_WRITE |,可以执行PROT_EXEC,

int(*func)() = (int(*)())mem;

memcpy就是把要执行的汇编代码复制到这块内存里,这句是要把mmap分配的内存让他指向一个函数执行,然后通过函数执行的方式去调用内存中的代码

munmap就是吧mmap分配的内存释放了

这个就是最简单的一个动态运行方式。

1.2在windows中

如果要在windows下运行的话,mmap函数在windows里好像是没有,就需要换成VirtualAlloc,不过功能是一样的

unsigned char code[] = { 0xB8,0x13,0x00,0x00,0x00,0xC3 };
void* mem = VirtualAlloc(0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(mem, code, sizeof(code));
int(*func)() = (int(*)())mem;
int r =  func();
VirtualFree(code, sizeof(code), MEM_RELEASE);

2.JIT框架xbyak

虽然从汇编翻译字节码的过程也可以自己实现,但这个过程还是比较复杂,汇编指令很多,每个指令都有自己的字节码,要记录下就很困难,但是使用了框架就会省事很多,jit框架很多,dynasm、asmjit等

还有一个 xbyak

xbyak是一个非常简单的可以跨平台的jit框架,使用C++编写,一共四个文件,不到400k。

使用时,直接在项目中直接引入xbyak.h即可。

使用xbyak编写,实际上就是写汇编,比如如下汇编

mov eax,1
ret

改为xbyak,如下

mov(eax,1)
ret()

xbyak的函数和汇编的指令是对应的,大部分写的时候改成函数调用就可以

注:以下代码在win10 vs2015中调试通过,代码仅作测试使用

2.1基本使用

从最简单的开始,首先创建xbyak使用的类

class generatorcode : public Xbyak::CodeGenerator
{
public:
	generatorcode(void *userPtr = 0, size_t size = Xbyak::DEFAULT_MAX_CODE_SIZE) : Xbyak::CodeGenerator(size, userPtr)
	{
	}
}

然后用xbyak改写return 19那段代码

class generatorcode : public Xbyak::CodeGenerator
{
public:
	generatorcode(void *userPtr *= 0, size_t size = Xbyak::DEFAULT_MAX_CODE_SIZE) : Xbyak::CodeGenerator(size, userPtr)
	{
	}
	void func()
	{
		mov(eax,19);
		ret();
	}
}
int main(int, char**) {
	generatorcode g;
	g.func();
	int(*func)() = g.getCode<int(*)()>();
	printf("%d", func());
	return 0;
}

运行结果是一样的

虽然加了一个类,但相同功能整个实现过程方便了不少

下面介绍一下xbyak的基本数据操作

2.2常用数据类型

代码如下,分别把int,double ,char* 存到一个struct中

struct datatable {
	int i;
	double d;
	char* c;
};

class generatorcode : public Xbyak::CodeGenerator
{
private:
	datatable * dt;
public:
	generatorcode(datatable* _dt) : Xbyak::CodeGenerator()
	{
		dt = _dt;
	}
	void endcode()
	{
		ret();
	}
	void funcint(int a)
	{
		mov(eax, dword[&dt]);
		mov(dword[eax],a);
	}
	void funcstr(char* a)
	{
		mov(eax, dword[&dt]);
		lea(ecx, dword[a]);
		mov(dword[eax+16],ecx);
	}
	void funcdouble(double *a)
	{
		mov(eax, dword[&dt]);
		movsd(xm0, qword[a]);
		movsd(qword[eax+8], xm0);
	}
};
int main(int, char**) {
	datatable * dt = new datatable();
	generatorcode g(dt);
	int a = 8;
	double b = 3.14;
	char* str = "aaa";
	g.funcint(8);
	g.funcdouble(&b);
	g.funcstr(str);
	g.endcode();
	int(*func)() = g.getCode<int(*)()>();
	func();
	return 1;
}

结构dt运行结果如下:
在这里插入图片描述

常用的也就这几中数据类型的操作

xbyak的写法更接近NASM的写法,32,64位都支持,也支持MASM的一些语法,比如@@标号,同时也支持AVX,基本上用汇编写程序的指令它这都有。

qword, dword, word,byte 这几个是xbyak数据类型,如果使用的时候确认不了,就写PTR

2.3函数调用

再介绍几个常用的汇编指令,push,pop,call这几个会涉及到函数调用

class generatorcode : public Xbyak::CodeGenerator
{
public:
	generatorcode() : Xbyak::CodeGenerator()
	{
	}
	void endcode()
	{
		ret();
	}
	void abc(int a,char* b)
	{
		push(a);
		lea(ecx, dword[b]);
		push(ecx);
		call(printf);
		pop(ebx);
		pop(ebx);
	}
};
int main(int, char**) {
	generatorcode g();
	int a = 8;
	char* str = "%d";
	g.abc(a, str);
	g.endcode();
	int(*func)() = g.getCode<int(*)()>();
	func();
	return 1;
}

以上程序会调用printf打印出8

2.4综合使用

下面从一个语言的的实际出发,写一段代码

int stacklist[100] = {0};
int * intp = &stacklist[0];
void pushint()
{
	*intp++ = 1;
	*intp++ = 3;
}
void run()
{
	int a = *(intp-2);
	int b = *(intp-1);
	*(intp - 2) = a + b;
	intp--;
	if (*(intp - 1) > 10)
	{
		printf(">10");
	}
	else
	{
		printf("<10");
	}
}
int main(int, char**) {
	pushint();
	run();
	return 1;
}

这个是模拟的了一个栈,然后分别压入两个int数据1,3,然后通过模拟sp指针,将两个数去取出,加在一起,如果结果大于10显示>10 ,小于10就显示<10

首先用内嵌ASM写一次看看

int stacklist[100] = {0};
int * intp = &stacklist[0];
int main(int, char**) {
	char * a10 = "<10";
	char * b10 = ">10";
		_asm {	
		mov eax, intp
		mov[eax], 1
		add eax,4
		mov[eax],3
		add eax,4

		mov ecx,dword ptr [eax - 8]
		mov edi,dword ptr [eax - 4]
		add edi, ecx
		mov [eax-8],edi
		sub eax,4
		mov edi,dword ptr [eax-4]	
		cmp edi,10
		jge l10
		push a10
		call printf
		pop eax
		jmp lend
l10:
		push b10
		call printf

		pop eax
lend:
	}
}

然后在使用xbyak写一次

int stacklist[100] = { 0 };
int * intp = &stacklist[0];
class generatorcode : public Xbyak::CodeGenerator
{
private:
	char * a10 = "<10";
	char * b10 = ">10";
public:
	generatorcode() : Xbyak::CodeGenerator()
	{
	}
	void endcode()
	{
		ret();
	}
	void abc()
	{
			mov(eax, dword[&intp]);
			mov(dword[eax], 1);
			add(eax, 4);
			mov(dword[eax], 3);
			add(eax, 4);
			mov(ecx, dword[eax - 8]);
			mov(edi, dword[eax - 4]);
			add(edi, ecx);
			mov(dword[eax - 8], edi);
			sub(eax, 4);
			mov(edi, dword[eax - 4]);
			cmp(edi, 10);
			jge(".l10");
			push(dword[&a10]);
			call(printf);
			pop(eax);
			jmp(".lend");
L(".l10");
			push(dword[&b10]);
			call(printf);
			pop(eax);
L(".lend");
	}
};
int main(int, char**) {
	generatorcode g;
	g.abc();
	g.endcode();
	int(*func)() = g.getCode<int(*)()>();
	func();
	return 1;
}

以上汇编代码仅作演示使用,实际使用中应该尽可能减少使用寄存器,将局部使用的数据存入栈内

可以看出来,写xbyak实际和写汇编几乎一样,除了几条有稍微差别外,写法基本相同,如果是按nasm的写法的话,可能就更接近了

事实上一个编译型语言做成JIT能不能更快,或者快多少,还是要看翻译后的汇编代码量,越少的代码量、越简单的数据类型,运行就会越快

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1084607.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

树 | 选择题

1. 若X是二叉树中序线索树中一个有左孩子的结点&#xff0c;且X不为根&#xff0c;则X的前驱为 X的双亲 X的右子树中最左的结点 X的左子树中最右结点&#xff08;正确答案&#xff09; X的左子树中最右叶结点&#xff08;可能没有&#xff09; 这里不是前驱结点&#xff0…

IDEA的使用(四)创建不同类型的工程(IntelliJ IDEA 2022.1.3版本)

1. 创建Java工程 创建之后&#xff0c;src下是空的。可以在src下创建软件包Package&#xff0c;命名采用域名倒序。在软件包下再创建Java类。Java类运行后出现中文乱码&#xff0c;就到控制台和文件编码这两个地方设置编码。 2. 创建JavaWeb工程 2.1 在win11和IDEA中配置Tomca…

Linux高性能服务器编程 学习笔记 第十三章 多进程编程

我们将讨论Linux多进程编程的以下内容&#xff1a; 1.复制进程映像的fork系统调用和替换进程映像的exec系列系统调用。 2.僵尸进程以及如何避免僵尸进程。 3.进程间通信&#xff08;Inter Process Communication&#xff0c;IPC&#xff09;最简单的方式&#xff1a;管道。 …

使用EasyDarwin+ffmpeg+EasyPlayerPro完成rtsp的推流操作和拉流操作

本文分享在做视频类测试过程中所用到的工具EasyDarwinffmpegEasyPlayerPro 首先说一下EasyDarwin,简单来讲&#xff0c;它就是个推流和拉流及系统消耗的监测软件&#xff0c;具体使用方法我会写在下方。 EasyDarwin 1、解压下载好的EasyDarwin压缩包&#xff0c;并找到EasyD…

el-upload手动上传图片,上传后隐藏上传样式(el-upload上传单张图片,vue2)

简介&#xff1a;上期介绍了使用el-upload上传文件&#xff0c;这次来介绍一下如何使用el-upload上传图片&#xff0c;只能上传一次&#xff0c;上传图片后隐藏上传按钮部分。 实现效果图&#xff1a; 1、首先&#xff0c;想要在项目中使用el-upload组件&#xff0c;同样&#…

ios app开发环境搭建

Xcode是Apple iOS的应用市场app store移动应用的开发工具&#xff0c;支持不同设备、不同应用场景的开发&#xff0c;本文主要描述xcode开发工具开发环境的搭建。 如上所示&#xff0c;在macos中&#xff0c;使用app store安装xcode开发工具 如上所示&#xff0c;在macos中&…

陪诊系统|陪诊助浴系统|养老护理系统开发功能

助浴陪诊小程序是一款为老年人提供贴心服务的手机应用&#xff0c;旨在帮助老年人在家中就能享受到专业的助浴和陪诊服务。该程序通过简单易用的界面和人性化的设计&#xff0c;为老年人提供全面的护理服务。 1、用户注册登录 为了提供更好的服务&#xff0c;用户需要注册并登…

线性系统时域分析

1、稳定性分析 2、动态品质的求取 3、稳定误差计算 典型输入信号&#xff1a; 1、抛物线函数(等加速度阶跃函数) 2、单位阶跃函数 3.斜坡函数 A1,是单位阶跃函数1(t) 4.脉冲函数 A0 记为 动态过程和稳态过程 超调量&#xff1a;&#xff08;系统最大值-系统稳态值&…

基于Qt C++的工具箱项目源码,含命令行工具、桌面宠物、文献翻译、文件处理工具、医学图像浏览器、插件市场、设置扩展等工具

一、介绍 1. 基本信息 完整代码下载地址&#xff1a;基于Qt C的工具箱项目源码 TBox是一款基于Qt C的工具箱。用户可以自行选择安装所需的工具&#xff08;以插件的形式&#xff09;&#xff0c;将TBox打造成专属于自己的效率软件。TBox基本界面展示如下&#xff1a; 2. 使用…

视频转二维码简单技巧,适用多种视频格式

现在很多商品介绍多是以视频的方式来展现&#xff0c;那么为了方便用户能够同时快速获取视频内容&#xff0c;所以很多的商家现在会将视频生成二维码放到宣传单、展板、海报等宣传内容上&#xff0c;让他人通过扫码获取信息。那么视频二维码生成器的使用方法有哪几个步骤呢&…

【webUI】gradio基础使用2——Gallery组件显示多张图片

参考&#xff1a;https://www.gradio.app/docs/gallery | 参考代码&#xff08;老版本&#xff0c;有错误&#xff09; gradio基础使用1&#xff1a;https://blog.csdn.net/imwaters/article/details/131400571 说明 基于python的浏览器上多图片显示&#xff0c;是很多复杂程序…

袖口收缩包装机包装效果如何调整

袖口收缩包装机是一种使用非常广泛的包装设备&#xff0c;老百姓最常见的啤酒瓶和可乐瓶的包装就是袖口包装&#xff0c;我们看到的成品效果都是非常好的&#xff0c;那是因为厂商在出厂时已经对设备进行了非常好的调试&#xff0c;那么对于初次使用或者已经使用了&#xff0c;…

Pulsar 之架构,客户端以及多区域容灾

Pulsar 之架构&#xff0c;客户端以及多区域容灾 架构BrokersClusters元数据存储配置存储区持久存储Apache BookKeeperLedgersLedgers读一致性托管Ledgers 日志存储 Pulsar 代理服务发现 Pulsar client(客户端)客户端设置阶段Reader interface 多区域容灾备份(GEO-REPLICATION)…

《向量数据库指南》——宏观解读向量数据库Milvus Cloud

宏观解读向量数据库 如今,强大的机器学习模型配合 Milvus 等向量数据库的模式已经为电子商务、推荐系统、语义检索、计算机安全、制药等领域和应用场景带来变革。而对于用户而言,除了足够多的应用场景,向量数据库还需要具备更多重要的特性,包括: 可灵活扩展、支持调参:当…

恶意样本自动化配置提取初探

前言&#xff1a; 本篇参考 github 上 [CAPEv2](CAPEv2/Emotet.py at f2ab891a278b2875c79b4f2916d086f870b54ed5 kevoreilly/CAPEv2 (github.com)) 沙箱的提取代码&#xff0c;在前面奇安信攻防社区-APT 恶意 DLL 分析及 C2 配置提取&#xff08;子 DLL 篇&#xff09; 分析…

three.js入门 ---- 相机控件OrbitControls

前言&#xff1a; 自用&#xff01;&#xff01;&#xff01; 文档中描述&#xff1a;OrbitControls本质上就是改变相机的参数&#xff0c;比如相机的位置属性&#xff0c;改变相机位置可以改变相机拍照场景中模型的角度&#xff0c;实现模型的360度旋转预览效果&#xff0c;改…

彻底颠覆无线蓝牙,华为全新黑科技「星闪」有何魅力

内容开始前先发个灵魂拷问&#xff0c;还有多少人日常在用着有线耳机&#xff1f; 别怪咱们抛弃多年旧爱&#xff0c;实在是 TWS 无线耳机便捷真香&#xff0c;用了就回不去啊喂。 咳咳&#xff0c;开个玩笑&#xff0c;不是不愿意用有线耳机&#xff0c;而是这年头「极为先进…

多任务学习

前言 一般的机器学习模型都是针对单一的特定任务&#xff0c; 比如手写体数字识别 、 物体检测等&#xff0e; 不同任务的模型都是在各自的训练集上单独学习得到的 &#xff0e; 如果有两个任务比较相关&#xff0c; 它们之间会存在一定的共享知识 &#xff0c; 这些知识对两个…

命令执行绕过 [GXYCTF2019]Ping Ping Ping1

参考原文&#xff1a;CTFWeb-命令执行漏洞过滤的绕过姿势_绕过空格过滤_Tr0e的博客-CSDN博客文章目录前言CTF题目绕过姿势命令联合执行关键词的绕过内联执行绕过多种解法变量拼接内联执行Base64编码总结前言为了备战&#xff08;划水&#xff09;5 月份广东省的 “红帽杯” 网络…

用路由器远程维护三菱PLC操作指南

用路由器远程维护三菱PLC操作指南