mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add verbatim builtin.
This commit is contained in:
parent
2969bc0f3e
commit
e2d8005737
@ -1,7 +1,8 @@
|
||||
### 0.8.5 (unreleased)
|
||||
|
||||
Language Features:
|
||||
* Allowing conversion from ``bytes`` and ``bytes`` slices to ``bytes1``/.../``bytes32``.
|
||||
* Allowing conversion from ``bytes`` and ``bytes`` slices to ``bytes1``/.../``bytes32``.
|
||||
* Yul: Add ``verbatim`` builtin function to inject arbitrary bytecode.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
|
78
docs/yul.rst
78
docs/yul.rst
@ -976,6 +976,84 @@ within one Yul subobject. If at least one ``memoryguard`` call is found in a sub
|
||||
the additional optimiser steps will be run on it.
|
||||
|
||||
|
||||
verbatim
|
||||
^^^^^^^^
|
||||
|
||||
The set of ``verbatim...`` builtin functions 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_<n>i_<m>o("<data>", ...)``, 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
|
||||
|
||||
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
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
let x := calldataload(0)
|
||||
let double := verbatim_1i_1o(hex"600202", x)
|
||||
|
||||
This code will result in a ``dup1`` opcode to retrieve ``x``
|
||||
(the optimizer might directly re-use result of the
|
||||
``calldataload`` opcode, though)
|
||||
directly followed by ``600202``. The code is assumed to
|
||||
consume the copied value of ``x`` and produce the result
|
||||
on the top of the stack. The compiler then generates code
|
||||
to allocate a stack slot for ``double`` and store the result there.
|
||||
|
||||
As with all opcodes, the arguments are arranged on the stack
|
||||
with the leftmost argument on the top, while the return values
|
||||
are assumed to be laid out such that the rightmost variable is
|
||||
at the top of the stack.
|
||||
|
||||
Since ``verbatim`` can be used to generate arbitrary opcodes
|
||||
or even opcodes unknown to the Solidity compiler, care has to be taken
|
||||
when using ``verbatim`` together with the optimizer. Even when the
|
||||
optimizer is switched off, the code generator has to determine
|
||||
the stack layout, which means that e.g. using ``verbatim`` to modify
|
||||
the stack height can lead to undefined behaviour.
|
||||
|
||||
The following is a non-exhaustive list of restrictions on
|
||||
verbatim bytecode that are not checked by
|
||||
the compiler. Violations of these restrictions can result in
|
||||
undefined behaviour.
|
||||
|
||||
- Control-flow should not jump into or out of verbatim blocks,
|
||||
but it can jump within the same verbatim block.
|
||||
- Stack contents apart from the input and output parameters
|
||||
should not be accessed.
|
||||
- The stack height difference should be exactly ``m - n``
|
||||
(output slots minus input slots).
|
||||
- Verbatim bytecode cannot make any assumptions about the
|
||||
surrounding bytecode. All required parameters have to be
|
||||
passed in as stack variables.
|
||||
|
||||
The optimizer does not analyze verbatim bytecode and always
|
||||
assumes that it modifies all aspects of state and thus can only
|
||||
do very few optimizations across ``verbatim`` function calls.
|
||||
|
||||
The optimizer treats verbatim bytecode as an opaque block of code.
|
||||
It will not split it but might move, duplicate
|
||||
or combine it with identical verbatim bytecode blocks.
|
||||
If a verbatim bytecode block is unreachable by the control-flow,
|
||||
it can be removed.
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
During discussions about whether or not EVM improvements
|
||||
might break existing smart contracts, features inside ``verbatim``
|
||||
cannot receive the same consideration as those used by the Solidity
|
||||
compiler itself.
|
||||
|
||||
.. note::
|
||||
|
||||
To avoid confusion, all identifiers starting with the string ``verbatim`` are reserved
|
||||
and cannot be used for user-defined identifiers.
|
||||
|
||||
.. _yul-object:
|
||||
|
||||
Specification of Yul Object
|
||||
|
@ -33,9 +33,11 @@
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <range/v3/algorithm/any_of.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::evmasm;
|
||||
@ -317,6 +319,9 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices)
|
||||
case PushData:
|
||||
collection.append(createJsonValue("PUSH data", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data())));
|
||||
break;
|
||||
case VerbatimBytecode:
|
||||
collection.append(createJsonValue("VERBATIM", sourceIndex, i.location().start, i.location().end, toHex(i.verbatimData())));
|
||||
break;
|
||||
default:
|
||||
assertThrow(false, InvalidOpcode, "");
|
||||
}
|
||||
@ -482,7 +487,9 @@ map<u256, u256> Assembly::optimiseInternal(
|
||||
// function types that can be stored in storage.
|
||||
AssemblyItems optimisedItems;
|
||||
|
||||
bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem{Instruction::MSIZE}) != m_items.end());
|
||||
bool usesMSize = ranges::any_of(m_items, [](AssemblyItem const& _i) {
|
||||
return _i == AssemblyItem{Instruction::MSIZE} || _i.type() == VerbatimBytecode;
|
||||
});
|
||||
|
||||
auto iter = m_items.begin();
|
||||
while (iter != m_items.end())
|
||||
@ -682,6 +689,9 @@ LinkerObject const& Assembly::assemble() const
|
||||
ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size());
|
||||
ret.bytecode.resize(ret.bytecode.size() + 32);
|
||||
break;
|
||||
case VerbatimBytecode:
|
||||
ret.bytecode += i.verbatimData();
|
||||
break;
|
||||
case AssignImmutable:
|
||||
{
|
||||
auto const& offsets = immutableReferencesBySub[i.data()].second;
|
||||
|
@ -73,6 +73,8 @@ public:
|
||||
void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); }
|
||||
void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); }
|
||||
|
||||
void appendVerbatim(bytes const& _data, int _stackDifference) { append(AssemblyItem(_data, _stackDifference)); }
|
||||
|
||||
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
|
||||
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
|
||||
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
|
||||
|
@ -91,6 +91,8 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength) const
|
||||
return 1 + (3 + 32) * *m_immutableOccurrences;
|
||||
else
|
||||
return 1 + (3 + 32) * 1024; // 1024 occurrences are beyond the maximum code size anyways.
|
||||
case VerbatimBytecode:
|
||||
return m_verbatimBytecode->second.size();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -241,6 +243,9 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
|
||||
case UndefinedItem:
|
||||
assertThrow(false, AssemblyException, "Invalid assembly item.");
|
||||
break;
|
||||
case VerbatimBytecode:
|
||||
text = string("verbatimbytecode_") + util::toHex(m_verbatimBytecode->second);
|
||||
break;
|
||||
default:
|
||||
assertThrow(false, InvalidOpcode, "");
|
||||
}
|
||||
@ -309,6 +314,9 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
|
||||
case AssignImmutable:
|
||||
_out << " AssignImmutable";
|
||||
break;
|
||||
case VerbatimBytecode:
|
||||
_out << " Verbatim " << util::toHex(_item.verbatimData());
|
||||
break;
|
||||
case UndefinedItem:
|
||||
_out << " ???";
|
||||
break;
|
||||
|
@ -33,7 +33,8 @@
|
||||
namespace solidity::evmasm
|
||||
{
|
||||
|
||||
enum AssemblyItemType {
|
||||
enum AssemblyItemType
|
||||
{
|
||||
UndefinedItem,
|
||||
Operation,
|
||||
Push,
|
||||
@ -47,7 +48,8 @@ enum AssemblyItemType {
|
||||
PushLibraryAddress, ///< Push a currently unknown address of another (library) contract.
|
||||
PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer.
|
||||
PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor.
|
||||
AssignImmutable ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code.
|
||||
AssignImmutable, ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code.
|
||||
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
|
||||
};
|
||||
|
||||
class Assembly;
|
||||
@ -75,6 +77,12 @@ public:
|
||||
else
|
||||
m_data = std::make_shared<u256>(std::move(_data));
|
||||
}
|
||||
explicit AssemblyItem(bytes const& _verbatimData, int _deposit):
|
||||
m_type(VerbatimBytecode),
|
||||
m_instruction{},
|
||||
m_verbatimBytecode{{_deposit, _verbatimData}}
|
||||
{}
|
||||
|
||||
AssemblyItem(AssemblyItem const&) = default;
|
||||
AssemblyItem(AssemblyItem&&) = default;
|
||||
AssemblyItem& operator=(AssemblyItem const&) = default;
|
||||
@ -95,6 +103,8 @@ public:
|
||||
u256 const& data() const { assertThrow(m_type != Operation, util::Exception, ""); return *m_data; }
|
||||
void setData(u256 const& _data) { assertThrow(m_type != Operation, util::Exception, ""); m_data = std::make_shared<u256>(_data); }
|
||||
|
||||
bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return m_verbatimBytecode->second; }
|
||||
|
||||
/// @returns the instruction of this item (only valid if type() == Operation)
|
||||
Instruction instruction() const { assertThrow(m_type == Operation, util::Exception, ""); return m_instruction; }
|
||||
|
||||
@ -105,6 +115,8 @@ public:
|
||||
return false;
|
||||
if (type() == Operation)
|
||||
return instruction() == _other.instruction();
|
||||
else if (type() == VerbatimBytecode)
|
||||
return *m_verbatimBytecode == *_other.m_verbatimBytecode;
|
||||
else
|
||||
return data() == _other.data();
|
||||
}
|
||||
@ -116,6 +128,8 @@ public:
|
||||
return type() < _other.type();
|
||||
else if (type() == Operation)
|
||||
return instruction() < _other.instruction();
|
||||
else if (type() == VerbatimBytecode)
|
||||
return *m_verbatimBytecode == *_other.m_verbatimBytecode;
|
||||
else
|
||||
return data() < _other.data();
|
||||
}
|
||||
@ -137,7 +151,7 @@ public:
|
||||
size_t bytesRequired(size_t _addressLength) const;
|
||||
size_t arguments() const;
|
||||
size_t returnValues() const;
|
||||
size_t deposit() const { return returnValues() - arguments(); }
|
||||
size_t deposit() const { return m_type == VerbatimBytecode ? static_cast<size_t>(m_verbatimBytecode->first) : returnValues() - arguments(); }
|
||||
|
||||
/// @returns true if the assembly item can be used in a functional context.
|
||||
bool canBeFunctional() const;
|
||||
@ -162,6 +176,8 @@ private:
|
||||
AssemblyItemType m_type;
|
||||
Instruction m_instruction; ///< Only valid if m_type == Operation
|
||||
std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
|
||||
/// If m_type == VerbatimBytecode, this holds stack difference and verbatim bytecode.
|
||||
std::optional<std::pair<int, bytes>> m_verbatimBytecode;
|
||||
langutil::SourceLocation m_location;
|
||||
JumpType m_jumpType = JumpType::Ordinary;
|
||||
/// Pushed value for operations with data to be determined during assembly stage,
|
||||
|
@ -38,6 +38,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
|
||||
case Tag:
|
||||
case PushDeployTimeAddress:
|
||||
case AssignImmutable:
|
||||
case VerbatimBytecode:
|
||||
return true;
|
||||
case Push:
|
||||
case PushString:
|
||||
@ -164,6 +165,8 @@ bool SemanticInformation::reverts(Instruction _instruction)
|
||||
|
||||
bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
|
||||
{
|
||||
assertThrow(_item.type() != VerbatimBytecode, AssemblyException, "");
|
||||
|
||||
if (_item.type() != Operation)
|
||||
return true;
|
||||
|
||||
|
@ -369,16 +369,26 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
||||
);
|
||||
else if (*literalArgumentKind == LiteralKind::String)
|
||||
{
|
||||
if (
|
||||
_funCall.functionName.name.str() == "datasize" ||
|
||||
_funCall.functionName.name.str() == "dataoffset"
|
||||
)
|
||||
string functionName = _funCall.functionName.name.str();
|
||||
if (functionName == "datasize" || functionName == "dataoffset")
|
||||
{
|
||||
if (!m_dataNames.count(get<Literal>(arg).value))
|
||||
m_errorReporter.typeError(
|
||||
3517_error,
|
||||
get<Literal>(arg).location,
|
||||
"Unknown data object \"" + std::get<Literal>(arg).value.str() + "\"."
|
||||
);
|
||||
}
|
||||
else if (functionName.substr(0, "verbatim_"s.size()) == "verbatim_")
|
||||
{
|
||||
if (get<Literal>(arg).value.empty())
|
||||
m_errorReporter.typeError(
|
||||
1844_error,
|
||||
get<Literal>(arg).location,
|
||||
"The \"verbatim_*\" builtins cannot be used with empty bytecode."
|
||||
);
|
||||
}
|
||||
|
||||
argTypes.emplace_back(expectUnlimitedStringLiteral(get<Literal>(arg)));
|
||||
continue;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ struct BuiltinFunction
|
||||
std::vector<Type> returns;
|
||||
SideEffects sideEffects;
|
||||
ControlFlowSideEffects controlFlowSideEffects;
|
||||
/// If true, this is the msize instruction.
|
||||
/// If true, this is the msize instruction or might contain it.
|
||||
bool isMSize = false;
|
||||
/// Must be empty or the same length as the arguments.
|
||||
/// If set at index i, the i'th argument has to be a literal which means it can't be moved to variables.
|
||||
|
@ -77,6 +77,9 @@ public:
|
||||
/// Currently, we assume that the value is always a 20 byte number.
|
||||
virtual void appendLinkerSymbol(std::string const& _name) = 0;
|
||||
|
||||
/// Append raw bytes that stay untouched by the optimizer.
|
||||
virtual void appendVerbatim(bytes const& _data, int _stackDifference) = 0;
|
||||
|
||||
/// 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.
|
||||
|
@ -99,6 +99,11 @@ void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol)
|
||||
m_assembly.appendLibraryAddress(_linkerSymbol);
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::appendVerbatim(bytes const& _data, int _stackDifference)
|
||||
{
|
||||
m_assembly.appendVerbatim(_data, _stackDifference);
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::appendJump(int _stackDiffAfter, JumpType _jumpType)
|
||||
{
|
||||
appendJumpInstruction(evmasm::Instruction::JUMP, _jumpType);
|
||||
|
@ -50,6 +50,7 @@ public:
|
||||
size_t newLabelId() override;
|
||||
size_t namedLabel(std::string const& _name) override;
|
||||
void appendLinkerSymbol(std::string const& _linkerSymbol) override;
|
||||
void appendVerbatim(bytes const& _data, int _stackDifference) override;
|
||||
void appendJump(int _stackDiffAfter, JumpType _jumpType) override;
|
||||
void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override;
|
||||
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
|
||||
|
@ -34,6 +34,9 @@
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <range/v3/view/reverse.hpp>
|
||||
#include <range/v3/view/tail.hpp>
|
||||
|
||||
#include <regex>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@ -280,6 +283,12 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
||||
return builtins;
|
||||
}
|
||||
|
||||
regex const& verbatimPattern()
|
||||
{
|
||||
regex static const pattern{"verbatim_([1-9]?[0-9])i_([1-9]?[0-9])o"};
|
||||
return pattern;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -293,6 +302,12 @@ EVMDialect::EVMDialect(langutil::EVMVersion _evmVersion, bool _objectAccess):
|
||||
|
||||
BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const
|
||||
{
|
||||
if (m_objectAccess)
|
||||
{
|
||||
smatch match;
|
||||
if (regex_match(_name.str(), match, verbatimPattern()))
|
||||
return verbatimFunction(stoul(match[1]), stoul(match[2]));
|
||||
}
|
||||
auto it = m_functions.find(_name);
|
||||
if (it != m_functions.end())
|
||||
return &it->second;
|
||||
@ -302,6 +317,9 @@ BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const
|
||||
|
||||
bool EVMDialect::reservedIdentifier(YulString _name) const
|
||||
{
|
||||
if (m_objectAccess)
|
||||
if (_name.str().substr(0, "verbatim"s.size()) == "verbatim")
|
||||
return true;
|
||||
return m_reserved.count(_name) != 0;
|
||||
}
|
||||
|
||||
@ -342,6 +360,38 @@ SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instructio
|
||||
};
|
||||
}
|
||||
|
||||
BuiltinFunctionForEVM const* EVMDialect::verbatimFunction(size_t _arguments, size_t _returnVariables) const
|
||||
{
|
||||
pair<size_t, size_t> key{_arguments, _returnVariables};
|
||||
shared_ptr<BuiltinFunctionForEVM const>& function = m_verbatimFunctions[key];
|
||||
if (!function)
|
||||
{
|
||||
BuiltinFunctionForEVM builtinFunction = createFunction(
|
||||
"verbatim_" + to_string(_arguments) + "i_" + to_string(_returnVariables) + "o",
|
||||
1 + _arguments,
|
||||
_returnVariables,
|
||||
SideEffects::worst(),
|
||||
vector<optional<LiteralKind>>{LiteralKind::String} + vector<optional<LiteralKind>>(_arguments),
|
||||
[=](
|
||||
FunctionCall const& _call,
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext&,
|
||||
std::function<void(Expression const&)> _visitExpression
|
||||
) {
|
||||
yulAssert(_call.arguments.size() == (1 + _arguments), "");
|
||||
for (Expression const& arg: _call.arguments | ranges::views::tail | ranges::views::reverse)
|
||||
_visitExpression(arg);
|
||||
Expression const& bytecode = _call.arguments.front();
|
||||
int diff = static_cast<int>(_returnVariables) - static_cast<int>(_arguments);
|
||||
_assembly.appendVerbatim(asBytes(std::get<Literal>(bytecode).value.str()), diff);
|
||||
}
|
||||
).second;
|
||||
builtinFunction.isMSize = true;
|
||||
function = make_shared<BuiltinFunctionForEVM const>(move(builtinFunction));
|
||||
}
|
||||
return function.get();
|
||||
}
|
||||
|
||||
EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectAccess):
|
||||
EVMDialect(_evmVersion, _objectAccess)
|
||||
{
|
||||
|
@ -93,9 +93,12 @@ struct EVMDialect: public Dialect
|
||||
static SideEffects sideEffectsOfInstruction(evmasm::Instruction _instruction);
|
||||
|
||||
protected:
|
||||
BuiltinFunctionForEVM const* verbatimFunction(size_t _arguments, size_t _returnVariables) const;
|
||||
|
||||
bool const m_objectAccess;
|
||||
langutil::EVMVersion const m_evmVersion;
|
||||
std::map<YulString, BuiltinFunctionForEVM> m_functions;
|
||||
std::map<std::pair<size_t, size_t>, std::shared_ptr<BuiltinFunctionForEVM const>> mutable m_verbatimFunctions;
|
||||
std::set<YulString> m_reserved;
|
||||
};
|
||||
|
||||
|
@ -69,6 +69,11 @@ void NoOutputAssembly::appendLinkerSymbol(string const&)
|
||||
yulAssert(false, "Linker symbols not yet implemented.");
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendVerbatim(bytes const&, int _stackDifference)
|
||||
{
|
||||
m_stackHeight += _stackDifference;
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendJump(int _stackDiffAfter, JumpType)
|
||||
{
|
||||
appendInstruction(evmasm::Instruction::JUMP);
|
||||
|
@ -58,6 +58,7 @@ public:
|
||||
LabelID newLabelId() override;
|
||||
LabelID namedLabel(std::string const& _name) override;
|
||||
void appendLinkerSymbol(std::string const& _name) override;
|
||||
void appendVerbatim(bytes const& _data, int _stackDifference) override;
|
||||
|
||||
void appendJump(int _stackDiffAfter, JumpType _jumpType) override;
|
||||
void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override;
|
||||
|
@ -131,7 +131,8 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Class that can be used to find out if certain code contains the MSize instruction.
|
||||
* Class that can be used to find out if certain code contains the MSize instruction
|
||||
* or a verbatim bytecode builtin (which is always assumed that it could contain MSize).
|
||||
*
|
||||
* Note that this is a purely syntactic property meaning that even if this is false,
|
||||
* the code can still contain calls to functions that contain the msize instruction.
|
||||
|
1
test/cmdlineTests/yul_verbatim/args
Normal file
1
test/cmdlineTests/yul_verbatim/args
Normal file
@ -0,0 +1 @@
|
||||
--strict-assembly
|
1
test/cmdlineTests/yul_verbatim/err
Normal file
1
test/cmdlineTests/yul_verbatim/err
Normal file
@ -0,0 +1 @@
|
||||
Warning: Yul is still experimental. Please use the output with care.
|
1
test/cmdlineTests/yul_verbatim/exit
Normal file
1
test/cmdlineTests/yul_verbatim/exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
11
test/cmdlineTests/yul_verbatim/input.yul
Normal file
11
test/cmdlineTests/yul_verbatim/input.yul
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
let x := 2
|
||||
let y := sub(x, 2)
|
||||
let t := verbatim_2i_1o("abc", x, y)
|
||||
sstore(t, x)
|
||||
let r := verbatim_0i_1o("def")
|
||||
verbatim_0i_0o("xyz")
|
||||
// more than 32 bytes
|
||||
verbatim_0i_0o(hex"01020304050607090001020304050607090001020304050607090001020102030405060709000102030405060709000102030405060709000102")
|
||||
r := 9
|
||||
}
|
56
test/cmdlineTests/yul_verbatim/output
Normal file
56
test/cmdlineTests/yul_verbatim/output
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
======= yul_verbatim/input.yul (EVM) =======
|
||||
|
||||
Pretty printed source:
|
||||
object "object" {
|
||||
code {
|
||||
let x := 2
|
||||
let y := sub(x, 2)
|
||||
let t := verbatim_2i_1o("abc", x, y)
|
||||
sstore(t, x)
|
||||
let r := verbatim_0i_1o("def")
|
||||
verbatim_0i_0o("xyz")
|
||||
verbatim_0i_0o("\x01\x02\x03\x04\x05\x06\x07\t\x00\x01\x02\x03\x04\x05\x06\x07\t\x00\x01\x02\x03\x04\x05\x06\x07\t\x00\x01\x02\x01\x02\x03\x04\x05\x06\x07\t\x00\x01\x02\x03\x04\x05\x06\x07\t\x00\x01\x02\x03\x04\x05\x06\x07\t\x00\x01\x02")
|
||||
r := 9
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary representation:
|
||||
600260028103808261626382815564656678797a010203040506070900010203040506070900010203040506070900010201020304050607090001020304050607090001020304050607090001026009905050505050
|
||||
|
||||
Text representation:
|
||||
/* "yul_verbatim/input.yul":15:16 */
|
||||
0x02
|
||||
/* "yul_verbatim/input.yul":37:38 */
|
||||
0x02
|
||||
/* "yul_verbatim/input.yul":34:35 */
|
||||
dup2
|
||||
/* "yul_verbatim/input.yul":30:39 */
|
||||
sub
|
||||
/* "yul_verbatim/input.yul":78:79 */
|
||||
dup1
|
||||
/* "yul_verbatim/input.yul":75:76 */
|
||||
dup3
|
||||
verbatimbytecode_616263
|
||||
/* "yul_verbatim/input.yul":95:96 */
|
||||
dup3
|
||||
/* "yul_verbatim/input.yul":92:93 */
|
||||
dup2
|
||||
/* "yul_verbatim/input.yul":85:97 */
|
||||
sstore
|
||||
verbatimbytecode_646566
|
||||
/* "yul_verbatim/input.yul":137:158 */
|
||||
verbatimbytecode_78797a
|
||||
/* "yul_verbatim/input.yul":189:326 */
|
||||
verbatimbytecode_01020304050607090001020304050607090001020304050607090001020102030405060709000102030405060709000102030405060709000102
|
||||
/* "yul_verbatim/input.yul":336:337 */
|
||||
0x09
|
||||
/* "yul_verbatim/input.yul":331:337 */
|
||||
swap1
|
||||
pop
|
||||
/* "yul_verbatim/input.yul":0:339 */
|
||||
pop
|
||||
pop
|
||||
pop
|
||||
pop
|
1
test/cmdlineTests/yul_verbatim_msize/args
Normal file
1
test/cmdlineTests/yul_verbatim_msize/args
Normal file
@ -0,0 +1 @@
|
||||
--strict-assembly --optimize
|
1
test/cmdlineTests/yul_verbatim_msize/err
Normal file
1
test/cmdlineTests/yul_verbatim_msize/err
Normal file
@ -0,0 +1 @@
|
||||
Warning: Yul is still experimental. Please use the output with care.
|
1
test/cmdlineTests/yul_verbatim_msize/exit
Normal file
1
test/cmdlineTests/yul_verbatim_msize/exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
7
test/cmdlineTests/yul_verbatim_msize/input.yul
Normal file
7
test/cmdlineTests/yul_verbatim_msize/input.yul
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
// The optimizer assumes verbatim could contain msize,
|
||||
// so it cannot optimize the mload away.
|
||||
let x := mload(0x2000)
|
||||
verbatim_0i_0o("")
|
||||
sstore(0, 2)
|
||||
}
|
32
test/cmdlineTests/yul_verbatim_msize/output
Normal file
32
test/cmdlineTests/yul_verbatim_msize/output
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
======= yul_verbatim_msize/input.yul (EVM) =======
|
||||
|
||||
Pretty printed source:
|
||||
object "object" {
|
||||
code {
|
||||
{
|
||||
pop(mload(0x2000))
|
||||
verbatim_0i_0o("aa")
|
||||
sstore(0, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary representation:
|
||||
612000515061616002600055
|
||||
|
||||
Text representation:
|
||||
/* "yul_verbatim_msize/input.yul":125:131 */
|
||||
0x2000
|
||||
/* "yul_verbatim_msize/input.yul":119:132 */
|
||||
mload
|
||||
pop
|
||||
/* "yul_verbatim_msize/input.yul":137:157 */
|
||||
verbatimbytecode_6161
|
||||
/* "yul_verbatim_msize/input.yul":172:173 */
|
||||
0x02
|
||||
/* "yul_verbatim_msize/input.yul":169:170 */
|
||||
0x00
|
||||
/* "yul_verbatim_msize/input.yul":162:174 */
|
||||
sstore
|
@ -33,6 +33,8 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <range/v3/algorithm/any_of.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
@ -67,7 +69,9 @@ namespace
|
||||
{
|
||||
AssemblyItems input = addDummyLocations(_input);
|
||||
|
||||
bool usesMsize = (find(_input.begin(), _input.end(), AssemblyItem{Instruction::MSIZE}) != _input.end());
|
||||
bool usesMsize = ranges::any_of(_input, [](AssemblyItem const& _i) {
|
||||
return _i == AssemblyItem{Instruction::MSIZE} || _i.type() == VerbatimBytecode;
|
||||
});
|
||||
evmasm::CommonSubexpressionEliminator cse(_state);
|
||||
BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), usesMsize) == input.end());
|
||||
AssemblyItems output = cse.getOptimizedItems();
|
||||
|
@ -0,0 +1,12 @@
|
||||
contract C {
|
||||
function f() pure public {
|
||||
assembly {
|
||||
let x := verbatim_1o(hex"6001")
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// optimize-yul: true
|
||||
// ----
|
||||
// DeclarationError 4619: (84-95): Function "verbatim_1o" not found.
|
||||
// DeclarationError 3812: (75-106): Variable count mismatch for declaration of "x": 1 variables and 0 values.
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
verbatim_0i_0o(hex"")
|
||||
}
|
||||
// ====
|
||||
// dialect: evm
|
||||
// ----
|
||||
// TypeError 1844: (21-26): The "verbatim_*" builtins cannot be used with empty bytecode.
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
let x := verbatim_01i_1o(hex"aa", 0)
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 4619: (15-30): Function "verbatim_01i_1o" not found.
|
||||
// DeclarationError 3812: (6-42): Variable count mismatch for declaration of "x": 1 variables and 0 values.
|
6
test/libyul/yulSyntaxTests/invalid/verbatim_reserved.yul
Normal file
6
test/libyul/yulSyntaxTests/invalid/verbatim_reserved.yul
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
let verbatim := 2
|
||||
let verbatim_1i_2o := 3
|
||||
}
|
||||
// ----
|
||||
// ParserError 5568: (32-46): Cannot use builtin function name "verbatim_1i_2o" as identifier name.
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
let verbatim := 2
|
||||
let verbatim_abc := 3
|
||||
function verbatim_fun() {}
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 5017: (10-18): The identifier "verbatim" is reserved and can not be used.
|
||||
// DeclarationError 5017: (32-44): The identifier "verbatim_abc" is reserved and can not be used.
|
||||
// DeclarationError 5017: (54-80): The identifier "verbatim_fun" is reserved and can not be used.
|
5
test/libyul/yulSyntaxTests/verbatim_leading_zero.yul
Normal file
5
test/libyul/yulSyntaxTests/verbatim_leading_zero.yul
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
let verbatim_01i_02o := 2
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 5017: (10-26): The identifier "verbatim_01i_02o" is reserved and can not be used.
|
Loading…
Reference in New Issue
Block a user