From fdc6c6bfc5c8875a36356e77725add26c160cfbd Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:06:38 +0200 Subject: [PATCH 1/9] Update EVMDialect.cpp --- libyul/backends/evm/EVMDialect.cpp | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index d68ef16348b1..ac7871ed26f4 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -250,6 +250,46 @@ std::vector> createBuiltins(langutil::EVMVe return createFunction(_name, _params, _returns, _sideEffects, _controlFlowSideEffects, std::move(_literalArguments), std::move(_generateCode)); }; + builtins.emplace_back(createIfObjectAccess( + "verbatim", + 3, + 0, + SideEffects::worst(), + ControlFlowSideEffects::worst(), // Worst control flow side effects because verbatim can do anything. + {LiteralKind::Number, LiteralKind::Number, LiteralKind::String}, + []( + FunctionCall const& _call, + AbstractAssembly& _assembly, + BuiltinContext& + ) { + yulAssert(_call.arguments.size() == 3, ""); + + // Get the number of inputs from the first argument + Literal const* argsLiteral = std::get_if(&_call.arguments[0]); + yulAssert(argsLiteral, "First argument must be a literal"); + size_t arguments = static_cast(argsLiteral->value.value()); + + // Get the number of outputs from the second argument + Literal const* retsLiteral = std::get_if(&_call.arguments[1]); + yulAssert(retsLiteral, "Second argument must be a literal"); + size_t returnVariables = static_cast(retsLiteral->value.value()); + + // Verify that arguments and returnVariables are in the allowed range + yulAssert(arguments <= EVMDialect::verbatimMaxInputSlots, "Too many verbatim input arguments"); + yulAssert(returnVariables <= EVMDialect::verbatimMaxOutputSlots, "Too many verbatim return values"); + + // Get the bytecode from the third argument + Literal const* bytecodeStr = std::get_if(&_call.arguments[2]); + yulAssert(bytecodeStr, "Third argument must be a literal"); + + _assembly.appendVerbatim( + asBytes(formatLiteral(*bytecodeStr)), + arguments, + returnVariables + ); + } + )); + builtins.emplace_back(createIfObjectAccess("linkersymbol", 1, 1, SideEffects{}, ControlFlowSideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, From 4657df27c6ad69ff2a855196dfc4f4e1133bb152 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:06:55 +0200 Subject: [PATCH 2/9] Update yul.rst --- docs/yul.rst | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/yul.rst b/docs/yul.rst index c81c9562a58c..6095d2f683ed 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -589,7 +589,7 @@ declaration. Functions can be referenced already before their declaration (if they are visible). As an exception to the general scoping rule, the scope of the "init" part of the for-loop -(the first block) extends across all other parts of the for loop. +(the first block) extends across all other parts of the for-loop. This means that variables (and functions) declared in the init part (but not inside a block inside the init part) are visible in all other parts of the for-loop. @@ -1050,15 +1050,28 @@ the additional optimiser steps will be run on it. verbatim ^^^^^^^^ -The set of ``verbatim...`` builtin functions lets you create bytecode for opcodes +The ``verbatim`` builtin function lets you create bytecode for opcodes that are not known to the Yul compiler. It also allows you to create bytecode sequences that will not be modified by the optimizer. -The functions are ``verbatim_i_o("", ...)``, where +This function can be used in two ways: -- ``n`` is a decimal between 0 and 99 that specifies the number of input stack slots / variables -- ``m`` is a decimal between 0 and 99 that specifies the number of output stack slots / variables -- ``data`` is a string literal that contains the sequence of bytes +1. (Recommended) Using the newer syntax ``verbatim(n, m, "", ...)``, where: + + - ``n`` is a literal number between 0 and 99 that specifies the number of input stack slots / variables + - ``m`` is a literal number between 0 and 99 that specifies the number of output stack slots / variables + - ``data`` is a string literal that contains the sequence of bytes + +2. (Legacy) Using the older syntax ``verbatim_i_o("", ...)``, where: + + - ``n`` is a decimal between 0 and 99 that specifies the number of input stack slots / variables + - ``m`` is a decimal between 0 and 99 that specifies the number of output stack slots / variables + - ``data`` is a string literal that contains the sequence of bytes + +.. note:: + + The legacy ``verbatim_i_o`` functions are deprecated and will be removed in a future version. + Please use the new ``verbatim`` function. If you for example want to define a function that multiplies the input by two, without the optimizer touching the constant two, you can use @@ -1066,7 +1079,7 @@ by two, without the optimizer touching the constant two, you can use .. code-block:: yul let x := calldataload(0) - let double := verbatim_1i_1o(hex"600202", x) + let double := verbatim(1, 1, hex"600202", x) This code will result in a ``dup1`` opcode to retrieve ``x`` (the optimizer might directly reuse result of the @@ -1344,13 +1357,6 @@ Complete ERC20 Example executeTransfer(from, to, amount) } - function executeTransfer(from, to, amount) { - revertIfZeroAddress(to) - deductFromBalance(from, amount) - addToBalance(to, amount) - emitTransfer(from, to, amount) - } - /* ---------- calldata decoding functions ----------- */ function selector() -> s { From a5f123a05e9c5646cf40b7da438f8643c5a824d1 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:08:25 +0200 Subject: [PATCH 3/9] Create verbatim_single_invalid_range.yul --- .../yulSyntaxTests/verbatim_single_invalid_range.yul | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/libyul/yulSyntaxTests/verbatim_single_invalid_range.yul diff --git a/test/libyul/yulSyntaxTests/verbatim_single_invalid_range.yul b/test/libyul/yulSyntaxTests/verbatim_single_invalid_range.yul new file mode 100644 index 000000000000..85a97c328873 --- /dev/null +++ b/test/libyul/yulSyntaxTests/verbatim_single_invalid_range.yul @@ -0,0 +1,7 @@ +{ + let x := verbatim(100, 1, "abc") +} +// ==== +// dialect: evm +// ---- +// Type of call argument too large (currently disallowed). From 13cd153dd9b8ccbb2df4dc51a5b68e3892d04c55 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:08:54 +0200 Subject: [PATCH 4/9] Create verbatim_single_invalid_args.yul --- .../yulSyntaxTests/verbatim_single_invalid_args.yul | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/libyul/yulSyntaxTests/verbatim_single_invalid_args.yul diff --git a/test/libyul/yulSyntaxTests/verbatim_single_invalid_args.yul b/test/libyul/yulSyntaxTests/verbatim_single_invalid_args.yul new file mode 100644 index 000000000000..ebd75d8045cc --- /dev/null +++ b/test/libyul/yulSyntaxTests/verbatim_single_invalid_args.yul @@ -0,0 +1,11 @@ +{ + // Using variables as arguments which should be literals + let n := 2 + let m := 1 + let c := "abc" + let x := verbatim(n, m, c) +} +// ==== +// dialect: evm +// ---- +// TypeError 9114: (92-112): Function expects direct literals as arguments. From af35564b923141ee4e0e45b4daf2110c7924c25c Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:09:21 +0200 Subject: [PATCH 5/9] Create verbatim_single.yul --- test/libyul/yulSyntaxTests/verbatim_single.yul | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/libyul/yulSyntaxTests/verbatim_single.yul diff --git a/test/libyul/yulSyntaxTests/verbatim_single.yul b/test/libyul/yulSyntaxTests/verbatim_single.yul new file mode 100644 index 000000000000..4d5e851cf454 --- /dev/null +++ b/test/libyul/yulSyntaxTests/verbatim_single.yul @@ -0,0 +1,7 @@ +{ + let x := verbatim(2, 1, "abc") + verbatim(0, 0, "xyz") +} +// ==== +// dialect: evm +// ---- From a012dd5d381a5bcd591a8989ad23189f498effb5 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:10:49 +0200 Subject: [PATCH 6/9] Create output.json --- test/cmdlineTests/yul_verbatim_single/output.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/cmdlineTests/yul_verbatim_single/output.json diff --git a/test/cmdlineTests/yul_verbatim_single/output.json b/test/cmdlineTests/yul_verbatim_single/output.json new file mode 100644 index 000000000000..a4088d8f40b0 --- /dev/null +++ b/test/cmdlineTests/yul_verbatim_single/output.json @@ -0,0 +1,12 @@ +{ + "contracts": {}, + "sourceList": [ + "input.yul" + ], + "sources": { + "input.yul": { + "id": 0 + } + }, + "version": "" +} From 759fd729f0aef9899d32c8c7f72013fbd83d2ebe Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:11:39 +0200 Subject: [PATCH 7/9] Create input.yul --- test/cmdlineTests/yul_verbatim_single/input.yul | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/cmdlineTests/yul_verbatim_single/input.yul diff --git a/test/cmdlineTests/yul_verbatim_single/input.yul b/test/cmdlineTests/yul_verbatim_single/input.yul new file mode 100644 index 000000000000..60d768744c95 --- /dev/null +++ b/test/cmdlineTests/yul_verbatim_single/input.yul @@ -0,0 +1,11 @@ +{ + let x := 2 + let y := sub(x, 2) + // Using the new verbatim builtin with literal args instead of verbatim_2i_1o + let r := verbatim(0, 1, "def") // No input arguments, 1 output value + let t := verbatim(2, 1, "abc", x, y) // 2 input arguments, 1 output value + sstore(t, x) + verbatim(0, 0, "xyz") // No input arguments, no output values + // more than 32 bytes + verbatim(0, 0, hex"01020304050607090001020304050607090001020304050607090001020102030405060709000102030405060709000102030405060709000102") +} From 943e30351ed6bccdbdacd1cb1536e45cf742b2e5 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:12:00 +0200 Subject: [PATCH 8/9] Create verbatim_single_valid.yul --- test/libyul/yulSyntaxTests/verbatim_single_valid.yul | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/libyul/yulSyntaxTests/verbatim_single_valid.yul diff --git a/test/libyul/yulSyntaxTests/verbatim_single_valid.yul b/test/libyul/yulSyntaxTests/verbatim_single_valid.yul new file mode 100644 index 000000000000..f31e4d723b45 --- /dev/null +++ b/test/libyul/yulSyntaxTests/verbatim_single_valid.yul @@ -0,0 +1,11 @@ +{ + // Test that we can provide the correct number of arguments + let x := 2 + let y := 3 + let t := verbatim(2, 1, "abc", x, y) // Correct: 2 inputs as specified + let z := verbatim(0, 1, "def") // Correct: 0 inputs as specified + verbatim(0, 0, "xyz") // Correct: 0 inputs, 0 outputs +} +// ==== +// dialect: evm +// ---- From b1a56e5ccd26ddc195ef60306ae2dc98efd0b6c1 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Sat, 26 Apr 2025 11:12:22 +0200 Subject: [PATCH 9/9] Create verbatim_single_insufficient_args.yul --- .../yulSyntaxTests/verbatim_single_insufficient_args.yul | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 test/libyul/yulSyntaxTests/verbatim_single_insufficient_args.yul diff --git a/test/libyul/yulSyntaxTests/verbatim_single_insufficient_args.yul b/test/libyul/yulSyntaxTests/verbatim_single_insufficient_args.yul new file mode 100644 index 000000000000..6e1b1902d4da --- /dev/null +++ b/test/libyul/yulSyntaxTests/verbatim_single_insufficient_args.yul @@ -0,0 +1,9 @@ +{ + // Test what happens when we provide fewer function arguments than specified in the first parameter + let x := 2 + let t := verbatim(2, 1, "abc", x) // Expecting 2 inputs but only given 1 +} +// ==== +// dialect: evm +// ---- +// TypeError 4323: (121-142): Function expects 5 arguments but got 4.