Jump types for yul functions.

This commit is contained in:
chriseth 2020-06-22 14:22:01 +02:00
parent 9009335b3f
commit 4d2b9cd38f
14 changed files with 151 additions and 35 deletions

View File

@ -10,6 +10,7 @@ Compiler Features:
* NatSpec: Add fields "kind" and "version" to the JSON output. * NatSpec: Add fields "kind" and "version" to the JSON output.
* NatSpec: Inherit tags from unique base if derived function does not provide any. * NatSpec: Inherit tags from unique base if derived function does not provide any.
* Commandline Interface: Prevent some incompatible commandline options from being used together. * 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. * NatSpec: Support NatSpec comments on events.
* Yul Optimizer: Store knowledge about storage / memory after ``a := sload(x)`` / ``a := mload(x)``. * Yul Optimizer: Store knowledge about storage / memory after ``a := sload(x)`` / ``a := mload(x)``.
* SMTChecker: Support external calls to unknown code. * SMTChecker: Support external calls to unknown code.

View File

@ -146,7 +146,7 @@ void CompilerContext::callYulFunction(
m_externallyUsedYulFunctions.insert(_name); m_externallyUsedYulFunctions.insert(_name);
auto const retTag = pushNewTag(); auto const retTag = pushNewTag();
CompilerUtils(*this).moveIntoStack(_inArgs); CompilerUtils(*this).moveIntoStack(_inArgs);
appendJumpTo(namedTag(_name)); appendJumpTo(namedTag(_name), evmasm::AssemblyItem::JumpType::IntoFunction);
adjustStackOffset(static_cast<int>(_outArgs) - 1 - static_cast<int>(_inArgs)); adjustStackOffset(static_cast<int>(_outArgs) - 1 - static_cast<int>(_inArgs));
*this << retTag.tag(); *this << retTag.tag();
} }

View File

@ -50,6 +50,7 @@ class AbstractAssembly
public: public:
using LabelID = size_t; using LabelID = size_t;
using SubID = size_t; using SubID = size_t;
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
virtual ~AbstractAssembly() = default; virtual ~AbstractAssembly() = default;
@ -78,13 +79,13 @@ public:
/// Append a jump instruction. /// Append a jump instruction.
/// @param _stackDiffAfter the stack adjustment after this instruction. /// @param _stackDiffAfter the stack adjustment after this instruction.
/// This is helpful to stack height analysis if there is no continuing control flow. /// 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. /// Append a jump-to-immediate operation.
/// @param _stackDiffAfter the stack adjustment after this instruction. /// @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. /// 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 /// Start a subroutine identified by @a _labelId that takes @a _arguments
/// stack slots as arguments. /// stack slots as arguments.
virtual void appendBeginsub(LabelID _labelId, int _arguments) = 0; virtual void appendBeginsub(LabelID _labelId, int _arguments) = 0;

View File

