头图

概要
自从C语言问世,而后类C语言犹如雨后春笋般地搅动着IT界,而这些语言有别于汇编语言那样。
它们就是更贴切自然语言的高级编程语言,可这些高级编程语言最终还是要编译成机器语言(汇编语言)。
EVM(Ethereum Virtual Machine)是一种栈(Stack)结构,我们知道栈是一种先进后出(LIFO)的数据结构。
为什么要用汇编来编写呢?
借您所问,既然Solidity可以编写出优秀的智能合约,那为什么还要使用低级的汇编语言呢?
在回答这个问题之前,我们来看看每个新的编程语言诞生都是为了解决当前编程语言无法解决,或者说使用当前编程语言解决起来比较麻烦,那么,新的编程语言就在这样的环境下应运而生,当然咯,并不是所有新编程语言都是为了解决当前编程语言不能解决的问题,才被开发出来,而是……(此处不便说出缘由,毕竟它也不是本文的重点)。
细粒度控制
Assembly允许您执行一些仅仅靠Solidity无法实现的逻辑,比如,指向特定的内存插槽(Memory Slot)。
当我们在编写库(library)时,细粒度控制特别有用,因为它们会被重复使用。
节省gas
在Solidity中使用Assembly的主要好处之一是节省gas。
让我们尝试通过创建一个将2个值x和y相加并返回结果的函数来比较Solidity和Assembly之间的gas成本。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract AssemblyExample {
    
    function addAssembly(uint x, uint y) public pure returns (uint) {
        assembly {
            let result := add(x, y)
            mstore(0x0, result)
            return(0x0, 32)
        }
    }
 
    function addSolidity(uint x, uint y) public pure returns (uint) {
        return x + y;
    }
  
}

图片
Solidity中两种方式实现Assembly
1、内联汇编:也可以在Solidity代码中使用。2、独立程序集:无需编写Solidity代码即可使用。
怎么使用Assembly?
正如上面的例子那样,汇编代码运行在assembly{...}汇编块中的。而汇编代码是使用YUL语言来编写的!内联汇编块不共享命名空间,即不能在一个汇编块调用另一个汇编块中定义的变量。

assembly {
   // some assembly code here
}

图片
以下是一个简单的示例,函数接受两个参数,并将它们的和作为返回值,看看使用Assembly是怎么实现的?了解它们在EVM的工作方式

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract AssemblyExample {
    
   function addition(uint x, uint y) public pure returns (uint) {
     
    assembly {
        
        //声明一个 result 变量,并将 x,y之和赋值给它
        let result := add(x, y)   // x + y
        
        //使用 mstore 操作码将 result存在 memory 中,地址是 0x0
        mstore(0x0, result)       // store result in memory
         
        //返回 32 字节的 memory 地址
        return(0x0, 32)          
        
    }
}
  
}

图片
数据存储
让我们来看看一个简单的例子。我们将数据存放在storage(存储)中,然后再去调用它。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract StorageDataExample {

    function setData(uint256 newValue) public {
        assembly {
            sstore(0, newValue)
        }
    }

    function getData() public view returns(uint256) {
        assembly {
            let v := sload(0)
            mstore(0x80, v)
            return(0x80, 32)
        }
    }
}

图片
setData函数使用了sstore操作码将变量newValue写入storage(存储)中。
getData函数先是用了sload操作码来加载storage(存储)中的数据,它并不能从storage中直接返回。
所以才需要mstore操作码将其写入memory(内存)中,最后我们返回引用memory(内存)中存放数据的地址和32字节长度的数据。https://docs.soliditylang.org/en/latest/yul.html
image.png


BSN研习社
16 声望10 粉丝