jpcsp源码解读12:本地码管理器与Compiler.xml
2012-03-29 16:13
316 查看
jpcsp这个模拟器的优化手段实在让人汗颜。
之前说过,他把系统调用功能全部用本地码实现了,也就是在软件需要的时候,调用java语言的实现,而不是跳转到内存中相应位置去解释执行,或者对系统调用代码做动态二进制翻译后去执行翻译结果。
NativeCodeManager,本地码管理器,这个更暴力,把psp上软件用到的库函数都给用本地码替换掉了。
这些需要被替换的代码保存在源码根目录下的Compiler.xml中,包括这段代码的名字(功能性描述),以及对应的,包含实现这个函数功能的类。
比如,贴一个例子:
-<NativeCodeSequence name="memcpy">
<Class>jpcsp.Allegrex.compiler.nativeCode.Memcpy</Class>
<IsReturning>true</IsReturning>
- <CodeInstructions>
-<![CDATA[
0887F4BC:[00A04025]: or $t0, $a1, $zr <=> move $t0, $a1
0887F4C0:[00C02825]: or $a1, $a2, $zr <=> move $a1, $a2
0887F4C4:[01003025]: or $a2, $t0, $zr <=> move $a2, $t0
0887F4C8:[2CA80010]: sltiu $t0, $a1, 16
0887F4CC:[15000022]: bne $t0, $zr, 0x0887F558
0887F4D0:[00803825]: or $a3, $a0, $zr <=> move $a3, $a0
0887F4D4:[00C74025]: or $t0, $a2, $a3
0887F4D8:[31080003]: andi $t0, $t0, 3
0887F4DC:[1500001F]: bne $t0, $zr, 0x0887F55C
0887F4E0:[00A04825]: or $t1, $a1, $zr <=> move $t1, $a1
0887F4E4:[8CC80000]: lw $t0, 0($a2)
0887F4E8:[24C60004]: addiu $a2, $a2, 4
0887F4EC:[ACE80000]: sw $t0, 0($a3)
0887F4F0:[8CC80000]: lw $t0, 0($a2)
0887F4F4:[24E70004]: addiu $a3, $a3, 4
0887F4F8:[24C60004]: addiu $a2, $a2, 4
0887F4FC:[ACE80000]: sw $t0, 0($a3)
0887F500:[8CC80000]: lw $t0, 0($a2)
0887F504:[24E70004]: addiu $a3, $a3, 4
0887F508:[24C60004]: addiu $a2, $a2, 4
0887F50C:[ACE80000]: sw $t0, 0($a3)
0887F510:[8CC80000]: lw $t0, 0($a2)
0887F514:[24E70004]: addiu $a3, $a3, 4
0887F518:[ACE80000]: sw $t0, 0($a3)
0887F51C:[24A5FFF0]: addiu $a1, $a1, -16
0887F520:[24C60004]: addiu $a2, $a2, 4
0887F524:[2CA80010]: sltiu $t0, $a1, 16
0887F528:[1100FFEE]: beq $t0, $zr, 0x0887F4E4
0887F52C:[24E70004]: addiu $a3, $a3, 4
0887F530:[2CA80004]: sltiu $t0, $a1, 4
0887F534:[15000008]: bne $t0, $zr, 0x0887F558
0887F538:[00000000]: nop
0887F53C:[8CC80000]: lw $t0, 0($a2)
0887F540:[24A5FFFC]: addiu $a1, $a1, -4
0887F544:[ACE80000]: sw $t0, 0($a3)
0887F548:[24C60004]: addiu $a2, $a2, 4
0887F54C:[2CA80004]: sltiu $t0, $a1, 4
0887F550:[1100FFFA]: beq $t0, $zr, 0x0887F53C
0887F554:[24E70004]: addiu $a3, $a3, 4
0887F558:[00A04825]: or $t1, $a1, $zr <=> move $t1, $a1
0887F55C:[24A8FFFF]: addiu $t0, $a1, -1
0887F560:[11200009]: beq $t1, $zr, 0x0887F588
0887F564:[01002825]: or $a1, $t0, $zr <=> move $a1, $t0
0887F568:[80C90000]: lb $t1, 0($a2)
0887F56C:[24C60001]: addiu $a2, $a2, 1
0887F570:[A0E90000]: sb $t1, 0($a3)
0887F574:[01004825]: or $t1, $t0, $zr <=> move $t1, $t0
0887F578:[24A8FFFF]: addiu $t0, $a1, -1
0887F57C:[24E70001]: addiu $a3, $a3, 1
0887F580:[1520FFF9]: bne $t1, $zr, 0x0887F568
0887F584:[01002825]: or $a1, $t0, $zr <=> move $a1, $t0
0887F588:[03E00008]: jr $ra
0887F58C:[00801025]: or $v0, $a0, $zr <=> move $v0, $a0
]]>
</CodeInstructions>
</NativeCodeSequence>
可以看到,这个函数的名字是mamcpy,内存拷贝。对应的本地代码在一个Memcpy类(jpcsp.Allegrex.compiler.nativeCode.Memcpy)中实现。这段代码是否返回。最后是这段代码的汇编码。
汇编码以表达形式存放,第二列是指令地址(labelGroup = 2),可以作该行的标号用,第三列是指令的十六进制编码(opcodeGroup = 3),第五列是该指令的掩码(opcodeMaskGroup = 5)。通常没有第5列。文件中也看不到第一列,原因暂时不清楚。
掩码表示比较指令是否一样时,可以忽略掩码中为0的部分。猜想潜在的用途是,只关心指令的类型,指令中的某个域无关紧要,可以用掩码清除掉之后再比较。
初始化时,会把这个文件(Compiler.xml)加载进来,由NativeCodeManager管理。在动态二进制翻译的生成代码开始之前,先调用NativeCodeManager,搜索将要翻译的代码块是否已经记录在案,有记录的话就用本地码,没有才去真正的生成可执行代码。
这些函数的本地码,在模拟器源码中的位置是:jpcsp.Allegrex.compiler.nativeCode。目前这里有三十多个类。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
具体来说,当需要编译器的实例,调用
Compiler.getInstance()
该方法判定编译器实例是否为空,非空则返回已有的实例,空则调用构建函数
Compiler()
构建函数中,只有一个语句,调用初始化函数
Initialise();
初始化函数中,打开了文件Compiler.xml
configuration = documentBuilder.parse(new File("Compiler.xml"));
从这个文件构建了 本地码管理器:
nativeCodeManager = new NativeCodeManager(configuration.getDocumentElement());
代码管理器的构建函数中,调用加载方法:
load(configuration);
加载函数中,读取所有本地码
NodeList nativeCodeBlocks =
configuration.getElementsByTagName("NativeCodeSequence");
然后提取并载入单个代码块
Element nativeCodeSequence = (Element)
nativeCodeBlocks.item(i);//取得单个代码块
loadNativeCodeSequence(nativeCodeSequence);//载入单个代码块
载入单个代码块的实现:
取得名字(name)和对应本地类(class),用这两个信息构建一个本地码序列nativeCodeSequence
取得nativeCodeSequence的各项属性,并注册进nativeCodeSequence
这些属性包括:是否返回,是否对应一个完整的代码块,函数名(就是对应类中的可执行方法,默认是call,也可以指定其他函数),mips指令序列(关于指令描述的序列,包括掩码,指令十六进制表示),函数需要的参数描述,第几条指令是分支指令,这些代码执行前要做的动作
其中红色字体是数据流,也就是Compiler.xml这个文件打开后,其中数据怎样一步步被提取并解析。红色变量的值,源头上都来自Compiler.xml。
将构建起来的这个nativeCodeSequence,加入到本地码管理器中:
addNativeCodeSequence(nativeCodeSequence);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
本地码管理器中,维护了两个哈希表,还有一个链表。
其中一个哈希表是前述的从Compiler.xml载入的本地码序列,用第一条指令作哈希索引,索引到的元素是一个指令序列的列表,再对这个列表中逐个匹配。
链表中存放的,是那些载入的代码序列中,首条指令带掩码的。因为带掩码,就不能拿首条指令简单匹配,只能放在这个链表中,逐个匹配。(这是因为,没有掩码就是整个指令32位匹配,这种匹配发生的概率很小,故如果真的配成功,这种成功,与 整体匹配 的相关性较大,也就是此时整体匹配的概率较大。)
这也就是根据指令和代码块来查找本地码的方法的实现:
NativeCodeManager.java
public NativeCodeSequence getNativeCodeSequence(CodeInstruction codeInstruction, CodeBlock codeBlock)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
总结起来,本文主要涉及两个类,本地码序列NativeCodeSequence,和本地码序列管理器NativeCodeManager。
从Compiler.xml提取到所有的本地码序列,注册进本地码序列管理器维护的列表中。管理器对于本地码序列提供 加载,查找 等方法。
之前说过,他把系统调用功能全部用本地码实现了,也就是在软件需要的时候,调用java语言的实现,而不是跳转到内存中相应位置去解释执行,或者对系统调用代码做动态二进制翻译后去执行翻译结果。
NativeCodeManager,本地码管理器,这个更暴力,把psp上软件用到的库函数都给用本地码替换掉了。
这些需要被替换的代码保存在源码根目录下的Compiler.xml中,包括这段代码的名字(功能性描述),以及对应的,包含实现这个函数功能的类。
比如,贴一个例子:
-<NativeCodeSequence name="memcpy">
<Class>jpcsp.Allegrex.compiler.nativeCode.Memcpy</Class>
<IsReturning>true</IsReturning>
- <CodeInstructions>
-<![CDATA[
0887F4BC:[00A04025]: or $t0, $a1, $zr <=> move $t0, $a1
0887F4C0:[00C02825]: or $a1, $a2, $zr <=> move $a1, $a2
0887F4C4:[01003025]: or $a2, $t0, $zr <=> move $a2, $t0
0887F4C8:[2CA80010]: sltiu $t0, $a1, 16
0887F4CC:[15000022]: bne $t0, $zr, 0x0887F558
0887F4D0:[00803825]: or $a3, $a0, $zr <=> move $a3, $a0
0887F4D4:[00C74025]: or $t0, $a2, $a3
0887F4D8:[31080003]: andi $t0, $t0, 3
0887F4DC:[1500001F]: bne $t0, $zr, 0x0887F55C
0887F4E0:[00A04825]: or $t1, $a1, $zr <=> move $t1, $a1
0887F4E4:[8CC80000]: lw $t0, 0($a2)
0887F4E8:[24C60004]: addiu $a2, $a2, 4
0887F4EC:[ACE80000]: sw $t0, 0($a3)
0887F4F0:[8CC80000]: lw $t0, 0($a2)
0887F4F4:[24E70004]: addiu $a3, $a3, 4
0887F4F8:[24C60004]: addiu $a2, $a2, 4
0887F4FC:[ACE80000]: sw $t0, 0($a3)
0887F500:[8CC80000]: lw $t0, 0($a2)
0887F504:[24E70004]: addiu $a3, $a3, 4
0887F508:[24C60004]: addiu $a2, $a2, 4
0887F50C:[ACE80000]: sw $t0, 0($a3)
0887F510:[8CC80000]: lw $t0, 0($a2)
0887F514:[24E70004]: addiu $a3, $a3, 4
0887F518:[ACE80000]: sw $t0, 0($a3)
0887F51C:[24A5FFF0]: addiu $a1, $a1, -16
0887F520:[24C60004]: addiu $a2, $a2, 4
0887F524:[2CA80010]: sltiu $t0, $a1, 16
0887F528:[1100FFEE]: beq $t0, $zr, 0x0887F4E4
0887F52C:[24E70004]: addiu $a3, $a3, 4
0887F530:[2CA80004]: sltiu $t0, $a1, 4
0887F534:[15000008]: bne $t0, $zr, 0x0887F558
0887F538:[00000000]: nop
0887F53C:[8CC80000]: lw $t0, 0($a2)
0887F540:[24A5FFFC]: addiu $a1, $a1, -4
0887F544:[ACE80000]: sw $t0, 0($a3)
0887F548:[24C60004]: addiu $a2, $a2, 4
0887F54C:[2CA80004]: sltiu $t0, $a1, 4
0887F550:[1100FFFA]: beq $t0, $zr, 0x0887F53C
0887F554:[24E70004]: addiu $a3, $a3, 4
0887F558:[00A04825]: or $t1, $a1, $zr <=> move $t1, $a1
0887F55C:[24A8FFFF]: addiu $t0, $a1, -1
0887F560:[11200009]: beq $t1, $zr, 0x0887F588
0887F564:[01002825]: or $a1, $t0, $zr <=> move $a1, $t0
0887F568:[80C90000]: lb $t1, 0($a2)
0887F56C:[24C60001]: addiu $a2, $a2, 1
0887F570:[A0E90000]: sb $t1, 0($a3)
0887F574:[01004825]: or $t1, $t0, $zr <=> move $t1, $t0
0887F578:[24A8FFFF]: addiu $t0, $a1, -1
0887F57C:[24E70001]: addiu $a3, $a3, 1
0887F580:[1520FFF9]: bne $t1, $zr, 0x0887F568
0887F584:[01002825]: or $a1, $t0, $zr <=> move $a1, $t0
0887F588:[03E00008]: jr $ra
0887F58C:[00801025]: or $v0, $a0, $zr <=> move $v0, $a0
]]>
</CodeInstructions>
</NativeCodeSequence>
可以看到,这个函数的名字是mamcpy,内存拷贝。对应的本地代码在一个Memcpy类(jpcsp.Allegrex.compiler.nativeCode.Memcpy)中实现。这段代码是否返回。最后是这段代码的汇编码。
汇编码以表达形式存放,第二列是指令地址(labelGroup = 2),可以作该行的标号用,第三列是指令的十六进制编码(opcodeGroup = 3),第五列是该指令的掩码(opcodeMaskGroup = 5)。通常没有第5列。文件中也看不到第一列,原因暂时不清楚。
掩码表示比较指令是否一样时,可以忽略掩码中为0的部分。猜想潜在的用途是,只关心指令的类型,指令中的某个域无关紧要,可以用掩码清除掉之后再比较。
初始化时,会把这个文件(Compiler.xml)加载进来,由NativeCodeManager管理。在动态二进制翻译的生成代码开始之前,先调用NativeCodeManager,搜索将要翻译的代码块是否已经记录在案,有记录的话就用本地码,没有才去真正的生成可执行代码。
这些函数的本地码,在模拟器源码中的位置是:jpcsp.Allegrex.compiler.nativeCode。目前这里有三十多个类。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
具体来说,当需要编译器的实例,调用
Compiler.getInstance()
该方法判定编译器实例是否为空,非空则返回已有的实例,空则调用构建函数
Compiler()
构建函数中,只有一个语句,调用初始化函数
Initialise();
初始化函数中,打开了文件Compiler.xml
configuration = documentBuilder.parse(new File("Compiler.xml"));
从这个文件构建了 本地码管理器:
nativeCodeManager = new NativeCodeManager(configuration.getDocumentElement());
代码管理器的构建函数中,调用加载方法:
load(configuration);
加载函数中,读取所有本地码
NodeList nativeCodeBlocks =
configuration.getElementsByTagName("NativeCodeSequence");
然后提取并载入单个代码块
Element nativeCodeSequence = (Element)
nativeCodeBlocks.item(i);//取得单个代码块
loadNativeCodeSequence(nativeCodeSequence);//载入单个代码块
载入单个代码块的实现:
取得名字(name)和对应本地类(class),用这两个信息构建一个本地码序列nativeCodeSequence
取得nativeCodeSequence的各项属性,并注册进nativeCodeSequence
这些属性包括:是否返回,是否对应一个完整的代码块,函数名(就是对应类中的可执行方法,默认是call,也可以指定其他函数),mips指令序列(关于指令描述的序列,包括掩码,指令十六进制表示),函数需要的参数描述,第几条指令是分支指令,这些代码执行前要做的动作
其中红色字体是数据流,也就是Compiler.xml这个文件打开后,其中数据怎样一步步被提取并解析。红色变量的值,源头上都来自Compiler.xml。
将构建起来的这个nativeCodeSequence,加入到本地码管理器中:
addNativeCodeSequence(nativeCodeSequence);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
本地码管理器中,维护了两个哈希表,还有一个链表。
其中一个哈希表是前述的从Compiler.xml载入的本地码序列,用第一条指令作哈希索引,索引到的元素是一个指令序列的列表,再对这个列表中逐个匹配。
链表中存放的,是那些载入的代码序列中,首条指令带掩码的。因为带掩码,就不能拿首条指令简单匹配,只能放在这个链表中,逐个匹配。(这是因为,没有掩码就是整个指令32位匹配,这种匹配发生的概率很小,故如果真的配成功,这种成功,与 整体匹配 的相关性较大,也就是此时整体匹配的概率较大。)
这也就是根据指令和代码块来查找本地码的方法的实现:
NativeCodeManager.java
public NativeCodeSequence getNativeCodeSequence(CodeInstruction codeInstruction, CodeBlock codeBlock)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
总结起来,本文主要涉及两个类,本地码序列NativeCodeSequence,和本地码序列管理器NativeCodeManager。
从Compiler.xml提取到所有的本地码序列,注册进本地码序列管理器维护的列表中。管理器对于本地码序列提供 加载,查找 等方法。
相关文章推荐
- jpcsp源码解读13:动态二进制翻译1
- JPCSP源码解读14:动态二进制翻译2
- jpcsp源码解读之一:源码的获取与编译,以及psp详尽硬件信息文档
- spring Ioc源码解读-xml资源加载与解析
- Spark学习笔记(12)源码解读之Executor容错安全性
- JPCSP源码解读16:HLE与模块装载过程
- spring beans源码解读之--XmlBeanFactory
- jpcsp源码解读之二:main函数与jpcsp的初始化流程
- 线程本地变量ThreadLocal源码解读
- jpcsp源码解读之三:Screen类
- MyBatis-3.4.2-源码分析12:XML解析之mapperElement(root.evalNode("mappers"))
- mvc源码解读(12)-mvc四大过滤器之ActionFilter
- struct2源码解读(12)之拦截器
- jpcsp源码解读之四:Clock类
- 12、Spark Streaming源码解读之Executor容错安全性
- 解读郭神LitePal源码-litepal.xml的解析
- mybatis--源码解读---XML的解析
- caffe源码解读(12)-convert_imageset.cpp
- jpcsp源码解读5:umd光盘镜像(.iso)
- spring beans源码解读之--XmlBeanFactory