mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #6774 from ethereum/dialectRefactor
Dialect refactor
This commit is contained in:
		
						commit
						4f3b7b232b
					
				| @ -69,10 +69,7 @@ bool AsmAnalyzer::analyze(Block const& _block) | ||||
| 	return success && !m_errorReporter.hasErrors(); | ||||
| } | ||||
| 
 | ||||
| AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect( | ||||
| 	shared_ptr<Dialect> _dialect, | ||||
| 	Block const& _ast | ||||
| ) | ||||
| AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, Block const& _ast) | ||||
| { | ||||
| 	ErrorList errorList; | ||||
| 	langutil::ErrorReporter errors(errorList); | ||||
| @ -134,7 +131,7 @@ bool AsmAnalyzer::operator()(Literal const& _literal) | ||||
| 	} | ||||
| 	else if (_literal.kind == LiteralKind::Boolean) | ||||
| 	{ | ||||
| 		solAssert(m_dialect->flavour == AsmFlavour::Yul, ""); | ||||
| 		solAssert(m_dialect.flavour == AsmFlavour::Yul, ""); | ||||
| 		solAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); | ||||
| 	} | ||||
| 	m_info.stackHeightInfo[&_literal] = m_stackHeight; | ||||
| @ -197,7 +194,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) | ||||
| 
 | ||||
| bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) | ||||
| { | ||||
| 	solAssert(m_dialect->flavour != AsmFlavour::Yul, ""); | ||||
| 	solAssert(m_dialect.flavour != AsmFlavour::Yul, ""); | ||||
| 	bool success = true; | ||||
| 	for (auto const& arg: _instr.arguments | boost::adaptors::reversed) | ||||
| 		if (!expectExpression(arg)) | ||||
| @ -215,9 +212,9 @@ bool AsmAnalyzer::operator()(ExpressionStatement const& _statement) | ||||
| { | ||||
| 	int initialStackHeight = m_stackHeight; | ||||
| 	bool success = boost::apply_visitor(*this, _statement.expression); | ||||
| 	if (m_stackHeight != initialStackHeight && (m_dialect->flavour != AsmFlavour::Loose || m_errorTypeForLoose)) | ||||
| 	if (m_stackHeight != initialStackHeight && (m_dialect.flavour != AsmFlavour::Loose || m_errorTypeForLoose)) | ||||
| 	{ | ||||
| 		Error::Type errorType = m_dialect->flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; | ||||
| 		Error::Type errorType = m_dialect.flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; | ||||
| 		string msg = | ||||
| 			"Top-level expressions are not supposed to return values (this expression returns " + | ||||
| 			to_string(m_stackHeight - initialStackHeight) + | ||||
| @ -333,7 +330,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) | ||||
| 	size_t parameters = 0; | ||||
| 	size_t returns = 0; | ||||
| 	bool needsLiteralArguments = false; | ||||
| 	if (BuiltinFunction const* f = m_dialect->builtin(_funCall.functionName.name)) | ||||
| 	if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name)) | ||||
| 	{ | ||||
| 		// TODO: compare types, too
 | ||||
| 		parameters = f->parameters.size(); | ||||
| @ -423,7 +420,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch) | ||||
| 	if (!expectExpression(*_switch.expression)) | ||||
| 		success = false; | ||||
| 
 | ||||
| 	if (m_dialect->flavour == AsmFlavour::Yul) | ||||
| 	if (m_dialect.flavour == AsmFlavour::Yul) | ||||
| 	{ | ||||
| 		YulString caseType; | ||||
| 		bool mismatchingTypes = false; | ||||
| @ -658,7 +655,7 @@ Scope& AsmAnalyzer::scope(Block const* _block) | ||||
| } | ||||
| void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) | ||||
| { | ||||
| 	if (m_dialect->flavour != AsmFlavour::Yul) | ||||
| 	if (m_dialect.flavour != AsmFlavour::Yul) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!builtinTypes.count(type)) | ||||
| @ -675,7 +672,7 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio | ||||
| 	solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); | ||||
| 	// Similarly we assume bitwise shifting and create2 go together.
 | ||||
| 	solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); | ||||
| 	solAssert(m_dialect->flavour != AsmFlavour::Yul, ""); | ||||
| 	solAssert(m_dialect.flavour != AsmFlavour::Yul, ""); | ||||
| 
 | ||||
| 	auto errorForVM = [=](string const& vmKindMessage) { | ||||
| 		m_errorReporter.typeError( | ||||
| @ -724,7 +721,7 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio | ||||
| 		_instr == dev::eth::Instruction::JUMPDEST | ||||
| 	) | ||||
| 	{ | ||||
| 		if (m_dialect->flavour == AsmFlavour::Loose) | ||||
| 		if (m_dialect.flavour == AsmFlavour::Loose) | ||||
| 			m_errorReporter.error( | ||||
| 				m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning, | ||||
| 				_location, | ||||
| @ -745,7 +742,7 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio | ||||
| 
 | ||||
| void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description) | ||||
| { | ||||
| 	if (m_dialect->flavour != AsmFlavour::Loose) | ||||
| 	if (m_dialect.flavour != AsmFlavour::Loose) | ||||
| 		solAssert(false, _description); | ||||
| 	else if (m_errorTypeForLoose) | ||||
| 		m_errorReporter.error(*m_errorTypeForLoose, _location, _description); | ||||
|  | ||||
| @ -60,25 +60,22 @@ public: | ||||
| 		AsmAnalysisInfo& _analysisInfo, | ||||
| 		langutil::ErrorReporter& _errorReporter, | ||||
| 		boost::optional<langutil::Error::Type> _errorTypeForLoose, | ||||
| 		std::shared_ptr<Dialect> _dialect, | ||||
| 		Dialect const& _dialect, | ||||
| 		ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver() | ||||
| 	): | ||||
| 		m_resolver(_resolver), | ||||
| 		m_info(_analysisInfo), | ||||
| 		m_errorReporter(_errorReporter), | ||||
| 		m_dialect(std::move(_dialect)), | ||||
| 		m_dialect(_dialect), | ||||
| 		m_errorTypeForLoose(_errorTypeForLoose) | ||||
| 	{ | ||||
| 		if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(m_dialect.get())) | ||||
| 		if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect)) | ||||
| 			m_evmVersion = evmDialect->evmVersion(); | ||||
| 	} | ||||
| 
 | ||||
| 	bool analyze(Block const& _block); | ||||
| 
 | ||||
| 	static AsmAnalysisInfo analyzeStrictAssertCorrect( | ||||
| 		std::shared_ptr<Dialect> _dialect, | ||||
| 		Block const& _ast | ||||
| 	); | ||||
| 	static AsmAnalysisInfo analyzeStrictAssertCorrect(Dialect const& _dialect, Block const& _ast); | ||||
| 
 | ||||
| 	bool operator()(Instruction const&); | ||||
| 	bool operator()(Literal const& _literal); | ||||
| @ -125,7 +122,7 @@ private: | ||||
| 	AsmAnalysisInfo& m_info; | ||||
| 	langutil::ErrorReporter& m_errorReporter; | ||||
| 	langutil::EVMVersion m_evmVersion; | ||||
| 	std::shared_ptr<Dialect> m_dialect; | ||||
| 	Dialect const& m_dialect; | ||||
| 	boost::optional<langutil::Error::Type> m_errorTypeForLoose; | ||||
| 	ForLoop const* m_currentForLoop = nullptr; | ||||
| }; | ||||
|  | ||||
| @ -146,14 +146,14 @@ Statement Parser::parseStatement() | ||||
| 	} | ||||
| 	case Token::Assign: | ||||
| 	{ | ||||
| 		if (m_dialect->flavour != AsmFlavour::Loose) | ||||
| 		if (m_dialect.flavour != AsmFlavour::Loose) | ||||
| 			break; | ||||
| 		StackAssignment assignment = createWithLocation<StackAssignment>(); | ||||
| 		advance(); | ||||
| 		expectToken(Token::Colon); | ||||
| 		assignment.variableName.location = location(); | ||||
| 		assignment.variableName.name = YulString(currentLiteral()); | ||||
| 		if (m_dialect->builtin(assignment.variableName.name)) | ||||
| 		if (m_dialect.builtin(assignment.variableName.name)) | ||||
| 			fatalParserError("Identifier expected, got builtin symbol."); | ||||
| 		else if (instructions().count(assignment.variableName.name.str())) | ||||
| 			fatalParserError("Identifier expected, got instruction name."); | ||||
| @ -197,7 +197,7 @@ Statement Parser::parseStatement() | ||||
| 
 | ||||
| 			auto const& identifier = boost::get<Identifier>(elementary); | ||||
| 
 | ||||
| 			if (m_dialect->builtin(identifier.name)) | ||||
| 			if (m_dialect.builtin(identifier.name)) | ||||
| 				fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\"."); | ||||
| 
 | ||||
| 			variableNames.emplace_back(identifier); | ||||
| @ -231,7 +231,7 @@ Statement Parser::parseStatement() | ||||
| 		advance(); | ||||
| 
 | ||||
| 		// label
 | ||||
| 		if (m_dialect->flavour != AsmFlavour::Loose) | ||||
| 		if (m_dialect.flavour != AsmFlavour::Loose) | ||||
| 			fatalParserError("Labels are not supported."); | ||||
| 
 | ||||