@ -98,22 +98,22 @@ void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol)
m_assembly.appendLibraryAddress(_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); m_assembly.adjustDeposit(_stackDiffAfter);
} }
void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter) void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType)
{ {
appendLabelReference(_labelId); appendLabelReference(_labelId);
appendJump(_stackDiffAfter); appendJump(_stackDiffAfter, _jumpType);
} }
void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId) void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId, JumpType _jumpType)
{ {
appendLabelReference(_labelId); appendLabelReference(_labelId);
appendInstruction(evmasm::Instruction::JUMPI); appendJumpInstruction(evmasm::Instruction::JUMPI, _jumpType);
} }
void EthAssemblyAdapter::appendBeginsub(LabelID, int) void EthAssemblyAdapter::appendBeginsub(LabelID, int)
@ -189,6 +189,25 @@ EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(evmasm::
return LabelID(id); 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( void CodeGenerator::assemble(
Block const& _parsedData, Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo, AsmAnalysisInfo& _analysisInfo,

View File

@ -49,9 +49,9 @@ public:
size_t newLabelId() override; size_t newLabelId() override;
size_t namedLabel(std::string const& _name) override; size_t namedLabel(std::string const& _name) override;
void appendLinkerSymbol(std::string const& _linkerSymbol) override; void appendLinkerSymbol(std::string const& _linkerSymbol) override;
void appendJump(int _stackDiffAfter) override; void appendJump(int _stackDiffAfter, JumpType _jumpType) override;
void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override;
void appendJumpToIf(LabelID _labelId) override; void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
void appendBeginsub(LabelID, int) override; void appendBeginsub(LabelID, int) override;
void appendJumpsub(LabelID, int, int) override; void appendJumpsub(LabelID, int, int) override;
void appendReturnsub(int, int) override; void appendReturnsub(int, int) override;
@ -66,6 +66,7 @@ public:
private: private:
static LabelID assemblyTagToIdentifier(evmasm::AssemblyItem const& _tag); static LabelID assemblyTagToIdentifier(evmasm::AssemblyItem const& _tag);
void appendJumpInstruction(evmasm::Instruction _instruction, JumpType _jumpType);
evmasm::Assembly& m_assembly; evmasm::Assembly& m_assembly;
std::map<SubID, u256> m_dataHashBySubId; std::map<SubID, u256> m_dataHashBySubId;

View File

@ -91,14 +91,14 @@ void EVMAssembly::appendLinkerSymbol(string const&)
yulAssert(false, "Linker symbols not yet implemented."); 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"); yulAssert(!m_evm15, "Plain JUMP used for EVM 1.5");
appendInstruction(evmasm::Instruction::JUMP); appendInstruction(evmasm::Instruction::JUMP);
m_stackHeight += _stackDiffAfter; m_stackHeight += _stackDiffAfter;
} }
void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter) void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType)
{ {
if (m_evm15) if (m_evm15)
{ {
@ -109,11 +109,11 @@ void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter)
else else
{ {
appendLabelReference(_labelId); appendLabelReference(_labelId);
appendJump(_stackDiffAfter); appendJump(_stackDiffAfter, _jumpType);
} }
} }
void EVMAssembly::appendJumpToIf(LabelID _labelId) void EVMAssembly::appendJumpToIf(LabelID _labelId, JumpType)
{ {
if (m_evm15) if (m_evm15)
{ {

View File

@ -64,11 +64,11 @@ public:
/// Append a jump instruction. /// Append a jump instruction.
/// @param _stackDiffAfter the stack adjustment after this 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. /// 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. /// Append a jump-to-if-immediate operation.
void appendJumpToIf(LabelID _labelId) override; void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
/// Start a subroutine. /// Start a subroutine.
void appendBeginsub(LabelID _labelId, int _arguments) override; void appendBeginsub(LabelID _labelId, int _arguments) override;
/// Call a subroutine. /// Call a subroutine.

View File

@ -291,7 +291,8 @@ void CodeTransform::operator()(FunctionCall const& _call)
{ {
m_assembly.appendJumpTo( m_assembly.appendJumpTo(
functionEntryID(_call.functionName.name, *function), functionEntryID(_call.functionName.name, *function),
static_cast<int>(function->returns.size() - function->arguments.size()) - 1 static_cast<int>(function->returns.size() - function->arguments.size()) - 1,
AbstractAssembly::JumpType::IntoFunction
); );
m_assembly.appendLabel(returnLabel); m_assembly.appendLabel(returnLabel);
} }
@ -511,7 +512,10 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
if (m_evm15) if (m_evm15)
m_assembly.appendReturnsub(static_cast<int>(_function.returnVariables.size()), stackHeightBefore); m_assembly.appendReturnsub(static_cast<int>(_function.returnVariables.size()), stackHeightBefore);
else else
m_assembly.appendJump(stackHeightBefore - static_cast<int>(_function.returnVariables.size())); m_assembly.appendJump(
stackHeightBefore - static_cast<int>(_function.returnVariables.size()),
AbstractAssembly::JumpType::OutOfFunction
);
m_assembly.setStackHeight(stackHeightBefore); m_assembly.setStackHeight(stackHeightBefore);
} }

View File

@ -70,25 +70,25 @@ void NoOutputAssembly::appendLinkerSymbol(string const&)
yulAssert(false, "Linker symbols not yet implemented."); 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"); yulAssert(!m_evm15, "Plain JUMP used for EVM 1.5");
appendInstruction(evmasm::Instruction::JUMP); appendInstruction(evmasm::Instruction::JUMP);
m_stackHeight += _stackDiffAfter; m_stackHeight += _stackDiffAfter;
} }
void NoOutputAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter) void NoOutputAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType)
{ {
if (m_evm15) if (m_evm15)
m_stackHeight += _stackDiffAfter; m_stackHeight += _stackDiffAfter;
else else
{ {
appendLabelReference(_labelId); appendLabelReference(_labelId);
appendJump(_stackDiffAfter); appendJump(_stackDiffAfter, _jumpType);
} }
} }
void NoOutputAssembly::appendJumpToIf(LabelID _labelId) void NoOutputAssembly::appendJumpToIf(LabelID _labelId, JumpType)
{ {
if (m_evm15) if (m_evm15)
m_stackHeight--; m_stackHeight--;

View File

@ -58,9 +58,9 @@ public:
LabelID namedLabel(std::string const& _name) override; LabelID namedLabel(std::string const& _name) override;
void appendLinkerSymbol(std::string const& _name) override; void appendLinkerSymbol(std::string const& _name) override;
void appendJump(int _stackDiffAfter) override; void appendJump(int _stackDiffAfter, JumpType _jumpType) override;
void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override;
void appendJumpToIf(LabelID _labelId) override; void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
void appendBeginsub(LabelID _labelId, int _arguments) override; void appendBeginsub(LabelID _labelId, int _arguments) override;
void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override; void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override;
void appendReturnsub(int _returns, int _stackDiffAfter) override; void appendReturnsub(int _returns, int _stackDiffAfter) override;

View File

@ -36,7 +36,8 @@ Binary representation:
Text representation: Text representation:
/* "yul_stack_opt/input.sol":495:500 */ /* "yul_stack_opt/input.sol":495:500 */
tag_1 tag_1
jump(tag_2) tag_2
jump // in
tag_1: tag_1:
/* "yul_stack_opt/input.sol":425:500 */ /* "yul_stack_opt/input.sol":425:500 */
pop pop
@ -56,7 +57,8 @@ tag_1:
pop pop
/* "yul_stack_opt/input.sol":572:577 */ /* "yul_stack_opt/input.sol":572:577 */
tag_3 tag_3
jump(tag_2) tag_2
jump // in
tag_3: tag_3:
/* "yul_stack_opt/input.sol":502:577 */ /* "yul_stack_opt/input.sol":502:577 */
pop pop
@ -198,5 +200,5 @@ tag_5:
swap14 swap14
swap15 swap15
swap16 swap16
jump jump // out
tag_4: tag_4:

View File

@ -182,6 +182,30 @@ BOOST_AUTO_TEST_CASE(location_test)
checkAssemblyLocations(items, locations); checkAssemblyLocations(items, locations);
} }
BOOST_AUTO_TEST_CASE(jump_type)
{
auto sourceCode = make_shared<CharStream>(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() BOOST_AUTO_TEST_SUITE_END()
} // end namespaces } // end namespaces

View File

@ -13,12 +13,12 @@ object "Contract" {
// tag_2: // tag_2:
// /* "source":46:48 */ // /* "source":46:48 */
// tag_3: // tag_3:
// jump // jump // out
// /* "source":53:68 */ // /* "source":53:68 */
// tag_4: // tag_4:
// /* "source":66:68 */ // /* "source":66:68 */
// tag_5: // tag_5:
// jump // jump // out
// tag_1: // tag_1:
// /* "source":83:84 */ // /* "source":83:84 */
// 0x01 // 0x01
@ -28,4 +28,4 @@ object "Contract" {
// sstore // sstore
// Bytecode: 6009565b5b565b5b565b6001600055 // Bytecode: 6009565b5b565b5b565b6001600055
// Opcodes: PUSH1 0x9 JUMP JUMPDEST JUMPDEST JUMP JUMPDEST JUMPDEST JUMP JUMPDEST PUSH1 0x1 PUSH1 0x0 SSTORE // 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

View File

@ -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;:::-