/src/solidity/libyul/backends/evm/EVMObjectCompiler.cpp
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** |
19 | | * Compiler that transforms Yul Objects to EVM bytecode objects. |
20 | | */ |
21 | | |
22 | | #include <libyul/backends/evm/EVMObjectCompiler.h> |
23 | | |
24 | | #include <libyul/backends/evm/ssa/CodeTransform.h> |
25 | | #include <libyul/backends/evm/ssa/SSACFGBuilder.h> |
26 | | #include <libyul/backends/evm/ssa/ControlFlowGraphs.h> |
27 | | |
28 | | #include <libyul/backends/evm/ssa/transform/OptimizationPipeline.h> |
29 | | |
30 | | #include <libyul/backends/evm/EVMCodeTransform.h> |
31 | | #include <libyul/backends/evm/EVMDialect.h> |
32 | | #include <libyul/backends/evm/OptimizedEVMCodeTransform.h> |
33 | | |
34 | | #include <libyul/optimiser/FunctionCallFinder.h> |
35 | | |
36 | | #include <libyul/Object.h> |
37 | | #include <libyul/Exceptions.h> |
38 | | |
39 | | #include <boost/algorithm/string.hpp> |
40 | | |
41 | | using namespace solidity::yul; |
42 | | |
43 | | void EVMObjectCompiler::compile( |
44 | | Object const& _object, |
45 | | AbstractAssembly& _assembly, |
46 | | bool _optimize, |
47 | | bool _viaSSACFG |
48 | | ) |
49 | 47.2k | { |
50 | 47.2k | EVMObjectCompiler compiler(_assembly); |
51 | 47.2k | compiler.run(_object, _optimize, _viaSSACFG); |
52 | 47.2k | } |
53 | | |
54 | | void EVMObjectCompiler::run(Object const& _object, bool _optimize, bool _viaSSACFG) |
55 | 47.2k | { |
56 | 47.2k | yulAssert(_object.dialect()); |
57 | 47.2k | auto const* evmDialect = dynamic_cast<EVMDialect const*>(_object.dialect()); |
58 | 47.2k | yulAssert(evmDialect); |
59 | | |
60 | 47.2k | BuiltinContext context; |
61 | 47.2k | context.currentObject = &_object; |
62 | | |
63 | | |
64 | 47.2k | for (auto const& subNode: _object.subObjects) |
65 | 16.6k | if (auto* subObject = dynamic_cast<Object*>(subNode.get())) |
66 | 8.07k | { |
67 | 8.07k | bool isCreation = !boost::ends_with(subObject->name, "_deployed"); |
68 | 8.07k | auto subAssemblyAndID = m_assembly.createSubAssembly(isCreation, subObject->name); |
69 | 8.07k | context.subIDs[subObject->name] = subAssemblyAndID.second; |
70 | 8.07k | subObject->subId = subAssemblyAndID.second; |
71 | 8.07k | compile(*subObject, *subAssemblyAndID.first, _optimize, _viaSSACFG); |
72 | 8.07k | } |
73 | 8.57k | else |
74 | 8.57k | { |
75 | 8.57k | Data const& data = dynamic_cast<Data const&>(*subNode); |
76 | | // Special handling of metadata. |
77 | 8.57k | if (data.name == Object::metadataName()) |
78 | 4.51k | m_assembly.appendToAuxiliaryData(data.data); |
79 | 4.06k | else |
80 | 4.06k | context.subIDs[data.name] = m_assembly.appendData(data.data); |
81 | 8.57k | } |
82 | | |
83 | 47.2k | yulAssert(_object.analysisInfo, "No analysis info."); |
84 | 47.2k | yulAssert(_object.hasCode(), "No code."); |
85 | 47.2k | if (_optimize && evmDialect->evmVersion().canOverchargeGasForCall()) |
86 | 42.5k | { |
87 | 42.5k | if (_viaSSACFG) |
88 | 0 | { |
89 | 0 | std::unique_ptr<ssa::ControlFlowGraphs> controlFlowGraphs = ssa::SSACFGBuilder::build( |
90 | 0 | *_object.analysisInfo, |
91 | 0 | *evmDialect, |
92 | 0 | _object.code()->root(), |
93 | 0 | false |
94 | 0 | ); |
95 | 0 | ssa::transform::optimize(*controlFlowGraphs); |
96 | 0 | ssa::ControlFlowGraphsLiveness const liveness(*controlFlowGraphs); |
97 | 0 | ssa::CodeTransform::run( |
98 | 0 | m_assembly, |
99 | 0 | *controlFlowGraphs, |
100 | 0 | liveness, |
101 | 0 | context |
102 | 0 | ); |
103 | 0 | } |
104 | 42.5k | else |
105 | 42.5k | { |
106 | 42.5k | auto stackErrors = OptimizedEVMCodeTransform::run( |
107 | 42.5k | m_assembly, |
108 | 42.5k | *_object.analysisInfo, |
109 | 42.5k | _object.code()->root(), |
110 | 42.5k | *evmDialect, |
111 | 42.5k | context, |
112 | 42.5k | OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName |
113 | 42.5k | ); |
114 | 42.5k | if (!stackErrors.empty()) |
115 | 972 | { |
116 | 972 | yulAssert(evmDialect->providesObjectAccess()); |
117 | 972 | auto const memoryGuardHandle = evmDialect->findBuiltin("memoryguard"); |
118 | 972 | yulAssert(memoryGuardHandle, "Compiling with object access, memoryguard should be available as builtin."); |
119 | 972 | std::vector<FunctionCall const*> memoryGuardCalls = findFunctionCalls( |
120 | 972 | _object.code()->root(), |
121 | 972 | *memoryGuardHandle |
122 | 972 | ); |
123 | 972 | auto stackError = stackErrors.front(); |
124 | 972 | std::string msg = stackError.comment() ? *stackError.comment() : ""; |
125 | 972 | if (memoryGuardCalls.empty()) |
126 | 916 | msg += "\nNo memoryguard was present. " |
127 | 916 | "Consider using memory-safe assembly only and annotating it via " |
128 | 916 | "'assembly (\"memory-safe\") { ... }'."; |
129 | 56 | else |
130 | 56 | msg += "\nmemoryguard was present."; |
131 | 972 | stackError << util::errinfo_comment(msg); |
132 | 972 | BOOST_THROW_EXCEPTION(stackError); |
133 | 972 | } |
134 | 42.5k | } |
135 | 42.5k | } |
136 | 4.70k | else |
137 | 4.70k | { |
138 | | // We do not catch and re-throw the stack too deep exception here because it is a YulException, |
139 | | // which should be native to this part of the code. |
140 | 4.70k | CodeTransform transform{ |
141 | 4.70k | m_assembly, |
142 | 4.70k | *_object.analysisInfo, |
143 | 4.70k | _object.code()->root(), |
144 | 4.70k | *evmDialect, |
145 | 4.70k | context, |
146 | 4.70k | _optimize, |
147 | 4.70k | {}, |
148 | 4.70k | CodeTransform::UseNamedLabels::ForFirstFunctionOfEachName |
149 | 4.70k | }; |
150 | 4.70k | transform(_object.code()->root()); |
151 | 4.70k | if (!transform.stackErrors().empty()) |
152 | | BOOST_THROW_EXCEPTION(transform.stackErrors().front()); |
153 | 4.70k | } |
154 | 47.2k | } |