国产gaysexchina男同gay,japanrcep老熟妇乱子伦视频,吃奶呻吟打开双腿做受动态图,成人色网站,国产av一区二区三区最新精品

基于 Solidity IR 的編碼更改

2022-05-24 11:17 更新

Solidity可以通過兩種不同的方式生成EVM字節(jié)碼:直接從Solidity到EVM操作碼(“舊codegen”),或者通過Yul(“new codegen”或“IR-based codegen”)中的中間表示(“IR”)。

引入基于 IR 的代碼生成器,不僅使代碼生成更加透明和可審計,而且還實現(xiàn)了跨功能的更強大的優(yōu)化傳遞。

您可以使用標準 json 中的選項在命令行上啟用它,我們鼓勵每個人嘗試一下!--via-ir{"viaIR": true}

出于幾個原因,舊的和基于IR的代碼生成器之間存在微小的語義差異,主要是在我們不希望人們依賴這種行為的領(lǐng)域。本節(jié)重點介紹舊的和基于 IR 的編碼機之間的主要區(qū)別。

僅語義更改

本節(jié)列出了僅語義的更改,因此可能會在現(xiàn)有代碼中隱藏新的和不同的行為。

  • 在繼承的情況下,狀態(tài)變量初始化的順序已更改。

    順序曾經(jīng)是:

    • 所有狀態(tài)變量在開始時都為零初始化。

    • 評估從最派生到大多數(shù)基本協(xié)定的基本構(gòu)造函數(shù)參數(shù)。

    • 初始化整個繼承層次結(jié)構(gòu)中的所有狀態(tài)變量,從最基本到最派生。

    • 對線性化層次結(jié)構(gòu)中從最基本到最派生的所有協(xié)定運行構(gòu)造函數(shù)(如果存在)。

    新訂單:

    • 所有狀態(tài)變量在開始時都為零初始化。

    • 評估從最派生到大多數(shù)基本協(xié)定的基本構(gòu)造函數(shù)參數(shù)。

    • 對于線性化層次結(jié)構(gòu)中從最基本到最派生的每個合約:

      1. 初始化狀態(tài)變量。

      2. 運行構(gòu)造函數(shù)(如果存在)。

    這會導(dǎo)致合約的差異,其中狀態(tài)變量的初始值依賴于另一個合約中構(gòu)造函數(shù)的結(jié)果:

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.7.1;
    
    contract A {
        uint x;
        constructor() {
            x = 42;
        }
        function f() public view returns(uint256) {
            return x;
        }
    }
    contract B is A {
        uint public y = f();
    }
    

    以前,將設(shè)置為 0。這是因為我們將首先初始化狀態(tài)變量:首先,設(shè)置為0,并且在初始化時,將返回0,導(dǎo)致也為0。使用新規(guī)則,將設(shè)置為42。我們首先初始化為 0,然后調(diào)用 A 的構(gòu)造函數(shù),該構(gòu)造函數(shù)設(shè)置為 42。最后,在初始化時,返回 42,導(dǎo)致為 42。yxyf()yyxxyf()y

  • 刪除存儲結(jié)構(gòu)時,包含結(jié)構(gòu)成員的每個存儲槽都將完全設(shè)置為零。以前,填充空間保持不變。因此,如果結(jié)構(gòu)中的填充空間用于存儲數(shù)據(jù)(例如,在合約升級的上下文中),您必須知道現(xiàn)在也會清除添加的成員(雖然過去不會清除它)。delete

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.7.1;
    
    contract C {
        struct S {
            uint64 y;
            uint64 z;
        }
        S s;
        function f() public {
            // ...
            delete s;
            // s occupies only first 16 bytes of the 32 bytes slot
            // delete will write zero to the full slot
        }
    }
    

    對于隱式刪除,我們具有相同的行為,例如,當結(jié)構(gòu)數(shù)組被縮短時。

  • 函數(shù)修飾符的實現(xiàn)方式與函數(shù)參數(shù)和返回變量略有不同。如果在修飾符中多次計算占位符,這尤其有效。在舊的代碼生成器中,每個函數(shù)參數(shù)和返回變量在堆棧上都有一個固定的槽。如果函數(shù)由于多次使用或在循環(huán)中使用而多次運行,則在下次執(zhí)行函數(shù)時,對函數(shù)參數(shù)或返回變量值的更改可見。新的代碼生成器使用實際函數(shù)實現(xiàn)修飾符,并傳遞函數(shù)參數(shù)。這意味著對函數(shù)體的多次計算將獲得相同的參數(shù)值,并且對返回變量的影響是,對于每次執(zhí)行,它們都會重置為其默認(零)值。_;_;

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.7.0;
    contract C {
        function f(uint a) public pure mod() returns (uint r) {
            r = a++;
        }
        modifier mod() { _; _; }
    }
    

    如果在舊代碼生成器中執(zhí)行,它將返回 ,而在使用新代碼生成器時將返回。f(0)21

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.7.1 <0.9.0;
    
    contract C {
        bool active = true;
        modifier mod()
        {
            _;
            active = false;
            _;
        }
        function foo() external mod() returns (uint ret)
        {
            if (active)
                ret = 1; // Same as ``return 1``
        }
    }
    

    該函數(shù)返回以下值:C.foo()

    • 舊代碼生成器:因為返回變量在第一次求值之前僅初始化為一次,然后被 .它不會在第二次評估中再次初始化,也不會顯式分配它(由于 ),因此它保留其第一個值。10_;return 1;_;foo()active == false

    • 新的代碼生成器:因為所有參數(shù)(包括返回參數(shù))將在每次評估之前重新初始化。0_;

  • 將數(shù)組從內(nèi)存復(fù)制到存儲以不同的方式實現(xiàn)。舊的代碼生成器總是復(fù)制完整的單詞,而新的代碼生成器在其結(jié)束后剪切字節(jié)數(shù)組。舊行為可能導(dǎo)致在陣列結(jié)束后(但仍在同一存儲插槽中)復(fù)制臟數(shù)據(jù)。這會導(dǎo)致某些合約的差異,例如:bytes

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.8.1;
    
    contract C {
        bytes x;
        function f() public returns (uint r) {
            bytes memory m = "tmp";
            assembly {
                mstore(m, 8)
                mstore(add(m, 32), "deadbeef15dead")
            }
            x = m;
            assembly {
                r := sload(x.slot)
            }
        }
    }
    

    以前會返回(它具有正確的長度,并且正確的前8個元素,但隨后它包含通過程序集設(shè)置的臟數(shù)據(jù))?,F(xiàn)在它正在返回(它具有正確的長度和正確的元素,但不包含多余的數(shù)據(jù))。f()0x64656164626565663135646561640000000000000000000000000000000000100x6465616462656566000000000000000000000000000000000000000000000010

  • 對于舊代碼生成器,表達式的計算順序是未指定的。對于新的代碼生成器,我們嘗試按源代碼順序(從左到右)進行評估,但不保證。這可能導(dǎo)致語義差異。

    例如:

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.8.1;
    contract C {
        function preincr_u8(uint8 a) public pure returns (uint8) {
            return ++a + a;
        }
    }
    

    該函數(shù)返回以下值:preincr_u8(1)

    • 舊代碼生成器:3(),但返回值通常未指定1 + 2

    • 新代碼生成器:4()但不保證返回值2 + 2

    另一方面,函數(shù)參數(shù)表達式由兩個代碼生成器以相同的順序計算,但全局函數(shù)和 .例如:addmodmulmod

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.8.1;
    contract C {
        function add(uint8 a, uint8 b) public pure returns (uint8) {
            return a + b;
        }
        function g(uint8 a, uint8 b) public pure returns (uint8) {
            return add(++a + ++b, a + b);
        }
    }
    

    該函數(shù)返回以下值:g(1, 2)

    • 舊代碼生成器:()但返回值通常未指定10add(2 + 3, 2 + 3)

    • 新代碼生成器:但不能保證返回值10

    全局函數(shù)的參數(shù),由舊代碼生成器從右到左計算,由新代碼生成器從左到右計算。例如:addmodmulmod

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.8.1;
    contract C {
        function f() public pure returns (uint256 aMod, uint256 mMod) {
            uint256 x = 3;
            // Old code gen: add/mulmod(5, 4, 3)
            // New code gen: add/mulmod(4, 5, 5)
            aMod = addmod(++x, ++x, x);
            mMod = mulmod(++x, ++x, x);
        }
    }
    

    該函數(shù)返回以下值:f()

    • 舊代碼生成器:和aMod = 0mMod = 2

    • 新的代碼生成器:和aMod = 4mMod = 0

  • 新的代碼生成器對可用內(nèi)存指針施加了 () 的硬限制。如果分配的值超過此限制,則會恢復(fù)。舊的代碼生成器沒有此限制。type(uint64).max0xffffffffffffffff

    例如:

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >0.8.0;
    contract C {
        function f() public {
            uint[] memory arr;
            // allocation size: 576460752303423481
            // assumes freeMemPtr points to 0x80 initially
            uint solYulMaxAllocationBeforeMemPtrOverflow = (type(uint64).max - 0x80 - 31) / 32;
            // freeMemPtr overflows UINT64_MAX
            arr = new uint[](solYulMaxAllocationBeforeMemPtrOverflow);
        }
    }
    

    函數(shù) f() 的行為如下:

    • 舊代碼生成器:在大內(nèi)存分配后將數(shù)組內(nèi)容清零時耗盡氣體

    • 新的代碼生成器:由于可用內(nèi)存指針溢出而恢復(fù)(不會耗盡氣體)

