首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >文字值是如何编码成字节码的?

文字值是如何编码成字节码的?
EN

Software Engineering用户
提问于 2016-12-22 19:03:23
回答 3查看 2.3K关注 0票数 6

注意:这个问题在某种程度上与字节码是如何“解析”的有关,但它不是重复的。在这个问题中,我问的是字节码是如何生成的,而不是字节码是如何被“解析”的。

正如标题中所述,文字(例如字符串、整数等)如何编码成字节码文件?我的困惑来自于这样一个事实:任何给定文字的字节表示都是可变长度的。这意味着虚拟机将不知道需要收集多少字节才能读取整个文本。如果我的问题仍然不清楚,我相信一个直观的例子将有助于说明我的困惑。

以这个例子为例。解析器刚刚构造了一个抽象语法树。它将表达式3 + 2转换为:

代码语言:javascript
复制
   +
  / \
 3   2

您的编译器/interprter现在开始遍历树。它生成以下字节码:

代码语言:javascript
复制
 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迫使所有的操作码都将其参数压缩或展开以填充两个字节。虽然我认为他可以做到这一点,但我相当肯定这不是他的意思。

当将字节表示为可变长度的文字值编码到字节码文件中时,常见的做法是什么?当然,我认为它们是一种常见的做法,但也许每种语言都不同吗?

EN

回答 3

Software Engineering用户

回答已采纳

发布于 2016-12-22 20:02:53

您的问题比字节码系统更广泛地适用于通用指令集体系结构、硬件或字节代码。

在编码文字值(其字节表示为可变长度)时,常见的做法是什么?

大约有六种合理的技术。

  • 操作码告诉您操作码后面文字的字节数。这意味着通常有几个相同的操作码。请注意,操作码必须(以某种方式)编码操作操作的大小或类型(例如,push 32位int),这可以与操作码后面的文字数据字节(通常称为即时)的大小/计数一起完成,也可以单独完成。如果这些不同(指令所描述的直接文字通常短于操作数的类型),则根据操作码的定义(例如使用符号扩展),将操作码后面的字节(S)从提供的直接文字的大小扩展到操作数类型的大小。
  • 在操作码之后还有其他位,但是被认为是独立于操作码的,它们告诉文字(和/或(所有)操作数的格式)的大小。当指令集具有分组子操作码时,有时主要操作码以外的位指示各种操作数的内容。
  • 最后一个变量是每个操作数都有自己的单独描述符(可能在文字之前分组)。这在具有多个操作数指令的CISC风格的寄存器机器(如VAX)中是典型的,例如addl3 (三个操作数加长)。
  • 文本本身中有一些位可以判断文本中是否有更多的数据跟随;例如,每个字节中有一个可以用于指示更多的字节,这意味着每个文字字节产生7位,并告诉下一个字节是文字还是文字已经完成。这在一定程度上不利于(软件)解释性能,但硬件可以更好地解码这一点,这比天真的方法所显示的要好。如果您正在执行JIT而不是解释器,这可能会正常工作。
  • 使用某种类型的间接,并将文字存储在其他地方。例如,Java/C#字节代码中的字符串就是这种情况。在Java中,push string操作码在常数表中使用索引。应用程序二进制接口通常为较大的常量(如字符串)或其他32位、64位或更大的blob常量指定一个机器寄存器或可访问的全局位置。
  • 有时,文字可能足够大或足够复杂的位模式,需要使用多个指令来组装文字。一些体系结构提供了一个立即加载,它将其文字操作数放入寄存器(或堆栈)的高字节中。然后,常规添加立即用于引入文字的低位。这种情况有时出现在使用固定大小指令的体系结构中。
票数 7
EN

Software Engineering用户

发布于 2016-12-22 19:11:33

要推送的参数是可变字节数,因此虚拟机不知道每个参数需要读取多少字节。

通常,体系结构要求所有参数都是固定的字节数。

请注意,可能有多个PUSH变体,每个变体占用不同的字节数。因此,您可能有一个PUSHWORD,一个PUSHBYTE,一个PUSHSHORT和每一个将有一个独特的操作码。它们可能都被称为只是在程序集中推送,但是在参数中需要有足够的上下文(例如,指定16位寄存器而不是32位寄存器)来确定推送实际上是哪个唯一的操作码操作码。

生成的指令看起来会更像这样:

代码语言:javascript
复制
 PUSH3         3           PUSH2        2         ADD
  |            |             |          |          |    
|-----| |--------------|  |-----|  |----------| |-----|
b'\x03' b'\x00\x00\x00\'  b'\x02'  b'\x00\x00\' b'\x05'

请注意,推送指令是不同的,有不同的操作码。这也不限于推送,每个算术和逻辑操作都可能有多个操作码,因此您可以指定您是ADDing字节还是单词,还是XORing只是一个字节,还是整个单词。

字符串(或任何非原子数据结构,如数组、结构或列表)通常不作为直接(即指令的一部分)提供。相反,它们被存储在存储器中的单独位置,并通过存储器地址(该地址将具有固定的大小,因此可以作为指令的一部分提供)指向。

因此(假设字节码中碰巧有字符串打印指令)‘`PRNT“"Hello”不会是这样的:

代码语言:javascript
复制
PRNT               "Hello World"
 |                      |
|--| |------------------------------------------|
\x45 \x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64

相反,应该是这样的:

代码语言:javascript
复制
// 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)。前者只使用一个单字节操作数,后者总是一个两个字节操作数.

票数 4
EN

Software Engineering用户

发布于 2016-12-22 21:04:25

文字对象存储在字节码之外的数组中。然后,put字节码只对该数组进行索引。

一个Ruby例子,

代码语言:javascript
复制
$ 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字节码。

  • 有些是1字节长,专门用于nil和公共数字,如0和1
  • 另一些则有两个字节长,并在文字对象数组中包含一个索引。
票数 1
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/338714

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档