aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md1
-rw-r--r--circle.yml108
-rw-r--r--docs/abi-spec.rst8
-rw-r--r--docs/assembly.rst272
-rw-r--r--libevmasm/ConstantOptimiser.h1
-rw-r--r--libevmasm/PeepholeOptimiser.h2
-rw-r--r--libjulia/optimiser/ASTCopier.h3
-rw-r--r--libjulia/optimiser/ASTWalker.h2
-rw-r--r--libsolidity/ast/AST.h4
-rw-r--r--libsolidity/ast/ASTVisitor.h2
-rw-r--r--libsolidity/ast/Types.h1
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp102
-rw-r--r--libsolidity/codegen/ABIFunctions.h8
-rw-r--r--libsolidity/codegen/CompilerContext.h3
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp23
-rw-r--r--libsolidity/codegen/CompilerUtils.h2
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp16
-rw-r--r--libsolidity/codegen/LValue.cpp2
-rw-r--r--libsolidity/formal/SolverInterface.h1
-rw-r--r--libsolidity/formal/SymbolicVariable.h1
-rw-r--r--libsolidity/interface/GasEstimator.cpp19
-rwxr-xr-xscripts/tests.sh29
-rw-r--r--test/ExecutionFramework.h1
-rw-r--r--test/libsolidity/AnalysisFramework.h1
-rw-r--r--test/libsolidity/Assembly.cpp5
25 files changed, 263 insertions, 354 deletions
diff --git a/Changelog.md b/Changelog.md
index 1cb96833..817365b9 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,6 +2,7 @@
Features:
* Build System: Update internal dependency of jsoncpp to 1.8.4, which introduces more strictness and reduces memory usage.
+ * Code Generator: Use native shift instructions on target Constantinople.
* Optimizer: Remove unnecessary masking of the result of known short instructions (``ADDRESS``, ``CALLER``, ``ORIGIN`` and ``COINBASE``).
* Type Checker: Deprecate the ``years`` unit denomination and raise a warning for it (or an error as experimental 0.5.0 feature).
* Type Checker: Make literals (without explicit type casting) an error for tight packing as experimental 0.5.0 feature.
diff --git a/circle.yml b/circle.yml
index f97b619a..e29f3e4a 100644
--- a/circle.yml
+++ b/circle.yml
@@ -4,6 +4,32 @@ defaults:
filters:
tags:
only: /.*/
+ - setup_prerelease_commit_hash: &setup_prerelease_commit_hash
+ name: Store commit hash and prerelease
+ command: |
+ if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
+ echo -n "$CIRCLE_SHA1" > commit_hash.txt
+ - run_build: &run_build
+ name: Build
+ command: |
+ mkdir -p build
+ cd build
+ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ make -j4
+ - run_tests: &run_tests
+ name: Tests
+ command: scripts/tests.sh --junit_report test_results
+ environment:
+ TERM: dumb
+ - solc_artifact: &solc_artifact
+ path: build/solc/solc
+ destination: solc
+ - all_artifacts: &all_artifacts
+ root: build
+ paths:
+ - solc/solc
+ - test/soltest
+ - test/tools/solfuzzer
version: 2
jobs:
@@ -87,7 +113,7 @@ jobs:
command: |
. /usr/local/nvm/nvm.sh
test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js
- build_x86:
+ build_x86_linux:
docker:
- image: buildpack-deps:artful
steps:
@@ -97,29 +123,31 @@ jobs:
command: |
apt-get -qq update
apt-get -qy install cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev
+ - run: *setup_prerelease_commit_hash
+ - run: *run_build
+ - store_artifacts: *solc_artifact
+ - persist_to_workspace: *all_artifacts
+
+ build_x86_mac:
+ macos:
+ xcode: "9.0"
+ steps:
+ - checkout
- run:
- name: Store commit hash and prerelease
- command: |
- if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
- echo -n "$CIRCLE_SHA1" > commit_hash.txt
- - run:
- name: Build
+ name: Install build dependencies
command: |
- mkdir -p build
- cd build
- cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
- make -j4
- - store_artifacts:
- path: build/solc/solc
- destination: solc
- - persist_to_workspace:
- root: build
- paths:
- - solc/solc
- - test/soltest
- - test/tools/solfuzzer
+ brew update
+ brew upgrade
+ brew unlink python
+ brew install z3
+ brew install boost
+ brew install cmake
+ - run: *setup_prerelease_commit_hash
+ - run: *run_build
+ - store_artifacts: *solc_artifact
+ - persist_to_workspace: *all_artifacts
- test_x86:
+ test_x86_linux:
docker:
- image: buildpack-deps:artful
steps:
@@ -132,9 +160,26 @@ jobs:
apt-get -qq update
apt-get -qy install libz3-dev libleveldb1v5
- run: mkdir -p test_results
+ - run: *run_tests
+ - store_test_results:
+ path: test_results/
+
+ test_x86_mac:
+ macos:
+ xcode: "9.0"
+ steps:
+ - checkout
+ - attach_workspace:
+ at: build
- run:
- name: Tests
- command: scripts/tests.sh --junit_report test_results
+ name: Install dependencies
+ command: |
+ brew update
+ brew upgrade
+ brew unlink python
+ brew install z3
+ - run: mkdir -p test_results
+ - run: *run_tests
- store_test_results:
path: test_results/
@@ -148,11 +193,7 @@ jobs:
command: |
apt-get -qq update
apt-get -qy install python-sphinx
- - run:
- name: Store commit hash and prerelease
- command: |
- if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
- echo -n "$CIRCLE_SHA1" > commit_hash.txt
+ - run: *setup_prerelease_commit_hash
- run:
name: Build documentation
command: ./scripts/docs.sh
@@ -173,9 +214,14 @@ workflows:
<<: *build_on_tags
requires:
- build_emscripten
- - build_x86: *build_on_tags
- - test_x86:
+ - build_x86_linux: *build_on_tags
+ - build_x86_mac: *build_on_tags
+ - test_x86_linux:
+ <<: *build_on_tags
+ requires:
+ - build_x86_linux
+ - test_x86_mac:
<<: *build_on_tags
requires:
- - build_x86
+ - build_x86_mac
- docs: *build_on_tags
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index 98301fdc..b0b16e28 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -77,7 +77,7 @@ of them inside parentheses, separated by commas:
- ``(T1,T2,...,Tn)``: tuple consisting of the types ``T1``, ..., ``Tn``, ``n >= 0``
-It is possible to form tuples of tuples, arrays of tuples and so on.
+It is possible to form tuples of tuples, arrays of tuples and so on. It is also possible to form zero-tuples (where ``n == 0``).
.. note::
Solidity supports all the types presented above with the same names with the exception of tuples. The ABI tuple type is utilised for encoding Solidity ``structs``.
@@ -117,7 +117,7 @@ on the type of ``X`` being
- ``(T1,...,Tk)`` for ``k >= 0`` and any types ``T1``, ..., ``Tk``
- ``enc(X) = head(X(1)) ... head(X(k-1)) tail(X(0)) ... tail(X(k-1))``
+ ``enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))``
where ``X(i)`` is the ``ith`` component of the value, and
``head`` and ``tail`` are defined for ``Ti`` being a static type as
@@ -126,7 +126,7 @@ on the type of ``X`` being
and as
- ``head(X(i)) = enc(len(head(X(0)) ... head(X(k-1)) tail(X(0)) ... tail(X(i-1))))``
+ ``head(X(i)) = enc(len( head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1)) ))``
``tail(X(i)) = enc(X(i))``
otherwise, i.e. if ``Ti`` is a dynamic type.
@@ -137,7 +137,7 @@ on the type of ``X`` being
- ``T[k]`` for any ``T`` and ``k``:
- ``enc(X) = enc((X[0], ..., X[k-1]))``
+ ``enc(X) = enc((X[1], ..., X[k]))``
i.e. it is encoded as if it were a tuple with ``k`` elements
of the same type.
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 978e71e3..443cb7da 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -418,6 +418,9 @@ changes during the call, and thus references to local variables will be wrong.
Labels
------
+.. note::
+ Labels are deprecated. Please use functions, loops, if or switch statements instead.
+
Another problem in EVM assembly is that ``jump`` and ``jumpi`` use absolute addresses
which can change easily. Solidity inline assembly provides labels to make the use of
jumps easier. Note that labels are a low-level feature and it is possible to write
@@ -519,6 +522,10 @@ is performed by replacing the variable's value on the stack by the new value.
=: v // instruction style assignment, puts the result of sload(10) into v
}
+.. note::
+ Instruction-style assignment is deprecated.
+
+
If
--
@@ -693,9 +700,9 @@ the form ``mul(add(x, y), 7)`` are preferred over pure opcode statements like
``7 y x add mul`` because in the first form, it is much easier to see which
operand is used for which opcode.
-The second goal is achieved by introducing a desugaring phase that only removes
-the higher level constructs in a very regular way and still allows inspecting
-the generated low-level assembly code. The only non-local operation performed
+The second goal is achieved by compiling the
+higher level constructs to bytecode in a very regular way.
+The only non-local operation performed
by the assembler is name lookup of user-defined identifiers (functions, variables, ...),
which follow very simple and regular scoping rules and cleanup of local variables from the stack.
@@ -716,8 +723,6 @@ keep track of the current so-called stack height. Since all local variables
are removed at the end of a block, the stack height before and after the block
should be the same. If this is not the case, a warning is issued.
-Why do we use higher-level constructs like ``switch``, ``for`` and functions:
-
Using ``switch``, ``for`` and functions, it should be possible to write
complex code without using ``jump`` or ``jumpi`` manually. This makes it much
easier to analyze the control flow, which allows for improved formal
@@ -726,13 +731,11 @@ verification and optimization.
Furthermore, if manual jumps are allowed, computing the stack height is rather complicated.
The position of all local variables on the stack needs to be known, otherwise
neither references to local variables nor removing local variables automatically
-from the stack at the end of a block will work properly. The desugaring
-mechanism correctly inserts operations at unreachable blocks that adjust the
-stack height properly in case of jumps that do not have a continuing control flow.
+from the stack at the end of a block will work properly.
Example:
-We will follow an example compilation from Solidity to desugared assembly.
+We will follow an example compilation from Solidity to assembly.
We consider the runtime bytecode of the following Solidity program::
pragma solidity ^0.4.16;
@@ -772,99 +775,9 @@ The following assembly will be generated::
}
}
-After the desugaring phase it looks as follows::
-
- {
- mstore(0x40, 0x60)
- {
- let $0 := div(calldataload(0), exp(2, 226))
- jumpi($case1, eq($0, 0xb3de648b))
- jump($caseDefault)
- $case1:
- {
- // the function call - we put return label and arguments on the stack
- $ret1 calldataload(4) jump(f)
- // This is unreachable code. Opcodes are added that mirror the
- // effect of the function on the stack height: Arguments are
- // removed and return values are introduced.
- pop pop
- let r := 0
- $ret1: // the actual return point
- $ret2 0x20 jump($allocate)
- pop pop let ret := 0
- $ret2:
- mstore(ret, r)
- return(ret, 0x20)
- // although it is useless, the jump is automatically inserted,
- // since the desugaring process is a purely syntactic operation that
- // does not analyze control-flow
- jump($endswitch)
- }
- $caseDefault:
- {
- revert(0, 0)
- jump($endswitch)
- }
- $endswitch:
- }
- jump($afterFunction)
- allocate:
- {
- // we jump over the unreachable code that introduces the function arguments
- jump($start)
- let $retpos := 0 let size := 0
- $start:
- // output variables live in the same scope as the arguments and is
- // actually allocated.
- let pos := 0
- {
- pos := mload(0x40)
- mstore(0x40, add(pos, size))
- }
- // This code replaces the arguments by the return values and jumps back.
- swap1 pop swap1 jump
- // Again unreachable code that corrects stack height.
- 0 0
- }
- f:
- {
- jump($start)
- let $retpos := 0 let x := 0
- $start:
- let y := 0
- {
- let i := 0
- $for_begin:
- jumpi($for_end, iszero(lt(i, x)))
- {
- y := mul(2, y)
- }
- $for_continue:
- { i := add(i, 1) }
- jump($for_begin)
- $for_end:
- } // Here, a pop instruction will be inserted for i
- swap1 pop swap1 jump
- 0 0
- }
- $afterFunction:
- stop
- }
-
-
-Assembly happens in four stages:
-1. Parsing
-2. Desugaring (removes switch, for and functions)
-3. Opcode stream generation
-4. Bytecode generation
-
-We will specify steps one to three in a pseudo-formal way. More formal
-specifications will follow.
-
-
-Parsing / Grammar
------------------
+Assembly Grammar
+----------------
The tasks of the parser are the following:
@@ -922,160 +835,3 @@ Grammar::
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
HexNumber = '0x' [0-9a-fA-F]+
DecimalNumber = [0-9]+
-
-
-Desugaring
-----------
-
-An AST transformation removes for, switch and function constructs. The result
-is still parseable by the same parser, but it will not use certain constructs.
-If jumpdests are added that are only jumped to and not continued at, information
-about the stack content is added, unless no local variables of outer scopes are
-accessed or the stack height is the same as for the previous instruction.
-
-Pseudocode::
-
- desugar item: AST -> AST =
- match item {
- AssemblyFunctionDefinition('function' name '(' arg1, ..., argn ')' '->' ( '(' ret1, ..., retm ')' body) ->
- <name>:
- {
- jump($<name>_start)
- let $retPC := 0 let argn := 0 ... let arg1 := 0
- $<name>_start:
- let ret1 := 0 ... let retm := 0
- { desugar(body) }
- swap and pop items so that only ret1, ... retm, $retPC are left on the stack
- jump
- 0 (1 + n times) to compensate removal of arg1, ..., argn and $retPC
- }
- AssemblyFor('for' { init } condition post body) ->
- {
- init // cannot be its own block because we want variable scope to extend into the body
- // find I such that there are no labels $forI_*
- $forI_begin:
- jumpi($forI_end, iszero(condition))
- { body }
- $forI_continue:
- { post }
- jump($forI_begin)
- $forI_end:
- }
- 'break' ->
- {
- // find nearest enclosing scope with label $forI_end
- pop all local variables that are defined at the current point
- but not at $forI_end
- jump($forI_end)
- 0 (as many as variables were removed above)
- }
- 'continue' ->
- {
- // find nearest enclosing scope with label $forI_continue
- pop all local variables that are defined at the current point
- but not at $forI_continue
- jump($forI_continue)
- 0 (as many as variables were removed above)
- }
- AssemblySwitch(switch condition cases ( default: defaultBlock )? ) ->
- {
- // find I such that there is no $switchI* label or variable
- let $switchI_value := condition
- for each of cases match {
- case val: -> jumpi($switchI_caseJ, eq($switchI_value, val))
- }
- if default block present: ->
- { defaultBlock jump($switchI_end) }
- for each of cases match {
- case val: { body } -> $switchI_caseJ: { body jump($switchI_end) }
- }
- $switchI_end:
- }
- FunctionalAssemblyExpression( identifier(arg1, arg2, ..., argn) ) ->
- {
- if identifier is function <name> with n args and m ret values ->
- {
- // find I such that $funcallI_* does not exist
- $funcallI_return argn ... arg2 arg1 jump(<name>)
- pop (n + 1 times)
- if the current context is `let (id1, ..., idm) := f(...)` ->
- let id1 := 0 ... let idm := 0
- $funcallI_return:
- else ->
- 0 (m times)
- $funcallI_return:
- turn the functional expression that leads to the function call
- into a statement stream
- }
- else -> desugar(children of node)
- }
- default node ->
- desugar(children of node)
- }
-
-Opcode Stream Generation
-------------------------
-
-During opcode stream generation, we keep track of the current stack height
-in a counter,
-so that accessing stack variables by name is possible. The stack height is modified with every opcode
-that modifies the stack and with every label that is annotated with a stack
-adjustment. Every time a new
-local variable is introduced, it is registered together with the current
-stack height. If a variable is accessed (either for copying its value or for
-assignment), the appropriate ``DUP`` or ``SWAP`` instruction is selected depending
-on the difference between the current stack height and the
-stack height at the point the variable was introduced.
-
-Pseudocode::
-
- codegen item: AST -> opcode_stream =
- match item {
- AssemblyBlock({ items }) ->
- join(codegen(item) for item in items)
- if last generated opcode has continuing control flow:
- POP for all local variables registered at the block (including variables
- introduced by labels)
- warn if the stack height at this point is not the same as at the start of the block
- Identifier(id) ->
- lookup id in the syntactic stack of blocks
- match type of id
- Local Variable ->
- DUPi where i = 1 + stack_height - stack_height_of_identifier(id)
- Label ->
- // reference to be resolved during bytecode generation
- PUSH<bytecode position of label>
- SubAssembly ->
- PUSH<bytecode position of subassembly data>
- FunctionalAssemblyExpression(id ( arguments ) ) ->
- join(codegen(arg) for arg in arguments.reversed())
- id (which has to be an opcode, might be a function name later)
- AssemblyLocalDefinition(let (id1, ..., idn) := expr) ->
- register identifiers id1, ..., idn as locals in current block at current stack height
- codegen(expr) - assert that expr returns n items to the stack
- FunctionalAssemblyAssignment((id1, ..., idn) := expr) ->
- lookup id1, ..., idn in the syntactic stack of blocks, assert that they are variables
- codegen(expr)
- for j = n, ..., i:
- SWAPi where i = 1 + stack_height - stack_height_of_identifier(idj)
- POP
- AssemblyAssignment(=: id) ->
- look up id in the syntactic stack of blocks, assert that it is a variable
- SWAPi where i = 1 + stack_height - stack_height_of_identifier(id)
- POP
- LabelDefinition(name:) ->
- JUMPDEST
- NumberLiteral(num) ->
- PUSH<num interpreted as decimal and right-aligned>
- HexLiteral(lit) ->
- PUSH32<lit interpreted as hex and left-aligned>
- StringLiteral(lit) ->
- PUSH32<lit utf-8 encoded and left-aligned>
- SubAssembly(assembly <name> block) ->
- append codegen(block) at the end of the code
- dataSize(<name>) ->
- assert that <name> is a subassembly ->
- PUSH32<size of code generated from subassembly <name>>
- linkerSymbol(<lit>) ->
- PUSH32<zeros> and append position to linker table
- }
diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h
index 9b60b26b..f0deb387 100644
--- a/libevmasm/ConstantOptimiser.h
+++ b/libevmasm/ConstantOptimiser.h
@@ -67,6 +67,7 @@ public:
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
m_params(_params), m_value(_value) {}
+ virtual ~ConstantOptimisationMethod() = default;
virtual bigint gasNeeded() const = 0;
/// Executes the method, potentially appending to the assembly and returns a vector of
/// assembly items the constant should be relpaced with in one sweep.
diff --git a/libevmasm/PeepholeOptimiser.h b/libevmasm/PeepholeOptimiser.h
index a74cc8b3..a651143d 100644
--- a/libevmasm/PeepholeOptimiser.h
+++ b/libevmasm/PeepholeOptimiser.h
@@ -34,6 +34,7 @@ using AssemblyItems = std::vector<AssemblyItem>;
class PeepholeOptimisationMethod
{
public:
+ virtual ~PeepholeOptimisationMethod() = default;
virtual size_t windowSize() const;
virtual bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out);
};
@@ -42,6 +43,7 @@ class PeepholeOptimiser
{
public:
explicit PeepholeOptimiser(AssemblyItems& _items): m_items(_items) {}
+ virtual ~PeepholeOptimiser() = default;
bool optimise();
diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h
index 36a1ced5..8681f2e0 100644
--- a/libjulia/optimiser/ASTCopier.h
+++ b/libjulia/optimiser/ASTCopier.h
@@ -37,6 +37,7 @@ namespace julia
class ExpressionCopier: public boost::static_visitor<Expression>
{
public:
+ virtual ~ExpressionCopier() = default;
virtual Expression operator()(Literal const& _literal) = 0;
virtual Expression operator()(Identifier const& _identifier) = 0;
virtual Expression operator()(FunctionalInstruction const& _instr) = 0;
@@ -46,6 +47,7 @@ public:
class StatementCopier: public boost::static_visitor<Statement>
{
public:
+ virtual ~StatementCopier() = default;
virtual Statement operator()(ExpressionStatement const& _statement) = 0;
virtual Statement operator()(Instruction const& _instruction) = 0;
virtual Statement operator()(Label const& _label) = 0;
@@ -66,6 +68,7 @@ public:
class ASTCopier: public ExpressionCopier, public StatementCopier
{
public:
+ virtual ~ASTCopier() = default;
virtual Expression operator()(Literal const& _literal) override;
virtual Statement operator()(Instruction const& _instruction) override;
virtual Expression operator()(Identifier const& _identifier) override;
diff --git a/libjulia/optimiser/ASTWalker.h b/libjulia/optimiser/ASTWalker.h
index 97891381..f09c2ff1 100644
--- a/libjulia/optimiser/ASTWalker.h
+++ b/libjulia/optimiser/ASTWalker.h
@@ -42,6 +42,7 @@ namespace julia
class ASTWalker: public boost::static_visitor<>
{
public:
+ virtual ~ASTWalker() = default;
virtual void operator()(Literal const&) {}
virtual void operator()(Instruction const&) { solAssert(false, ""); }
virtual void operator()(Identifier const&) {}
@@ -82,6 +83,7 @@ protected:
class ASTModifier: public boost::static_visitor<>
{
public:
+ virtual ~ASTModifier() = default;
virtual void operator()(Literal&) {}
virtual void operator()(Instruction&) { solAssert(false, ""); }
virtual void operator()(Identifier&) {}
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index a53987bf..fa0d6921 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -146,6 +146,7 @@ private:
class Scopable
{
public:
+ virtual ~Scopable() = default;
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
ASTNode const* scope() const { return m_scope; }
@@ -307,6 +308,7 @@ private:
class VariableScope
{
public:
+ virtual ~VariableScope() = default;
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& localVariables() const { return m_localVariables; }
@@ -320,6 +322,7 @@ private:
class Documented
{
public:
+ virtual ~Documented() = default;
explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
/// @return A shared pointer of an ASTString.
@@ -336,6 +339,7 @@ protected:
class ImplementationOptional
{
public:
+ virtual ~ImplementationOptional() = default;
explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
/// @return whether this node is fully implemented or not
diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h
index b1389f0f..6c0ce6f8 100644
--- a/libsolidity/ast/ASTVisitor.h
+++ b/libsolidity/ast/ASTVisitor.h
@@ -43,6 +43,7 @@ namespace solidity
class ASTVisitor
{
public:
+ virtual ~ASTVisitor() = default;
virtual bool visit(SourceUnit& _node) { return visitNode(_node); }
virtual bool visit(PragmaDirective& _node) { return visitNode(_node); }
virtual bool visit(ImportDirective& _node) { return visitNode(_node); }
@@ -147,6 +148,7 @@ protected:
class ASTConstVisitor
{
public:
+ virtual ~ASTConstVisitor() = default;
virtual bool visit(SourceUnit const& _node) { return visitNode(_node); }
virtual bool visit(PragmaDirective const& _node) { return visitNode(_node); }
virtual bool visit(ImportDirective const& _node) { return visitNode(_node); }
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index a9536657..ca6822c9 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -138,6 +138,7 @@ private:
class Type: private boost::noncopyable, public std::enable_shared_from_this<Type>
{
public:
+ virtual ~Type() = default;
enum class Category
{
Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array,
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index 8e890854..3e3aa0ae 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -371,7 +371,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
if (toCategory == Type::Category::Integer)
body =
Whiskers("converted := <convert>(<shift>(value))")
- ("shift", shiftRightFunction(256 - from.numBytes() * 8, false))
+ ("shift", shiftRightFunction(256 - from.numBytes() * 8))
("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
.render();
else
@@ -458,8 +458,8 @@ string ABIFunctions::splitExternalFunctionIdFunction()
}
)")
("functionName", functionName)
- ("shr32", shiftRightFunction(32, false))
- ("shr64", shiftRightFunction(64, false))
+ ("shr32", shiftRightFunction(32))
+ ("shr64", shiftRightFunction(64))
.render();
});
}
@@ -831,7 +831,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
templ("encodeToMemoryFun", encodeToMemoryFun);
std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
for (size_t i = 0; i < itemsPerSlot; ++i)
- items[i]["shiftRightFun"] = shiftRightFunction(i * storageBytes * 8, false);
+ items[i]["shiftRightFun"] = shiftRightFunction(i * storageBytes * 8);
templ("items", items);
return templ.render();
}
@@ -927,7 +927,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
}
else
memberTempl("preprocess", "");
- memberTempl("retrieveValue", shiftRightFunction(intraSlotOffset * 8, false) + "(slotValue)");
+ memberTempl("retrieveValue", shiftRightFunction(intraSlotOffset * 8) + "(slotValue)");
}
else
{
@@ -1401,37 +1401,75 @@ string ABIFunctions::copyToMemoryFunction(bool _fromCalldata)
string ABIFunctions::shiftLeftFunction(size_t _numBits)
{
+ solAssert(_numBits < 256, "");
+
string functionName = "shift_left_" + to_string(_numBits);
- return createFunction(functionName, [&]() {
- solAssert(_numBits < 256, "");
- return
- Whiskers(R"(
- function <functionName>(value) -> newValue {
- newValue := mul(value, <multiplier>)
- }
- )")
- ("functionName", functionName)
- ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
- .render();
- });
+ if (m_evmVersion.hasBitwiseShifting())
+ {
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
+ newValue := shl(<numBits>, value)
+ }
+ )")
+ ("functionName", functionName)
+ ("numBits", to_string(_numBits))
+ .render();
+ });
+ }
+ else
+ {
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
+ newValue := mul(value, <multiplier>)
+ }
+ )")
+ ("functionName", functionName)
+ ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
+ .render();
+ });
+ }
}
-string ABIFunctions::shiftRightFunction(size_t _numBits, bool _signed)
+string ABIFunctions::shiftRightFunction(size_t _numBits)
{
- string functionName = "shift_right_" + to_string(_numBits) + (_signed ? "_signed" : "_unsigned");
- return createFunction(functionName, [&]() {
- solAssert(_numBits < 256, "");
- return
- Whiskers(R"(
- function <functionName>(value) -> newValue {
- newValue := <div>(value, <multiplier>)
- }
- )")
- ("functionName", functionName)
- ("div", _signed ? "sdiv" : "div")
- ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
- .render();
- });
+ solAssert(_numBits < 256, "");
+
+ // Note that if this is extended with signed shifts,
+ // the opcodes SAR and SDIV behave differently with regards to rounding!
+
+ string functionName = "shift_right_" + to_string(_numBits) + "_unsigned";
+ if (m_evmVersion.hasBitwiseShifting())
+ {
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
+ newValue := shr(<numBits>, value)
+ }
+ )")
+ ("functionName", functionName)
+ ("numBits", to_string(_numBits))
+ .render();
+ });
+ }
+ else
+ {
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
+ newValue := div(value, <multiplier>)
+ }
+ )")
+ ("functionName", functionName)
+ ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
+ .render();
+ });
+ }
}
string ABIFunctions::roundUpFunction()
diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h
index 2b582e84..db4d40f5 100644
--- a/libsolidity/codegen/ABIFunctions.h
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -22,6 +22,8 @@
#pragma once
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libsolidity/ast/ASTForward.h>
#include <vector>
@@ -48,6 +50,8 @@ using TypePointers = std::vector<TypePointer>;
class ABIFunctions
{
public:
+ explicit ABIFunctions(EVMVersion _evmVersion = EVMVersion{}) : m_evmVersion(_evmVersion) {}
+
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes
/// into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <headStart> <value_n> ... <value_1>, i.e.
@@ -191,7 +195,7 @@ private:
std::string copyToMemoryFunction(bool _fromCalldata);
std::string shiftLeftFunction(size_t _numBits);
- std::string shiftRightFunction(size_t _numBits, bool _signed);
+ std::string shiftRightFunction(size_t _numBits);
/// @returns the name of a function that rounds its input to the next multiple
/// of 32 or the input if it is a multiple of 32.
std::string roundUpFunction();
@@ -225,6 +229,8 @@ private:
/// Map from function name to code for a multi-use function.
std::map<std::string, std::string> m_requestedFunctions;
+
+ EVMVersion m_evmVersion;
};
}
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 098472f7..5776b5d1 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -55,7 +55,8 @@ public:
explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr):
m_asm(std::make_shared<eth::Assembly>()),
m_evmVersion(_evmVersion),
- m_runtimeContext(_runtimeContext)
+ m_runtimeContext(_runtimeContext),
+ m_abiFunctions(m_evmVersion)
{
if (m_runtimeContext)
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 4af7d905..48b77eb3 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -599,15 +599,15 @@ void CompilerUtils::splitExternalFunctionType(bool _leftAligned)
if (_leftAligned)
{
m_context << Instruction::DUP1;
- rightShiftNumberOnStack(64 + 32, false);
+ rightShiftNumberOnStack(64 + 32);
// <input> <address>
m_context << Instruction::SWAP1;
- rightShiftNumberOnStack(64, false);
+ rightShiftNumberOnStack(64);
}
else
{
m_context << Instruction::DUP1;
- rightShiftNumberOnStack(32, false);
+ rightShiftNumberOnStack(32);
m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::SWAP1;
}
m_context << u256(0xffffffffUL) << Instruction::AND;
@@ -675,7 +675,7 @@ void CompilerUtils::convertType(
// conversion from bytes to integer. no need to clean the high bit
// only to shift right because of opposite alignment
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
- rightShiftNumberOnStack(256 - typeOnStack.numBytes() * 8, false);
+ rightShiftNumberOnStack(256 - typeOnStack.numBytes() * 8);
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
}
@@ -1242,7 +1242,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
bool leftAligned = _type.category() == Type::Category::FixedBytes;
// add leading or trailing zeros by dividing/multiplying depending on alignment
int shiftFactor = (32 - numBytes) * 8;
- rightShiftNumberOnStack(shiftFactor, false);
+ rightShiftNumberOnStack(shiftFactor);
if (leftAligned)
leftShiftNumberOnStack(shiftFactor);
}
@@ -1265,13 +1265,20 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
void CompilerUtils::leftShiftNumberOnStack(unsigned _bits)
{
solAssert(_bits < 256, "");
- m_context << (u256(1) << _bits) << Instruction::MUL;
+ if (m_context.evmVersion().hasBitwiseShifting())
+ m_context << _bits << Instruction::SHL;
+ else
+ m_context << (u256(1) << _bits) << Instruction::MUL;
}
-void CompilerUtils::rightShiftNumberOnStack(unsigned _bits, bool _isSigned)
+void CompilerUtils::rightShiftNumberOnStack(unsigned _bits)
{
solAssert(_bits < 256, "");
- m_context << (u256(1) << _bits) << Instruction::SWAP1 << (_isSigned ? Instruction::SDIV : Instruction::DIV);
+ // NOTE: If we add signed right shift, SAR rounds differently than SDIV
+ if (m_context.evmVersion().hasBitwiseShifting())
+ m_context << _bits << Instruction::SHR;
+ else
+ m_context << (u256(1) << _bits) << Instruction::SWAP1 << Instruction::DIV;
}
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords)
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 476a7559..8e3a8a5d 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -254,7 +254,7 @@ public:
/// Helper function to shift top value on the stack to the right.
/// Stack pre: <value> <shift_by_bits>
/// Stack post: <shifted_value>
- void rightShiftNumberOnStack(unsigned _bits, bool _isSigned = false);
+ void rightShiftNumberOnStack(unsigned _bits);
/// Appends code that computes tha Keccak-256 hash of the topmost stack element of 32 byte type.
void computeHashStatic();
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 3cf46a9d..a8222e21 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -548,7 +548,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (m_context.runtimeContext())
// We have a runtime context, so we need the creation part.
- utils().rightShiftNumberOnStack(32, false);
+ utils().rightShiftNumberOnStack(32);
else
// Extract the runtime part.
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
@@ -1706,13 +1706,23 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type co
m_context.appendConditionalInvalid();
}
+ m_context << Instruction::SWAP1;
+ // stack: value_to_shift shift_amount
+
switch (_operator)
{
case Token::SHL:
- m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::MUL;
+ if (m_context.evmVersion().hasBitwiseShifting())
+ m_context << Instruction::SHL;
+ else
+ m_context << u256(2) << Instruction::EXP << Instruction::MUL;
break;
case Token::SAR:
- m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_valueSigned ? Instruction::SDIV : Instruction::DIV);
+ // NOTE: SAR rounds differently than SDIV
+ if (m_context.evmVersion().hasBitwiseShifting() && !c_valueSigned)
+ m_context << Instruction::SHR;
+ else
+ m_context << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_valueSigned ? Instruction::SDIV : Instruction::DIV);
break;
case Token::SHR:
default:
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
index e19cf41e..77684683 100644
--- a/libsolidity/codegen/LValue.cpp
+++ b/libsolidity/codegen/LValue.cpp
@@ -267,7 +267,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
else if (m_dataType->category() == Type::Category::FixedBytes)
{
solAssert(_sourceType.category() == Type::Category::FixedBytes, "source not fixed bytes");
- CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes(), false);
+ CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes());
}
else
{
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
index e127bb55..16796684 100644
--- a/libsolidity/formal/SolverInterface.h
+++ b/libsolidity/formal/SolverInterface.h
@@ -193,6 +193,7 @@ DEV_SIMPLE_EXCEPTION(SolverError);
class SolverInterface
{
public:
+ virtual ~SolverInterface() = default;
virtual void reset() = 0;
virtual void push() = 0;
diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h
index e4e4ea8d..e29ded26 100644
--- a/libsolidity/formal/SymbolicVariable.h
+++ b/libsolidity/formal/SymbolicVariable.h
@@ -40,6 +40,7 @@ public:
Declaration const& _decl,
smt::SolverInterface& _interface
);
+ virtual ~SymbolicVariable() = default;
smt::Expression operator()(int _seq) const
{
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index a496cc21..a532f86e 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -136,13 +136,22 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
ExpressionClasses& classes = state->expressionClasses();
using Id = ExpressionClasses::Id;
using Ids = vector<Id>;
- // div(calldataload(0), 1 << 224) equals to hashValue
Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(_signature)))));
Id calldata = classes.find(Instruction::CALLDATALOAD, Ids{classes.find(u256(0))});
- classes.forceEqual(hashValue, Instruction::DIV, Ids{
- calldata,
- classes.find(u256(1) << 224)
- });
+ if (!m_evmVersion.hasBitwiseShifting())
+ // div(calldataload(0), 1 << 224) equals to hashValue
+ classes.forceEqual(
+ hashValue,
+ Instruction::DIV,
+ Ids{calldata, classes.find(u256(1) << 224)}
+ );
+ else
+ // shr(0xe0, calldataload(0)) equals to hashValue
+ classes.forceEqual(
+ hashValue,
+ Instruction::SHR,
+ Ids{classes.find(u256(0xe0)), calldata}
+ );
// lt(calldatasize(), 4) equals to 0 (ignore the shortcut for fallback functions)
classes.forceEqual(
classes.find(u256(0)),
diff --git a/scripts/tests.sh b/scripts/tests.sh
index 38073bf3..d63c1fe4 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -30,6 +30,17 @@ set -e
REPO_ROOT="$(dirname "$0")"/..
+IPC_ENABLED=true
+if [[ "$OSTYPE" == "darwin"* ]]
+then
+ SMT_FLAGS="--no-smt"
+ if [ "$CIRCLECI" ]
+ then
+ IPC_ENABLED=false
+ IPC_FLAGS="--no-ipc"
+ fi
+fi
+
if [ "$1" = --junit_report ]
then
if [ -z "$2" ]
@@ -98,8 +109,11 @@ function run_eth()
sleep 2
}
-download_eth
-ETH_PID=$(run_eth /tmp/test)
+if [ "$IPC_ENABLED" = true ];
+then
+ download_eth
+ ETH_PID=$(run_eth /tmp/test)
+fi
progress="--show-progress"
if [ "$CIRCLECI" ]
@@ -131,7 +145,7 @@ do
log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt
fi
fi
- "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc
+ "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" $SMT_FLAGS $IPC_FLAGS --ipcpath /tmp/test/geth.ipc
done
done
@@ -141,6 +155,9 @@ then
exit 1
fi
-pkill "$ETH_PID" || true
-sleep 4
-pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
+if [ "$IPC_ENABLED" = true ]
+then
+ pkill "$ETH_PID" || true
+ sleep 4
+ pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
+fi
diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h
index ee8da322..4525cbf9 100644
--- a/test/ExecutionFramework.h
+++ b/test/ExecutionFramework.h
@@ -53,6 +53,7 @@ class ExecutionFramework
public:
ExecutionFramework();
+ virtual ~ExecutionFramework() = default;
virtual bytes const& compileAndRunWithoutCheck(
std::string const& _sourceCode,
diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h
index 05490a42..a904617d 100644
--- a/test/libsolidity/AnalysisFramework.h
+++ b/test/libsolidity/AnalysisFramework.h
@@ -52,6 +52,7 @@ protected:
bool _insertVersionPragma = true,
bool _allowMultipleErrors = false
);
+ virtual ~AnalysisFramework() = default;
SourceUnit const* parseAndAnalyse(std::string const& _source);
bool success(std::string const& _source);
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index 77ca363a..7b3df043 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -158,8 +158,9 @@ BOOST_AUTO_TEST_CASE(location_test)
}
)";
AssemblyItems items = compileContract(sourceCode);
+ bool hasShifts = dev::test::Options::get().evmVersion().hasBitwiseShifting();
vector<SourceLocation> locations =
- vector<SourceLocation>(24, SourceLocation(2, 75, make_shared<string>(""))) +
+ vector<SourceLocation>(hasShifts ? 23 : 24, SourceLocation(2, 75, make_shared<string>(""))) +
vector<SourceLocation>(2, SourceLocation(20, 72, make_shared<string>(""))) +
vector<SourceLocation>(1, SourceLocation(8, 17, make_shared<string>("--CODEGEN--"))) +
vector<SourceLocation>(3, SourceLocation(5, 7, make_shared<string>("--CODEGEN--"))) +
@@ -172,8 +173,6 @@ BOOST_AUTO_TEST_CASE(location_test)
vector<SourceLocation>(1, SourceLocation(65, 67, make_shared<string>(""))) +
vector<SourceLocation>(2, SourceLocation(58, 67, make_shared<string>(""))) +
vector<SourceLocation>(2, SourceLocation(20, 72, make_shared<string>("")));
-
-
checkAssemblyLocations(items, locations);
}