本文参考:https://learnblockchain.cn/article/675
碎片
汇编可以直接于EVM进行交互
汇编相当于直接操作OPCODE,每个OPCODE都会有一个对应的汇编语法,比如:mstore是汇编,MSTORE是OPCODE一样
以太坊一共有:144个操作吗,即指令集,这些指令杯solidity抽象之后,用来写智能合约(画个图)
PC(程序计数器)和SP(堆栈指针)
指令执行时,通常会先从堆栈弹出一个或多个值作为参数,再将执行结果压回堆栈,这通常被称为逆波兰表示法(Reverse Polish Notatioin)
a + b a b add
使用汇编的好处
更加细颗粒度的控制:
有些操作合约solidity语法无法直接支持,例如:指向特定的内存slot,很多标准库的实现都离不开汇编,例如:
- bytes-utils:https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
- stringutils:https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol
节约gas
function addAssembly(uint x, uint y) public pure returns (uint) {
assembly {
// Add some code here
let result := add(x, y)
mstore(0x0, result)
return(0x0, 32)
}
}
function addSolidity(uint x, uint y) public pure returns (uint) {
return x + y;
}
增强功能
两种汇编
- 内联汇编(inline assembley):可以在内部solidity源代码中使用。
- 独立汇编(standalone Assembly):可以单独使用,无需solidity。
汇编语法
使用时需要注意:每个操作码是有参数的,例如指定内存地址,起始位置,偏移量等,这些在solidity中不需要指定。
1. 引入汇编
assembly {
// some assembly code here
}
在assembly块内的开发语言为Yul,且多个代码块之前的变量不共享,各自独立。
assembly {
let x := 2
}
assembly {
let y := x // Error
}
// DeclarationError: identifier not found
// let y := x
// ^
2. 简单汇编示例
function addition(uint x, uint y) public pure returns (uint) {
assembly {
let result := add(x, y) // x + y
mstore(0x0, result) // 在内存中保存结果
return(0x0, 32) // 从内存中返回32字节
}
}
详解:
function addition(uint x, uint y) public pure returns (uint) {
assembly {
// 创建一个新的变量 result
// -> 使用add操作码计算x+y
// -> 将计算结果赋值给变量result
let result := add(x, y) // x + y
// 使用mstore操作码
// -> 将result变量的值存入内存
// -> 指定内存地址 0x0
mstore(0x0, result) // 将结果存入内存
// 从内存地址0x返回32字节
return(0x0, 32)
}
}
3. 定义变量与赋值
使用let定义变量,使用 := 进行赋值,自动初始化为0
assembly {
let x := 7
let y := add(x, 3)
// 对从0x00开始,20个字节的内容进行hash处理
let z := add(keccak256(0x0, 0x20), div(slength, 32))
let n // 自动初始化为 n = 0
}
4.let指令的作用
- 创建一个新的堆栈槽位
- 为变量保留该槽位
- 当到达代码块结束时,自动销毁该槽位(因此在外部block无法使用这个变量)
5. 汇编的注释
单行注释//
多行注释/**/
6. 汇编中的字面量
字符串字面量最多包含32字符。(每个字符1byte,所以最大为32byte,正好时一个槽位)
assembly {
let a := 0x123 // 16进制
let b := 42 // 10进制
let c := "hello world" // 字符串
let d := "very long string more than 32 bytes" // 超长字符串,出错!
}
// TypeError: String literal too long (35 < 32)
// let d := "really long string more than 32 bytes"
//
7. 块和作用域
assembly {
let x := 3 // x在各处可见
// Scope 1
{
let y := x // ok
} // 到此处会销毁y
// Scope 2
{
let z := y // Error
} // 到此处会销毁z
}
// DeclarationError: identifier not found
// let z := y
// ^