Split external identifier access into resolving and code generation.

This commit is contained in:
chriseth
2017-04-25 16:49:03 +02:00
committed by chriseth
parent 5d6747eb32
commit e0849f2f3b
9 changed files with 189 additions and 144 deletions
+81 -73
View File
@@ -522,91 +522,99 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
ErrorList errors;
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errors);
unsigned startStackHeight = m_context.stackHeight();
codeGen.assemble(
m_context.nonConstAssembly(),
[&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) {
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end())
return false;
Declaration const* decl = ref->second;
solAssert(!!decl, "");
if (_context == assembly::CodeGenerator::IdentifierContext::RValue)
assembly::ExternalIdentifierAccess identifierAccess;
identifierAccess.resolve = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end())
return size_t(-1);
return ref->second.valueSize;
};
identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, eth::Assembly& _assembly)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), "");
Declaration const* decl = ref->second.declaration;
solAssert(!!decl, "");
if (_context == assembly::IdentifierContext::RValue)
{
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
{
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
functionDef = &m_context.resolveVirtualFunction(*functionDef);
_assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
// If there is a runtime context, we have to merge both labels into the same
// stack slot in case we store it in storage.
if (CompilerContext* rtc = m_context.runtimeContext())
{
functionDef = &m_context.resolveVirtualFunction(*functionDef);
_assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
// If there is a runtime context, we have to merge both labels into the same
// stack slot in case we store it in storage.
if (CompilerContext* rtc = m_context.runtimeContext())
{
_assembly.append(u256(1) << 32);
_assembly.append(Instruction::MUL);
_assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
_assembly.append(Instruction::OR);
}
_assembly.append(u256(1) << 32);
_assembly.append(Instruction::MUL);
_assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
_assembly.append(Instruction::OR);
}
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
}
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
{
solAssert(!variable->isConstant(), "");
if (m_context.isLocalVariable(variable))
{
solAssert(!variable->isConstant(), "");
if (m_context.isLocalVariable(variable))
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable);
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i)
_assembly.append(dupInstruction(stackDiff));
}
else
{
solAssert(m_context.isStateVariable(variable), "Invalid variable type.");
auto const& location = m_context.storageLocationOfVariable(*variable);
if (!variable->type()->isValueType())
{
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable);
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i)
_assembly.append(dupInstruction(stackDiff));
solAssert(location.second == 0, "Intra-slot offest assumed to be zero.");
_assembly.append(location.first);
}
else
{
solAssert(m_context.isStateVariable(variable), "Invalid variable type.");
auto const& location = m_context.storageLocationOfVariable(*variable);
if (!variable->type()->isValueType())
{
solAssert(location.second == 0, "Intra-slot offest assumed to be zero.");
_assembly.append(location.first);
}
else
{
_assembly.append(location.first);
_assembly.append(u256(location.second));
}
_assembly.append(location.first);
_assembly.append(u256(location.second));
}
}
else if (auto contract = dynamic_cast<ContractDefinition const*>(decl))
{
solAssert(contract->isLibrary(), "");
_assembly.appendLibraryAddress(contract->fullyQualifiedName());
}
else
solAssert(false, "Invalid declaration type.");
} else {
// lvalue context
auto variable = dynamic_cast<VariableDeclaration const*>(decl);
solAssert(
!!variable && m_context.isLocalVariable(variable),
"Can only assign to stack variables in inline assembly."
);
unsigned size = variable->type()->sizeOnStack();
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - size;
if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < size; ++i) {
_assembly.append(swapInstruction(stackDiff));
_assembly.append(Instruction::POP);
}
}
return true;
else if (auto contract = dynamic_cast<ContractDefinition const*>(decl))
{
solAssert(contract->isLibrary(), "");
_assembly.appendLibraryAddress(contract->fullyQualifiedName());
}
else
solAssert(false, "Invalid declaration type.");
} else {
// lvalue context
auto variable = dynamic_cast<VariableDeclaration const*>(decl);
solAssert(
!!variable && m_context.isLocalVariable(variable),
"Can only assign to stack variables in inline assembly."
);
unsigned size = variable->type()->sizeOnStack();
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - size;
if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < size; ++i) {
_assembly.append(swapInstruction(stackDiff));
_assembly.append(Instruction::POP);
}
}
};
codeGen.assemble(
m_context.nonConstAssembly(),
identifierAccess
);
solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested.");
m_context.setStackOffset(startStackHeight);