From 4d2b9cd38f95d9338c1b29b37f311e614bb18792 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 22 Jun 2020 14:22:01 +0200 Subject: [PATCH] Jump types for yul functions. --- Changelog.md | 1 + libsolidity/codegen/CompilerContext.cpp | 2 +- libyul/backends/evm/AbstractAssembly.h | 7 +- libyul/backends/evm/AsmCodeGen.cpp | 31 +++++++-- libyul/backends/evm/AsmCodeGen.h | 7 +- libyul/backends/evm/EVMAssembly.cpp | 8 +-- libyul/backends/evm/EVMAssembly.h | 6 +- libyul/backends/evm/EVMCodeTransform.cpp | 8 ++- libyul/backends/evm/NoOutputAssembly.cpp | 8 +-- libyul/backends/evm/NoOutputAssembly.h | 6 +- test/cmdlineTests/yul_stack_opt/output | 8 ++- test/libsolidity/Assembly.cpp | 24 +++++++ .../libyul/objectCompiler/function_series.yul | 6 +- test/libyul/objectCompiler/jump_tags.yul | 64 +++++++++++++++++++ 14 files changed, 151 insertions(+), 35 deletions(-) create mode 100644 test/libyul/objectCompiler/jump_tags.yul diff --git a/Changelog.md b/Changelog.md index 9736a352c..2228741c5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: * NatSpec: Add fields "kind" and "version" to the JSON output. * NatSpec: Inherit tags from unique base if derived function does not provide any. * Commandline Interface: Prevent some incompatible commandline options from being used together. + * Debug data: Also tag jumps into and out of Yul functions as jumps into and out of functions. * NatSpec: Support NatSpec comments on events. * Yul Optimizer: Store knowledge about storage / memory after ``a := sload(x)`` / ``a := mload(x)``. * SMTChecker: Support external calls to unknown code. diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 4a9b1918d..2f3659010 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -146,7 +146,7 @@ void CompilerContext::callYulFunction( m_externallyUsedYulFunctions.insert(_name); auto const retTag = pushNewTag(); CompilerUtils(*this).moveIntoStack(_inArgs); - appendJumpTo(namedTag(_name)); + appendJumpTo(namedTag(_name), evmasm::AssemblyItem::JumpType::IntoFunction); adjustStackOffset(static_cast(_outArgs) - 1 - static_cast(_inArgs)); *this << retTag.tag(); } diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index b46c2f734..42622272c 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -50,6 +50,7 @@ class AbstractAssembly public: using LabelID = size_t; using SubID = size_t; + enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; virtual ~AbstractAssembly() = default; @@ -78,13 +79,13 @@ public: /// Append a jump instruction. /// @param _stackDiffAfter the stack adjustment after this instruction. /// This is helpful to stack height analysis if there is no continuing control flow. - virtual void appendJump(int _stackDiffAfter) = 0; + virtual void appendJump(int _stackDiffAfter, JumpType _jumpType = JumpType::Ordinary) = 0; /// Append a jump-to-immediate operation. /// @param _stackDiffAfter the stack adjustment after this instruction. - virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter = 0) = 0; + virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter = 0, JumpType _jumpType = JumpType::Ordinary) = 0; /// Append a jump-to-if-immediate operation. - virtual void appendJumpToIf(LabelID _labelId) = 0; + virtual void appendJumpToIf(LabelID _labelId, JumpType _jumpType = JumpType::Ordinary) = 0; /// Start a subroutine identified by @a _labelId that takes @a _arguments /// stack slots as arguments. virtual void appendBeginsub(LabelID _labelId, int _arguments) = 0; diff --git a/libyul/backends/evm/AsmCodeGen.cpp b/libyul/backends/evm/AsmCodeGen.cpp index 5827f6524..327ce8895 100644 --- a/libyul/backends/evm/AsmCodeGen.cpp +++ b/libyul/backends/evm/AsmCodeGen.cpp @@ -98,22 +98,22 @@ void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol) m_assembly.appendLibraryAddress(_linkerSymbol); } -void EthAssemblyAdapter::appendJump(int _stackDiffAfter) +void EthAssemblyAdapter::appendJump(int _stackDiffAfter, JumpType _jumpType) { - appendInstruction(evmasm::Instruction::JUMP); + appendJumpInstruction(evmasm::Instruction::JUMP, _jumpType); m_assembly.adjustDeposit(_stackDiffAfter); } -void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter) +void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) { appendLabelReference(_labelId); - appendJump(_stackDiffAfter); + appendJump(_stackDiffAfter, _jumpType); } -void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId) +void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId, JumpType _jumpType) { appendLabelReference(_labelId); - appendInstruction(evmasm::Instruction::JUMPI); + appendJumpInstruction(evmasm::Instruction::JUMPI, _jumpType); } void EthAssemblyAdapter::appendBeginsub(LabelID, int) @@ -189,6 +189,25 @@ EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(evmasm:: return LabelID(id); } +void EthAssemblyAdapter::appendJumpInstruction(evmasm::Instruction _instruction, JumpType _jumpType) +{ + yulAssert(_instruction == evmasm::Instruction::JUMP || _instruction == evmasm::Instruction::JUMPI, ""); + evmasm::AssemblyItem jump(_instruction); + switch (_jumpType) + { + case JumpType::Ordinary: + yulAssert(jump.getJumpType() == evmasm::AssemblyItem::JumpType::Ordinary, ""); + break; + case JumpType::IntoFunction: + jump.setJumpType(evmasm::AssemblyItem::JumpType::IntoFunction); + break; + case JumpType::OutOfFunction: + jump.setJumpType(evmasm::AssemblyItem::JumpType::OutOfFunction); + break; + } + m_assembly.append(std::move(jump)); +} + void CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, diff --git a/libyul/backends/evm/AsmCodeGen.h b/libyul/backends/evm/AsmCodeGen.h index e126ba2c0..6ff02009c 100644 --- a/libyul/backends/evm/AsmCodeGen.h +++ b/libyul/backends/evm/AsmCodeGen.h @@ -49,9 +49,9 @@ public: size_t newLabelId() override; size_t namedLabel(std::string const& _name) override; void appendLinkerSymbol(std::string const& _linkerSymbol) override; - void appendJump(int _stackDiffAfter) override; - void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; - void appendJumpToIf(LabelID _labelId) override; + void appendJump(int _stackDiffAfter, JumpType _jumpType) override; + void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override; + void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override; void appendBeginsub(LabelID, int) override; void appendJumpsub(LabelID, int, int) override; void appendReturnsub(int, int) override; @@ -66,6 +66,7 @@ public: private: static LabelID assemblyTagToIdentifier(evmasm::AssemblyItem const& _tag); + void appendJumpInstruction(evmasm::Instruction _instruction, JumpType _jumpType); evmasm::Assembly& m_assembly; std::map m_dataHashBySubId; diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index c13a16083..73194ef8a 100644 --- a/libyul/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -91,14 +91,14 @@ void EVMAssembly::appendLinkerSymbol(string const&) yulAssert(false, "Linker symbols not yet implemented."); } -void EVMAssembly::appendJump(int _stackDiffAfter) +void EVMAssembly::appendJump(int _stackDiffAfter, JumpType) { yulAssert(!m_evm15, "Plain JUMP used for EVM 1.5"); appendInstruction(evmasm::Instruction::JUMP); m_stackHeight += _stackDiffAfter; } -void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter) +void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) { if (m_evm15) { @@ -109,11 +109,11 @@ void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter) else { appendLabelReference(_labelId); - appendJump(_stackDiffAfter); + appendJump(_stackDiffAfter, _jumpType); } } -void EVMAssembly::appendJumpToIf(LabelID _labelId) +void EVMAssembly::appendJumpToIf(LabelID _labelId, JumpType) { if (m_evm15) { diff --git a/libyul/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h index da4f5c037..9ee974bbb 100644 --- a/libyul/backends/evm/EVMAssembly.h +++ b/libyul/backends/evm/EVMAssembly.h @@ -64,11 +64,11 @@ public: /// Append a jump instruction. /// @param _stackDiffAfter the stack adjustment after this instruction. - void appendJump(int _stackDiffAfter) override; + void appendJump(int _stackDiffAfter, JumpType _jumpType) override; /// Append a jump-to-immediate operation. - void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; + void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override; /// Append a jump-to-if-immediate operation. - void appendJumpToIf(LabelID _labelId) override; + void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override; /// Start a subroutine. void appendBeginsub(LabelID _labelId, int _arguments) override; /// Call a subroutine. diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index f57fd35f3..9182bb393 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -291,7 +291,8 @@ void CodeTransform::operator()(FunctionCall const& _call) { m_assembly.appendJumpTo( functionEntryID(_call.functionName.name, *function), - static_cast(function->returns.size() - function->arguments.size()) - 1 + static_cast(function->returns.size() - function->arguments.size()) - 1, + AbstractAssembly::JumpType::IntoFunction ); m_assembly.appendLabel(returnLabel); } @@ -511,7 +512,10 @@ void CodeTransform::operator()(FunctionDefinition const& _function) if (m_evm15) m_assembly.appendReturnsub(static_cast(_function.returnVariables.size()), stackHeightBefore); else - m_assembly.appendJump(stackHeightBefore - static_cast(_function.returnVariables.size())); + m_assembly.appendJump( + stackHeightBefore - static_cast(_function.returnVariables.size()), + AbstractAssembly::JumpType::OutOfFunction + ); m_assembly.setStackHeight(stackHeightBefore); } diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index 8e26014a9..ee280338d 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -70,25 +70,25 @@ void NoOutputAssembly::appendLinkerSymbol(string const&) yulAssert(false, "Linker symbols not yet implemented."); } -void NoOutputAssembly::appendJump(int _stackDiffAfter) +void NoOutputAssembly::appendJump(int _stackDiffAfter, JumpType) { yulAssert(!m_evm15, "Plain JUMP used for EVM 1.5"); appendInstruction(evmasm::Instruction::JUMP); m_stackHeight += _stackDiffAfter; } -void NoOutputAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter) +void NoOutputAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) { if (m_evm15) m_stackHeight += _stackDiffAfter; else { appendLabelReference(_labelId); - appendJump(_stackDiffAfter); + appendJump(_stackDiffAfter, _jumpType); } } -void NoOutputAssembly::appendJumpToIf(LabelID _labelId) +void NoOutputAssembly::appendJumpToIf(LabelID _labelId, JumpType) { if (m_evm15) m_stackHeight--; diff --git a/libyul/backends/evm/NoOutputAssembly.h b/libyul/backends/evm/NoOutputAssembly.h index 7101a30f5..ec7e5f2ec 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -58,9 +58,9 @@ public: LabelID namedLabel(std::string const& _name) override; void appendLinkerSymbol(std::string const& _name) override; - void appendJump(int _stackDiffAfter) override; - void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; - void appendJumpToIf(LabelID _labelId) override; + void appendJump(int _stackDiffAfter, JumpType _jumpType) override; + void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override; + void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override; void appendBeginsub(LabelID _labelId, int _arguments) override; void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override; void appendReturnsub(int _returns, int _stackDiffAfter) override; diff --git a/test/cmdlineTests/yul_stack_opt/output b/test/cmdlineTests/yul_stack_opt/output index e0ac5ad77..e5731f1e1 100644 --- a/test/cmdlineTests/yul_stack_opt/output +++ b/test/cmdlineTests/yul_stack_opt/output @@ -36,7 +36,8 @@ Binary representation: Text representation: /* "yul_stack_opt/input.sol":495:500 */ tag_1 - jump(tag_2) + tag_2 + jump // in tag_1: /* "yul_stack_opt/input.sol":425:500 */ pop @@ -56,7 +57,8 @@ tag_1: pop /* "yul_stack_opt/input.sol":572:577 */ tag_3 - jump(tag_2) + tag_2 + jump // in tag_3: /* "yul_stack_opt/input.sol":502:577 */ pop @@ -198,5 +200,5 @@ tag_5: swap14 swap15 swap16 - jump + jump // out tag_4: diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 885f0b2d3..23bf1a2f8 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -182,6 +182,30 @@ BOOST_AUTO_TEST_CASE(location_test) checkAssemblyLocations(items, locations); } + +BOOST_AUTO_TEST_CASE(jump_type) +{ + auto sourceCode = make_shared(R"( + contract C { + function f(uint a) public pure returns (uint t) { + assembly { + function g(x) -> y { if x { leave } y := 8 } + t := g(a) + } + } + } + )", ""); + AssemblyItems items = compileContract(sourceCode); + + string jumpTypes; + for (AssemblyItem const& item: items) + if (item.getJumpType() != AssemblyItem::JumpType::Ordinary) + jumpTypes += item.getJumpTypeAsString() + "\n"; + + BOOST_CHECK_EQUAL(jumpTypes, "[in]\n[out]\n[in]\n[out]\n"); +} + + BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/libyul/objectCompiler/function_series.yul b/test/libyul/objectCompiler/function_series.yul index 90dc88753..34c66ec46 100644 --- a/test/libyul/objectCompiler/function_series.yul +++ b/test/libyul/objectCompiler/function_series.yul @@ -13,12 +13,12 @@ object "Contract" { // tag_2: // /* "source":46:48 */ // tag_3: -// jump +// jump // out // /* "source":53:68 */ // tag_4: // /* "source":66:68 */ // tag_5: -// jump +// jump // out // tag_1: // /* "source":83:84 */ // 0x01 @@ -28,4 +28,4 @@ object "Contract" { // sstore // Bytecode: 6009565b5b565b5b565b6001600055 // Opcodes: PUSH1 0x9 JUMP JUMPDEST JUMPDEST JUMP JUMPDEST JUMPDEST JUMP JUMPDEST PUSH1 0x1 PUSH1 0x0 SSTORE -// SourceMappings: 33:15:0:-:0;;;46:2;;53:15;66:2;;;83:1;80;73:12 +// SourceMappings: 33:15:0:-:0;;;46:2;:::o;53:15::-;66:2;:::o;:::-;83:1;80;73:12 diff --git a/test/libyul/objectCompiler/jump_tags.yul b/test/libyul/objectCompiler/jump_tags.yul new file mode 100644 index 000000000..718b5a4a4 --- /dev/null +++ b/test/libyul/objectCompiler/jump_tags.yul @@ -0,0 +1,64 @@ +object "Contract" { + code { + function f() { g(1) } + function g(x) { if x { leave } g(add(x, 2)) } + g(1) + } +} + +// ---- +// Assembly: +// /* "source":33:54 */ +// jump(tag_1) +// tag_2: +// /* "source":48:52 */ +// tag_4 +// /* "source":50:51 */ +// 0x01 +// /* "source":48:52 */ +// tag_5 +// jump // in +// tag_4: +// /* "source":46:54 */ +// tag_3: +// jump // out +// /* "source":59:104 */ +// tag_5: +// /* "source":78:79 */ +// dup1 +// /* "source":75:77 */ +// iszero +// tag_7 +// jumpi +// /* "source":82:87 */ +// jump(tag_6) +// /* "source":75:77 */ +// tag_7: +// /* "source":90:102 */ +// tag_8 +// /* "source":99:100 */ +// 0x02 +// /* "source":96:97 */ +// dup3 +// /* "source":92:101 */ +// add +// /* "source":90:102 */ +// tag_5 +// jump // in +// tag_8: +// /* "source":73:104 */ +// tag_6: +// pop +// jump // out +// tag_1: +// /* "source":109:113 */ +// tag_9 +// /* "source":111:112 */ +// 0x01 +// /* "source":109:113 */ +// tag_5 +// jump // in +// tag_9: +// Bytecode: 6025565b600b6001600e565b5b565b80156017576022565b602160028201600e565b5b50565b602d6001600e565b +// Opcodes: PUSH1 0x25 JUMP JUMPDEST PUSH1 0xB PUSH1 0x1 PUSH1 0xE JUMP JUMPDEST JUMPDEST JUMP JUMPDEST DUP1 ISZERO PUSH1 0x17 JUMPI PUSH1 0x22 JUMP JUMPDEST PUSH1 0x21 PUSH1 0x2 DUP3 ADD PUSH1 0xE JUMP JUMPDEST JUMPDEST POP JUMP JUMPDEST PUSH1 0x2D PUSH1 0x1 PUSH1 0xE JUMP JUMPDEST +// SourceMappings: 33:21:0:-:0;;;48:4;50:1;48:4;:::i;:::-;46:8;:::o;59:45::-;78:1;75:2;;;82:5;;75:2;90:12;99:1;96;92:9;90:12;:::i;:::-;73:31;;:::o;:::-;109:4;111:1;109:4;:::i;:::-