| 		Label label = createWithLocation<Label>(identifier.location); | ||||
| @ -239,7 +239,7 @@ Statement Parser::parseStatement() | ||||
| 		return label; | ||||
| 	} | ||||
| 	default: | ||||
| 		if (m_dialect->flavour != AsmFlavour::Loose) | ||||
| 		if (m_dialect.flavour != AsmFlavour::Loose) | ||||
| 			fatalParserError("Call or assignment expected."); | ||||
| 		break; | ||||
| 	} | ||||
| @ -325,7 +325,7 @@ Expression Parser::parseExpression() | ||||
| 				instructionNames().at(instr.instruction) + | ||||
| 				"\" not allowed in this context." | ||||
| 			); | ||||
| 		if (m_dialect->flavour != AsmFlavour::Loose && currentToken() != Token::LParen) | ||||
| 		if (m_dialect.flavour != AsmFlavour::Loose && currentToken() != Token::LParen) | ||||
| 			fatalParserError( | ||||
| 				"Non-functional instructions are not allowed in this context." | ||||
| 			); | ||||
| @ -345,7 +345,7 @@ Expression Parser::parseExpression() | ||||
| 	else if (operation.type() == typeid(Instruction)) | ||||
| 	{ | ||||
| 		// Instructions not taking arguments are allowed as expressions.
 | ||||
| 		solAssert(m_dialect->flavour == AsmFlavour::Loose, ""); | ||||
| 		solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); | ||||
| 		Instruction& instr = boost::get<Instruction>(operation); | ||||
| 		return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; | ||||
| 	} | ||||
| @ -393,9 +393,9 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() | ||||
| 		else | ||||
| 			literal = YulString{currentLiteral()}; | ||||
| 		// first search the set of builtins, then the instructions.
 | ||||
