WIP Solidity Assemblyを触ってみる
Solidity Assembly is 何
Solidityには EVM向けのインラインアセンブリを直接書ける機能がある。
これを使うことでSolidityだけではできないopcodeを使ったEVMのメモリへのアクセスやstringの高速な比較機能機能などをマニュアルで実装できるようになる
インラインAssemblyが使われているコード例
そもそもStackMachineって何
情報が古いので現在のデバッガーの使い方は公式Docsをみた方が良い。基本構成は変わっていないがterminalから起動するようになっている。
Solidity Assemblyをコンパイルして生成されたopcodeを覗いてみる
code:sol
contract Sample {
function addition(uint x, uint y) public pure returns (uint) {
assembly {
let result := add(x, y)
mstore(0x0, result)
return(0x0, 32)
}
}
}
試しに↑をコンパイルした結果のopcodeの一部↓
code:asm
PUSH1 00
DUP2
DUP4
ADD
DUP1
PUSH1 00
MSTORE
PUSH1 20
PUSH1 00
RETURN
この時のstackの状態
code:txt
0: 0x02
1: 0x01
これをstackの状態とともに上から実行時の遷移を書き下すと
code:asm
初期状態
0: 0x02
1: 0x01
PUSH1 00
0: 0x00
1: 0x02
2: 0x01
DUP2
0: 0x02
1: 0x00
2: 0x02
3: 0x01
DUP4
0: 0x01
1: 0x02
2: 0x00
3: 0x02
4: 0x01
ADD // 0, 1番目を足して1番上のstackに積む
0: 0x03
1: 0x00
2: 0x02
3: 0x01
DUP1
0: 0x03
1: 0x03
2: 0x00
3: 0x02
4: 0x01
PUSH1 00
0: 0x00
1: 0x03
2: 0x03
3: 0x00
4: 0x02
5: 0x01
MSTORE // 0番目の値(0)の位置+32をした位置(=つまり0x10の位置)に1番目の0x03を保存する
0: 0x03
1: 0x00
2: 0x02
3: 0x01
PUSH1 20
0: 0x20
1: 0x03
2: 0x00
3: 0x02
4: 0x01
PUSH1 00
0: 0x00
1: 0x20
2: 0x03
3: 0x00
4: 0x02
5: 0x01
RETURN // 0番目の値(0)の位置から1番目の値(0x20)の位置(=つまり0x00から0x20なので32byte)を返す
stack
ざっくりとローカル領域みたいなもの
PUSH/DUP/POP/SWAP/四則演算などのopcodeが主に利用する
永続化はされない
memory
32byte単位のデータ保存領域
MSTORE/MSTOREB/MLOADなどのopcodeが利用する。各opcodeの引数はstackの上から必要な分だけpopされて利用される。
永続化はされない
storage
永続化したい値を置く領域
SSTORE/SLOADの2つのopcodeが利用する。各opcodeの引数はstackの上から必要な分だけpopされて利用される。
永続化されるのでgas代が必要。
calldata
関数の引数を格納する領域
transactionトリガーで外部から実行された場合/call or delegatecallで呼び出された時に利用される。
つまりContract内部から利用された時はcalldata領域は使われない。その場合はstack領域を使う。
returndata
他のContractの関数を呼び出した時の返却データを置く領域
0x40~0x5fまではフリーポインタ。Solidityからメモリのアロケートがされるとここが消費される。
0x80以降は全てフリーメモリ。
メモリのアロケートは下記の時になされる。
関数の引数に配列が渡された時。(bytes, stringも含む)
メモリ上のデータを操作する処理を実行した時。(keccakが一番有名かな)
メモリ上に配列やstructを宣言した時。(i.e. uint[] memory arrays;)
外部contractから関数の戻り値として動的データを受け取る時。
gas代がめっちゃかかるので配列の削除ができない
配列はサイズを小さくするより大きくする方が安い
forループには範囲外アクセスをチェックする初期化処理のOPCODEが差し込まれるのでガス代が上がりがち
二次元配列は定義時とアクセス時で位置が逆になるMemory uint[a][b] arrにはarr[b][a]とアクセス
Solidityのinline assemblyについてのmediumの解説