mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-10-20 18:56:17 +00:00
Fixes a ICE wth folded guard in inlined for-loop in NY. Closes #560 --------- Signed-off-by: xermicus <bigcyrill@hotmail.com>
174 lines
6.9 KiB
Plaintext
174 lines
6.9 KiB
Plaintext
/// ICE PoC (paritytech/revive#560): the one-armed `if` lowering in `to_llvm`
|
|
/// built the conditional branch first and materialized the fall-through
|
|
/// `inputs` afterwards, with the builder still positioned at the end of the
|
|
/// now-terminated entry block. That is invisible while every input is already
|
|
/// word-typed (no instruction is emitted), but a narrow input — here the raw
|
|
/// `gt` comparison result flowing out of the inlined `fun_h0` — needs a `zext`
|
|
/// to reach the join phi, and that `zext` landed after the terminator:
|
|
/// "Basic Block in function 'fun_run_runtime' does not have terminator!".
|
|
///
|
|
/// The shape needs all of:
|
|
/// - `fun_h0`'s `if 0x01 { ... leave }`: the constant-true guard folds, so
|
|
/// the `leave`-carried return value copy-propagates to the bare `gt`
|
|
/// (an i1 value that is neither a constant nor a word-typed if-output);
|
|
/// - `fun_h1(acc, acc)`: equal arguments fold `iszero(lt(a, b))` to
|
|
/// constant true, collapsing the leave-`if` so the `leave`-elimination
|
|
/// guard around the remainder keeps the raw `gt` as its fall-through
|
|
/// input with a constant-false condition;
|
|
/// - the rotated `for` loop (condition `1`, `break` in the body — solc's
|
|
/// unoptimized lowering) before that guard, which keeps the simplifier
|
|
/// from folding the constant-condition `if` away;
|
|
/// - `fun_run` staying above the never-inline size threshold (the verbose
|
|
/// copy chains are load-bearing): fully inlined into the entry block,
|
|
/// the guard folds and the shape disappears.
|
|
///
|
|
/// This is solc's `--no-optimize-yul` output for issue #560's contract with
|
|
/// the selector dispatch and revert-helper indirections trimmed. `run(s)`
|
|
/// never assigns its unnamed return value, so every call must return 0.
|
|
object "FoldedGuardZext" {
|
|
code {
|
|
let size := datasize("FoldedGuardZext_deployed")
|
|
codecopy(0, dataoffset("FoldedGuardZext_deployed"), size)
|
|
return(0, size)
|
|
}
|
|
object "FoldedGuardZext_deployed" {
|
|
code {
|
|
{
|
|
mstore(64, memoryguard(0x80))
|
|
external_fun_run()
|
|
revert(0, 0)
|
|
}
|
|
function allocate_unbounded() -> memPtr
|
|
{ memPtr := mload(64) }
|
|
function cleanup_uint256(value) -> cleaned
|
|
{ cleaned := value }
|
|
function validator_revert_uint256(value)
|
|
{
|
|
if iszero(eq(value, cleanup_uint256(value))) { revert(0, 0) }
|
|
}
|
|
function abi_decode_uint256(offset, end) -> value
|
|
{
|
|
value := calldataload(offset)
|
|
validator_revert_uint256(value)
|
|
}
|
|
function abi_decode_tuple_uint256(headStart, dataEnd) -> value0
|
|
{
|
|
if slt(sub(dataEnd, headStart), 32)
|
|
{
|
|
revert(0, 0)
|
|
}
|
|
let offset := 0
|
|
value0 := abi_decode_uint256(add(headStart, offset), dataEnd)
|
|
}
|
|
function abi_encode_uint256_to_uint256(value, pos)
|
|
{
|
|
mstore(pos, cleanup_uint256(value))
|
|
}
|
|
function abi_encode_uint256(headStart, value0) -> tail
|
|
{
|
|
tail := add(headStart, 32)
|
|
abi_encode_uint256_to_uint256(value0, add(headStart, 0))
|
|
}
|
|
function external_fun_run()
|
|
{
|
|
if callvalue()
|
|
{
|
|
revert(0, 0)
|
|
}
|
|
let param := abi_decode_tuple_uint256(4, calldatasize())
|
|
let ret := fun_run(param)
|
|
let memPos := allocate_unbounded()
|
|
let memEnd := abi_encode_uint256(memPos, ret)
|
|
return(memPos, sub(memEnd, memPos))
|
|
}
|
|
function zero_value_for_split_uint256() -> ret
|
|
{ ret := 0 }
|
|
function fun_run(var_s) -> var
|
|
{
|
|
let zero_uint256 := zero_value_for_split_uint256()
|
|
var := zero_uint256
|
|
let _1 := var_s
|
|
let expr := _1
|
|
let var_acc := expr
|
|
let _2 := var_acc
|
|
let expr_1 := _2
|
|
let _3 := var_acc
|
|
let expr_2 := _3
|
|
let expr_3 := fun_h0(expr_1, expr_2)
|
|
var_acc := expr_3
|
|
let _4 := var_acc
|
|
let expr_4 := _4
|
|
let _5 := var_acc
|
|
let expr_5 := _5
|
|
let expr_6 := fun_h1(expr_4, expr_5)
|
|
var_acc := expr_6
|
|
}
|
|
function fun_h0(var_a, var_b) -> var_r
|
|
{
|
|
let zero_t_uint256 := zero_value_for_split_uint256()
|
|
var_r := zero_t_uint256
|
|
let expr := 0x01
|
|
if expr
|
|
{
|
|
var_r := gt(var_r, var_b)
|
|
let _1 := var_r
|
|
let expr_1 := _1
|
|
var_r := expr_1
|
|
leave
|
|
}
|
|
}
|
|
function cleanup_t_rational_by(value) -> cleaned
|
|
{ cleaned := value }
|
|
function identity(value) -> ret
|
|
{ ret := value }
|
|
function convert_t_rational_by_to_t_uint256(value) -> converted
|
|
{
|
|
converted := cleanup_uint256(identity(cleanup_t_rational_by(value)))
|
|
}
|
|
function increment_wrapping_uint256(value) -> ret
|
|
{
|
|
ret := cleanup_uint256(add(value, 1))
|
|
}
|
|
function cleanup_rational_by(value) -> cleaned
|
|
{ cleaned := value }
|
|
function convert_rational_by_to_uint256(value) -> converted
|
|
{
|
|
converted := cleanup_uint256(identity(cleanup_rational_by(value)))
|
|
}
|
|
function fun_h1(var_a, var_b) -> var_r
|
|
{
|
|
let zero_uint256 := zero_value_for_split_uint256()
|
|
var_r := zero_uint256
|
|
let expr := 0x00
|
|
let var_i := convert_t_rational_by_to_t_uint256(expr)
|
|
for { }
|
|
1
|
|
{
|
|
let _1 := var_i
|
|
let _2 := increment_wrapping_uint256(_1)
|
|
var_i := _2
|
|
}
|
|
{
|
|
let _3 := var_i
|
|
let expr_1 := _3
|
|
let expr_2 := 0x01
|
|
let expr_3 := lt(cleanup_uint256(expr_1), convert_rational_by_to_uint256(expr_2))
|
|
if iszero(expr_3) { break }
|
|
}
|
|
let _4 := var_a
|
|
let expr_4 := _4
|
|
let _5 := var_b
|
|
let expr_5 := _5
|
|
let expr_6 := iszero(lt(cleanup_uint256(expr_4), cleanup_uint256(expr_5)))
|
|
if expr_6
|
|
{
|
|
let _6 := var_a
|
|
let expr_7 := _6
|
|
var_r := expr_7
|
|
leave
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|