mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10341 from ethereum/optimizeAllocation
Do not allocate memory objects if they will be assigned directly.
This commit is contained in:
commit
2d235bf7b0
@ -4,9 +4,11 @@ Language Features:
|
|||||||
* The fallback function can now also have a single ``calldata`` argument (equaling ``msg.data``) and return ``bytes memory`` (which will not be ABI-encoded but returned as-is).
|
* The fallback function can now also have a single ``calldata`` argument (equaling ``msg.data``) and return ``bytes memory`` (which will not be ABI-encoded but returned as-is).
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* Code Generator: Avoid memory allocation for default value if it is not used.
|
||||||
* SMTChecker: Support named arguments in function calls.
|
* SMTChecker: Support named arguments in function calls.
|
||||||
* SMTChecker: Support struct constructor.
|
* SMTChecker: Support struct constructor.
|
||||||
|
|
||||||
|
|
||||||
### 0.7.5 (2020-11-18)
|
### 0.7.5 (2020-11-18)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
@ -612,7 +612,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: _function.returnParameters())
|
for (ASTPointer<VariableDeclaration> const& variable: _function.returnParameters())
|
||||||
appendStackVariableInitialisation(*variable);
|
appendStackVariableInitialisation(*variable, /* _provideDefaultValue = */ true);
|
||||||
|
|
||||||
if (_function.isConstructor())
|
if (_function.isConstructor())
|
||||||
if (auto c = dynamic_cast<ContractDefinition const&>(*_function.scope()).nextConstructor(
|
if (auto c = dynamic_cast<ContractDefinition const&>(*_function.scope()).nextConstructor(
|
||||||
@ -1230,7 +1230,7 @@ bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclar
|
|||||||
// and freed in the end of their scope.
|
// and freed in the end of their scope.
|
||||||
for (auto decl: _variableDeclarationStatement.declarations())
|
for (auto decl: _variableDeclarationStatement.declarations())
|
||||||
if (decl)
|
if (decl)
|
||||||
appendStackVariableInitialisation(*decl);
|
appendStackVariableInitialisation(*decl, !_variableDeclarationStatement.initialValue());
|
||||||
|
|
||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
if (Expression const* expression = _variableDeclarationStatement.initialValue())
|
if (Expression const* expression = _variableDeclarationStatement.initialValue())
|
||||||
@ -1376,11 +1376,20 @@ void ContractCompiler::appendModifierOrFunctionCode()
|
|||||||
m_context.setModifierDepth(m_modifierDepth);
|
m_context.setModifierDepth(m_modifierDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration const& _variable)
|
void ContractCompiler::appendStackVariableInitialisation(
|
||||||
|
VariableDeclaration const& _variable,
|
||||||
|
bool _provideDefaultValue
|
||||||
|
)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter location(m_context, _variable);
|
CompilerContext::LocationSetter location(m_context, _variable);
|
||||||
m_context.addVariable(_variable);
|
m_context.addVariable(_variable);
|
||||||
CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type);
|
if (!_provideDefaultValue && _variable.type()->dataStoredIn(DataLocation::Memory))
|
||||||
|
{
|
||||||
|
solAssert(_variable.type()->sizeOnStack() == 1, "");
|
||||||
|
m_context << u256(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
|
void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
|
||||||
|
@ -130,7 +130,10 @@ private:
|
|||||||
/// body itself if the last modifier was reached.
|
/// body itself if the last modifier was reached.
|
||||||
void appendModifierOrFunctionCode();
|
void appendModifierOrFunctionCode();
|
||||||
|
|
||||||
void appendStackVariableInitialisation(VariableDeclaration const& _variable);
|
/// Creates a stack slot for the given variable and assigns a default value.
|
||||||
|
/// If the default value is complex (needs memory allocation) and @a _provideDefaultValue
|
||||||
|
/// is false, this might be skipped.
|
||||||
|
void appendStackVariableInitialisation(VariableDeclaration const& _variable, bool _provideDefaultValue);
|
||||||
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
||||||
|
|
||||||
/// Frees the variables of a certain scope (to be used when leaving).
|
/// Frees the variables of a certain scope (to be used when leaving).
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
uint[] memory x;
|
||||||
|
uint y;
|
||||||
|
assembly {
|
||||||
|
y := x
|
||||||
|
}
|
||||||
|
// The value of an uninitialized dynamic array is not zero but rather
|
||||||
|
// an address of a location in memory that has the value of zero.
|
||||||
|
assert(y != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() ->
|
@ -0,0 +1,30 @@
|
|||||||
|
contract C {
|
||||||
|
function memorySize() internal pure returns (uint s) {
|
||||||
|
assembly { s := mload(0x40) }
|
||||||
|
}
|
||||||
|
function f() public returns (uint, uint, uint) {
|
||||||
|
uint a = memorySize();
|
||||||
|
g();
|
||||||
|
uint b = memorySize();
|
||||||
|
h();
|
||||||
|
uint c = memorySize();
|
||||||
|
i();
|
||||||
|
uint d = memorySize();
|
||||||
|
return (b - a, c - b, d - c);
|
||||||
|
}
|
||||||
|
// In these functions, we do allocate memory in both cases.
|
||||||
|
// In `i()`, this could be avoided but we would have to check
|
||||||
|
// that all code paths return explicitly and provide a value.
|
||||||
|
function g() internal returns (uint[40] memory) {
|
||||||
|
}
|
||||||
|
function h() internal returns (uint[40] memory t) {
|
||||||
|
}
|
||||||
|
function i() internal returns (uint[40] memory) {
|
||||||
|
uint[40] memory x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x0500, 0x0500, 0x0a00
|
@ -0,0 +1,24 @@
|
|||||||
|
contract C {
|
||||||
|
function memorySize() internal pure returns (uint s) {
|
||||||
|
assembly { s := mload(0x40) }
|
||||||
|
}
|
||||||
|
function withValue() public pure returns (uint) {
|
||||||
|
uint[20] memory x;
|
||||||
|
uint memorySizeBefore = memorySize();
|
||||||
|
uint[20] memory t = x;
|
||||||
|
uint memorySizeAfter = memorySize();
|
||||||
|
return memorySizeAfter - memorySizeBefore;
|
||||||
|
}
|
||||||
|
function withoutValue() public pure returns (uint) {
|
||||||
|
uint[20] memory x;
|
||||||
|
uint memorySizeBefore = memorySize();
|
||||||
|
uint[20] memory t;
|
||||||
|
uint memorySizeAfter = memorySize();
|
||||||
|
return memorySizeAfter - memorySizeBefore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// withValue() -> 0x00
|
||||||
|
// withoutValue() -> 0x0280
|
@ -0,0 +1,24 @@
|
|||||||
|
contract C {
|
||||||
|
struct S { uint x; uint y; uint z; }
|
||||||
|
function memorySize() internal pure returns (uint s) {
|
||||||
|
assembly { s := mload(0x40) }
|
||||||
|
}
|
||||||
|
function withValue() public pure returns (uint) {
|
||||||
|
S memory x = S(1, 2, 3);
|
||||||
|
uint memorySizeBefore = memorySize();
|
||||||
|
S memory t = x;
|
||||||
|
uint memorySizeAfter = memorySize();
|
||||||
|
return memorySizeAfter - memorySizeBefore;
|
||||||
|
}
|
||||||
|
function withoutValue() public pure returns (uint) {
|
||||||
|
uint memorySizeBefore = memorySize();
|
||||||
|
S memory t;
|
||||||
|
uint memorySizeAfter = memorySize();
|
||||||
|
return memorySizeAfter - memorySizeBefore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// withValue() -> 0x00
|
||||||
|
// withoutValue() -> 0x60
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
uint[] memory x = x[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 7576: (70-71): Undeclared identifier. "x" is not (or not yet) visible at this point.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
struct S { uint y; }
|
||||||
|
function f() public pure {
|
||||||
|
S memory x = x.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 7576: (90-91): Undeclared identifier. "x" is not (or not yet) visible at this point.
|
Loading…
Reference in New Issue
Block a user