Skip to content

Commit e6848ca

Browse files
committed
Allow annotating inline assembly as memory-safe.
1 parent e8520a6 commit e6848ca

File tree

9 files changed

+100
-9
lines changed

9 files changed

+100
-9
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### 0.8.13 (unreleased)
22

33
Language Features:
4+
* General: Allow annotating inline assembly as memory-safe to allow optimizations and stack limit evasion that rely on respecting Solidity's memory model.
45

56

67
Compiler Features:

libsolidity/analysis/DocStringTagParser.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <liblangutil/Common.h>
3131

3232
#include <range/v3/algorithm/any_of.hpp>
33+
#include <range/v3/view/filter.hpp>
3334

3435
#include <boost/algorithm/string.hpp>
3536

@@ -162,6 +163,71 @@ bool DocStringTagParser::visit(ErrorDefinition const& _error)
162163
return true;
163164
}
164165

166+
bool DocStringTagParser::visit(InlineAssembly const& _assembly)
167+
{
168+
if (!_assembly.documentation())
169+
return true;
170+
StructuredDocumentation documentation{-1, _assembly.location(), _assembly.documentation()};
171+
ErrorList errors;
172+
ErrorReporter errorReporter{errors};
173+
auto docTags = DocStringParser{documentation, errorReporter}.parse();
174+
175+
if (!errors.empty())
176+
{
177+
SecondarySourceLocation ssl;
178+
for (auto const& error: errors)
179+
if (error->comment())
180+
ssl.append(
181+
*error->comment(),
182+
_assembly.location()
183+
);
184+
m_errorReporter.warning(
185+
7828_error,
186+
_assembly.location(),
187+
"Inline assembly has invalid NatSpec documentation.",
188+
ssl
189+
);
190+
}
191+
192+
for (auto const& [tagName, tagValue]: docTags)
193+
{
194+
if (tagName == "solidity")
195+
{
196+
vector<string> values;
197+
boost::split(values, tagValue.content, isWhiteSpace);
198+
199+
set<string> valuesSeen;
200+
set<string> duplicates;
201+
for (auto const& value: values | ranges::views::filter(not_fn(&string::empty)))
202+
if (valuesSeen.insert(value).second)
203+
{
204+
if (value == "memory-safe-assembly")
205+
_assembly.annotation().memorySafe = true;
206+
else
207+
m_errorReporter.warning(
208+
8787_error,
209+
_assembly.location(),
210+
"Unexpected value for @solidity tag in inline assembly: " + value
211+
);
212+
}
213+
else if (duplicates.insert(value).second)
214+
m_errorReporter.warning(
215+
4377_error,
216+
_assembly.location(),
217+
"Value for @solidity tag in inline assembly specified multiple times: " + value
218+
);
219+
}
220+
else
221+
m_errorReporter.warning(
222+
6269_error,
223+
_assembly.location(),
224+
"Unexpected NatSpec tag \"" + tagName + "\" with value \"" + tagValue.content + "\" in inline assembly."
225+
);
226+
}
227+
228+
return true;
229+
}
230+
165231
void DocStringTagParser::checkParameters(
166232
CallableDeclaration const& _callable,
167233
StructurallyDocumented const& _node,

libsolidity/analysis/DocStringTagParser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class DocStringTagParser: private ASTConstVisitor
4848
bool visit(ModifierDefinition const& _modifier) override;
4949
bool visit(EventDefinition const& _event) override;
5050
bool visit(ErrorDefinition const& _error) override;
51+
bool visit(InlineAssembly const& _assembly) override;
5152

5253
void checkParameters(
5354
CallableDeclaration const& _callable,

libsolidity/ast/ASTAnnotations.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ struct InlineAssemblyAnnotation: StatementAnnotation
220220
std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences;
221221
/// Information generated during analysis phase.
222222
std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;
223+
/// True, if the assembly block was annotated to be memory-safe.
224+
bool memorySafe = false;
223225
};
224226

225227
struct BlockAnnotation: StatementAnnotation, ScopableAnnotation

libsolidity/codegen/ir/IRGenerationContext.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ class IRGenerationContext
160160

161161
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
162162

163-
bool inlineAssemblySeen() const { return m_inlineAssemblySeen; }
164-
void setInlineAssemblySeen() { m_inlineAssemblySeen = true; }
163+
bool memoryUnsafeInlineAssemblySeen() const { return m_memoryUnsafeInlineAssemblySeen; }
164+
void setMemoryUnsafeInlineAssemblySeen() { m_memoryUnsafeInlineAssemblySeen = true; }
165165

166166
/// @returns the runtime ID to be used for the function in the dispatch routine
167167
/// and for internal function pointers.
@@ -202,8 +202,8 @@ class IRGenerationContext
202202
/// Whether to use checked or wrapping arithmetic.
203203
Arithmetic m_arithmetic = Arithmetic::Checked;
204204

205-
/// Flag indicating whether any inline assembly block was seen.
206-
bool m_inlineAssemblySeen = false;
205+
/// Flag indicating whether any memory-unsafe inline assembly block was seen.
206+
bool m_memoryUnsafeInlineAssemblySeen = false;
207207

208208
/// Function definitions queued for code generation. They're the Solidity functions whose calls
209209
/// were discovered by the IR generator during AST traversal.

libsolidity/codegen/ir/IRGenerator.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ string IRGenerator::generate(
213213
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
214214

215215
// This has to be called only after all other code generation for the creation object is complete.
216-
bool creationInvolvesAssembly = m_context.inlineAssemblySeen();
217-
t("memoryInitCreation", memoryInit(!creationInvolvesAssembly));
216+
bool creationInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
217+
t("memoryInitCreation", memoryInit(!creationInvolvesMemoryUnsafeAssembly));
218218
t("useSrcMapCreation", formatUseSrcMap(m_context));
219219

220220
resetContext(_contract, ExecutionContext::Deployed);
@@ -239,8 +239,8 @@ string IRGenerator::generate(
239239
t("useSrcMapDeployed", formatUseSrcMap(m_context));
240240

241241
// This has to be called only after all other code generation for the deployed object is complete.
242-
bool deployedInvolvesAssembly = m_context.inlineAssemblySeen();
243-
t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly));
242+
bool deployedInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
243+
t("memoryInitDeployed", memoryInit(!deployedInvolvesMemoryUnsafeAssembly));
244244

245245
solAssert(_contract.annotation().creationCallGraph->get() != nullptr, "");
246246
solAssert(_contract.annotation().deployedCallGraph->get() != nullptr, "");

libsolidity/codegen/ir/IRGeneratorForStatements.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2138,7 +2138,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
21382138
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
21392139
{
21402140
setLocation(_inlineAsm);
2141-
m_context.setInlineAssemblySeen();
2141+
if (!_inlineAsm.annotation().memorySafe)
2142+
m_context.setMemoryUnsafeInlineAssemblySeen();
21422143
CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences};
21432144

21442145
yul::Statement modified = bodyCopier(_inlineAsm.operations());
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
contract C {
2+
function f() public pure {
3+
/// @test test
4+
assembly {}
5+
/// @solidity test
6+
assembly {}
7+
/// @param
8+
assembly {}
9+
}
10+
}
11+
// ----
12+
// Warning 6269: (60-71): Unexpected natspec tag in inline assembly: test
13+
// Warning 8787: (95-106): Unexpected value for @solidity tag in inline assembly: test
14+
// Warning 7828: (122-133): Inline assembly has invalid natspec documentation.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
contract C {
2+
function f() public pure {
3+
// @solidity memory-safe-assembly
4+
assembly {}
5+
}
6+
}

0 commit comments

Comments
 (0)