mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Legacy codegeneration for immutable state variables.
This commit is contained in:
committed by
chriseth
parent
83cbfbb7bf
commit
04d8ad2ae1
+87
-2
@@ -283,6 +283,24 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices)
|
||||
createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end)
|
||||
);
|
||||
break;
|
||||
case PushImmutable:
|
||||
collection.append(createJsonValue(
|
||||
"PUSHIMMUTABLE",
|
||||
sourceIndex,
|
||||
i.location().start,
|
||||
i.location().end,
|
||||
m_immutables.at(h256(i.data()))
|
||||
));
|
||||
break;
|
||||
case AssignImmutable:
|
||||
collection.append(createJsonValue(
|
||||
"ASSIGNIMMUTABLE",
|
||||
sourceIndex,
|
||||
i.location().start,
|
||||
i.location().end,
|
||||
m_immutables.at(h256(i.data()))
|
||||
));
|
||||
break;
|
||||
case Tag:
|
||||
collection.append(
|
||||
createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data())));
|
||||
@@ -333,6 +351,20 @@ AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
|
||||
return AssemblyItem{PushLibraryAddress, h};
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::newPushImmutable(string const& _identifier)
|
||||
{
|
||||
h256 h(util::keccak256(_identifier));
|
||||
m_immutables[h] = _identifier;
|
||||
return AssemblyItem{PushImmutable, h};
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::newImmutableAssignment(string const& _identifier)
|
||||
{
|
||||
h256 h(util::keccak256(_identifier));
|
||||
m_immutables[h] = _identifier;
|
||||
return AssemblyItem{AssignImmutable, h};
|
||||
}
|
||||
|
||||
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
|
||||
{
|
||||
OptimiserSettings settings;
|
||||
@@ -495,16 +527,44 @@ LinkerObject const& Assembly::assemble() const
|
||||
// Otherwise ensure the object is actually clear.
|
||||
assertThrow(m_assembledObject.linkReferences.empty(), AssemblyException, "Unexpected link references.");
|
||||
|
||||
LinkerObject& ret = m_assembledObject;
|
||||
|
||||
size_t subTagSize = 1;
|
||||
map<u256, vector<size_t>> immutableReferencesBySub;
|
||||
for (auto const& sub: m_subs)
|
||||
{
|
||||
sub->assemble();
|
||||
auto const& linkerObject = sub->assemble();
|
||||
if (!linkerObject.immutableReferences.empty())
|
||||
{
|
||||
assertThrow(
|
||||
immutableReferencesBySub.empty(),
|
||||
AssemblyException,
|
||||
"More than one sub-assembly references immutables."
|
||||
);
|
||||
immutableReferencesBySub = linkerObject.immutableReferences;
|
||||
}
|
||||
for (size_t tagPos: sub->m_tagPositionsInBytecode)
|
||||
if (tagPos != size_t(-1) && tagPos > subTagSize)
|
||||
subTagSize = tagPos;
|
||||
}
|
||||
|
||||
LinkerObject& ret = m_assembledObject;
|
||||
bool setsImmutables = false;
|
||||
bool pushesImmutables = false;
|
||||
|
||||
for (auto const& i: m_items)
|
||||
if (i.type() == AssignImmutable)
|
||||
{
|
||||
i.setImmutableOccurrences(immutableReferencesBySub[i.data()].size());
|
||||
setsImmutables = true;
|
||||
}
|
||||
else if (i.type() == PushImmutable)
|
||||
pushesImmutables = true;
|
||||
if (setsImmutables || pushesImmutables)
|
||||
assertThrow(
|
||||
setsImmutables != pushesImmutables,
|
||||
AssemblyException,
|
||||
"Cannot push and assign immutables in the same assembly subroutine."
|
||||
);
|
||||
|
||||
size_t bytesRequiredForCode = bytesRequired(subTagSize);
|
||||
m_tagPositionsInBytecode = vector<size_t>(m_usedTags, -1);
|
||||
@@ -598,6 +658,24 @@ LinkerObject const& Assembly::assemble() const
|
||||
ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data());
|
||||
ret.bytecode.resize(ret.bytecode.size() + 20);
|
||||
break;
|
||||
case PushImmutable:
|
||||
ret.bytecode.push_back(uint8_t(Instruction::PUSH32));
|
||||
ret.immutableReferences[i.data()].emplace_back(ret.bytecode.size());
|
||||
ret.bytecode.resize(ret.bytecode.size() + 32);
|
||||
break;
|
||||
case AssignImmutable:
|
||||
for (auto const& offset: immutableReferencesBySub[i.data()])
|
||||
{
|
||||
ret.bytecode.push_back(uint8_t(Instruction::DUP1));
|
||||
// TODO: should we make use of the constant optimizer methods for pushing the offsets?
|
||||
bytes offsetBytes = toCompactBigEndian(u256(offset));
|
||||
ret.bytecode.push_back(uint8_t(Instruction::PUSH1) - 1 + offsetBytes.size());
|
||||
ret.bytecode += offsetBytes;
|
||||
ret.bytecode.push_back(uint8_t(Instruction::MSTORE));
|
||||
}
|
||||
immutableReferencesBySub.erase(i.data());
|
||||
ret.bytecode.push_back(uint8_t(Instruction::POP));
|
||||
break;
|
||||
case PushDeployTimeAddress:
|
||||
ret.bytecode.push_back(uint8_t(Instruction::PUSH20));
|
||||
ret.bytecode.resize(ret.bytecode.size() + 20);
|
||||
@@ -615,6 +693,13 @@ LinkerObject const& Assembly::assemble() const
|
||||
}
|
||||
}
|
||||
|
||||
assertThrow(
|
||||
immutableReferencesBySub.empty(),
|
||||
AssemblyException,
|
||||
"Some immutables were read from but never assigned."
|
||||
);
|
||||
|
||||
|
||||
if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty())
|
||||
// Append an INVALID here to help tests find miscompilation.
|
||||
ret.bytecode.push_back(uint8_t(Instruction::INVALID));
|
||||
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
||||
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
|
||||
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
|
||||
AssemblyItem newPushImmutable(std::string const& _identifier);
|
||||
AssemblyItem newImmutableAssignment(std::string const& _identifier);
|
||||
|
||||
AssemblyItem const& append(AssemblyItem const& _i);
|
||||
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
|
||||
@@ -64,6 +66,8 @@ public:
|
||||
/// after compilation and CODESIZE is not an option.
|
||||
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
|
||||
void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); }
|
||||
void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); }
|
||||
void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); }
|
||||
|
||||
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
|
||||
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
|
||||
@@ -166,6 +170,7 @@ protected:
|
||||
std::vector<std::shared_ptr<Assembly>> m_subs;
|
||||
std::map<util::h256, std::string> m_strings;
|
||||
std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
||||
std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.
|
||||
|
||||
mutable LinkerObject m_assembledObject;
|
||||
mutable std::vector<size_t> m_tagPositionsInBytecode;
|
||||
|
||||
@@ -80,6 +80,13 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
|
||||
case PushLibraryAddress:
|
||||
case PushDeployTimeAddress:
|
||||
return 1 + 20;
|
||||
case PushImmutable:
|
||||
return 1 + 32;
|
||||
case AssignImmutable:
|
||||
if (m_immutableOccurrences)
|
||||
return 1 + (3 + 32) * *m_immutableOccurrences;
|
||||
else
|
||||
return 1 + (3 + 32) * 1024; // 1024 occurrences are beyond the maximum code size anyways.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -90,6 +97,8 @@ int AssemblyItem::arguments() const
|
||||
{
|
||||
if (type() == Operation)
|
||||
return instructionInfo(instruction()).args;
|
||||
else if (type() == AssignImmutable)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
@@ -108,6 +117,7 @@ int AssemblyItem::returnValues() const
|
||||
case PushSubSize:
|
||||
case PushProgramSize:
|
||||
case PushLibraryAddress:
|
||||
case PushImmutable:
|
||||
case PushDeployTimeAddress:
|
||||
return 1;
|
||||
case Tag:
|
||||
@@ -135,6 +145,7 @@ bool AssemblyItem::canBeFunctional() const
|
||||
case PushProgramSize:
|
||||
case PushLibraryAddress:
|
||||
case PushDeployTimeAddress:
|
||||
case PushImmutable:
|
||||
return true;
|
||||
case Tag:
|
||||
return false;
|
||||
@@ -210,6 +221,12 @@ string AssemblyItem::toAssemblyText() const
|
||||
case PushDeployTimeAddress:
|
||||
text = string("deployTimeAddress()");
|
||||
break;
|
||||
case PushImmutable:
|
||||
text = string("immutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")";
|
||||
break;
|
||||
case AssignImmutable:
|
||||
text = string("assignImmutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")";
|
||||
break;
|
||||
case UndefinedItem:
|
||||
assertThrow(false, AssemblyException, "Invalid assembly item.");
|
||||
break;
|
||||
@@ -275,6 +292,12 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
|
||||
case PushDeployTimeAddress:
|
||||
_out << " PushDeployTimeAddress";
|
||||
break;
|
||||
case PushImmutable:
|
||||
_out << " PushImmutable";
|
||||
break;
|
||||
case AssignImmutable:
|
||||
_out << " AssignImmutable";
|
||||
break;
|
||||
case UndefinedItem:
|
||||
_out << " ???";
|
||||
break;
|
||||
|
||||
@@ -44,7 +44,9 @@ enum AssemblyItemType {
|
||||
Tag,
|
||||
PushData,
|
||||
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.
|
||||
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.
|
||||
};
|
||||
|
||||
class Assembly;
|
||||
@@ -153,6 +155,8 @@ public:
|
||||
|
||||
size_t m_modifierDepth = 0;
|
||||
|
||||
void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = std::make_shared<size_t>(_n); }
|
||||
|
||||
private:
|
||||
AssemblyItemType m_type;
|
||||
Instruction m_instruction; ///< Only valid if m_type == Operation
|
||||
@@ -162,6 +166,8 @@ private:
|
||||
/// Pushed value for operations with data to be determined during assembly stage,
|
||||
/// e.g. PushSubSize, PushTag, PushSub, etc.
|
||||
mutable std::shared_ptr<u256> m_pushedValue;
|
||||
/// Number of PushImmutable's with the same hash. Only used for AssignImmutable.
|
||||
mutable std::shared_ptr<size_t> m_immutableOccurrences;
|
||||
};
|
||||
|
||||
inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength)
|
||||
|
||||
@@ -91,6 +91,10 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
|
||||
{
|
||||
// can be ignored
|
||||
}
|
||||
else if (_item.type() == AssignImmutable)
|
||||
// Since AssignImmutable breaks blocks, it should be fine to only consider its changes to the stack, which
|
||||
// is the same as POP.
|
||||
return feedItem(AssemblyItem(Instruction::POP), _copyItem);
|
||||
else if (_item.type() != Operation)
|
||||
{
|
||||
assertThrow(_item.deposit() == 1, InvalidDeposit, "");
|
||||
|
||||
@@ -40,6 +40,10 @@ struct LinkerObject
|
||||
/// need to be replaced by the actual addresses by the linker.
|
||||
std::map<size_t, std::string> linkReferences;
|
||||
|
||||
/// Map from hashes of the identifiers of immutable variables to a list of offsets into the bytecode
|
||||
/// that refer to their values.
|
||||
std::map<u256, std::vector<size_t>> immutableReferences;
|
||||
|
||||
/// Appends the bytecode of @a _other and incorporates its link references.
|
||||
void append(LinkerObject const& _other);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
|
||||
case UndefinedItem:
|
||||
case Tag:
|
||||
case PushDeployTimeAddress:
|
||||
case AssignImmutable:
|
||||
return true;
|
||||
case Push:
|
||||
case PushString:
|
||||
@@ -45,6 +46,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
|
||||
case PushProgramSize:
|
||||
case PushData:
|
||||
case PushLibraryAddress:
|
||||
case PushImmutable:
|
||||
return false;
|
||||
case Operation:
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user