比特帝国

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫描二维码登录本站

比特帝国 首页 EOS EOS观点 查看内容

EOS智能合约和虚拟机分析

2018-6-29 23:14| 发布者: admin| 查看: 1440| 评论: 0|原作者: EOS LIVE|来自: EOS LIVE

EOS智能合约和虚拟机分析

导读:上一篇文章整体介绍了各个阶段区块链虚拟机的代表机特点,本篇文章就EOSIO中的智能合约和虚拟机进行分析来从更加全面的角度来看EOS是如何构建和实现。

EOS智能合约和虚拟机分析

EOS虚拟机同经典的EVM,是EOS中运行智能合约的容器,但是从设计上讲它与EOS.IO是分离的。进一步脚本语言和虚拟机的技术设计与EOS.IO分离。从宏观来讲任何语言或者虚拟机,只要满足条件适合沙盒模式运行,同时满足一定的运行效率,都可以通过满足EOS.IO提供的API来加入到EOS.IO的消息传递过程中。以下为github上官方的说明:

The EOS.IO software will be first and foremost a platform for coordinating the delivery of authenticated messages (called Actions) to accounts. The details of scripting language and virtual machine are implementation specific details that are mostly independent from the design of the EOS.IO technology. Any language or virtual machine that is deterministic and properly sandboxed with sufficient performance can be integrated with the EOS.IO software API.

本文就EOSIO中的智能合约和虚拟机进行分析来从更加全面的角度来看EOS是如何构建和实现。

相关背景知识

LLVM相关内容

LLVM相关技术的理解对于我们深入理解EOS虚拟机的运行机制至关重要,所以必要的LLVM的相关知识在这里是需要的。同时LLVM作为一个成熟的编译器后端实现,无论从架构还是相关设计思想以及相关的工具的实现都是值得学习的。

LLVM架构概述

概括来讲LLVM项目是一系列分模块、可重用的编译工具链。它提供了一种代码良好的中间表示(IR),LLVM实现上可以作为多种语言的后端,还可以提供与语言无关的优化和针对多种CPU的代码生成功能。最初UIUC的Chris Lattner主持开发了一套称为LLVM(Low Level Virtual Machine)的编译器工具库套件,但是后来随着LLVM的范围的不断扩大,则这个简写并不代表底层虚拟机的含义,而作为整个项目的正式名称使用,并一直延续至今。所以现在的LLVM并不代表Low Level Virtual Machine。

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Despite its name, LLVM has little to do with traditional virtual machines. The name "LLVM" itself is not an acronym; it is the full name of the project.

LLVM不同于传统的我们熟知的编译器。传统的静态编译器(如gcc)通常将编译分为三个阶段,分别由三个组件来完成具体工作,分别为前端、优化器和后端,如下图所示。

LLVM项目在整体上也分为三个部分,同传统编译器一致,如下图所示,不同的语言的前端,统一的优化器,以及针对不同平台的机器码生成。从图2我们也可以得到启发,如果想实现一门自定义的语言,目前主要的工作可以集中在如何实现一个LLVM的前端上来。

LLVM的架构相对于传统编译器更加的灵活,有其他编译器不具备的优势,从LLVM整体的流程中我们就可以看到这一点,如下图所示为LLVM整体的流程,编译前端将源码编译成LLVM中间格式的文件,然后使用LLVM Linker进行链接。Linker执行大量的链接时优化,特别是过程间优化。链接得到的LLVM code最终会被翻译成特定平台的机器码,另外LLVM支持JIT。本地代码生成器会在代码生成过程中插入一些轻量级的操作指令来收集运行时的一些信息,例如识别hot region。运行时收集到的信息可以用于离线优化,执行一些更为激进的profile-driven的优化策略,调整native code以适应特定的架构。

从图中我们也可以得出LLVM突出的几个优势:

  • 持续的程序信息,每个阶段都可以获得程序的信息内容

  • 离线代码生成,产生较高的可执行程序

  • 便捷profiling及优化,方便优化的实施

  • 透明的运行时模型

  • 统一,全程序编译


LLVM IR介绍与分析

根据编译原理可知,编译器不是直接将源语言翻译为目标语言,而是翻译为一种“中间语言”,即"IR"。之后再由中间语言,利用后端程序翻译为目标平台的汇编语言。由于中间语言相当于一款编译器前端和后端的“桥梁”,不同编译器的中间语言IR是不一样的,IR语言的设计直接会影响到编译器后端的优化工作。LLVM IR官方介绍见: http://llvm.org/docs/LangRef.html。

LLVM IR格式