| 		if (m_dialect->builtin(literal)) | ||||
| 		if (m_dialect.builtin(literal)) | ||||
| 			ret = Identifier{location(), literal}; | ||||
| 		else if (m_dialect->flavour != AsmFlavour::Yul && instructions().count(literal.str())) | ||||
| 		else if (m_dialect.flavour != AsmFlavour::Yul && instructions().count(literal.str())) | ||||
| 		{ | ||||
| 			dev::eth::Instruction const& instr = instructions().at(literal.str()); | ||||
| 			ret = Instruction{location(), instr}; | ||||
| @ -436,7 +436,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() | ||||
| 			{} | ||||
| 		}; | ||||
| 		advance(); | ||||
| 		if (m_dialect->flavour == AsmFlavour::Yul) | ||||
| 		if (m_dialect.flavour == AsmFlavour::Yul) | ||||
| 		{ | ||||
| 			expectToken(Token::Colon); | ||||
| 			literal.location.end = endPosition(); | ||||
| @ -449,7 +449,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() | ||||
| 	} | ||||
| 	default: | ||||
| 		fatalParserError( | ||||
| 			m_dialect->flavour == AsmFlavour::Yul ? | ||||
| 			m_dialect.flavour == AsmFlavour::Yul ? | ||||
| 			"Literal or identifier expected." : | ||||
| 			"Literal, identifier or instruction expected." | ||||
| 		); | ||||
| @ -530,7 +530,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	if (_initialOp.type() == typeid(Instruction)) | ||||
| 	{ | ||||
| 		solAssert(m_dialect->flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); | ||||
| 		solAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); | ||||
| 		Instruction& instruction = boost::get<Instruction>(_initialOp); | ||||
| 		FunctionalInstruction ret; | ||||
| 		ret.instruction = instruction.instruction; | ||||
| @ -601,7 +601,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) | ||||
| 	} | ||||
| 	else | ||||
| 		fatalParserError( | ||||
| 			m_dialect->flavour == AsmFlavour::Yul ? | ||||
| 			m_dialect.flavour == AsmFlavour::Yul ? | ||||
| 			"Function name expected." : | ||||
| 			"Assembly instruction or function name required in front of \"(\")" | ||||
| 		); | ||||
| @ -614,7 +614,7 @@ TypedName Parser::parseTypedName() | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	TypedName typedName = createWithLocation<TypedName>(); | ||||
| 	typedName.name = expectAsmIdentifier(); | ||||
| 	if (m_dialect->flavour == AsmFlavour::Yul) | ||||
| 	if (m_dialect.flavour == AsmFlavour::Yul) | ||||
| 	{ | ||||
| 		expectToken(Token::Colon); | ||||
| 		typedName.location.end = endPosition(); | ||||
| @ -626,7 +626,7 @@ TypedName Parser::parseTypedName() | ||||
| YulString Parser::expectAsmIdentifier() | ||||
| { | ||||
| 	YulString name = YulString{currentLiteral()}; | ||||
| 	if (m_dialect->flavour == AsmFlavour::Yul) | ||||
| 	if (m_dialect.flavour == AsmFlavour::Yul) | ||||
| 	{ | ||||
| 		switch (currentToken()) | ||||
| 		{ | ||||
| @ -640,7 +640,7 @@ YulString Parser::expectAsmIdentifier() | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	else if (m_dialect->builtin(name)) | ||||
| 	else if (m_dialect.builtin(name)) | ||||
| 		fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name."); | ||||
| 	else if (instructions().count(name.str())) | ||||
| 		fatalParserError("Cannot use instruction names for identifier names."); | ||||
|  | ||||
| @ -44,8 +44,8 @@ public: | ||||
| 		None, ForLoopPre, ForLoopPost, ForLoopBody | ||||
| 	}; | ||||
| 
 | ||||
| 	explicit Parser(langutil::ErrorReporter& _errorReporter, std::shared_ptr<Dialect> _dialect): | ||||
| 		ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {} | ||||
| 	explicit Parser(langutil::ErrorReporter& _errorReporter, Dialect const& _dialect): | ||||
| 		ParserBase(_errorReporter), m_dialect(_dialect) {} | ||||
| 
 | ||||
| 	/// Parses an inline assembly block starting with `{` and ending with `}`.
 | ||||
| 	/// @param _reuseScanner if true, do check for end of input after the `}`.
 | ||||
| @ -97,7 +97,7 @@ protected: | ||||
| 	static bool isValidNumberLiteral(std::string const& _literal); | ||||
| 
 | ||||
| private: | ||||
| 	std::shared_ptr<Dialect> m_dialect; | ||||
| 	Dialect const& m_dialect; | ||||
| 	ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -47,7 +47,7 @@ using namespace yul; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| shared_ptr<Dialect> languageToDialect(AssemblyStack::Language _language, EVMVersion _version) | ||||
| Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _version) | ||||
| { | ||||
| 	switch (_language) | ||||
| 	{ | ||||
| @ -58,7 +58,7 @@ shared_ptr<Dialect> languageToDialect(AssemblyStack::Language _language, EVMVers | ||||
| 	case AssemblyStack::Language::Yul: | ||||
| 		return Dialect::yul(); | ||||
| 	case AssemblyStack::Language::EWasm: | ||||
| 		return make_shared<WasmDialect>(); | ||||
| 		return WasmDialect::instance(); | ||||
| 	} | ||||
| 	solAssert(false, ""); | ||||
| 	return Dialect::yul(); | ||||
| @ -124,14 +124,14 @@ bool AssemblyStack::analyzeParsed(Object& _object) | ||||
| 
 | ||||
| void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _evm15, bool _optimize) const | ||||
| { | ||||
| 	shared_ptr<EVMDialect> dialect; | ||||
| 	EVMDialect const* dialect = nullptr; | ||||
| 
 | ||||
| 	if (m_language == Language::Assembly) | ||||
| 		dialect = EVMDialect::looseAssemblyForEVM(m_evmVersion); | ||||
| 		dialect = &EVMDialect::looseAssemblyForEVM(m_evmVersion); | ||||
| 	else if (m_language == AssemblyStack::Language::StrictAssembly) | ||||
| 		dialect = EVMDialect::strictAssemblyForEVMObjects(m_evmVersion); | ||||
| 		dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion); | ||||
| 	else if (m_language == AssemblyStack::Language::Yul) | ||||
| 		dialect = EVMDialect::yulForEVM(m_evmVersion); | ||||
| 		dialect = &EVMDialect::yulForEVM(m_evmVersion); | ||||
| 	else | ||||
| 		solAssert(false, "Invalid language."); | ||||
| 
 | ||||
| @ -184,10 +184,10 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const | ||||
| 	case Machine::eWasm: | ||||
| 	{ | ||||
| 		solAssert(m_language == Language::EWasm, ""); | ||||
| 		shared_ptr<Dialect> dialect = languageToDialect(m_language, EVMVersion{}); | ||||
| 		Dialect const& dialect = languageToDialect(m_language, EVMVersion{}); | ||||
| 
 | ||||
| 		MachineAssemblyObject object; | ||||
| 		object.assembly = EWasmObjectCompiler::compile(*m_parserResult, *dialect); | ||||
| 		object.assembly = EWasmObjectCompiler::compile(*m_parserResult, dialect); | ||||
| 		return object; | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| @ -33,24 +33,25 @@ using namespace yul; | ||||
| using namespace dev; | ||||
| 
 | ||||
| map<YulString, int> CompilabilityChecker::run( | ||||
| 	shared_ptr<Dialect> _dialect, | ||||
| 	Dialect const& _dialect, | ||||
| 	Block const& _ast, | ||||
| 	bool _optimizeStackAllocation | ||||
| ) | ||||
| { | ||||
| 	if (_dialect->flavour == AsmFlavour::Yul) | ||||
| 	if (_dialect.flavour == AsmFlavour::Yul) | ||||
| 		return {}; | ||||
| 
 | ||||
| 	solAssert(_dialect->flavour == AsmFlavour::Strict, ""); | ||||
| 	solAssert(_dialect.flavour == AsmFlavour::Strict, ""); | ||||
| 
 | ||||
| 	solAssert(dynamic_cast<EVMDialect const*>(_dialect.get()), ""); | ||||
| 	shared_ptr<NoOutputEVMDialect> noOutputDialect = make_shared<NoOutputEVMDialect>(dynamic_pointer_cast<EVMDialect>(_dialect)); | ||||
| 	solAssert(dynamic_cast<EVMDialect const*>(&_dialect), ""); | ||||
| 	NoOutputEVMDialect noOutputDialect(dynamic_cast<EVMDialect const&>(_dialect)); | ||||
| 	BuiltinContext builtinContext; | ||||
| 
 | ||||
| 	yul::AsmAnalysisInfo analysisInfo = | ||||
| 		yul::AsmAnalyzer::analyzeStrictAssertCorrect(noOutputDialect, _ast); | ||||
| 
 | ||||
| 	NoOutputAssembly assembly; | ||||
| 	CodeTransform transform(assembly, analysisInfo, _ast, *noOutputDialect, _optimizeStackAllocation); | ||||
| 	CodeTransform transform(assembly, analysisInfo, _ast, noOutputDialect, builtinContext, _optimizeStackAllocation); | ||||
| 	try | ||||
| 	{ | ||||
| 		transform(_ast); | ||||
|  | ||||
| @ -40,7 +40,7 @@ class CompilabilityChecker | ||||
| { | ||||
| public: | ||||
| 	static std::map<YulString, int> run( | ||||
| 		std::shared_ptr<Dialect> _dialect, | ||||
| 		Dialect const& _dialect, | ||||
| 		Block const& _ast, | ||||
| 		bool _optimizeStackAllocation | ||||
| 	); | ||||
|  | ||||
| @ -64,10 +64,10 @@ struct Dialect: boost::noncopyable | ||||
| 	Dialect(AsmFlavour _flavour): flavour(_flavour) {} | ||||
| 	virtual ~Dialect() = default; | ||||
| 
 | ||||
| 	static std::shared_ptr<Dialect> yul() | ||||
| 	static Dialect const& yul() | ||||
| 	{ | ||||
| 		// Will have to add builtins later.
 | ||||
| 		return std::make_shared<Dialect>(AsmFlavour::Yul); | ||||
| 		static Dialect yulDialect(AsmFlavour::Yul); | ||||
| 		return yulDialect; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -45,11 +45,8 @@ namespace yul | ||||
| class ObjectParser: public langutil::ParserBase | ||||
| { | ||||
| public: | ||||
| 	explicit ObjectParser( | ||||
| 		langutil::ErrorReporter& _errorReporter, | ||||
| 		std::shared_ptr<Dialect> _dialect | ||||
| 	): | ||||
| 		ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {} | ||||
| 	explicit ObjectParser(langutil::ErrorReporter& _errorReporter, Dialect const& _dialect): | ||||
| 		ParserBase(_errorReporter), m_dialect(_dialect) {} | ||||
| 
 | ||||
| 	/// Parses a Yul object.
 | ||||
| 	/// Falls back to code-only parsing if the source starts with `{`.
 | ||||
| @ -67,7 +64,7 @@ private: | ||||
| 	YulString parseUniqueName(Object const* _containingObject); | ||||
| 	void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr<ObjectNode> _subObject); | ||||
| 
 | ||||
| 	std::shared_ptr<Dialect> m_dialect; | ||||
| 	Dialect const& m_dialect; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -184,12 +184,13 @@ void CodeGenerator::assemble( | ||||
| ) | ||||
| { | ||||
| 	EthAssemblyAdapter assemblyAdapter(_assembly); | ||||
| 	shared_ptr<EVMDialect> dialect = EVMDialect::strictAssemblyForEVM(_evmVersion); | ||||
| 	BuiltinContext builtinContext; | ||||
| 	CodeTransform transform( | ||||
| 		assemblyAdapter, | ||||
| 		_analysisInfo, | ||||
| 		_parsedData, | ||||
| 		*dialect, | ||||
| 		EVMDialect::strictAssemblyForEVM(_evmVersion), | ||||
| 		builtinContext, | ||||
| 		_optimizeStackAllocation, | ||||
| 		false, | ||||
| 		_identifierAccess, | ||||
|  | ||||
| @ -96,6 +96,7 @@ CodeTransform::CodeTransform( | ||||
| 	Block const& _block, | ||||
| 	bool _allowStackOpt, | ||||
| 	EVMDialect const& _dialect, | ||||
| 	BuiltinContext& _builtinContext, | ||||
| 	bool _evm15, | ||||
| 	ExternalIdentifierAccess const& _identifierAccess, | ||||
| 	bool _useNamedLabelsForFunctions, | ||||
| @ -105,6 +106,7 @@ CodeTransform::CodeTransform( | ||||
| 	m_assembly(_assembly), | ||||
| 	m_info(_analysisInfo), | ||||
| 	m_dialect(_dialect), | ||||
| 	m_builtinContext(_builtinContext), | ||||
| 	m_allowStackOpt(_allowStackOpt), | ||||
| 	m_evm15(_evm15), | ||||
| 	m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions), | ||||
| @ -280,7 +282,7 @@ void CodeTransform::operator()(FunctionCall const& _call) | ||||
| 
 | ||||
| 	if (BuiltinFunctionForEVM const* builtin = m_dialect.builtin(_call.functionName.name)) | ||||
| 	{ | ||||
| 		builtin->generateCode(_call, m_assembly, [&]() { | ||||
| 		builtin->generateCode(_call, m_assembly, m_builtinContext, [&]() { | ||||
| 			for (auto const& arg: _call.arguments | boost::adaptors::reversed) | ||||
| 				visitExpression(arg); | ||||
| 			m_assembly.setSourceLocation(_call.location); | ||||
| @ -519,6 +521,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) | ||||
| 			_function.body, | ||||
| 			m_allowStackOpt, | ||||
| 			m_dialect, | ||||
| 			m_builtinContext, | ||||
| 			m_evm15, | ||||
| 			m_identifierAccess, | ||||
| 			m_useNamedLabelsForFunctions, | ||||
|  | ||||
| @ -121,6 +121,7 @@ public: | ||||
| 		AsmAnalysisInfo& _analysisInfo, | ||||
| 		Block const& _block, | ||||
| 		EVMDialect const& _dialect, | ||||
| 		BuiltinContext& _builtinContext, | ||||
| 		bool _allowStackOpt = false, | ||||
| 		bool _evm15 = false, | ||||
| 		ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), | ||||
| @ -131,6 +132,7 @@ public: | ||||
| 		_block, | ||||
| 		_allowStackOpt, | ||||
| 		_dialect, | ||||
| 		_builtinContext, | ||||
| 		_evm15, | ||||
| 		_identifierAccess, | ||||
| 		_useNamedLabelsForFunctions, | ||||
| @ -151,6 +153,7 @@ protected: | ||||
| 		Block const& _block, | ||||
| 		bool _allowStackOpt, | ||||
| 		EVMDialect const& _dialect, | ||||
| 		BuiltinContext& _builtinContext, | ||||
| 		bool _evm15, | ||||
| 		ExternalIdentifierAccess const& _identifierAccess, | ||||
| 		bool _useNamedLabelsForFunctions, | ||||
| @ -225,6 +228,7 @@ private: | ||||
| 	AsmAnalysisInfo& m_info; | ||||
| 	Scope* m_scope = nullptr; | ||||
| 	EVMDialect const& m_dialect; | ||||
| 	BuiltinContext& m_builtinContext; | ||||
| 	bool const m_allowStackOpt = true; | ||||
| 	bool const m_evm15 = false; | ||||
| 	bool const m_useNamedLabelsForFunctions = false; | ||||
|  | ||||
| @ -35,55 +35,98 @@ using namespace std; | ||||
| using namespace dev; | ||||
| using namespace yul; | ||||
| 
 | ||||
| EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVersion _evmVersion): | ||||
| 	Dialect{_flavour}, m_objectAccess(_objectAccess), m_evmVersion(_evmVersion) | ||||
| namespace | ||||
| { | ||||
| 	// The EVM instructions will be moved to builtins at some point.
 | ||||
| 	if (!m_objectAccess) | ||||
| 		return; | ||||
| pair<YulString, BuiltinFunctionForEVM> createFunction( | ||||
| 	string _name, | ||||
| 	size_t _params, | ||||
| 	size_t _returns, | ||||
| 	bool _movable, | ||||
| 	bool _sideEffectFree, | ||||
| 	bool _literalArguments, | ||||
| 	std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> _generateCode | ||||
| ) | ||||
| { | ||||
| 	YulString name{std::move(_name)}; | ||||
| 	BuiltinFunctionForEVM f; | ||||
| 	f.name = name; | ||||
| 	f.parameters.resize(_params); | ||||
| 	f.returns.resize(_returns); | ||||
| 	f.movable = _movable; | ||||
| 	f.literalArguments = _literalArguments; | ||||
| 	f.sideEffectFree = _sideEffectFree; | ||||
| 	f.generateCode = std::move(_generateCode); | ||||
| 	return {name, f}; | ||||
| } | ||||
| 
 | ||||
| 	addFunction("datasize", 1, 1, true, true, true, [this]( | ||||
| 		FunctionCall const& _call, | ||||
| 		AbstractAssembly& _assembly, | ||||
| 		std::function<void()> | ||||
| 	) { | ||||
| 		yulAssert(m_currentObject, "No object available."); | ||||
| 		yulAssert(_call.arguments.size() == 1, ""); | ||||
| 		Expression const& arg = _call.arguments.front(); | ||||
| 		YulString dataName = boost::get<Literal>(arg).value; | ||||
| 		if (m_currentObject->name == dataName) | ||||
| 			_assembly.appendAssemblySize(); | ||||
| 		else | ||||
| 		{ | ||||
| 			yulAssert(m_subIDs.count(dataName) != 0, "Could not find assembly object <" + dataName.str() + ">."); | ||||
| 			_assembly.appendDataSize(m_subIDs.at(dataName)); | ||||
| 		} | ||||
| 	}); | ||||
| 	addFunction("dataoffset", 1, 1, true, true, true, [this]( | ||||
| 		FunctionCall const& _call, | ||||
| 		AbstractAssembly& _assembly, | ||||
| 		std::function<void()> | ||||
| 	) { | ||||
| 		yulAssert(m_currentObject, "No object available."); | ||||
| 		yulAssert(_call.arguments.size() == 1, ""); | ||||
| 		Expression const& arg = _call.arguments.front(); | ||||
| 		YulString dataName = boost::get<Literal>(arg).value; | ||||
| 		if (m_currentObject->name == dataName) | ||||
| 			_assembly.appendConstant(0); | ||||
| 		else | ||||
| 		{ | ||||
| 			yulAssert(m_subIDs.count(dataName) != 0, "Could not find assembly object <" + dataName.str() + ">."); | ||||
| 			_assembly.appendDataOffset(m_subIDs.at(dataName)); | ||||
| 		} | ||||
| 	}); | ||||
| 	addFunction("datacopy", 3, 0, false, false, false, []( | ||||
| 		FunctionCall const&, | ||||
| 		AbstractAssembly& _assembly, | ||||
| 		std::function<void()> _visitArguments | ||||
| 	) { | ||||
| 		_visitArguments(); | ||||
| 		_assembly.appendInstruction(dev::eth::Instruction::CODECOPY); | ||||
| 	}); | ||||
| map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion, bool _objectAccess) | ||||
| { | ||||
| 	map<YulString, BuiltinFunctionForEVM> builtins; | ||||
| 	if (_objectAccess) | ||||
| 	{ | ||||
| 		builtins.emplace(createFunction("datasize", 1, 1, true, true, true, []( | ||||
| 			FunctionCall const& _call, | ||||
| 			AbstractAssembly& _assembly, | ||||
| 			BuiltinContext& _context, | ||||
| 			function<void()> | ||||
| 		) { | ||||
| 			yulAssert(_context.currentObject, "No object available."); | ||||
| 			yulAssert(_call.arguments.size() == 1, ""); | ||||
| 			Expression const& arg = _call.arguments.front(); | ||||
| 			YulString dataName = boost::get<Literal>(arg).value; | ||||
| 			if (_context.currentObject->name == dataName) | ||||
| 				_assembly.appendAssemblySize(); | ||||
| 			else | ||||
| 			{ | ||||
| 				yulAssert( | ||||
| 					_context.subIDs.count(dataName) != 0, | ||||
| 					"Could not find assembly object <" + dataName.str() + ">." | ||||
| 				); | ||||
| 				_assembly.appendDataSize(_context.subIDs.at(dataName)); | ||||
| 			} | ||||
| 		})); | ||||
| 		builtins.emplace(createFunction("dataoffset", 1, 1, true, true, true, []( | ||||
| 			FunctionCall const& _call, | ||||
| 			AbstractAssembly& _assembly, | ||||
| 			BuiltinContext& _context, | ||||
| 			std::function<void()> | ||||
| 		) { | ||||
| 			yulAssert(_context.currentObject, "No object available."); | ||||
| 			yulAssert(_call.arguments.size() == 1, ""); | ||||
| 			Expression const& arg = _call.arguments.front(); | ||||
| 			YulString dataName = boost::get<Literal>(arg).value; | ||||
| 			if (_context.currentObject->name == dataName) | ||||
| 				_assembly.appendConstant(0); | ||||
| 			else | ||||
| 			{ | ||||
| 				yulAssert( | ||||
| 					_context.subIDs.count(dataName) != 0, | ||||
| 					"Could not find assembly object <" + dataName.str() + ">." | ||||
| 				); | ||||
| 				_assembly.appendDataOffset(_context.subIDs.at(dataName)); | ||||
| 			} | ||||
| 		})); | ||||
| 		builtins.emplace(createFunction("datacopy", 3, 0, false, false, false, []( | ||||
| 			FunctionCall const&, | ||||
| 			AbstractAssembly& _assembly, | ||||
| 			BuiltinContext&, | ||||
| 			std::function<void()> _visitArguments | ||||
| 		) { | ||||
| 			_visitArguments(); | ||||
| 			_assembly.appendInstruction(dev::eth::Instruction::CODECOPY); | ||||
| 		})); | ||||
| 	} | ||||
| 	return builtins; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVersion _evmVersion): | ||||
| 	Dialect{_flavour}, | ||||
| 	m_objectAccess(_objectAccess), | ||||
| 	m_evmVersion(_evmVersion), | ||||
| 	m_functions(createBuiltins(_evmVersion, _objectAccess)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const | ||||
| @ -95,55 +138,34 @@ BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const | ||||
| 		return nullptr; | ||||
| } | ||||
| 
 | ||||
| shared_ptr<EVMDialect> EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version) | ||||
| EVMDialect const& EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version) | ||||
| { | ||||
| 	return make_shared<EVMDialect>(AsmFlavour::Loose, false, _version); | ||||
| 	static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects; | ||||
| 	if (!dialects[_version]) | ||||
| 		dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Loose, false, _version); | ||||
| 	return *dialects[_version]; | ||||
| } | ||||
| 
 | ||||
| shared_ptr<EVMDialect> EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version) | ||||
| EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version) | ||||
| { | ||||
| 	return make_shared<EVMDialect>(AsmFlavour::Strict, false, _version); | ||||
| 	static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects; | ||||
| 	if (!dialects[_version]) | ||||
| 		dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Strict, false, _version); | ||||
| 	return *dialects[_version]; | ||||
| } | ||||
| 
 | ||||
| shared_ptr<EVMDialect> EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _version) | ||||
| EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _version) | ||||
| { | ||||
| 	return make_shared<EVMDialect>(AsmFlavour::Strict, true, _version); | ||||
| 	static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects; | ||||
| 	if (!dialects[_version]) | ||||
| 		dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Strict, true, _version); | ||||
| 	return *dialects[_version]; | ||||
| } | ||||
| 
 | ||||
| shared_ptr<yul::EVMDialect> EVMDialect::yulForEVM(langutil::EVMVersion _version) | ||||
| EVMDialect const& EVMDialect::yulForEVM(langutil::EVMVersion _version) | ||||
| { | ||||
| 	return make_shared<EVMDialect>(AsmFlavour::Yul, false, _version); | ||||
| } | ||||
| 
 | ||||
| void EVMDialect::setSubIDs(map<YulString, AbstractAssembly::SubID> _subIDs) | ||||
| { | ||||
| 	yulAssert(m_objectAccess, "Sub IDs set with dialect that does not support object access."); | ||||
| 	m_subIDs = std::move(_subIDs); | ||||
| } | ||||
| 
 | ||||
| void EVMDialect::setCurrentObject(Object const* _object) | ||||
| { | ||||
| 	yulAssert(m_objectAccess, "Current object set with dialect that does not support object access."); | ||||
| 	m_currentObject = _object; | ||||
| } | ||||
| 
 | ||||
| void EVMDialect::addFunction( | ||||
| 	string _name, | ||||
| 	size_t _params, | ||||
| 	size_t _returns, | ||||
| 	bool _movable, | ||||
| 	bool _sideEffectFree, | ||||
| 	bool _literalArguments, | ||||
| 	std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode | ||||
| ) | ||||
| { | ||||
| 	YulString name{std::move(_name)}; | ||||
| 	BuiltinFunctionForEVM& f = m_functions[name]; | ||||
| 	f.name = name; | ||||
| 	f.parameters.resize(_params); | ||||
| 	f.returns.resize(_returns); | ||||
| 	f.movable = _movable; | ||||
| 	f.sideEffectFree = _sideEffectFree; | ||||
| 	f.literalArguments = _literalArguments; | ||||
| 	f.generateCode = std::move(_generateCode); | ||||
| 	static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects; | ||||
| 	if (!dialects[_version]) | ||||
| 		dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Yul, false, _version); | ||||
| 	return *dialects[_version]; | ||||
| } | ||||
|  | ||||
| @ -35,14 +35,25 @@ using Type = YulString; | ||||
| struct FunctionCall; | ||||
| struct Object; | ||||
| 
 | ||||
| /**
 | ||||
|  * Context used during code generation. | ||||
|  */ | ||||
| struct BuiltinContext | ||||
| { | ||||
| 	Object const* currentObject = nullptr; | ||||
| 	/// Mapping from named objects to abstract assembly sub IDs.
 | ||||
| 	std::map<YulString, AbstractAssembly::SubID> subIDs; | ||||
| }; | ||||
| 
 | ||||
| struct BuiltinFunctionForEVM: BuiltinFunction | ||||
| { | ||||
| 	/// Function to generate code for the given function call and append it to the abstract
 | ||||
| 	/// assembly. The third parameter is called to visit (and generate code for) the arguments
 | ||||
| 	/// assembly. The fourth parameter is called to visit (and generate code for) the arguments
 | ||||
| 	/// from right to left.
 | ||||
| 	std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> generateCode; | ||||
| 	std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> generateCode; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Yul dialect for EVM as a backend. | ||||
|  * The main difference is that the builtin functions take an AbstractAssembly for the | ||||
| @ -50,41 +61,24 @@ struct BuiltinFunctionForEVM: BuiltinFunction | ||||
|  */ | ||||
| struct EVMDialect: public Dialect | ||||
| { | ||||
| 	/// Constructor, should only be used internally. Use the factory functions below.
 | ||||
| 	EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVersion _evmVersion); | ||||
| 
 | ||||
| 	/// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
 | ||||
| 	BuiltinFunctionForEVM const* builtin(YulString _name) const override; | ||||
| 
 | ||||
| 	static std::shared_ptr<EVMDialect> looseAssemblyForEVM(langutil::EVMVersion _version); | ||||
| 	static std::shared_ptr<EVMDialect> strictAssemblyForEVM(langutil::EVMVersion _version); | ||||
| 	static std::shared_ptr<EVMDialect> strictAssemblyForEVMObjects(langutil::EVMVersion _version); | ||||
| 	static std::shared_ptr<EVMDialect> yulForEVM(langutil::EVMVersion _version); | ||||
| 	static EVMDialect const& looseAssemblyForEVM(langutil::EVMVersion _version); | ||||
| 	static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version); | ||||
| 	static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version); | ||||
| 	static EVMDialect const& yulForEVM(langutil::EVMVersion _version); | ||||
| 
 | ||||
| 	langutil::EVMVersion evmVersion() const { return m_evmVersion; } | ||||
| 
 | ||||
| 	bool providesObjectAccess() const { return m_objectAccess; } | ||||
| 
 | ||||
| 	/// Sets the mapping of current sub assembly IDs. Used during code generation.
 | ||||
| 	void setSubIDs(std::map<YulString, AbstractAssembly::SubID> _subIDs); | ||||
| 	/// Sets the current object. Used during code generation.
 | ||||
| 	void setCurrentObject(Object const* _object); | ||||
| 
 | ||||
| protected: | ||||
| 	void addFunction( | ||||
| 		std::string _name, | ||||
| 		size_t _params, | ||||
| 		size_t _returns, | ||||
| 		bool _movable, | ||||
| 		bool _sideEffectFree, | ||||
| 		bool _literalArguments, | ||||
| 		std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode | ||||
| 	); | ||||
| 
 | ||||
| 	bool m_objectAccess; | ||||
| 	langutil::EVMVersion m_evmVersion; | ||||
| 	Object const* m_currentObject = nullptr; | ||||
| 	/// Mapping from named objects to abstract assembly sub IDs.
 | ||||
| 	std::map<YulString, AbstractAssembly::SubID> m_subIDs; | ||||
| 	bool const m_objectAccess; | ||||
| 	langutil::EVMVersion const m_evmVersion; | ||||
| 	std::map<YulString, BuiltinFunctionForEVM> m_functions; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -29,7 +29,7 @@ | ||||
| using namespace yul; | ||||
| using namespace std; | ||||
| 
 | ||||
| void EVMObjectCompiler::compile(Object& _object, AbstractAssembly& _assembly, EVMDialect& _dialect, bool _evm15, bool _optimize) | ||||
| void EVMObjectCompiler::compile(Object& _object, AbstractAssembly& _assembly, EVMDialect const& _dialect, bool _evm15, bool _optimize) | ||||
| { | ||||
| 	EVMObjectCompiler compiler(_assembly, _dialect, _evm15); | ||||
| 	compiler.run(_object, _optimize); | ||||
| @ -37,32 +37,27 @@ void EVMObjectCompiler::compile(Object& _object, AbstractAssembly& _assembly, EV | ||||
| 
 | ||||
| void EVMObjectCompiler::run(Object& _object, bool _optimize) | ||||
| { | ||||
| 	map<YulString, AbstractAssembly::SubID> subIDs; | ||||
| 	BuiltinContext context; | ||||
| 	context.currentObject = &_object; | ||||
| 
 | ||||
| 	for (auto& subNode: _object.subObjects) | ||||
| 		if (Object* subObject = dynamic_cast<Object*>(subNode.get())) | ||||
| 		{ | ||||
| 			auto subAssemblyAndID = m_assembly.createSubAssembly(); | ||||
| 			subIDs[subObject->name] = subAssemblyAndID.second; | ||||
| 			context.subIDs[subObject->name] = subAssemblyAndID.second; | ||||
| 			compile(*subObject, *subAssemblyAndID.first, m_dialect, m_evm15, _optimize); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			Data const& data = dynamic_cast<Data const&>(*subNode); | ||||
| 			subIDs[data.name] = m_assembly.appendData(data.data); | ||||
| 			context.subIDs[data.name] = m_assembly.appendData(data.data); | ||||
| 		} | ||||
| 
 | ||||
| 	if (m_dialect.providesObjectAccess()) | ||||
| 	{ | ||||
| 		m_dialect.setSubIDs(std::move(subIDs)); | ||||
| 		m_dialect.setCurrentObject(&_object); | ||||
| 	} | ||||
| 
 | ||||
| 	yulAssert(_object.analysisInfo, "No analysis info."); | ||||
| 	yulAssert(_object.code, "No code."); | ||||
| 	// We do not catch and re-throw the stack too deep exception here because it is a YulException,
 | ||||
| 	// which should be native to this part of the code.
 | ||||
| 	CodeTransform transform{m_assembly, *_object.analysisInfo, *_object.code, m_dialect, _optimize, m_evm15}; | ||||
| 	CodeTransform transform{m_assembly, *_object.analysisInfo, *_object.code, m_dialect, context, _optimize, m_evm15}; | ||||
| 	transform(*_object.code); | ||||
| 	yulAssert(transform.stackErrors().empty(), "Stack errors present but not thrown."); | ||||
| } | ||||
|  | ||||
| @ -29,16 +29,16 @@ struct EVMDialect; | ||||
| class EVMObjectCompiler | ||||
| { | ||||
| public: | ||||
| 	static void compile(Object& _object, AbstractAssembly& _assembly, EVMDialect& _dialect, bool _evm15, bool _optimize); | ||||
| 	static void compile(Object& _object, AbstractAssembly& _assembly, EVMDialect const& _dialect, bool _evm15, bool _optimize); | ||||
| private: | ||||
| 	EVMObjectCompiler(AbstractAssembly& _assembly, EVMDialect& _dialect, bool _evm15): | ||||
| 	EVMObjectCompiler(AbstractAssembly& _assembly, EVMDialect const& _dialect, bool _evm15): | ||||
| 		m_assembly(_assembly), m_dialect(_dialect), m_evm15(_evm15) | ||||
| 	{} | ||||
| 
 | ||||
| 	void run(Object& _object, bool _optimize); | ||||
| 
 | ||||
| 	AbstractAssembly& m_assembly; | ||||
| 	EVMDialect& m_dialect; | ||||
| 	EVMDialect const& m_dialect; | ||||
| 	bool m_evm15 = false; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -142,14 +142,14 @@ AbstractAssembly::SubID NoOutputAssembly::appendData(bytes const&) | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| NoOutputEVMDialect::NoOutputEVMDialect(shared_ptr<EVMDialect> const& _copyFrom): | ||||
| 	EVMDialect(_copyFrom->flavour, _copyFrom->providesObjectAccess(), _copyFrom->evmVersion()) | ||||
| NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom): | ||||
| 	EVMDialect(_copyFrom.flavour, _copyFrom.providesObjectAccess(), _copyFrom.evmVersion()) | ||||
| { | ||||
| 	for (auto& fun: m_functions) | ||||
| 	{ | ||||
| 		size_t parameters = fun.second.parameters.size(); | ||||
| 		size_t returns = fun.second.returns.size(); | ||||
| 		fun.second.generateCode = [=](FunctionCall const&, AbstractAssembly& _assembly, std::function<void()> _visitArguments) | ||||
| 		fun.second.generateCode = [=](FunctionCall const&, AbstractAssembly& _assembly, BuiltinContext&, std::function<void()> _visitArguments) | ||||
| 		{ | ||||
| 			_visitArguments(); | ||||
| 			for (size_t i = 0; i < parameters; i++) | ||||
|  | ||||
| @ -81,7 +81,7 @@ private: | ||||
|  */ | ||||
| struct NoOutputEVMDialect: public EVMDialect | ||||
| { | ||||
| 	explicit NoOutputEVMDialect(std::shared_ptr<EVMDialect> const& _copyFrom); | ||||
| 	explicit NoOutputEVMDialect(EVMDialect const& _copyFrom); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -28,7 +28,7 @@ | ||||
| using namespace yul; | ||||
| using namespace std; | ||||
| 
 | ||||
| string EWasmObjectCompiler::compile(Object& _object, Dialect& _dialect) | ||||
| string EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect) | ||||
| { | ||||
| 	EWasmObjectCompiler compiler(_dialect); | ||||
| 	return compiler.run(_object); | ||||
|  | ||||
| @ -30,15 +30,15 @@ struct Dialect; | ||||
| class EWasmObjectCompiler | ||||
| { | ||||
| public: | ||||
| 	static std::string compile(Object& _object, Dialect& _dialect); | ||||
| 	static std::string compile(Object& _object, Dialect const& _dialect); | ||||
| private: | ||||
| 	EWasmObjectCompiler(Dialect& _dialect): | ||||
| 	EWasmObjectCompiler(Dialect const& _dialect): | ||||
| 		m_dialect(_dialect) | ||||
| 	{} | ||||
| 
 | ||||
| 	std::string run(Object& _object); | ||||
| 
 | ||||
| 	Dialect& m_dialect; | ||||
| 	Dialect const& m_dialect; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -42,11 +42,17 @@ struct Object; | ||||
|  */ | ||||
| struct WasmDialect: public Dialect | ||||
| { | ||||
| 	WasmDialect(); | ||||
| 
 | ||||
| 	BuiltinFunction const* builtin(YulString _name) const override; | ||||
| 
 | ||||
| 	static WasmDialect const& instance() | ||||
| 	{ | ||||
| 		static WasmDialect dialect; | ||||
| 		return dialect; | ||||
| 	} | ||||
| 
 | ||||
| protected: | ||||
| 	WasmDialect(); | ||||
| 
 | ||||
| 	void addFunction(std::string _name, size_t _params, size_t _returns); | ||||
| 
 | ||||
| 	std::map<YulString, BuiltinFunction> m_functions; | ||||
|  | ||||
| @ -112,9 +112,9 @@ public: | ||||
| }; | ||||
| 
 | ||||
| template <typename ASTNode> | ||||
| void eliminateVariables(shared_ptr<Dialect> const& _dialect, ASTNode& _node, size_t _numVariables) | ||||
| void eliminateVariables(Dialect const& _dialect, ASTNode& _node, size_t _numVariables) | ||||
| { | ||||
| 	RematCandidateSelector selector{*_dialect}; | ||||
| 	RematCandidateSelector selector{_dialect}; | ||||
| 	selector(_node); | ||||
| 
 | ||||
| 	// Select at most _numVariables
 | ||||
| @ -126,14 +126,14 @@ void eliminateVariables(shared_ptr<Dialect> const& _dialect, ASTNode& _node, siz | ||||
| 		varsToEliminate.insert(costs.second); | ||||
| 	} | ||||
| 
 | ||||
| 	Rematerialiser::run(*_dialect, _node, std::move(varsToEliminate)); | ||||
| 	UnusedPruner::runUntilStabilised(*_dialect, _node); | ||||
| 	Rematerialiser::run(_dialect, _node, std::move(varsToEliminate)); | ||||
| 	UnusedPruner::runUntilStabilised(_dialect, _node); | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| bool StackCompressor::run( | ||||
| 	shared_ptr<Dialect> const& _dialect, | ||||
| 	Dialect const& _dialect, | ||||
| 	Block& _ast, | ||||
| 	bool _optimizeStackAllocation, | ||||
| 	size_t _maxIterations | ||||
|  | ||||
| @ -42,7 +42,7 @@ public: | ||||
| 	/// Try to remove local variables until the AST is compilable.
 | ||||
| 	/// @returns true if it was successful.
 | ||||
| 	static bool run( | ||||
| 		std::shared_ptr<Dialect> const& _dialect, | ||||
| 		Dialect const& _dialect, | ||||
| 		Block& _ast, | ||||
| 		bool _optimizeStackAllocation, | ||||
| 		size_t _maxIterations | ||||
|  | ||||
| @ -58,7 +58,7 @@ using namespace dev; | ||||
| using namespace yul; | ||||
| 
 | ||||
| void OptimiserSuite::run( | ||||
| 	shared_ptr<Dialect> const& _dialect, | ||||
| 	Dialect const& _dialect, | ||||
| 	Block& _ast, | ||||
| 	AsmAnalysisInfo const& _analysisInfo, | ||||
| 	bool _optimizeStackAllocation, | ||||
| @ -67,7 +67,7 @@ void OptimiserSuite::run( | ||||
| { | ||||
| 	set<YulString> reservedIdentifiers = _externallyUsedIdentifiers; | ||||
| 
 | ||||
| 	Block ast = boost::get<Block>(Disambiguator(*_dialect, _analysisInfo, reservedIdentifiers)(_ast)); | ||||
| 	Block ast = boost::get<Block>(Disambiguator(_dialect, _analysisInfo, reservedIdentifiers)(_ast)); | ||||
| 
 | ||||
| 	VarDeclInitializer{}(ast); | ||||
| 	FunctionHoister{}(ast); | ||||
| @ -76,16 +76,16 @@ void OptimiserSuite::run( | ||||
| 	DeadCodeEliminator{}(ast); | ||||
| 	FunctionGrouper{}(ast); | ||||
| 	EquivalentFunctionCombiner::run(ast); | ||||
| 	UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 	UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 	BlockFlattener{}(ast); | ||||
| 	ControlFlowSimplifier{}(ast); | ||||
| 	StructuralSimplifier{*_dialect}(ast); | ||||
| 	StructuralSimplifier{_dialect}(ast); | ||||
| 	ControlFlowSimplifier{}(ast); | ||||
| 	BlockFlattener{}(ast); | ||||
| 
 | ||||
| 	// None of the above can make stack problems worse.
 | ||||
| 
 | ||||
| 	NameDispenser dispenser{*_dialect, ast}; | ||||
| 	NameDispenser dispenser{_dialect, ast}; | ||||
| 
 | ||||
| 	size_t codeSize = 0; | ||||
| 	for (size_t rounds = 0; rounds < 12; ++rounds) | ||||
| @ -99,35 +99,35 @@ void OptimiserSuite::run( | ||||
| 
 | ||||
| 		{ | ||||
| 			// Turn into SSA and simplify
 | ||||
| 			ExpressionSplitter{*_dialect, dispenser}(ast); | ||||
| 			ExpressionSplitter{_dialect, dispenser}(ast); | ||||
| 			SSATransform::run(ast, dispenser); | ||||
| 			RedundantAssignEliminator::run(*_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(*_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(_dialect, ast); | ||||
| 
 | ||||
| 			ExpressionSimplifier::run(*_dialect, ast); | ||||
| 			CommonSubexpressionEliminator{*_dialect}(ast); | ||||
| 			ExpressionSimplifier::run(_dialect, ast); | ||||
| 			CommonSubexpressionEliminator{_dialect}(ast); | ||||
| 		} | ||||
| 
 | ||||
| 		{ | ||||
| 			// still in SSA, perform structural simplification
 | ||||
| 			ControlFlowSimplifier{}(ast); | ||||
| 			StructuralSimplifier{*_dialect}(ast); | ||||
| 			StructuralSimplifier{_dialect}(ast); | ||||
| 			ControlFlowSimplifier{}(ast); | ||||
| 			BlockFlattener{}(ast); | ||||
| 			DeadCodeEliminator{}(ast); | ||||
| 			UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 			UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 		} | ||||
| 		{ | ||||
| 			// simplify again
 | ||||
| 			CommonSubexpressionEliminator{*_dialect}(ast); | ||||
| 			UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 			CommonSubexpressionEliminator{_dialect}(ast); | ||||
| 			UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 		} | ||||
| 
 | ||||
| 		{ | ||||
| 			// reverse SSA
 | ||||
| 			SSAReverser::run(ast); | ||||
| 			CommonSubexpressionEliminator{*_dialect}(ast); | ||||
| 			UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 			CommonSubexpressionEliminator{_dialect}(ast); | ||||
| 			UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 
 | ||||
| 			ExpressionJoiner::run(ast); | ||||
| 			ExpressionJoiner::run(ast); | ||||
| @ -137,17 +137,17 @@ void OptimiserSuite::run( | ||||
| 
 | ||||
| 		{ | ||||
| 			// run functional expression inliner
 | ||||
| 			ExpressionInliner(*_dialect, ast).run(); | ||||
| 			UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 			ExpressionInliner(_dialect, ast).run(); | ||||
| 			UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 		} | ||||
| 
 | ||||
| 		{ | ||||
| 			// Turn into SSA again and simplify
 | ||||
| 			ExpressionSplitter{*_dialect, dispenser}(ast); | ||||
| 			ExpressionSplitter{_dialect, dispenser}(ast); | ||||
| 			SSATransform::run(ast, dispenser); | ||||
| 			RedundantAssignEliminator::run(*_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(*_dialect, ast); | ||||
| 			CommonSubexpressionEliminator{*_dialect}(ast); | ||||
| 			RedundantAssignEliminator::run(_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(_dialect, ast); | ||||
| 			CommonSubexpressionEliminator{_dialect}(ast); | ||||
| 		} | ||||
| 
 | ||||
| 		{ | ||||
| @ -161,39 +161,39 @@ void OptimiserSuite::run( | ||||
| 		{ | ||||
| 			// SSA plus simplify
 | ||||
| 			SSATransform::run(ast, dispenser); | ||||
| 			RedundantAssignEliminator::run(*_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(*_dialect, ast); | ||||
| 			ExpressionSimplifier::run(*_dialect, ast); | ||||
| 			StructuralSimplifier{*_dialect}(ast); | ||||
| 			RedundantAssignEliminator::run(_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(_dialect, ast); | ||||
| 			ExpressionSimplifier::run(_dialect, ast); | ||||
| 			StructuralSimplifier{_dialect}(ast); | ||||
| 			BlockFlattener{}(ast); | ||||
| 			DeadCodeEliminator{}(ast); | ||||
| 			ControlFlowSimplifier{}(ast); | ||||
| 			CommonSubexpressionEliminator{*_dialect}(ast); | ||||
| 			CommonSubexpressionEliminator{_dialect}(ast); | ||||
| 			SSATransform::run(ast, dispenser); | ||||
| 			RedundantAssignEliminator::run(*_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(*_dialect, ast); | ||||
| 			UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 			CommonSubexpressionEliminator{*_dialect}(ast); | ||||
| 			RedundantAssignEliminator::run(_dialect, ast); | ||||
| 			RedundantAssignEliminator::run(_dialect, ast); | ||||
| 			UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 			CommonSubexpressionEliminator{_dialect}(ast); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Make source short and pretty.
 | ||||
| 
 | ||||
| 	ExpressionJoiner::run(ast); | ||||
| 	Rematerialiser::run(*_dialect, ast); | ||||
| 	UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 	Rematerialiser::run(_dialect, ast); | ||||
| 	UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 	ExpressionJoiner::run(ast); | ||||
| 	UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 	UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 	ExpressionJoiner::run(ast); | ||||
| 	UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 	UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 
 | ||||
| 	SSAReverser::run(ast); | ||||
| 	CommonSubexpressionEliminator{*_dialect}(ast); | ||||
| 	UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 	CommonSubexpressionEliminator{_dialect}(ast); | ||||
| 	UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 
 | ||||
| 	ExpressionJoiner::run(ast); | ||||
| 	Rematerialiser::run(*_dialect, ast); | ||||
| 	UnusedPruner::runUntilStabilised(*_dialect, ast, reservedIdentifiers); | ||||
| 	Rematerialiser::run(_dialect, ast); | ||||
| 	UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); | ||||
| 
 | ||||
| 	// This is a tuning parameter, but actually just prevents infinite loops.
 | ||||
| 	size_t stackCompressorMaxIterations = 16; | ||||
| @ -206,7 +206,7 @@ void OptimiserSuite::run( | ||||
| 	ControlFlowSimplifier{}(ast); | ||||
| 
 | ||||
| 	FunctionGrouper{}(ast); | ||||
| 	VarNameCleaner{ast, *_dialect, reservedIdentifiers}(ast); | ||||
| 	VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast); | ||||
| 	yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, ast); | ||||
| 
 | ||||
| 	_ast = std::move(ast); | ||||
|  | ||||
| @ -39,7 +39,7 @@ class OptimiserSuite | ||||
| { | ||||
| public: | ||||
| 	static void run( | ||||
| 		std::shared_ptr<Dialect> const& _dialect, | ||||
| 		Dialect const& _dialect, | ||||
| 		Block& _ast, | ||||
| 		AsmAnalysisInfo const& _analysisInfo, | ||||
| 		bool _optimizeStackAllocation, | ||||
|  | ||||
| @ -43,7 +43,7 @@ using namespace yul; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| shared_ptr<Dialect> defaultDialect(bool _yul) | ||||
| Dialect const& defaultDialect(bool _yul) | ||||
| { | ||||
| 	return _yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM(dev::test::Options::get().evmVersion()); | ||||
| } | ||||
| @ -75,7 +75,7 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(strin | ||||
| yul::Block yul::test::disambiguate(string const& _source, bool _yul) | ||||
| { | ||||
| 	auto result = parse(_source, _yul); | ||||
| 	return boost::get<Block>(Disambiguator(*defaultDialect(_yul), *result.second, {})(*result.first)); | ||||
| 	return boost::get<Block>(Disambiguator(defaultDialect(_yul), *result.second, {})(*result.first)); | ||||
| } | ||||
| 
 | ||||
| string yul::test::format(string const& _source, bool _yul) | ||||
|  | ||||
| @ -49,7 +49,7 @@ namespace test | ||||
| namespace | ||||
| { | ||||
| 
 | ||||
| bool parse(string const& _source, std::shared_ptr<Dialect> _dialect, ErrorReporter& errorReporter) | ||||
| bool parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter) | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| @ -73,7 +73,7 @@ bool parse(string const& _source, std::shared_ptr<Dialect> _dialect, ErrorReport | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| boost::optional<Error> parseAndReturnFirstError(string const& _source, shared_ptr<Dialect> _dialect, bool _allowWarnings = true) | ||||
| boost::optional<Error> parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarnings = true) | ||||
| { | ||||
| 	ErrorList errors; | ||||
| 	ErrorReporter errorReporter(errors); | ||||
| @ -98,12 +98,12 @@ boost::optional<Error> parseAndReturnFirstError(string const& _source, shared_pt | ||||
| 	return {}; | ||||
| } | ||||
| 
 | ||||
| bool successParse(std::string const& _source, shared_ptr<Dialect> _dialect = Dialect::yul(), bool _allowWarnings = true) | ||||
| bool successParse(std::string const& _source, Dialect const& _dialect = Dialect::yul(), bool _allowWarnings = true) | ||||
| { | ||||
| 	return !parseAndReturnFirstError(_source, _dialect, _allowWarnings); | ||||
| } | ||||
| 
 | ||||
| Error expectError(std::string const& _source, shared_ptr<Dialect> _dialect = Dialect::yul(), bool _allowWarnings = false) | ||||
| Error expectError(std::string const& _source, Dialect const& _dialect = Dialect::yul(), bool _allowWarnings = false) | ||||
| { | ||||
| 
 | ||||
| 	auto error = parseAndReturnFirstError(_source, _dialect, _allowWarnings); | ||||
| @ -324,41 +324,39 @@ BOOST_AUTO_TEST_CASE(if_statement) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(break_outside_of_for_loop) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ let x if x { break } }", | ||||
| 		SyntaxError, | ||||
| 		"Keyword \"break\" needs to be inside a for-loop body.", | ||||
| 		dialect | ||||
| 		EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()) | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(continue_outside_of_for_loop) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ let x if x { continue } }", | ||||
| 		SyntaxError, | ||||
| 		"Keyword \"continue\" needs to be inside a for-loop body.", | ||||
| 		dialect | ||||
| 		EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()) | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {} }", dialect)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_break) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {break} }", dialect)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_break_init) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ for {let i := 0 break} iszero(eq(i, 10)) {i := add(i, 1)} {} }", | ||||
| 		SyntaxError, | ||||
| @ -369,7 +367,7 @@ BOOST_AUTO_TEST_CASE(for_statement_break_init) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_break_post) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) break} {} }", | ||||
| 		SyntaxError, | ||||
| @ -380,7 +378,7 @@ BOOST_AUTO_TEST_CASE(for_statement_break_post) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_nested_break) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ for {let i := 0} iszero(eq(i, 10)) {} { function f() { break } } }", | ||||
| 		SyntaxError, | ||||
| @ -391,13 +389,13 @@ BOOST_AUTO_TEST_CASE(for_statement_nested_break) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_continue) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {continue} }", dialect)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_continue_fail_init) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ for {let i := 0 continue} iszero(eq(i, 10)) {i := add(i, 1)} {} }", | ||||
| 		SyntaxError, | ||||
| @ -408,7 +406,7 @@ BOOST_AUTO_TEST_CASE(for_statement_continue_fail_init) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_continue_fail_post) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) continue} {} }", | ||||
| 		SyntaxError, | ||||
| @ -419,7 +417,7 @@ BOOST_AUTO_TEST_CASE(for_statement_continue_fail_post) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_nested_continue) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ for {let i := 0} iszero(eq(i, 10)) {} { function f() { continue } } }", | ||||
| 		SyntaxError, | ||||
| @ -430,7 +428,7 @@ BOOST_AUTO_TEST_CASE(for_statement_nested_continue) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_continue_nested_init_in_body) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| 		"{ for {} 1 {} {let x for { continue } x {} {}} }", | ||||
| 		SyntaxError, | ||||
| @ -441,31 +439,31 @@ BOOST_AUTO_TEST_CASE(for_statement_continue_nested_init_in_body) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_continue_nested_body_in_init) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	BOOST_CHECK(successParse("{ for {let x for {} x {} { continue }} 1 {} {} }", dialect)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_break_nested_body_in_init) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	BOOST_CHECK(successParse("{ for {let x for {} x {} { break }} 1 {} {} }", dialect)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_continue_nested_body_in_post) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	BOOST_CHECK(successParse("{ for {} 1 {let x for {} x {} { continue }} {} }", dialect)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(for_statement_break_nested_body_in_post) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	BOOST_CHECK(successParse("{ for {} 1 {let x for {} x {} { break }} {} }", dialect)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(function_defined_in_init_block) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	BOOST_CHECK(successParse("{ for { } 1 { function f() {} } {} }", dialect)); | ||||
| 	BOOST_CHECK(successParse("{ for { } 1 {} { function f() {} } }", dialect)); | ||||
| 	CHECK_ERROR_DIALECT( | ||||
| @ -478,7 +476,7 @@ BOOST_AUTO_TEST_CASE(function_defined_in_init_block) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(function_defined_in_init_nested) | ||||
| { | ||||
| 	auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); | ||||
| 	BOOST_CHECK(successParse( | ||||
| 		"{ for {" | ||||
| 			"for { } 1 { function f() {} } {}" | ||||
| @ -548,7 +546,7 @@ BOOST_AUTO_TEST_CASE(builtins_parser) | ||||
| 		BuiltinFunction f; | ||||
| 	}; | ||||
| 
 | ||||
| 	shared_ptr<Dialect> dialect = make_shared<SimpleDialect>(); | ||||
| 	SimpleDialect dialect; | ||||
| 	CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect); | ||||
| 	CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect); | ||||
| 	CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect); | ||||
| @ -567,7 +565,7 @@ BOOST_AUTO_TEST_CASE(builtins_analysis) | ||||
| 		BuiltinFunction f{"builtin"_yulstring, vector<Type>(2), vector<Type>(3), false, false}; | ||||
| 	}; | ||||
| 
 | ||||
| 	shared_ptr<Dialect> dialect = make_shared<SimpleDialect>(); | ||||
| 	SimpleDialect dialect; | ||||
| 	BOOST_CHECK(successParse("{ let a, b, c := builtin(1, 2) }", dialect)); | ||||
| 	CHECK_ERROR_DIALECT("{ let a, b, c := builtin(1) }", TypeError, "Function expects 2 arguments but got 1", dialect); | ||||
| 	CHECK_ERROR_DIALECT("{ let a, b := builtin(1, 2) }", DeclarationError, "Variable count mismatch: 2 variables and 3 values.", dialect); | ||||
|  | ||||
| @ -132,8 +132,7 @@ string YulInterpreterTest::interpret() | ||||
| 	state.maxTraceSize = 10000; | ||||
| 	state.maxSteps = 10000; | ||||
| 	state.maxMemSize = 0x20000000; | ||||
| 	shared_ptr<Dialect> dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{})); | ||||
| 	Interpreter interpreter(state, *dialect); | ||||
| 	Interpreter interpreter(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{})); | ||||
| 	try | ||||
| 	{ | ||||
| 		interpreter(*m_ast); | ||||
|  | ||||
| @ -109,6 +109,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line | ||||
| 	if (!parse(_stream, _linePrefix, _formatted)) | ||||
| 		return TestResult::FatalError; | ||||
| 
 | ||||
| 	soltestAssert(m_dialect, "Dialect not set."); | ||||
| 	if (m_optimizerStep == "disambiguator") | ||||
| 		disambiguate(); | ||||
| 	else if (m_optimizerStep == "blockFlattener") | ||||
| @ -270,7 +271,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line | ||||
| 		disambiguate(); | ||||
| 		(FunctionGrouper{})(*m_ast); | ||||
| 		size_t maxIterations = 16; | ||||
| 		StackCompressor::run(m_dialect, *m_ast, true, maxIterations); | ||||
| 		StackCompressor::run(*m_dialect, *m_ast, true, maxIterations); | ||||
| 		(BlockFlattener{})(*m_ast); | ||||
| 	} | ||||
| 	else if (m_optimizerStep == "wordSizeTransform") | ||||
| @ -281,7 +282,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line | ||||
| 		WordSizeTransform::run(*m_ast, nameDispenser); | ||||
| 	} | ||||
| 	else if (m_optimizerStep == "fullSuite") | ||||
| 		OptimiserSuite::run(m_dialect, *m_ast, *m_analysisInfo, true); | ||||
| 		OptimiserSuite::run(*m_dialect, *m_ast, *m_analysisInfo, true); | ||||
| 	else | ||||
| 	{ | ||||
| 		AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl; | ||||
| @ -353,7 +354,7 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c | ||||
| 		printErrors(_stream, stack.errors()); | ||||
| 		return false; | ||||
| 	} | ||||
| 	m_dialect = m_yul ? Dialect::yul() : EVMDialect::strictAssemblyForEVMObjects(dev::test::Options::get().evmVersion()); | ||||
| 	m_dialect = m_yul ? &Dialect::yul() : &EVMDialect::strictAssemblyForEVMObjects(dev::test::Options::get().evmVersion()); | ||||
| 	m_ast = stack.parserResult()->code; | ||||
| 	m_analysisInfo = stack.parserResult()->analysisInfo; | ||||
| 	return true; | ||||
|  | ||||
| @ -66,7 +66,7 @@ private: | ||||
| 	std::string m_optimizerStep; | ||||
| 	std::string m_expectation; | ||||
| 
 | ||||
| 	std::shared_ptr<Dialect> m_dialect; | ||||
| 	Dialect const* m_dialect = nullptr; | ||||
| 	std::shared_ptr<Block> m_ast; | ||||
| 	std::shared_ptr<AsmAnalysisInfo> m_analysisInfo; | ||||
| 	std::string m_obtainedResult; | ||||
|  | ||||
| @ -81,7 +81,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) | ||||
| 		yulFuzzerUtil::interpret( | ||||
| 			os1, | ||||
| 			stack.parserResult()->code, | ||||
| 			*EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) | ||||
| 			EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) | ||||
| 		); | ||||
| 	} | ||||
| 	catch (yul::test::StepLimitReached const&) | ||||
| @ -98,7 +98,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) | ||||
| 		yulFuzzerUtil::interpret( | ||||
| 			os2, | ||||
| 			stack.parserResult()->code, | ||||
| 			*EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), | ||||
| 			EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), | ||||
| 			(yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 1.5) | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| @ -79,7 +79,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) | ||||
| 		yulFuzzerUtil::interpret( | ||||
| 			os1, | ||||
| 			stack.parserResult()->code, | ||||
| 			*EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) | ||||
| 			EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) | ||||
| 		); | ||||
| 	} | ||||
| 	catch (yul::test::StepLimitReached const&) | ||||
| @ -96,7 +96,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) | ||||
| 		yulFuzzerUtil::interpret( | ||||
| 			os2, | ||||
| 			stack.parserResult()->code, | ||||
| 			*EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), | ||||
| 			EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), | ||||
| 			(yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 1.5) | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| @ -122,9 +122,9 @@ public: | ||||
| 				return; | ||||
| 			if (!disambiguated) | ||||
| 			{ | ||||
| 				*m_ast = boost::get<yul::Block>(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast)); | ||||
| 				*m_ast = boost::get<yul::Block>(Disambiguator(m_dialect, *m_analysisInfo)(*m_ast)); | ||||
| 				m_analysisInfo.reset(); | ||||
| 				m_nameDispenser = make_shared<NameDispenser>(*m_dialect, *m_ast); | ||||
| 				m_nameDispenser = make_shared<NameDispenser>(m_dialect, *m_ast); | ||||
| 				disambiguated = true; | ||||
| 			} | ||||
| 			cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; | ||||
| @ -146,16 +146,16 @@ public: | ||||
| 				ForLoopInitRewriter{}(*m_ast); | ||||
| 				break; | ||||
| 			case 'c': | ||||
| 				(CommonSubexpressionEliminator{*m_dialect})(*m_ast); | ||||
| 				(CommonSubexpressionEliminator{m_dialect})(*m_ast); | ||||
| 				break; | ||||
| 			case 'd': | ||||
| 				(VarDeclInitializer{})(*m_ast); | ||||
| 				break; | ||||
| 			case 'l': | ||||
| 				VarNameCleaner{*m_ast, *m_dialect}(*m_ast); | ||||
| 				VarNameCleaner{*m_ast, m_dialect}(*m_ast); | ||||
| 				break; | ||||
| 			case 'x': | ||||
| 				ExpressionSplitter{*m_dialect, *m_nameDispenser}(*m_ast); | ||||
| 				ExpressionSplitter{m_dialect, *m_nameDispenser}(*m_ast); | ||||
| 				break; | ||||
| 			case 'j': | ||||
| 				ExpressionJoiner::run(*m_ast); | ||||
| @ -167,22 +167,22 @@ public: | ||||
| 				(FunctionHoister{})(*m_ast); | ||||
| 				break; | ||||
| 			case 'e': | ||||
| 				ExpressionInliner{*m_dialect, *m_ast}.run(); | ||||
| 				ExpressionInliner{m_dialect, *m_ast}.run(); | ||||
| 				break; | ||||
| 			case 'i': | ||||
| 				FullInliner(*m_ast, *m_nameDispenser).run(); | ||||
| 				break; | ||||
| 			case 's': | ||||
| 				ExpressionSimplifier::run(*m_dialect, *m_ast); | ||||
| 				ExpressionSimplifier::run(m_dialect, *m_ast); | ||||
| 				break; | ||||
| 			case 't': | ||||
| 				(StructuralSimplifier{*m_dialect})(*m_ast); | ||||
| 				(StructuralSimplifier{m_dialect})(*m_ast); | ||||
| 				break; | ||||
| 			case 'n': | ||||
| 				(ControlFlowSimplifier{})(*m_ast); | ||||
| 				break; | ||||
| 			case 'u': | ||||
| 				UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); | ||||
| 				UnusedPruner::runUntilStabilised(m_dialect, *m_ast); | ||||
| 				break; | ||||
| 			case 'D': | ||||
| 				DeadCodeEliminator{}(*m_ast); | ||||
| @ -191,10 +191,10 @@ public: | ||||
| 				SSATransform::run(*m_ast, *m_nameDispenser); | ||||
| 				break; | ||||
| 			case 'r': | ||||
| 				RedundantAssignEliminator::run(*m_dialect, *m_ast); | ||||
| 				RedundantAssignEliminator::run(m_dialect, *m_ast); | ||||
| 				break; | ||||
| 			case 'm': | ||||
| 				Rematerialiser::run(*m_dialect, *m_ast); | ||||
| 				Rematerialiser::run(m_dialect, *m_ast); | ||||
| 				break; | ||||
| 			case 'v': | ||||
| 				EquivalentFunctionCombiner::run(*m_ast); | ||||
| @ -215,7 +215,7 @@ public: | ||||
| private: | ||||
| 	ErrorList m_errors; | ||||
| 	shared_ptr<yul::Block> m_ast; | ||||
| 	shared_ptr<Dialect> m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; | ||||
| 	Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; | ||||
| 	shared_ptr<AsmAnalysisInfo> m_analysisInfo; | ||||
| 	shared_ptr<NameDispenser> m_nameDispenser; | ||||
| }; | ||||
|  | ||||
| @ -88,8 +88,8 @@ void interpret(string const& _source) | ||||
| 	InterpreterState state; | ||||
| 	state.maxTraceSize = 10000; | ||||
| 	state.maxMemSize = 0x20000000; | ||||
| 	shared_ptr<Dialect> dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{})); | ||||
| 	Interpreter interpreter(state, *dialect); | ||||
| 	Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{})); | ||||
| 	Interpreter interpreter(state, dialect); | ||||
| 	try | ||||
| 	{ | ||||
| 		interpreter(*ast); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user