内联汇编的使用

Posted xiaocongcong888

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内联汇编的使用相关的知识,希望对你有一定的参考价值。

基础知识:

语法;
内联编译语言也会像Solidity一样解析注释,字面量和标识符。所以你可以使用//和/**/的方式注释。内联编译的在Solidity中的语法是包裹在assembly { ... },下面是可用的语法,后续有更详细的内容。

字面量。如0x123,42或abc(字符串最多是32个字符)
操作码(指令的方式),如mload sload dup1 sstore,后面有可支持的指令列表
函数风格的操作码,如add(1, mlod(0)
标签,如name:
变量定义,如let x := 7 或 let x := add(y, 3)
标识符(标签或内联局部变量或外部),如jump(name),3 x add
赋值(指令风格),如,3 =: x。
函数风格的赋值,如x := add(y, 3)
支持块级的局部变量,如{ let x := 3 { let y := add(x, 1) } }

预定义操作符
在语法中,操作码被表示为预先定义的标识符。

操作码 栈 说明
stop - stop execution, identical to return(0,0)
add(x, y) x + y
sub(x, y) x - y
mul(x, y) x * y
div(x, y) x / y
sdiv(x, y) x / y, for signed numbers in two’s complement
mod(x, y) x % y
smod(x, y) x % y, for signed numbers in two’s complement
exp(x, y) x to the power of y
not(x) ~x, every bit of x is negated
lt(x, y) 1 if x < y, 0 otherwise
gt(x, y) 1 if x > y, 0 otherwise
slt(x, y) 1 if x < y, 0 otherwise, for signed numbers in two’s complement
sgt(x, y) 1 if x > y, 0 otherwise, for signed numbers in two’s complement
eq(x, y) 1 if x == y, 0 otherwise
iszero(x) 1 if x == 0, 0 otherwise
and(x, y) bitwise and of x and y
or(x, y) bitwise or of x and y
xor(x, y) bitwise xor of x and y
byte(n, x) nth byte of x, where the most significant byte is the 0th byte
addmod(x, y, m) (x + y) % m with arbitrary precision arithmetics
mulmod(x, y, m) (x * y) % m with arbitrary precision arithmetics
signextend(i, x) sign extend from (i8+7)th bit counting from least significant
keccak256(p, n) keccak(mem[p...(p+n)))
sha3(p, n) keccak(mem[p...(p+n)))
jump(label) - jump to label / code position
jumpi(label, cond) - jump to label if cond is nonzero
pc current position in code
pop(x) - remove the element pushed by x
dup1 ... dup16 copy ith stack slot to the top (counting from top)
swap1 ... swap16
swap topmost and ith stack slot below it
mload(p) mem[p..(p+32))
mstore(p, v) - mem[p..(p+32)) := v
mstore8(p, v) - mem[p] := v & 0xff - only modifies a single byte
sload(p) storage[p]
sstore(p, v) - storage[p] := v
msize size of memory, i.e. largest accessed memory index
gas gas still available to execution
address address of the current contract / execution context
balance(a) wei balance at address a
caller call sender (excluding delegatecall)
callvalue wei sent together with the current call
calldataload(p) call data starting from position p (32 bytes)
calldatasize size of call data in bytes
calldatacopy(t, f, s) - copy s bytes from calldata at position f to mem at position t
codesize size of the code of the current contract / execution context
codecopy(t, f, s) - copy s bytes from code at position f to mem at position t
extcodesize(a) size of the code at address a
extcodecopy(a, t, f, s) - like codecopy(t, f, s) but take code at address a
returndatasize size of the last returndata
returndatacopy(t, f, s) - copy s bytes from returndata at position f to mem at position t
create(v, p, s) create new contract with code mem[p..(p+s)) and send v wei and return the new address
create2(v, n, p, s) create new contract with code mem[p..(p+s)) at address keccak256(
. n . keccak256(mem[p..(p+s))) and send v wei and return the new address
call(g, a, v, in, insize, out, outsize) call contract at address a with input mem[in..(in+insize)) providing g gas and v wei and output area mem[out..(out+outsize)) returning 0 on error (eg. out of gas) and 1 on success
callcode(g, a, v, in, insize, out, outsize) identical to call but only use the code from a and stay in the context of the current contract otherwise
delegatecall(g, a, in, insize, out, outsize) identical to callcode but also keep caller and callvalue
staticcall(g, a, in, insize, out, outsize) identical to call(g, a, 0, in, insize, out, outsize) but do not allow state modifications
return(p, s) - end execution, return data mem[p..(p+s))
revert(p, s) - end execution, revert state changes, return data mem[p..(p+s))
selfdestruct(a) - end execution, destroy current contract and send funds to a
invalid - end execution with invalid instruction
log0(p, s) - log without topics and data mem[p..(p+s))
log1(p, s, t1) - log with topic t1 and data mem[p..(p+s))
log2(p, s, t1, t2) - log with topics t1, t2 and data mem[p..(p+s))
log3(p, s, t1, t2, t3) - log with topics t1, t2, t3 and data mem[p..(p+s))
log4(p, s, t1, t2, t3, t4) - log with topics t1, t2, t3, t4 and data mem[p..(p+s))
origin transaction sender
gasprice gas price of the transaction
blockhash(b) hash of block nr b - only for last 256 blocks excluding current
coinbase current mining beneficiary
timestamp timestamp of the current block in seconds since the epoch
number current block number
difficulty difficulty of the current block
gaslimit block gas limit of the current block

重点:
mem [a ... b]表示从位置a开始到(不包括)位置b的存储器的字节,存储器[p]表示位置p处的存储内容。

你可以在操作码后接着输入操作码,它们最终都会生成正确的字节码。比如:

3 0x80 mload add 0x80 mstore
下面将会添加3与memory中位置0x80的值。

由于经常很难直观的看到某个操作码真正的参数,Solidity内联编译提供了一个函数风格的表达式,上面的代码与下述等同:

mstore(0x80, add(mload(0x80), 3))

表示的意思都是 将3添加到位置ox80 并保存 等同于storage

举例子:
pragma solidity ^0.4.0;

library GetCode {
function at(address _addr) returns (bytes o_code) {
assembly {
// 检索代码的大小,这需要汇编
let size := extcodesize(_addr)
// 分配输出字节数组 - 这也可以在没有汇编的情况下完成
// by using o_code = new bytes(size)
o_code := mload(0x40)
// 新的“内存端”包括填充
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// 存储长度在内存中
mstore(o_code, size)
// 实际上检索代码,这需要汇编
extcodecopy(_addr, add(o_code, 0x20), 0, size)
}
}
}

pragma solidity ^0.4.12;

library VectorSum {
// 此功能效率较低,因为优化程序当前无法删除阵列访问中的边界检查。
function sumSolidity(uint[] _data) returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i)
o_sum += _data[i];
}

// 我们知道我们只能在边界访问数组,所以我们可以避免检查。 0x20需要添加到数组,因为第一个插槽包含数组长度。
function sumAsm(uint[] _data) returns (uint o_sum) {
    for (uint i = 0; i < _data.length; ++i) {
        assembly {
            o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
        }
    }
}

// 与上述相同,但在内联汇编中完成整个代码。
function sumPureAsm(uint[] _data) returns (uint o_sum) {
    assembly {
       // 加载长度(前32个字节)
       let len := mload(_data)

       // 跳过长度字段。
       //
       // 保持临时变量,使其可以增加到位。
       //
       // 注意:增加_data将导致此程序集后不可用的_data变量
       let data := add(_data, 0x20)

       // 迭代,直到绑定不满足。
       for
           { let end := add(data, len) }
           lt(data, end)
           { data := add(data, 0x20) }
       {
           o_sum := add(o_sum, mload(data))
       }
    }
}

}














































































































以上是关于内联汇编的使用的主要内容,如果未能解决你的问题,请参考以下文章

实用技能分享,充分利用内联函数,内联汇编,内部函数和嵌入式汇编提升代码执行效率和便捷性(2021-12-17)

ARM嵌入式开发中的GCC内联汇编__asm__

使用 gcc 编译内联汇编时出错,“shl”

GCC 内联汇编到 IAR 内联汇编

内联汇编代码和存储 128 位结果

带有 CUDA 内联汇编的 LLVM