diff --git a/Changelog.md b/Changelog.md index 2ab1bb037..e79b13e6e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,6 +21,7 @@ Bugfixes: * Commandline Interface: Disallow ``--error-recovery`` option outside of the compiler mode. * SMTChecker: Fix internal error in magic type access (``block``, ``msg``, ``tx``). * TypeChecker: Fix internal error when using user defined value types in public library functions. + * Yul Assembler: Fix internal error when function names are not unique. * Yul IR Generator: Do not output empty switches/if-bodies for empty contracts. * Commandline Interface: When linking only accept exact matches for library names passed to the ``--libraries`` option. Library names not prefixed with a file name used to match any library with that name. diff --git a/libyul/backends/evm/AsmCodeGen.cpp b/libyul/backends/evm/AsmCodeGen.cpp index 5c7865c01..6ef142906 100644 --- a/libyul/backends/evm/AsmCodeGen.cpp +++ b/libyul/backends/evm/AsmCodeGen.cpp @@ -52,7 +52,9 @@ void CodeGenerator::assemble( builtinContext, _optimizeStackAllocation, _identifierAccessCodeGen, - _useNamedLabelsForFunctions + _useNamedLabelsForFunctions ? + CodeTransform::UseNamedLabels::YesAndForceUnique : + CodeTransform::UseNamedLabels::Never ); transform(_parsedData); if (!transform.stackErrors().empty()) diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 38eecc245..531fd1b92 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -52,7 +52,7 @@ CodeTransform::CodeTransform( EVMDialect const& _dialect, BuiltinContext& _builtinContext, ExternalIdentifierAccess::CodeGenerator _identifierAccessCodeGen, - bool _useNamedLabelsForFunctions, + UseNamedLabels _useNamedLabelsForFunctions, shared_ptr _context, vector _delayedReturnVariables, optional _functionExitLabel @@ -405,8 +405,12 @@ void CodeTransform::operator()(FunctionDefinition const& _function) if (!m_allowStackOpt) subTransform.setupReturnVariablesAndFunctionExit(); + subTransform.m_assignedNamedLabels = move(m_assignedNamedLabels); + subTransform(_function.body); + m_assignedNamedLabels = move(subTransform.m_assignedNamedLabels); + m_assembly.setSourceLocation(originLocationOf(_function)); if (!subTransform.m_stackErrors.empty()) { @@ -585,8 +589,16 @@ void CodeTransform::createFunctionEntryID(FunctionDefinition const& _function) if (_function.debugData) astID = _function.debugData->astID; + bool nameAlreadySeen = !m_assignedNamedLabels.insert(_function.name).second; + + if (m_useNamedLabelsForFunctions == UseNamedLabels::YesAndForceUnique) + yulAssert(!nameAlreadySeen); + m_context->functionEntryIDs[&scopeFunction] = - m_useNamedLabelsForFunctions ? + ( + m_useNamedLabelsForFunctions != UseNamedLabels::Never && + !nameAlreadySeen + ) ? m_assembly.namedLabel( _function.name.str(), _function.parameters.size(), diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index 8a605b472..bad9a357f 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -64,6 +64,10 @@ struct CodeTransformContext class CodeTransform { public: + /// Use named labels for functions 1) Yes and check that the names are unique + /// 2) For none of the functions 3) for the first function of each name. + enum class UseNamedLabels { YesAndForceUnique, Never, ForFirstFunctionOfEachName }; + /// Create the code transformer. /// @param _identifierAccessCodeGen used to generate code for identifiers external to the inline assembly /// As a side-effect of its construction, translates the Yul code and appends it to the @@ -78,7 +82,7 @@ public: BuiltinContext& _builtinContext, bool _allowStackOpt = false, ExternalIdentifierAccess::CodeGenerator const& _identifierAccessCodeGen = {}, - bool _useNamedLabelsForFunctions = false + UseNamedLabels _useNamedLabelsForFunctions = UseNamedLabels::Never ): CodeTransform( _assembly, _analysisInfo, @@ -108,7 +112,7 @@ protected: EVMDialect const& _dialect, BuiltinContext& _builtinContext, ExternalIdentifierAccess::CodeGenerator _identifierAccessCodeGen, - bool _useNamedLabelsForFunctions, + UseNamedLabels _useNamedLabelsForFunctions, std::shared_ptr _context, std::vector _delayedReturnVariables, std::optional _functionExitLabel @@ -193,7 +197,8 @@ private: EVMDialect const& m_dialect; BuiltinContext& m_builtinContext; bool const m_allowStackOpt = true; - bool const m_useNamedLabelsForFunctions = false; + UseNamedLabels const m_useNamedLabelsForFunctions = UseNamedLabels::Never; + std::set m_assignedNamedLabels; ExternalIdentifierAccess::CodeGenerator m_identifierAccessCodeGen; std::shared_ptr m_context; diff --git a/libyul/backends/evm/EVMObjectCompiler.cpp b/libyul/backends/evm/EVMObjectCompiler.cpp index a526bf218..e41a68387 100644 --- a/libyul/backends/evm/EVMObjectCompiler.cpp +++ b/libyul/backends/evm/EVMObjectCompiler.cpp @@ -72,7 +72,7 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize) context, _optimize, {}, - true /* _useNamedLabelsForFunctions */ + CodeTransform::UseNamedLabels::ForFirstFunctionOfEachName }; transform(*_object.code); if (!transform.stackErrors().empty()) diff --git a/test/cmdlineTests/inline_assembly_function_name_clash/args b/test/cmdlineTests/inline_assembly_function_name_clash/args new file mode 100644 index 000000000..4299d023e --- /dev/null +++ b/test/cmdlineTests/inline_assembly_function_name_clash/args @@ -0,0 +1 @@ +--experimental-via-ir --combined-json function-debug-runtime --pretty-json --json-indent 4 \ No newline at end of file diff --git a/test/cmdlineTests/inline_assembly_function_name_clash/err b/test/cmdlineTests/inline_assembly_function_name_clash/err new file mode 100644 index 000000000..5284ffe5c --- /dev/null +++ b/test/cmdlineTests/inline_assembly_function_name_clash/err @@ -0,0 +1,5 @@ +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +--> inline_assembly_function_name_clash/input.sol + +Warning: Source file does not specify required compiler version! +--> inline_assembly_function_name_clash/input.sol diff --git a/test/cmdlineTests/inline_assembly_function_name_clash/input.sol b/test/cmdlineTests/inline_assembly_function_name_clash/input.sol new file mode 100644 index 000000000..55032c56e --- /dev/null +++ b/test/cmdlineTests/inline_assembly_function_name_clash/input.sol @@ -0,0 +1,18 @@ +contract C { + uint x; + modifier m() { + uint t; + assembly { + function f() -> x { x := 8 } + t := f() + } + x = t; + _; + } + function f() m m public returns (uint r) { + assembly { function f() -> x { x := 1 } r := f() } + } + function g() m m public returns (uint r) { + assembly { function f() -> x { x := 2 } r := f() } + } +} \ No newline at end of file diff --git a/test/cmdlineTests/inline_assembly_function_name_clash/output b/test/cmdlineTests/inline_assembly_function_name_clash/output new file mode 100644 index 000000000..00192f8f4 --- /dev/null +++ b/test/cmdlineTests/inline_assembly_function_name_clash/output @@ -0,0 +1,168 @@ +{ + "contracts": + { + "inline_assembly_function_name_clash/input.sol:C": + { + "function-debug-runtime": + { + "abi_decode_tuple_": + { + "entryPoint": 216, + "parameterSlots": 2, + "returnSlots": 0 + }, + "abi_encode_t_uint256_to_t_uint256_fromStack": + { + "entryPoint": 250, + "parameterSlots": 2, + "returnSlots": 0 + }, + "abi_encode_tuple_t_uint256__to_t_uint256__fromStack": + { + "entryPoint": 265, + "parameterSlots": 2, + "returnSlots": 1 + }, + "allocate_unbounded": + { + "entryPoint": 196, + "parameterSlots": 0, + "returnSlots": 1 + }, + "cleanup_t_uint256": + { + "entryPoint": 240, + "parameterSlots": 1, + "returnSlots": 1 + }, + "convert_t_uint256_to_t_uint256": + { + "entryPoint": 391, + "parameterSlots": 1, + "returnSlots": 1 + }, + "fun_f_25": + { + "entryPoint": 658, + "id": 25, + "parameterSlots": 0, + "returnSlots": 1 + }, + "fun_f_25_inner": + { + "entryPoint": 624, + "parameterSlots": 1, + "returnSlots": 1 + }, + "fun_g_36": + { + "entryPoint": 874, + "id": 36, + "parameterSlots": 0, + "returnSlots": 1 + }, + "fun_g_36_inner": + { + "entryPoint": 840, + "parameterSlots": 1, + "returnSlots": 1 + }, + "identity": + { + "entryPoint": 381, + "parameterSlots": 1, + "returnSlots": 1 + }, + "modifier_m_17": + { + "entryPoint": 470, + "id": 14, + "parameterSlots": 1, + "returnSlots": 1 + }, + "modifier_m_19": + { + "entryPoint": 547, + "id": 14, + "parameterSlots": 1, + "returnSlots": 1 + }, + "modifier_m_28": + { + "entryPoint": 686, + "id": 14, + "parameterSlots": 1, + "returnSlots": 1 + }, + "modifier_m_30": + { + "entryPoint": 763, + "id": 14, + "parameterSlots": 1, + "returnSlots": 1 + }, + "prepare_store_t_uint256": + { + "entryPoint": 425, + "parameterSlots": 1, + "returnSlots": 1 + }, + "revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74": + { + "entryPoint": 292, + "parameterSlots": 0, + "returnSlots": 0 + }, + "revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb": + { + "entryPoint": 206, + "parameterSlots": 0, + "returnSlots": 0 + }, + "revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b": + { + "entryPoint": 211, + "parameterSlots": 0, + "returnSlots": 0 + }, + "shift_left_0": + { + "entryPoint": 302, + "parameterSlots": 1, + "returnSlots": 1 + }, + "shift_right_224_unsigned": + { + "entryPoint": 183, + "parameterSlots": 1, + "returnSlots": 1 + }, + "update_byte_slice_32_shift_0": + { + "entryPoint": 315, + "parameterSlots": 2, + "returnSlots": 1 + }, + "update_storage_value_offset_0t_uint256_to_t_uint256": + { + "entryPoint": 435, + "parameterSlots": 2, + "returnSlots": 0 + }, + "usr$f": + { + "entryPoint": 493, + "parameterSlots": 0, + "returnSlots": 1 + }, + "zero_value_for_split_t_uint256": + { + "entryPoint": 297, + "parameterSlots": 0, + "returnSlots": 1 + } + } + } + }, + "version": "" +} diff --git a/test/cmdlineTests/yul_function_name_clashes/args b/test/cmdlineTests/yul_function_name_clashes/args new file mode 100644 index 000000000..903cc6eb4 --- /dev/null +++ b/test/cmdlineTests/yul_function_name_clashes/args @@ -0,0 +1 @@ +--strict-assembly --debug-info none \ No newline at end of file diff --git a/test/cmdlineTests/yul_function_name_clashes/err b/test/cmdlineTests/yul_function_name_clashes/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/yul_function_name_clashes/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/yul_function_name_clashes/input.yul b/test/cmdlineTests/yul_function_name_clashes/input.yul new file mode 100644 index 000000000..0ede2d25e --- /dev/null +++ b/test/cmdlineTests/yul_function_name_clashes/input.yul @@ -0,0 +1,17 @@ +object "object" { + code { + let a + let b + { + function z() -> y + { y := calldataload(0) } + a := z() + } + { + function z() -> y + { y := calldataload(0x20) } + b := z() + } + sstore(a, b) + } +} \ No newline at end of file diff --git a/test/cmdlineTests/yul_function_name_clashes/output b/test/cmdlineTests/yul_function_name_clashes/output new file mode 100644 index 000000000..8a7bec790 --- /dev/null +++ b/test/cmdlineTests/yul_function_name_clashes/output @@ -0,0 +1,66 @@ + +======= yul_function_name_clashes/input.yul (EVM) ======= + +Pretty printed source: +object "object" { + code { + let a + let b + { + function z() -> y + { y := calldataload(0) } + a := z() + } + { + function z() -> y + { y := calldataload(0x20) } + b := z() + } + sstore(a, b) + } +} + + +Binary representation: +600080600f565b60008035905090565b60156006565b91506025565b6000602035905090565b602b601b565b90508082555050 + +Text representation: + 0x00 + dup1 + jump(tag_2) +tag_1: + 0x00 + dup1 + calldataload + swap1 + pop + swap1 + jump // out +tag_2: + tag_4 + tag_1 + jump // in +tag_4: + swap2 + pop + jump(tag_6) +tag_5: + 0x00 + 0x20 + calldataload + swap1 + pop + swap1 + jump // out +tag_6: + tag_8 + tag_5 + jump // in +tag_8: + swap1 + pop + dup1 + dup3 + sstore + pop + pop diff --git a/test/cmdlineTests/yul_function_name_clashes_different_params/args b/test/cmdlineTests/yul_function_name_clashes_different_params/args new file mode 100644 index 000000000..2c89c24e0 --- /dev/null +++ b/test/cmdlineTests/yul_function_name_clashes_different_params/args @@ -0,0 +1 @@ +--strict-assembly diff --git a/test/cmdlineTests/yul_function_name_clashes_different_params/err b/test/cmdlineTests/yul_function_name_clashes_different_params/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/yul_function_name_clashes_different_params/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/yul_function_name_clashes_different_params/input.yul b/test/cmdlineTests/yul_function_name_clashes_different_params/input.yul new file mode 100644 index 000000000..e27ca014a --- /dev/null +++ b/test/cmdlineTests/yul_function_name_clashes_different_params/input.yul @@ -0,0 +1,17 @@ +object "object" { + code { + let a + let b + { + function z() -> y + { y := calldataload(0) } + a := z() + } + { + function z(r) -> y + { y := calldataload(r) } + b := z(0x70) + } + sstore(a, b) + } +} diff --git a/test/cmdlineTests/yul_function_name_clashes_different_params/output b/test/cmdlineTests/yul_function_name_clashes_different_params/output new file mode 100644 index 000000000..f3dc84340 --- /dev/null +++ b/test/cmdlineTests/yul_function_name_clashes_different_params/output @@ -0,0 +1,93 @@ + +======= yul_function_name_clashes_different_params/input.yul (EVM) ======= + +Pretty printed source: +object "object" { + code { + let a + let b + { + function z() -> y + { y := calldataload(0) } + a := z() + } + { + function z(r) -> y + { y := calldataload(r) } + b := z(0x70) + } + sstore(a, b) + } +} + + +Binary representation: +600080600f565b60008035905090565b60156006565b91506026565b600081359050919050565b602e6070601b565b90508082555050 + +Text representation: + /* "yul_function_name_clashes_different_params/input.yul":37:42 */ + 0x00 + /* "yul_function_name_clashes_different_params/input.yul":51:56 */ + dup1 + /* "yul_function_name_clashes_different_params/input.yul":79:133 */ + jump(tag_2) +tag_1: + /* "yul_function_name_clashes_different_params/input.yul":95:96 */ + 0x00 + /* "yul_function_name_clashes_different_params/input.yul":129:130 */ + dup1 + /* "yul_function_name_clashes_different_params/input.yul":116:131 */ + calldataload + /* "yul_function_name_clashes_different_params/input.yul":111:131 */ + swap1 + pop + /* "yul_function_name_clashes_different_params/input.yul":79:133 */ + swap1 + jump // out +tag_2: + /* "yul_function_name_clashes_different_params/input.yul":151:154 */ + tag_4 + tag_1 + jump // in +tag_4: + /* "yul_function_name_clashes_different_params/input.yul":146:154 */ + swap2 + pop + /* "yul_function_name_clashes_different_params/input.yul":187:242 */ + jump(tag_6) +tag_5: + /* "yul_function_name_clashes_different_params/input.yul":204:205 */ + 0x00 + /* "yul_function_name_clashes_different_params/input.yul":238:239 */ + dup2 + /* "yul_function_name_clashes_different_params/input.yul":225:240 */ + calldataload + /* "yul_function_name_clashes_different_params/input.yul":220:240 */ + swap1 + pop + /* "yul_function_name_clashes_different_params/input.yul":187:242 */ + swap2 + swap1 + pop + jump // out +tag_6: + /* "yul_function_name_clashes_different_params/input.yul":260:267 */ + tag_8 + /* "yul_function_name_clashes_different_params/input.yul":262:266 */ + 0x70 + /* "yul_function_name_clashes_different_params/input.yul":260:267 */ + tag_5 + jump // in +tag_8: + /* "yul_function_name_clashes_different_params/input.yul":255:267 */ + swap1 + pop + /* "yul_function_name_clashes_different_params/input.yul":296:297 */ + dup1 + /* "yul_function_name_clashes_different_params/input.yul":293:294 */ + dup3 + /* "yul_function_name_clashes_different_params/input.yul":286:298 */ + sstore + /* "yul_function_name_clashes_different_params/input.yul":27:304 */ + pop + pop diff --git a/test/libsolidity/semanticTests/inlineAssembly/function_name_clash.sol b/test/libsolidity/semanticTests/inlineAssembly/function_name_clash.sol new file mode 100644 index 000000000..21fbdb28a --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/function_name_clash.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure returns (uint r) { + assembly { function f() -> x { x := 1 } r := f() } + } + function g() public pure returns (uint r) { + assembly { function f() -> x { x := 2 } r := f() } + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1 +// g() -> 2