內(nèi)部

內(nèi)部函數(shù)指針

舊的代碼生成器使用代碼偏移量或標記作為內(nèi)部函數(shù)指針的值。這尤其復(fù)雜,因為這些偏移量在構(gòu)造時和部署后是不同的,并且值可以通過存儲跨越此邊界。因此,兩個偏移量在構(gòu)造時被編碼為相同的值(不同的字節(jié))。

在新的代碼生成器中,函數(shù)指針使用按順序分配的內(nèi)部 ID。由于無法通過跳轉(zhuǎn)進行調(diào)用,因此通過函數(shù)指針的調(diào)用始終必須使用內(nèi)部調(diào)度函數(shù),該函數(shù)使用語句來選擇正確的函數(shù)。switch

該 ID 是為未初始化的函數(shù)指針保留的,這些指針在調(diào)用時會導(dǎo)致調(diào)度函數(shù)中的死機。0

在舊的代碼生成器中,內(nèi)部函數(shù)指針使用一個特殊函數(shù)進行初始化,該函數(shù)總是會導(dǎo)致死機。這會導(dǎo)致在構(gòu)造時對存儲中的內(nèi)部函數(shù)指針進行存儲寫入。

清理

舊代碼生成器僅在操作之前執(zhí)行清理,其結(jié)果可能受臟位值的影響。新的代碼生成器在任何可能導(dǎo)致臟位的操作后執(zhí)行清理。希望優(yōu)化器足夠強大,可以消除冗余的清理操作。

例如:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.1;
contract C {
    function f(uint8 a) public pure returns (uint r1, uint r2)
    {
        a = ~a;
        assembly {
            r1 := a
        }
        r2 = a;
    }
}

該函數(shù)返回以下值:f(1)

  • 舊代碼生成器:(,fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000000000000000000000000000000000000000000000000000000000fe)

  • 新的代碼生成器:(,00000000000000000000000000000000000000000000000000000000000000fe00000000000000000000000000000000000000000000000000000000000000fe)

請注意,與新代碼生成器不同,舊代碼生成器在位不賦值 () 之后不執(zhí)行清理。這會導(dǎo)致分配不同的值(在內(nèi)聯(lián)程序集塊內(nèi))以在舊代碼生成器和新代碼生成器之間返回值。但是,在將 的新值 分配給 之前,這兩個代碼生成器都會執(zhí)行清理。a = ~ar1ar2

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號