diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index c2cf1eef3..b2f63933f 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -71,51 +71,51 @@ std::vector> simplificationRuleListPart1( { using Word = typename Pattern::Word; using Builtins = typename Pattern::Builtins; - return std::vector> { + return std::vector>{ // arithmetic on constants - {Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }, false}, - {Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }, false}, - {Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }, false}, - {Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, - {Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, - {Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, - {Builtins::NOT(A), [=]{ return ~A.d(); }, false}, - {Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, - {Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, - {Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, - {Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, - {Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, - {Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, - {Builtins::AND(A, B), [=]{ return A.d() & B.d(); }, false}, - {Builtins::OR(A, B), [=]{ return A.d() | B.d(); }, false}, - {Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }, false}, + {Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }}, + {Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }}, + {Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }}, + {Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }}, + {Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }}, + {Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }}, + {Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }}, + {Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }}, + {Builtins::NOT(A), [=]{ return ~A.d(); }}, + {Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }}, + {Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }}, + {Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }}, + {Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }}, + {Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }}, + {Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }}, + {Builtins::AND(A, B), [=]{ return A.d() & B.d(); }}, + {Builtins::OR(A, B), [=]{ return A.d() | B.d(); }}, + {Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }}, {Builtins::BYTE(A, B), [=]{ return A.d() >= Pattern::WordSize / 8 ? 0 : (B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff; - }, false}, - {Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, - {Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, + }}, + {Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }}, + {Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }}, {Builtins::SIGNEXTEND(A, B), [=]() -> Word { if (A.d() >= Pattern::WordSize / 8 - 1) return B.d(); unsigned testBit = unsigned(A.d()) * 8 + 7; Word mask = (Word(1) << testBit) - 1; return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask; - }, false}, + }}, {Builtins::SHL(A, B), [=]{ if (A.d() >= Pattern::WordSize) return Word(0); return shlWorkaround(B.d(), unsigned(A.d())); - }, false}, + }}, {Builtins::SHR(A, B), [=]{ if (A.d() >= Pattern::WordSize) return Word(0); return B.d() >> unsigned(A.d()); - }, false} + }} }; } @@ -133,48 +133,48 @@ std::vector> simplificationRuleListPart2( using Builtins = typename Pattern::Builtins; return std::vector> { // invariants involving known constants - {Builtins::ADD(X, 0), [=]{ return X; }, false}, - {Builtins::ADD(0, X), [=]{ return X; }, false}, - {Builtins::SUB(X, 0), [=]{ return X; }, false}, - {Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }, false}, - {Builtins::MUL(X, 0), [=]{ return Word(0); }, true}, - {Builtins::MUL(0, X), [=]{ return Word(0); }, true}, - {Builtins::MUL(X, 1), [=]{ return X; }, false}, - {Builtins::MUL(1, X), [=]{ return X; }, false}, - {Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, - {Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, - {Builtins::DIV(X, 0), [=]{ return Word(0); }, true}, - {Builtins::DIV(0, X), [=]{ return Word(0); }, true}, - {Builtins::DIV(X, 1), [=]{ return X; }, false}, - {Builtins::SDIV(X, 0), [=]{ return Word(0); }, true}, - {Builtins::SDIV(0, X), [=]{ return Word(0); }, true}, - {Builtins::SDIV(X, 1), [=]{ return X; }, false}, - {Builtins::AND(X, ~Word(0)), [=]{ return X; }, false}, - {Builtins::AND(~Word(0), X), [=]{ return X; }, false}, - {Builtins::AND(X, 0), [=]{ return Word(0); }, true}, - {Builtins::AND(0, X), [=]{ return Word(0); }, true}, - {Builtins::OR(X, 0), [=]{ return X; }, false}, - {Builtins::OR(0, X), [=]{ return X; }, false}, - {Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }, true}, - {Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }, true}, - {Builtins::XOR(X, 0), [=]{ return X; }, false}, - {Builtins::XOR(0, X), [=]{ return X; }, false}, - {Builtins::MOD(X, 0), [=]{ return Word(0); }, true}, - {Builtins::MOD(0, X), [=]{ return Word(0); }, true}, - {Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, - {Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, - {Builtins::SHL(0, X), [=]{ return X; }, false}, - {Builtins::SHR(0, X), [=]{ return X; }, false}, - {Builtins::SHL(X, 0), [=]{ return Word(0); }, true}, - {Builtins::SHR(X, 0), [=]{ return Word(0); }, true}, - {Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, - {Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, - {Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }, true}, - {Builtins::LT(~Word(0), X), [=]{ return Word(0); }, true}, - {Builtins::GT(0, X), [=]{ return Word(0); }, true}, - {Builtins::LT(X, 0), [=]{ return Word(0); }, true}, - {Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }, false}, - {Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }, false} + {Builtins::ADD(X, 0), [=]{ return X; }}, + {Builtins::ADD(0, X), [=]{ return X; }}, + {Builtins::SUB(X, 0), [=]{ return X; }}, + {Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }}, + {Builtins::MUL(X, 0), [=]{ return Word(0); }}, + {Builtins::MUL(0, X), [=]{ return Word(0); }}, + {Builtins::MUL(X, 1), [=]{ return X; }}, + {Builtins::MUL(1, X), [=]{ return X; }}, + {Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }}, + {Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }}, + {Builtins::DIV(X, 0), [=]{ return Word(0); }}, + {Builtins::DIV(0, X), [=]{ return Word(0); }}, + {Builtins::DIV(X, 1), [=]{ return X; }}, + {Builtins::SDIV(X, 0), [=]{ return Word(0); }}, + {Builtins::SDIV(0, X), [=]{ return Word(0); }}, + {Builtins::SDIV(X, 1), [=]{ return X; }}, + {Builtins::AND(X, ~Word(0)), [=]{ return X; }}, + {Builtins::AND(~Word(0), X), [=]{ return X; }}, + {Builtins::AND(X, 0), [=]{ return Word(0); }}, + {Builtins::AND(0, X), [=]{ return Word(0); }}, + {Builtins::OR(X, 0), [=]{ return X; }}, + {Builtins::OR(0, X), [=]{ return X; }}, + {Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }}, + {Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }}, + {Builtins::XOR(X, 0), [=]{ return X; }}, + {Builtins::XOR(0, X), [=]{ return X; }}, + {Builtins::MOD(X, 0), [=]{ return Word(0); }}, + {Builtins::MOD(0, X), [=]{ return Word(0); }}, + {Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); },}, + {Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); },}, + {Builtins::SHL(0, X), [=]{ return X; }}, + {Builtins::SHR(0, X), [=]{ return X; }}, + {Builtins::SHL(X, 0), [=]{ return Word(0); }}, + {Builtins::SHR(X, 0), [=]{ return Word(0); }}, + {Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }}, + {Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }}, + {Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }}, + {Builtins::LT(~Word(0), X), [=]{ return Word(0); }}, + {Builtins::GT(0, X), [=]{ return Word(0); }}, + {Builtins::LT(X, 0), [=]{ return Word(0); }}, + {Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }}, + {Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }}, }; } @@ -191,16 +191,16 @@ std::vector> simplificationRuleListPart3( using Builtins = typename Pattern::Builtins; return std::vector> { // operations involving an expression and itself - {Builtins::AND(X, X), [=]{ return X; }, true}, - {Builtins::OR(X, X), [=]{ return X; }, true}, - {Builtins::XOR(X, X), [=]{ return Word(0); }, true}, - {Builtins::SUB(X, X), [=]{ return Word(0); }, true}, - {Builtins::EQ(X, X), [=]{ return Word(1); }, true}, - {Builtins::LT(X, X), [=]{ return Word(0); }, true}, - {Builtins::SLT(X, X), [=]{ return Word(0); }, true}, - {Builtins::GT(X, X), [=]{ return Word(0); }, true}, - {Builtins::SGT(X, X), [=]{ return Word(0); }, true}, - {Builtins::MOD(X, X), [=]{ return Word(0); }, true} + {Builtins::AND(X, X), [=]{ return X; }}, + {Builtins::OR(X, X), [=]{ return X; }}, + {Builtins::XOR(X, X), [=]{ return Word(0); }}, + {Builtins::SUB(X, X), [=]{ return Word(0); }}, + {Builtins::EQ(X, X), [=]{ return Word(1); }}, + {Builtins::LT(X, X), [=]{ return Word(0); }}, + {Builtins::SLT(X, X), [=]{ return Word(0); }}, + {Builtins::GT(X, X), [=]{ return Word(0); }}, + {Builtins::SGT(X, X), [=]{ return Word(0); }}, + {Builtins::MOD(X, X), [=]{ return Word(0); }} }; } @@ -217,23 +217,23 @@ std::vector> simplificationRuleListPart4( using Builtins = typename Pattern::Builtins; return std::vector> { // logical instruction combinations - {Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }, false}, - {Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }, true}, - {Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }, true}, - {Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }, true}, - {Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }, true}, - {Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }, true}, - {Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }, true}, - {Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }, true}, - {Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }, true}, - {Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }, true}, - {Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }, true}, - {Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }, true}, - {Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }, true}, - {Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }, true}, - {Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }, true}, - {Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }, true}, - {Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }, true}, + {Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }}, + {Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }}, + {Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }}, + {Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }}, + {Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }}, + {Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }}, + {Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }}, + {Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }}, + {Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }}, + {Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }}, + {Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }}, + {Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }}, + {Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }}, + {Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }}, + {Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }}, + {Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }}, + {Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }}, }; } @@ -249,14 +249,14 @@ std::vector> simplificationRuleListPart4_5( using Builtins = typename Pattern::Builtins; return std::vector>{ // idempotent operations - {Builtins::AND(Builtins::AND(X, Y), Y), [=]{ return Builtins::AND(X, Y); }, true}, - {Builtins::AND(Y, Builtins::AND(X, Y)), [=]{ return Builtins::AND(X, Y); }, true}, - {Builtins::AND(Builtins::AND(Y, X), Y), [=]{ return Builtins::AND(Y, X); }, true}, - {Builtins::AND(Y, Builtins::AND(Y, X)), [=]{ return Builtins::AND(Y, X); }, true}, - {Builtins::OR(Builtins::OR(X, Y), Y), [=]{ return Builtins::OR(X, Y); }, true}, - {Builtins::OR(Y, Builtins::OR(X, Y)), [=]{ return Builtins::OR(X, Y); }, true}, - {Builtins::OR(Builtins::OR(Y, X), Y), [=]{ return Builtins::OR(Y, X); }, true}, - {Builtins::OR(Y, Builtins::OR(Y, X)), [=]{ return Builtins::OR(Y, X); }, true}, + {Builtins::AND(Builtins::AND(X, Y), Y), [=]{ return Builtins::AND(X, Y); }}, + {Builtins::AND(Y, Builtins::AND(X, Y)), [=]{ return Builtins::AND(X, Y); }}, + {Builtins::AND(Builtins::AND(Y, X), Y), [=]{ return Builtins::AND(Y, X); }}, + {Builtins::AND(Y, Builtins::AND(Y, X)), [=]{ return Builtins::AND(Y, X); }}, + {Builtins::OR(Builtins::OR(X, Y), Y), [=]{ return Builtins::OR(X, Y); }}, + {Builtins::OR(Y, Builtins::OR(X, Y)), [=]{ return Builtins::OR(X, Y); }}, + {Builtins::OR(Builtins::OR(Y, X), Y), [=]{ return Builtins::OR(Y, X); }}, + {Builtins::OR(Y, Builtins::OR(Y, X)), [=]{ return Builtins::OR(Y, X); }}, }; } @@ -280,8 +280,7 @@ std::vector> simplificationRuleListPart5( Word value = Word(1) << i; rules.push_back({ Builtins::MOD(X, value), - [=]() -> Pattern { return Builtins::AND(X, value - 1); }, - false + [=]() -> Pattern { return Builtins::AND(X, value - 1); } }); } @@ -289,7 +288,6 @@ std::vector> simplificationRuleListPart5( rules.push_back({ Builtins::SHL(A, X), [=]() -> Pattern { return Word(0); }, - true, [=]() { return A.d() >= Pattern::WordSize; } }); @@ -297,7 +295,6 @@ std::vector> simplificationRuleListPart5( rules.push_back({ Builtins::SHR(A, X), [=]() -> Pattern { return Word(0); }, - true, [=]() { return A.d() >= Pattern::WordSize; } }); @@ -305,7 +302,6 @@ std::vector> simplificationRuleListPart5( rules.push_back({ Builtins::BYTE(A, X), [=]() -> Pattern { return Word(0); }, - true, [=]() { return A.d() >= Pattern::WordSize / 8; } }); @@ -320,13 +316,11 @@ std::vector> simplificationRuleListPart5( Word const mask = (Word(1) << 160) - 1; rules.push_back({ Builtins::AND(Pattern{instr}, mask), - [=]() -> Pattern { return {instr}; }, - false + [=]() -> Pattern { return {instr}; } }); rules.push_back({ Builtins::AND(mask, Pattern{instr}), - [=]() -> Pattern { return {instr}; }, - false + [=]() -> Pattern { return {instr}; } }); } @@ -357,21 +351,18 @@ std::vector> simplificationRuleListPart6( typename Builtins::PatternGeneratorInstance op{instr}; rules.push_back({ Builtins::ISZERO(Builtins::ISZERO(op(X, Y))), - [=]() -> Pattern { return op(X, Y); }, - false + [=]() -> Pattern { return op(X, Y); } }); } rules.push_back({ Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))), - [=]() -> Pattern { return Builtins::ISZERO(X); }, - false + [=]() -> Pattern { return Builtins::ISZERO(X); } }); rules.push_back({ Builtins::ISZERO(Builtins::XOR(X, Y)), - [=]() -> Pattern { return Builtins::EQ(X, Y); }, - false + [=]() -> Pattern { return Builtins::EQ(X, Y); } }); return rules; @@ -409,23 +400,19 @@ std::vector> simplificationRuleListPart7( rules += std::vector>{{ // (X+A)+B -> X+(A+B) op(opXA, B), - [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, - false + [=]() -> Pattern { return op(X, fun(A.d(), B.d())); } }, { // (X+A)+Y -> (X+Y)+A op(opXA, Y), - [=]() -> Pattern { return op(op(X, Y), A); }, - false + [=]() -> Pattern { return op(op(X, Y), A); } }, { // B+(X+A) -> X+(A+B) op(B, opXA), - [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, - false + [=]() -> Pattern { return op(X, fun(A.d(), B.d())); } }, { // Y+(X+A) -> (Y+X)+A op(Y, opXA), - [=]() -> Pattern { return op(op(Y, X), A); }, - false + [=]() -> Pattern { return op(op(Y, X), A); } }}; } } @@ -440,8 +427,7 @@ std::vector> simplificationRuleListPart7( return Builtins::AND(X, Word(0)); else return Builtins::SHL(Word(sum), X); - }, - false + } }); // Combine two SHR by constant @@ -454,8 +440,7 @@ std::vector> simplificationRuleListPart7( return Builtins::AND(X, Word(0)); else return Builtins::SHR(Word(sum), X); - }, - false + } }); // Combine SHL-SHR by constant @@ -472,7 +457,6 @@ std::vector> simplificationRuleListPart7( else return Builtins::AND(X, mask); }, - false, [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } }); @@ -490,7 +474,6 @@ std::vector> simplificationRuleListPart7( else return Builtins::AND(X, mask); }, - false, [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } }); @@ -509,14 +492,12 @@ std::vector> simplificationRuleListPart7( // SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) shiftOp(B, Builtins::AND(X, A)), replacement, - false, [=] { return B.d() < Pattern::WordSize; } }); rules.push_back({ // SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) shiftOp(B, Builtins::AND(A, X)), replacement, - false, [=] { return B.d() < Pattern::WordSize; } }); } @@ -526,17 +507,14 @@ std::vector> simplificationRuleListPart7( Builtins::MUL(X, Builtins::SHL(Y, Word(1))), [=]() -> Pattern { return Builtins::SHL(Y, X); - }, - // Actually only changes the order, does not remove. - true + } }); rules.push_back({ // MUL(SHL(X, 1), Y) -> SHL(X, Y) Builtins::MUL(Builtins::SHL(X, Word(1)), Y), [=]() -> Pattern { return Builtins::SHL(X, Y); - }, - false + } }); rules.push_back({ @@ -544,9 +522,7 @@ std::vector> simplificationRuleListPart7( Builtins::DIV(X, Builtins::SHL(Y, Word(1))), [=]() -> Pattern { return Builtins::SHR(Y, X); - }, - // Actually only changes the order, does not remove. - true + } }); std::function feasibilityFunction = [=]() { @@ -560,7 +536,6 @@ std::vector> simplificationRuleListPart7( // AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B) Builtins::AND(A, Builtins::SHR(B, X)), [=]() -> Pattern { return Builtins::SHR(B, X); }, - false, feasibilityFunction }); @@ -568,28 +543,24 @@ std::vector> simplificationRuleListPart7( // AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B) Builtins::AND(Builtins::SHR(B, X), A), [=]() -> Pattern { return Builtins::SHR(B, X); }, - false, feasibilityFunction }); rules.push_back({ Builtins::BYTE(A, Builtins::SHL(B, X)), [=]() -> Pattern { return Builtins::BYTE(A.d() + B.d() / 8, X); }, - false, [=] { return B.d() % 8 == 0 && A.d() <= 32 && B.d() <= 256; } }); rules.push_back({ Builtins::BYTE(A, Builtins::SHR(B, X)), [=]() -> Pattern { return Word(0); }, - true, [=] { return A.d() < B.d() / 8; } }); rules.push_back({ Builtins::BYTE(A, Builtins::SHR(B, X)), [=]() -> Pattern { return Builtins::BYTE(A.d() - B.d() / 8, X); }, - false, [=] { return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize && A.d() >= B.d() / 8; } @@ -615,28 +586,23 @@ std::vector> simplificationRuleListPart8( { // X - A -> X + (-A) Builtins::SUB(X, A), - [=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); }, - false + [=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); } }, { // (X + A) - Y -> (X - Y) + A Builtins::SUB(Builtins::ADD(X, A), Y), - [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, - false + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); } }, { // (A + X) - Y -> (X - Y) + A Builtins::SUB(Builtins::ADD(A, X), Y), - [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, - false + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); } }, { // X - (Y + A) -> (X - Y) + (-A) Builtins::SUB(X, Builtins::ADD(Y, A)), - [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, - false + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); } }, { // X - (A + Y) -> (X - Y) + (-A) Builtins::SUB(X, Builtins::ADD(A, Y)), - [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, - false + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); } } }; return rules; @@ -662,24 +628,20 @@ std::vector> simplificationRuleListPart9( // CREATE rules.push_back({ Builtins::AND(Builtins::CREATE(W, X, Y), mask), - [=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, - false + [=]() -> Pattern { return Builtins::CREATE(W, X, Y); } }); rules.push_back({ Builtins::AND(mask, Builtins::CREATE(W, X, Y)), - [=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, - false + [=]() -> Pattern { return Builtins::CREATE(W, X, Y); } }); // CREATE2 rules.push_back({ Builtins::AND(Builtins::CREATE2(W, X, Y, Z), mask), - [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, - false + [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); } }); rules.push_back({ Builtins::AND(mask, Builtins::CREATE2(W, X, Y, Z)), - [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, - false + [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); } }); return rules; @@ -703,7 +665,7 @@ std::vector> evmRuleList( if (_evmVersion.hasSelfBalance()) rules.push_back({ Builtins::BALANCE(Instruction::ADDRESS), - []() -> Pattern { return Instruction::SELFBALANCE; }, false + []() -> Pattern { return Instruction::SELFBALANCE; } }); return rules; diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index 6ce9a9e7c..b6a704a56 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -30,9 +30,8 @@ namespace solidity::evmasm /** * Rule that contains a pattern, an action that can be applied - * after the pattern has matched and a bool that indicates - * whether the action would remove something from the expression - * than is not a constant literal. + * after the pattern has matched and optional condition to check if the + * action should be applied. */ template struct SimplificationRule @@ -40,18 +39,15 @@ struct SimplificationRule SimplificationRule( Pattern _pattern, std::function _action, - bool _removesNonConstants, std::function _feasible = {} ): pattern(std::move(_pattern)), action(std::move(_action)), - removesNonConstants(_removesNonConstants), feasible(std::move(_feasible)) {} Pattern pattern; std::function action; - bool removesNonConstants; std::function feasible; }; diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index ec3a6eb78..623bc12b4 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -22,12 +22,9 @@ #include #include -#include #include #include -#include - using namespace std; using namespace solidity; using namespace solidity::yul; @@ -40,17 +37,7 @@ void ExpressionSimplifier::run(OptimiserStepContext& _context, Block& _ast) void ExpressionSimplifier::visit(Expression& _expression) { ASTModifier::visit(_expression); - while (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value)) - { - // Do not apply the rule if it removes non-constant parts of the expression. - // TODO: The check could actually be less strict than "movable". - // We only require "Does not cause side-effects". - // Note: References to variables that are only assigned once are always movable, - // so if the value of the variable is not movable, the expression that references - // the variable still is. - if (match->removesNonConstants && !SideEffectsCollector(m_dialect, _expression).movable()) - return; + while (auto const* match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value)) _expression = match->action().toExpression(locationOf(_expression)); - } } diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index b8703e2a2..f5e1781cf 100644 --- a/libyul/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -33,7 +33,8 @@ struct OptimiserStepContext; /** * Applies simplification rules to all expressions. * The component will work best if the code is in SSA form, but - * this is not required for correctness. + * this is not required for correctness. Using CommonSubexpressionEliminator + * also helps this component track equivalent sub-expressions. * * It tracks the current values of variables using the DataFlowAnalyzer * and takes them into account for replacements. diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 2fe5de3e5..cb797975b 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -171,12 +171,22 @@ bool Pattern::matches( return false; assertThrow(m_arguments.size() == instrAndArgs->second->size(), OptimizerException, ""); for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i].matches(instrAndArgs->second->at(i), _dialect, _ssaValues)) + { + Expression const& arg = instrAndArgs->second->at(i); + // If this is a direct function call instead of a variable or literal, + // we reject the match because side-effects could prevent us from + // arbitrarily modifying the code. + if ( + holds_alternative(arg) || + !m_arguments[i].matches(arg, _dialect, _ssaValues) + ) return false; + } } else { assertThrow(m_arguments.empty(), OptimizerException, "\"Any\" should not have arguments."); + assertThrow(!holds_alternative(*expr), OptimizerException, "\"Any\" at top-level."); } if (m_matchGroup) @@ -197,9 +207,14 @@ bool Pattern::matches( assertThrow(m_kind == PatternKind::Any, OptimizerException, "Match group repetition for non-any."); Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; assertThrow(firstMatch, OptimizerException, "Match set but to null."); - return - SyntacticallyEqual{}(*firstMatch, _expr) && - SideEffectsCollector(_dialect, _expr).movable(); + assertThrow( + !holds_alternative(_expr) && + !holds_alternative(*firstMatch), + OptimizerException, + "Group matches have to be literals or variables." + ); + + return SyntacticallyEqual{}(*firstMatch, _expr); } else if (m_kind == PatternKind::Any) (*m_matchGroups)[m_matchGroup] = &_expr; diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 2f9f4c75e..18576384d 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -225,9 +225,14 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line else if (m_optimizerStep == "expressionSimplifier") { disambiguate(); + ExpressionSplitter::run(*m_context, *m_object->code); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); ExpressionSimplifier::run(*m_context, *m_object->code); ExpressionSimplifier::run(*m_context, *m_object->code); ExpressionSimplifier::run(*m_context, *m_object->code); + UnusedPruner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "fullSimplify") { diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul index 783319395..595b9c792 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul @@ -2,6 +2,7 @@ function f() -> x, z {} let c, d := f() let y := add(d, add(c, 7)) + sstore(y, 20) } // ---- // step: expressionSimplifier @@ -10,5 +11,5 @@ // function f() -> x, z // { } // let c, d := f() -// let y := add(add(d, c), 7) +// sstore(add(add(d, c), 7), 20) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul index d465600e3..7288ef56f 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul @@ -1,7 +1,11 @@ { + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. let x := calldataload(0) let a := and(0xff, shr(248, shl(248, shr(248, x)))) let b := shr(12, shl(8, and(x, 0xf0f0))) + sstore(a, b) } // ==== // EVMVersion: >byzantium @@ -10,6 +14,6 @@ // // { // let x := calldataload(0) -// let a := shr(248, x) -// let b := and(shr(4, x), 3855) +// let a := and(0xff, and(shr(248, x), 255)) +// sstore(a, shr(12, and(shl(8, x), 15790080))) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul index 6649a86b5..f57a5108b 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul @@ -1,4 +1,7 @@ { + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. let x := calldataload(0) let a := and(0xff, shr(248, shl(248, shr(248, and(x, 0xf))))) let b := shl(12, shr(4, and(x, 0xf0f0))) @@ -7,6 +10,13 @@ let e := shl(255, shr(4, and(0xf0f0, x))) let f := shl(12, shr(256, and(0xf0f0, x))) let g := shl(256, shr(4, and(0xf0f0, x))) + sstore(10, a) + sstore(11, b) + sstore(12, c) + sstore(13, d) + sstore(14, e) + sstore(15, f) + sstore(16, g) } // ==== // EVMVersion: >byzantium @@ -15,11 +25,24 @@ // // { // let x := calldataload(0) -// let a := 0 -// let b := and(shl(8, x), 15790080) -// let c := and(shl(8, x), 15790080) -// let d := 0 -// let e := and(shl(251, x), 0x8000000000000000000000000000000000000000000000000000000000000000) +// let _2 := 0xf +// let _5 := and(shr(248, x), 0) +// let _10 := 0xff +// let a := and(_5, 255) +// let _14 := and(shr(4, x), 3855) +// let _15 := 12 +// let b := shl(_15, _14) +// let _19 := and(shr(4, x), 3855) +// let c := shl(_15, _19) +// let d := shl(_15, and(shr(255, x), 0)) +// let e := shl(_10, _19) // let f := 0 // let g := 0 +// sstore(10, a) +// sstore(11, b) +// sstore(_15, c) +// sstore(13, d) +// sstore(14, e) +// sstore(_2, f) +// sstore(16, g) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul index 4211b6afe..14ff1b375 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul @@ -1,10 +1,19 @@ { + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. let x := calldataload(0) let a := shl(12, shr(4, x)) let b := shl(4, shr(12, x)) let c := shr(12, shl(4, x)) let d := shr(4, shl(12, x)) let e := shl(150, shr(2, shl(150, x))) + sstore(15, x) + sstore(16, a) + sstore(17, b) + sstore(18, c) + sstore(19, d) + sstore(20, e) } // ==== // EVMVersion: >byzantium @@ -17,5 +26,12 @@ // let b := and(shr(8, x), 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0) // let c := and(shr(8, x), 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) // let d := and(shl(8, x), 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00) -// let e := 0 +// let _14 := 150 +// let e := shl(_14, and(shl(148, x), 0x3ffffffffffffffffffffffffff0000000000000000000000000000000000000)) +// sstore(15, x) +// sstore(16, a) +// sstore(17, b) +// sstore(18, c) +// sstore(19, d) +// sstore(20, e) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul index 2f655efba..56d18fb7b 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul @@ -1,5 +1,8 @@ -{ let a := add(7, sub(mload(0), 7)) } +{ + let a := add(7, sub(mload(0), 7)) + mstore(20, a) +} // ---- // step: expressionSimplifier // -// { let a := mload(0) } +// { mstore(20, mload(0)) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul index 8bf04b987..ce2949993 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul @@ -1,5 +1,8 @@ -{ let a := add(1, mul(3, 4)) } +{ + let a := add(1, mul(3, 4)) + sstore(7, a) +} // ---- // step: expressionSimplifier // -// { let a := 13 } +// { sstore(7, 13) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul index bbda84653..8a5265992 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul @@ -1,6 +1,10 @@ { + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. let a := and(create2(0, 0, 0x20, 0), 0xffffffffffffffffffffffffffffffffffffffff) let b := and(0xffffffffffffffffffffffffffffffffffffffff, create2(0, 0, 0x20, 0)) + sstore(a, b) } // ==== // EVMVersion: >=constantinople @@ -8,6 +12,9 @@ // step: expressionSimplifier // // { -// let a := create2(0, 0, 0x20, 0) -// let b := create2(0, 0, 0x20, 0) +// let _1 := 0xffffffffffffffffffffffffffffffffffffffff +// let _2 := 0 +// let _3 := 0x20 +// let a := and(create2(_2, _2, _3, _2), _1) +// sstore(a, and(_1, create2(_2, _2, _3, _2))) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul index 44a8cabb0..8e77b8726 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul @@ -1,11 +1,20 @@ { - let a := and(create(0, 0, 0x20), 0xffffffffffffffffffffffffffffffffffffffff) + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. + let c := create(0, 0, 0x20) + let a := and(c, 0xffffffffffffffffffffffffffffffffffffffff) let b := and(0xffffffffffffffffffffffffffffffffffffffff, create(0, 0, 0x20)) + sstore(a, b) } // ---- // step: expressionSimplifier // // { -// let a := create(0, 0, 0x20) -// let b := create(0, 0, 0x20) +// let _1 := 0x20 +// let _2 := 0 +// let c := create(_2, _2, _1) +// let _4 := 0xffffffffffffffffffffffffffffffffffffffff +// let a := and(c, _4) +// sstore(a, and(_4, create(_2, _2, _1))) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/idempotency.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/idempotency.yul index 78d51db73..9698dfdff 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/idempotency.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/idempotency.yul @@ -3,6 +3,7 @@ let z := calldataload(1) let t := and(and(x, z), x) let w := or(or(x, z), x) + sstore(t, w) } // ---- // step: expressionSimplifier @@ -11,5 +12,5 @@ // let x := calldataload(0) // let z := calldataload(1) // let t := and(x, z) -// let w := or(x, z) +// sstore(t, or(x, z)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul index 38502d22c..6bb6a751b 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul @@ -1,5 +1,8 @@ -{ let a := sub(calldataload(0), calldataload(0)) } +{ + let a := sub(calldataload(0), calldataload(0)) + sstore(0, a) +} // ---- // step: expressionSimplifier // -// { let a := 0 } +// { sstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul index 5f3d268ca..686ae2b63 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul @@ -1,7 +1,11 @@ -{ let a := sub(calldataload(1), calldataload(0)) } +{ + let a := sub(calldataload(1), calldataload(0)) + sstore(0, a) +} // ---- // step: expressionSimplifier // // { -// let a := sub(calldataload(1), calldataload(0)) +// let _1 := 0 +// sstore(_1, sub(calldataload(1), calldataload(_1))) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul index d75ceb831..57d84ae15 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul @@ -1,11 +1,9 @@ { let a := mload(0) let b := sub(a, a) + sstore(0, b) } // ---- // step: expressionSimplifier // -// { -// let a := mload(0) -// let b := 0 -// } +// { sstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul index 73b6bfa54..a035494b8 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul @@ -1,6 +1,7 @@ { function f() -> a {} let b := add(7, sub(f(), 7)) + sstore(0, b) } // ---- // step: expressionSimplifier @@ -8,5 +9,5 @@ // { // function f() -> a // { } -// let b := f() +// sstore(0, f()) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul index fc75f131f..d7da5e41f 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul @@ -1,11 +1,9 @@ { let a := mload(sub(7, 7)) let b := sub(a, 0) + sstore(0, b) } // ---- // step: expressionSimplifier // -// { -// let a := mload(0) -// let b := a -// } +// { sstore(0, mload(0)) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul index 073db8d4e..22bfaf91f 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul @@ -4,13 +4,23 @@ let c := byte(20, a) // create cannot be removed. let d := byte(33, create(0, 0, 0x20)) + sstore(7, a) + sstore(8, b) + sstore(9, c) + sstore(10, d) } // ---- // step: expressionSimplifier // // { -// let a := calldataload(0) +// let _1 := 0 +// let a := calldataload(_1) // let b := 0 // let c := byte(20, a) -// let d := byte(33, create(0, 0, 0x20)) +// pop(create(_1, _1, 0x20)) +// let d := 0 +// sstore(7, a) +// sstore(8, b) +// sstore(9, c) +// sstore(10, d) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul index 65a325d6c..696d0b111 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul @@ -5,5 +5,6 @@ // step: expressionSimplifier // // { -// mstore(0, and(calldataload(0), 255)) +// let _4 := 0 +// mstore(_4, and(calldataload(_4), 255)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul index d4e35af33..81fe7e117 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul @@ -5,5 +5,6 @@ // step: expressionSimplifier // // { -// mstore(0, and(calldataload(0), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) +// let _4 := 0 +// mstore(_4, and(calldataload(_4), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul index b7bc3e4f9..ccb490892 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul @@ -1,6 +1,7 @@ { function f(a) -> b { } let c := sub(f(0), f(1)) + sstore(0, c) } // ---- // step: expressionSimplifier @@ -8,5 +9,7 @@ // { // function f(a) -> b // { } -// let c := sub(f(0), f(1)) +// let _2 := f(1) +// let _3 := 0 +// sstore(_3, sub(f(_3), _2)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul index b43440128..7f44c2208 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul @@ -2,6 +2,7 @@ function f1() -> a { } function f2() -> b { } let c := sub(f1(), f2()) + sstore(0, c) } // ---- // step: expressionSimplifier @@ -11,5 +12,5 @@ // { } // function f2() -> b // { } -// let c := sub(f1(), f2()) +// sstore(0, sub(f1(), f2())) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul index c0fa43e2c..6ed704aea 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul @@ -1,13 +1,14 @@ // Even if the functions pass the equality check, they are not movable. { - function f() -> a { } + function f() -> a { mstore(0, 1) } let b := sub(f(), f()) + sstore(b, 8) } // ---- // step: expressionSimplifier // // { // function f() -> a -// { } -// let b := sub(f(), f()) +// { mstore(a, 1) } +// sstore(sub(f(), f()), 8) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul index d153ae724..37065cd4d 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul @@ -1,11 +1,17 @@ // The first argument of div is not constant. // keccak256 is not movable. { + sstore(0, msize()) let a := div(keccak256(0, 0), 0) + sstore(20, a) } // ---- // step: expressionSimplifier // // { -// let a := div(keccak256(0, 0), 0) +// let _1 := msize() +// let _2 := 0 +// sstore(_2, _1) +// pop(keccak256(_2, _2)) +// sstore(20, 0) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul index e30a0418c..af34e76bd 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul @@ -1,5 +1,5 @@ { - pop(byte(0, shr(0x8, call(0, 0, 0, 0, 0, 0, 0)))) + sstore(0, byte(0, shr(0x8, call(0, 0, 0, 0, 0, 0, 0)))) } // ==== // EVMVersion: >=constantinople @@ -7,5 +7,7 @@ // step: expressionSimplifier // // { -// pop(byte(0, shr(0x8, call(0, 0, 0, 0, 0, 0, 0)))) +// let _1 := 0 +// pop(call(_1, _1, _1, _1, _1, _1, _1)) +// sstore(_1, 0) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul index f24add1dd..0d9513ec2 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul @@ -1,6 +1,6 @@ { function f() -> x { mstore(0, 1337) } - pop(byte(0, shr(0x8, f()))) + mstore(0, byte(0, shr(0x8, f()))) } // ==== // EVMVersion: >=constantinople @@ -9,6 +9,7 @@ // // { // function f() -> x -// { mstore(0, 1337) } -// pop(byte(0, shr(0x8, f()))) +// { mstore(x, 1337) } +// pop(f()) +// mstore(0, 0) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul index 3fc5ee558..448e071d8 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul @@ -1,14 +1,10 @@ { function f() -> x {} - pop(byte(0, shr(0x8, f()))) + sstore(0, byte(0, shr(0x8, f()))) } // ==== // EVMVersion: >=constantinople // ---- // step: expressionSimplifier // -// { -// function f() -> x -// { } -// pop(byte(0, shr(0x8, f()))) -// } +// { sstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul index fbc5befd0..d87b84f59 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul @@ -7,7 +7,8 @@ // step: expressionSimplifier // // { -// let x := mload(0) -// x := 0 -// mstore(0, 7) +// let _1 := 0 +// let x := mload(_1) +// x := _1 +// mstore(_1, 7) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul index b81ae9619..060a4e13f 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul @@ -3,6 +3,8 @@ let b := and(shr(248, calldataload(0)), 0xff) let c := and(shr(249, calldataload(0)), 0xfa) let d := and(shr(247, calldataload(0)), 0xff) + sstore(a, b) + sstore(c, d) } // ==== // EVMVersion: >=constantinople @@ -10,8 +12,12 @@ // step: expressionSimplifier // // { -// let a := shr(248, calldataload(0)) -// let b := shr(248, calldataload(0)) -// let c := and(shr(249, calldataload(0)), 0xfa) -// let d := and(shr(247, calldataload(0)), 0xff) +// let _2 := calldataload(0) +// let _5 := 0xff +// let a := shr(248, _2) +// let b := shr(248, _2) +// let c := and(shr(249, _2), 0xfa) +// let d := and(shr(247, _2), _5) +// sstore(a, b) +// sstore(c, d) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul index 550c7becb..88263e98e 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul @@ -3,6 +3,8 @@ let b := shr(299, calldataload(1)) let c := shl(255, calldataload(2)) let d := shr(255, calldataload(3)) + sstore(a, b) + sstore(c, d) } // ==== // EVMVersion: >=constantinople @@ -12,6 +14,10 @@ // { // let a := 0 // let b := 0 -// let c := shl(255, calldataload(2)) -// let d := shr(255, calldataload(3)) +// let _8 := calldataload(2) +// let _9 := 255 +// let c := shl(_9, _8) +// let d := shr(_9, calldataload(3)) +// sstore(a, b) +// sstore(c, d) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul index c1c0093ef..9b0087dd7 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul @@ -2,12 +2,15 @@ { function f() -> c, d { let y := add(d, add(c, 7)) + sstore(0, y) } + let t, v := f() } // ---- // step: expressionSimplifier // // { // function f() -> c, d -// { let y := 7 } +// { sstore(d, 7) } +// let t, v := f() // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul index 9ca956c10..32c877572 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul @@ -1,7 +1,11 @@ { let a := add(0, mload(0)) + sstore(0, a) } // ---- // step: expressionSimplifier // -// { let a := mload(0) } +// { +// let _1 := 0 +// sstore(_1, mload(_1)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_not_supported.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_not_supported.yul index 693c05c42..8a9bb7c19 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_not_supported.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_not_supported.yul @@ -1,9 +1,9 @@ { - let ret := balance(address()) + sstore(0, balance(address())) } // ==== // EVMVersion: =istanbul @@ -9,5 +10,5 @@ // // { // let a := address() -// let ret := selfbalance() +// sstore(a, selfbalance()) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_supported.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_supported.yul index 7b0a7524e..bc3c6c4d7 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_supported.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_supported.yul @@ -1,9 +1,9 @@ { - let ret := balance(address()) + sstore(0, balance(address())) } // ==== // EVMVersion: >=istanbul // ---- // step: expressionSimplifier // -// { let ret := selfbalance() } +// { sstore(0, selfbalance()) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul index e88877365..b052fe403 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul @@ -2,11 +2,12 @@ { let c, d let y := add(d, add(c, 7)) + sstore(0, y) } // ---- // step: expressionSimplifier // // { // let c, d -// let y := 7 +// sstore(d, 7) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul index d66d4efb2..9dd4e3365 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul @@ -3,12 +3,9 @@ let c let d let y := add(d, add(c, 7)) + sstore(8, y) } // ---- // step: expressionSimplifier // -// { -// let c -// let d -// let y := 7 -// } +// { sstore(8, 7) } diff --git a/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and.yul b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and.yul new file mode 100644 index 000000000..1abea23e7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and.yul @@ -0,0 +1,17 @@ +{ + let x := calldataload(0) + let a := and(0xff, shr(248, shl(248, shr(248, x)))) + let b := shr(12, shl(8, and(x, 0xf0f0))) + sstore(a, b) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// let x := calldataload(0) +// sstore(shr(248, x), and(shr(4, x), 3855)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_2.yul b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_2.yul new file mode 100644 index 000000000..513a23aa5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_2.yul @@ -0,0 +1,35 @@ +{ + let x := calldataload(0) + let a := and(0xff, shr(248, shl(248, shr(248, and(x, 0xf))))) + let b := shl(12, shr(4, and(x, 0xf0f0))) + let c := shl(12, shr(4, and(0xf0f0, x))) + let d := shl(12, shr(255, and(0xf0f0, x))) + let e := shl(255, shr(4, and(0xf0f0, x))) + let f := shl(12, shr(256, and(0xf0f0, x))) + let g := shl(256, shr(4, and(0xf0f0, x))) + sstore(10, a) + sstore(11, b) + sstore(12, c) + sstore(13, d) + sstore(14, e) + sstore(15, f) + sstore(16, g) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// let x := calldataload(0) +// let b := and(shl(8, x), 15790080) +// sstore(10, 0) +// sstore(11, b) +// sstore(12, b) +// sstore(13, 0) +// sstore(14, and(shl(251, x), shl(255, 1))) +// sstore(0xf, 0) +// sstore(16, 0) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_3.yul b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_3.yul new file mode 100644 index 000000000..17de07de7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_3.yul @@ -0,0 +1,32 @@ +{ + let x := calldataload(0) + let a := shl(12, shr(4, x)) + let b := shl(4, shr(12, x)) + let c := shr(12, shl(4, x)) + let d := shr(4, shl(12, x)) + let e := shl(150, shr(2, shl(150, x))) + sstore(15, x) + sstore(16, a) + sstore(17, b) + sstore(18, c) + sstore(19, d) + sstore(20, e) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// let x := calldataload(0) +// let _1 := shl(8, x) +// let _2 := shr(8, x) +// sstore(15, x) +// sstore(16, and(_1, not(4095))) +// sstore(17, and(_2, sub(shl(248, 1), 16))) +// sstore(18, and(_2, sub(shl(244, 1), 1))) +// sstore(19, and(_1, sub(shl(252, 1), 256))) +// sstore(20, 0) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_unsplit.yul b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_unsplit.yul new file mode 100644 index 000000000..923a18f4f --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_unsplit.yul @@ -0,0 +1,19 @@ +{ + let x := calldataload(0) + // This checks that the expression simplifier + // does not modify unsplit expressions. + let a := and(0xff, shr(248, shl(248, shr(248, x)))) + let b := shr(12, shl(8, and(x, 0xf0f0))) + sstore(a, b) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// let x := calldataload(0) +// sstore(shr(248, x), and(shr(4, x), 3855)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/create2_and_mask.yul b/test/libyul/yulOptimizerTests/fullSuite/create2_and_mask.yul new file mode 100644 index 000000000..813c2706e --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/create2_and_mask.yul @@ -0,0 +1,21 @@ +{ + // This does not optimize the masks away. Due to the way the expression simplifier + // is built, it would have to create another `create2` opcode for the simplification + // which would be fatal. + let a := and(create2(0, 0, 0x20, 0), 0xffffffffffffffffffffffffffffffffffffffff) + let b := and(0xffffffffffffffffffffffffffffffffffffffff, create2(0, 0, 0x20, 0)) + sstore(a, b) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: fullSuite +// +// { +// { +// let _1 := sub(shl(160, 1), 1) +// let _2 := 0 +// let a := and(create2(_2, _2, 0x20, _2), _1) +// sstore(a, and(_1, create2(_2, _2, 0x20, _2))) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/create_and_mask.yul b/test/libyul/yulOptimizerTests/fullSuite/create_and_mask.yul new file mode 100644 index 000000000..c89c620bb --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/create_and_mask.yul @@ -0,0 +1,20 @@ +{ + // This does not optimize the masks away. Due to the way the expression simplifier + // is built, it would have to create another `create` opcode for the simplification + // which would be fatal. + let a := and(create(0, 0, 0x20), 0xffffffffffffffffffffffffffffffffffffffff) + let b := and(0xffffffffffffffffffffffffffffffffffffffff, create(0, 0, 0x20)) + sstore(a, b) +} +// ==== +// EVMVersion: >=istanbul +// ---- +// step: fullSuite +// +// { +// { +// let _1 := sub(shl(160, 1), 1) +// let a := and(create(0, 0, 0x20), _1) +// sstore(a, and(_1, create(0, 0, 0x20))) +// } +// }