注意:这个问题在某种程度上与字节码是如何“解析”的有关,但它不是重复的。在这个问题中,我问的是字节码是如何生成的,而不是字节码是如何被“解析”的。
正如标题中所述,文字(例如字符串、整数等)如何编码成字节码文件?我的困惑来自于这样一个事实:任何给定文字的字节表示都是可变长度的。这意味着虚拟机将不知道需要收集多少字节才能读取整个文本。如果我的问题仍然不清楚,我相信一个直观的例子将有助于说明我的困惑。
以这个例子为例。解析器刚刚构造了一个抽象语法树。它将表达式3 + 2转换为:
+
/ \
3 2您的编译器/interprter现在开始遍历树。它生成以下字节码:
PUSH 3 PUSH 2 ADD
| | | | |
|-----| |--------------| |-----| |----------| |-----|
b'\x00' b'\x00\x00\x00\' b'\x00' b'\x00\x00\' b'\x05'然后,虚拟机开始读取字节码文件。它读取第一个字节,并看到它是操作码推送。它现在需要读取操作码推送的参数。
但问题是。虚拟机无法知道需要读取多少字节才能推送整个参数。要推送的参数是可变字节数,因此虚拟机不知道每个参数需要读取多少字节。如上面的伪字节码所示,用于表示不同值的字节数可能有所不同,而且不一致。
虽然上面的例子只使用整数,但这也适用于其他事物。例如字符串,或标识符名称的字符串表示形式。
我试着搜索各种博客,甚至一些语言字节码的官方文档,但我仍然没有找到文字编码的解释。
我发现的壁橱信息,是这个答案给出的一个句子,棘轮Freak给出了我在标题中链接到的问题。它的内容如下:
为了给出一个使每个操作的字节非常显式的例子,有SPIR-V。每条指令的前4字节字被构造为2字节长+2字节操作码.
他说的似乎是SPIR迫使所有的操作码都将其参数压缩或展开以填充两个字节。虽然我认为他可以做到这一点,但我相当肯定这不是他的意思。
当将字节表示为可变长度的文字值编码到字节码文件中时,常见的做法是什么?当然,我认为它们是一种常见的做法,但也许每种语言都不同吗?
发布于 2016-12-22 20:02:53
您的问题比字节码系统更广泛地适用于通用指令集体系结构、硬件或字节代码。
在编码文字值(其字节表示为可变长度)时,常见的做法是什么?
大约有六种合理的技术。
发布于 2016-12-22 19:11:33
要推送的参数是可变字节数,因此虚拟机不知道每个参数需要读取多少字节。
通常,体系结构要求所有参数都是固定的字节数。
请注意,可能有多个PUSH变体,每个变体占用不同的字节数。因此,您可能有一个PUSHWORD,一个PUSHBYTE,一个PUSHSHORT和每一个将有一个独特的操作码。它们可能都被称为只是在程序集中推送,但是在参数中需要有足够的上下文(例如,指定16位寄存器而不是32位寄存器)来确定推送实际上是哪个唯一的操作码操作码。
生成的指令看起来会更像这样:
PUSH3 3 PUSH2 2 ADD
| | | | |
|-----| |--------------| |-----| |----------| |-----|
b'\x03' b'\x00\x00\x00\' b'\x02' b'\x00\x00\' b'\x05'请注意,推送指令是不同的,有不同的操作码。这也不限于推送,每个算术和逻辑操作都可能有多个操作码,因此您可以指定您是ADDing字节还是单词,还是XORing只是一个字节,还是整个单词。
字符串(或任何非原子数据结构,如数组、结构或列表)通常不作为直接(即指令的一部分)提供。相反,它们被存储在存储器中的单独位置,并通过存储器地址(该地址将具有固定的大小,因此可以作为指令的一部分提供)指向。
因此(假设字节码中碰巧有字符串打印指令)‘`PRNT“"Hello”不会是这样的:
PRNT "Hello World"
| |
|--| |------------------------------------------|
\x45 \x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64相反,应该是这样的:
// data section
// This example assumes the string is loaded at address /xcafebeef.
// HWString is a label referring to that. The label is useful in
// assembly, but probably not needed in the actual bytecode.
HWString: "Hello World"
|
|----------------------------------------------|
\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x00 // null terminator, if you're a fan of C-style strings.
// later in the file
// text section
PRNT HWString
| |
|--| |--------|
\x45 \xcafebeef您可能想看看MIPS (32位)体系结构,在这个体系结构中,所有指令都是32位,并且所有指令都适合于一个三种格式。
Java是另一个例子。特别是bipush (b_yte _i_mmediate _push)和sipush (s_hort _i_mmediate _push)。前者只使用一个单字节操作数,后者总是一个两个字节操作数.
发布于 2016-12-22 21:04:25
文字对象存储在字节码之外的数组中。然后,put字节码只对该数组进行索引。
一个Ruby例子,
$ ruby --dump insns -e '[nil,0,1,2,"str",/regexp/]'
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
0000 trace 1 ( 1)
0002 putnil
0003 putobject_OP_INT2FIX_O_0_C_
0004 putobject_OP_INT2FIX_O_1_C_
0005 putobject 2
0007 putstring "str"
0009 putobject /regexp/
0011 newarray 6
0013 leave 如您所见,有不同的put字节码。
nil和公共数字,如0和1https://softwareengineering.stackexchange.com/questions/338714
复制相似问题