The LLVM code representation is designed to be used in three different forms: as an in-memory compiler IR, as an on-disk bitcode representation (suitable for fast loading by a Just-In-Time compiler), and as a human readable assembly language representation. This allows LLVM to provide a powerful intermediate representation for efficient compiler transformations and analysis, while providing a natural means to debug and visualize the transformations.

由上诉的引用得知目前LLVM IR提供三种格式,分别是内存里面的IR模型,存储在磁盘上的二进制格式,存储在磁盘上的文本可读格式。三者本质上没有区别,其中二进制格式以bc为文件扩展名,文本格式以ll为文件扩展名。除了以上两个格式文件外,和IR相关的文件格式还有s和out文件,这两种一个是由IR生成汇编的格式文件,一个是生成的可执行文件格式(linux下如ELF格式),

  • bc结尾,LLVM IR文件,二进制格式,可以通过lli执行

  • ll结尾,LLVM IR文件,文本格式,可以通过lli执行

  • s结尾,本地汇编文件

  • out, 本地可执行文件

以上几种不同文件的转化图如下所示,整体上我们可以看一下这几种格式的转化关系,同时从中我们也可以看出工具clang、llvm-dis、llvm-as等工具的作用和使用。

中间语言IR的表示,一般是按照如下的结构进行组织的由外到内分别是:

  • 模块(Module)

  • 函数(Function)

  • 代码块(BasicBlock)

  • 指令(Instruction)

模块包含了函数,函数又包含了代码块,后者又是由指令组成。除了模块以外,所有结构都是从值产生而来的。如下为一个ll文件的片段,从中可以简单的看出这种组织关系。

; ModuleID = 'main.ll'
source_filename = "main.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: noinline nounwind uwtable define i32 @add(i32, i32) #0 {  %3 = alloca i32, align 4  %4 = alloca i32, align 4  store i32 %0, i32* %3, align 4  store i32 %1, i32* %4, align 4  %5 = load i32, i32* %3, align 4  %6 = load i32, i32* %4, align 4  %7 = add nsw i32 %5, %6  ret i32 %7
}
LLVM IR指令集

指令集的分类大致可以分为基于栈的,基于运算器的还有基于寄存器的,基于栈的和基于寄存器的虚拟机目前是比较常见的,两种不同之处主要在运行效率,指令集大小和性能三个方面。LLVMIR采用的是基于寄存器的满足RISC架构以及load/store模式,也就是说只能通过将load和store指令来进行CPU和内存间的数据交换。LLVM IR指令集拥有普通CPU一些关键的操作,屏蔽掉了一些和机器相关的一些约束。LLVM提供了足够多的寄存器来存储基本类型值,寄存器是为SSA形式(静态单态赋值),这种形式的UD链(use-define chain, 赋值代表define, 使用变量代表use)便于优化。LLVM指令集仅包含31条操作码。LLVM中的内存地址没有使用SSA形式,因为内存地址有可能会存在别名或指针指向,这样就很难构造出来一个紧凑可靠的SSA表示。在LLVM中一个function就是一组基本块的组合,一个基本块就是一组连续执行的指令并以中指指令结束(包括branch, return, unwind, 或者invoke等),中止指令指明了欲跳转的目的地址。

LLVM IR类型系统

LLVM的类型系统为语言无关。每一个SSA寄存器或者显示的内存对象都有其对应的类型。这些类型和操作码一起表明这个操作的语义,这些类型信息让LLVM能够在低层次code的基础上进行一些高层次的分析与转换,LLVM IR包含了一些语言共有的基本类型,并给他们一些预定义的大小,从8bytes到64bytes不等,基本类型的定义保证了LLVM IR的移植性。同时LLVM又包含了四种复杂类型,pointer,arrays, structures和functions。这四种类型足够表示现有的所有语言类型。为了支持类型转换,LLVM提供了一个cast操作来实现类型的转换,同时为了支持地址运算,LLVM提供了getelementptr的命令。LLVM中的许多优化都是基于地址做的(后续的总结再分析)。

LLVM IR内存模型

LLVM提供特定类型的内存分配,可以使用malloc指令在堆上分配一个或多个同一类型的内存对象,free指令用来释放malloc分配的内存(和C语言中的内存分配类似)。另外提供了alloca指令用于在栈上分配内存对象,该内存对象在通常在函数结尾会被释放。统一内存模型,所有能够取地址的对象都必须显示分配。局部变量也要使用alloca来显示分配,没有隐式地手段来获取内存地址,这就简化了关于内存的分析。


12下一页
比特帝国区块链交易所

最新评论

返回顶部