项目的完整代码在 C2j-Compiler

前言

第十一篇,终于要进入代码生成部分了,但是但是在此之前,因为我们要做的是C语言到字节码的编译,所以自然要了解一些字节码,但是由于C语言比较简单,所以只需要了解一些字节码基础

JVM的基本机制

JVM有一个执行环境叫做stack frame

这个环境有两个基本数据结构

  • 执行堆栈:指令的执行,都会围绕这个堆栈来进行
  • 局部变量数组,参数和局部变量就存储在这个数组。

还有一个PC指针,它指向下一条要执行的指令。

举一个例子

int f(int a, int b) {return a+b;
}f(1,2);

JVM的执行环境是这样变化的

stack:
localarray:1,2
pc:把a从localarray取出放到stack
stack:1
localarray:2
pc:把b从localarray取出放到stack
stack:1,2
localarray:
pc:把a,b弹出堆栈并且相加压入堆栈

对于JVM提供的对象

.class public CSourceToJava
.super java/lang/Object
.method public static main([Ljava/lang/String;)Vgetstatic java/lang/System/out Ljava/io/PrintStream;ldc "Hello World!"invokevirtual java/io/PrintStream/println(Ljava/lang/String;)Vreturn
.end method
.end class

getstatic、ldc和invokevirtual都相当于JVM提供的指令

getstatic和ldc相当于压入堆栈操作。invokevirtual则是从堆栈弹出参数,然后调用方法

stack: out "Hello World!"

JVM的基本指令

pusu load store

JVM的运行基本都是围绕着堆栈来进行,所以指令也都是和堆栈相关,比如进行一个乘法1 * 2:

bipush 1
bipush 2
imul

可以看到JVM的指令操作时带数据的类型,b代表byte,也就是只能操作-128 ~ 128之间的数,而i代表是整形操作,所以相应也会有sipush等等了

下面加入要把1 * 2打印用prinft打印在控制台上,就需要把out对象压入堆栈,此时的堆栈:

stack: 2 out

但是调用out的参数需要在堆栈顶部,所以这时候就需要两个指令iload、istore

istore 0把2放到局部变量队列,再把out压入堆栈,再用iload 0把2放入堆栈中

stack: out 2

局部变量和函数参数

局部变量

在字节码里,局部变量和函数参数都会存储在队列上

int func() {int a;int b;a = 1;b = 2;return a + b;
}

看一下这个方法执行的时候堆栈的变化情况

// 执行a = 1,把1压到stack上,再把1放入到队列里
stack:
array:1// 执行b = 1,也同理
stack:
array:1, 2

最后的return也有相应的return指令,所以完整的指令如下

sipush 1
istore 0
sipush 2
istore 1
iload 0
iload 1
iadd
ireturn

函数参数

int func(int a, int b, int c, int d){}

在调用这个函数的适合,函数参数就会按照顺序被压入堆栈中,然后拷贝到队列上

stack: a b c d
array:stack:
array: d c b a

所以在之后的代码生成部分就需要一个来找到局部变量的位置的函数

数组

创建数组

下面这段指令的作用是创建一个大小为100的整形数组

sipush 100
newarray int
astore 0
  • sipush 100 把元素个数压入堆栈
  • newarray int 创建一个数组,后面是数据类型
  • astore 表示把数组对象移入队列 a表示的是一个对象引用

读取数组

下面这段指令是读取数组的第66个元素

aload 0
sipush 66
iaload
  • aload 0 把数组对象放到堆栈上
  • sipush 放入要读取的元素下标
  • iaload 把读取的值压入堆栈

元素赋值

aload 0
sipush 7
sipush 10
iastore
  • aload 0 把数组对象加载到堆栈
  • sipush 7 把要赋值的值压入堆栈
  • sipush 10 把元素下标压入堆栈
  • iastore 进行赋值

结构体

C语言里的结构体其实就相当于没有方法只有属性的类,所以可以把结构体编译成一个类

创建一个类

new MyClass //创建一个名字为MyClass的类
invokespecial ClassName/<init>() V //调用类的无参构造函数

例子

public class MyClass {public int a;public char c;public MyClass () {this.a = 0;this.c = 0;}
}

public class MyClass生成下面的代码,都是对应生成一个类的特殊指令

.class public MyClass
.super java/lang/Object

下面的则是对应属性的声明

.field public c C
.field public a I

声明完属性,就是构造函数了,首先是先把类的实例加载到堆栈,再调用它的父类构造函数,对属性的赋值:

  1. 加载类的实例到堆栈上 aload 0
  2. 压入值 sipush 0
  3. 赋值的对应指令 putfield MyClass/c C
aload 0
invokespecial java/lang/Object/<init>()V
aload 0
sipush 0
putfield MyClass/c C
aload 0
sipush 0
putfield MyClass/a I
return

完整的对应的Java字节码如下:

.class public MyClass
.super java/lang/Object
.field public c C
.field public a I
.method public <init>()Vaload 0invokespecial java/lang/Object/<init>()Vaload 0sipush 0putfield MyClass/c Caload 0sipush 0putfield MyClass/a Ireturn
.end method
.end class

读取类的属性

aload 3 ;假设类实例位于局部变量队列第3个位置
putfield ClassName/x I

结构体数组

下面的指令创建了10个字符串类型的数组,这时候堆栈上的对象是一个引用,指向heap上一个10个字符串类型的数组

sipush 10
anewarray java/lang/String

下面的指令则是对数组的第一个元素进行赋值

astore 0
aload  0
sipush 0
ldc "hello world"
aastore

所以对于我们自己定义的类也是一样的

sipush  10
anewarray  MyClass
astore  0

下面则是对数组第一个下标生成一个MyClass对象

aload   0
sipush  1
new MyClass
invokespecial CTag/<init>()V
aastore

下面是对数组里的对象的属性的取值和赋值操作,只是组合了之前的指令而已

aload   0
sipush  1
aaload
sipush  1
putfield  MyClass/x Iaload   0
sipush  1
aaload
getfield  MyClass/x I

分支语句

JVM指令还有两个个非常重要的指令就是分支和循环指令,我们先来看分支指令

if (1 < 2) {a = 1;
} else {a = 2;
}

上面对应的JVM指令如下:

  • 先把1和2压入堆栈
  • if_cmpge指令是大于等于,即如果1大于等于2就去执行else分支
  • goto指令是跳转到相应的标签,也就是执行完if,就跳出else部分
sipush 1
sipush 2
if_cmpge branch0
sipush 1
astore 0
goto out_branch0
branch0:
sipush 2
istore 0
out_branch0:
sipush 3
istore 0

循环语句

基本的JVM指令只剩循环语句了,逻辑也不困难,基本的JVM指令相对于汇编算是非常简单了

for (i = 0; i < 3; i++) { a[i] = i;
}

上面生成的对应字节码如下(假设现在变量i在队列的第5个位置,a在队列的第2个位置):

  • 首先对i赋值
  • 再把3压入堆栈和i做比较,判断i < 3
  • 之后就是对数组的操作
  • 然后修改i的值
  • 返回loop0继续判断i < 3
sipush 0
istore 5
loop0:
iload 5
sipush 3
if_icmpge branch0
aload 2               ;加载数组
iload 3               ;加载标i
iload 3               ;加载变量i
iastore               ;把i的值存入到a[i]
iload 3               ;加i
sipush 1              ;把1压入堆栈
iadd                  ;i++
istore 3              ;把i+1后的值放入到i的队列上的位置
goto loop0            ;跳转到循环开头
branch0:

小结

这一篇主要就是了解一下Java基本的字节码,因为C语言的语法比较简单,所以只需要知道一点就足够生成代码了。所以相对于汇编来说,是非常简单的了。这样下一篇就可以正式进入代码生成部分

另外,欢迎Star这个项目!

转载于:https://www.cnblogs.com/secoding/p/11384619.html

从零写一个编译器(十一):代码生成之Java字节码基础相关推荐

  1. 从零写一个编译器(完结):总结和系列索引

    前言 这个系列算作我自己的学习笔记,到现在已经有十三篇了,加上这篇一共十四篇.一步一步的从词法分析到语法分析.语义分析,再到代码生成,准备在这一篇做一个总结收尾和一个这个系列以前文章的索引. (另外, ...

  2. 从零写一个编译器(十三):代码生成之遍历AST

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成对JVM指令的生成,下面就可以真正进入代码生成部分了.通常现代编译器都是先把生成IR,再经过代码优化等等,最后才编译成目标平台代码.但是时 ...

  3. 从零写一个编译器(十二):代码生成之生成逻辑

    项目的完整代码在 C2j-Compiler 前言 在上一篇解释完了一些基础的Java字节码指令后,就可以正式进入真正的代码生成部分了.但是这部分先说的是代码生成依靠的几个类,也就是用来生成指令的操作. ...

  4. 从零写一个编译器(一):输入系统和词法分析

    项目的完整代码在 C2j-Compiler 前言 从半抄半改的完成一个把C语言编译到Java字节码到现在也有些时间,一直想写一个系列来回顾整理一下写一个编译器的过程,也算是学习笔记吧.就从今天开始动笔 ...

  5. 从零写一个编译器(三):语法分析之几个基础数据结构

    项目的完整代码在 C2j-Compiler 写在前面 这个系列算作为我自己在学习写一个编译器的过程的一些记录,算法之类的都没有记录原理性的东西,想知道原理的在龙书里都写得非常清楚,但是我自己一开始是不 ...

  6. 从一个class文件深入理解Java字节码结构

    前言 我们都知道,Java程序最终是转换成class文件执行在虚拟机上的,那么class文件是个怎样的结构,虚拟机又是如何处理去执行class文件里面的内容呢,这篇文章带你深入理解Java字节码中的结 ...

  7. 从零写一个编译器(十):编译前传之直接解释执行

    项目的完整代码在 C2j-Compiler 前言 这一篇不看也不会影响后面代码生成部分 现在经过词法分析语法分析语义分析,终于可以进入最核心的部分了.前面那部分可以称作编译器的前端,代码生成代码优化都 ...

  8. 从零写一个编译器(九):语义分析之构造抽象语法树(AST)

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...

  9. 从零写一个编译器(八):语义分析之构造符号表

    项目的完整代码在 C2j-Compiler 前言 在之前完成了描述符号表的数据结构,现在就可以正式构造符号表了.符号表的创建自然是要根据语法分析过程中走的,所以符号表的创建就在LRStateTable ...

最新文章

  1. 高效模式编写者的7个习惯
  2. python 判断图片是否损坏_检查图片是否损坏、图片后缀是否与实际图片类型对应 - Python...
  3. 手把手教你报表工具PentahoBI安装和简单使用
  4. 无状态shiro认证组件(禁用默认session)
  5. 补丁生成与应用工具 V1.5.4
  6. c++ qt qlistwidget清空_Qt音视频开发16-mpv通用接口
  7. win32com excel转pdf
  8. jmeter面试题及答案
  9. Autumn中文文档0:为什么使用Autumn
  10. 用python爬取全网百度网盘资源的神器
  11. Android音视频开发:AudioRecord录制音频
  12. Recoil 状态管理方案的浅入浅出
  13. 安卓 TextView显示温度符号
  14. 泛化、过拟合与欠拟合
  15. speedoffice(Excel)如何全选表格
  16. STM32 -- 做一块自己的开发板(STM32F103C8T6)
  17. html escape unescape
  18. 数据库操作:删除数据delete
  19. STM32 看门狗详解
  20. ApexCharts 柱状图上显示具体数值

热门文章

  1. no result type defined for type 'dispatch'mapped
  2. 图像的熵、灰度平均值、灰度中值、方差
  3. android-远程图片获取和本地缓存
  4. Exchange 2007 安裝(-)
  5. django的admin界面删除因为外键约束导致失败
  6. fatal error: opencv2/core/cvdef.h: 沒有此一檔案或目錄
  7. multiple versions of scala libraries detected解决方案
  8. 08_MinNumberInRotateArrary
  9. Linux 向文件末尾追加命令(转载)
  10. 是否使用wraps的區別