mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	EWasm binary transform.
This commit is contained in:
		
							parent
							
								
									9bec533453
								
							
						
					
					
						commit
						081845d775
					
				| @ -200,7 +200,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const | |||||||
| 		Dialect const& dialect = languageToDialect(m_language, EVMVersion{}); | 		Dialect const& dialect = languageToDialect(m_language, EVMVersion{}); | ||||||
| 
 | 
 | ||||||
| 		MachineAssemblyObject object; | 		MachineAssemblyObject object; | ||||||
| 		object.assembly = EWasmObjectCompiler::compile(*m_parserResult, dialect); | 		object.assembly = EWasmObjectCompiler::compile(*m_parserResult, dialect).first; | ||||||
| 		return object; | 		return object; | ||||||
| 	} | 	} | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -50,6 +50,8 @@ add_library(yul | |||||||
| 	backends/wasm/EWasmObjectCompiler.h | 	backends/wasm/EWasmObjectCompiler.h | ||||||
| 	backends/wasm/EWasmToText.cpp | 	backends/wasm/EWasmToText.cpp | ||||||
| 	backends/wasm/EWasmToText.h | 	backends/wasm/EWasmToText.h | ||||||
|  | 	backends/wasm/BinaryTransform.cpp | ||||||
|  | 	backends/wasm/BinaryTransform.h | ||||||
| 	backends/wasm/WasmDialect.cpp | 	backends/wasm/WasmDialect.cpp | ||||||
| 	backends/wasm/WasmDialect.h | 	backends/wasm/WasmDialect.h | ||||||
| 	backends/wasm/WordSizeTransform.cpp | 	backends/wasm/WordSizeTransform.cpp | ||||||
|  | |||||||
							
								
								
									
										582
									
								
								libyul/backends/wasm/BinaryTransform.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										582
									
								
								libyul/backends/wasm/BinaryTransform.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,582 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | /**
 | ||||||
|  |  * EWasm to binary encoder. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <libyul/backends/wasm/BinaryTransform.h> | ||||||
|  | 
 | ||||||
|  | #include <libyul/Exceptions.h> | ||||||
|  | #include <libdevcore/CommonData.h> | ||||||
|  | 
 | ||||||
|  | #include <boost/range/adaptor/reversed.hpp> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace yul; | ||||||
|  | using namespace dev; | ||||||
|  | using namespace yul::wasm; | ||||||
|  | 
 | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | bytes toBytes(uint8_t _b) | ||||||
|  | { | ||||||
|  | 	return bytes(1, _b); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum class Section: uint8_t | ||||||
|  | { | ||||||
|  | 	CUSTOM = 0x00, | ||||||
|  | 	TYPE = 0x01, | ||||||
|  | 	IMPORT = 0x02, | ||||||
|  | 	FUNCTION = 0x03, | ||||||
|  | 	MEMORY = 0x05, | ||||||
|  | 	GLOBAL = 0x06, | ||||||
|  | 	EXPORT = 0x07, | ||||||
|  | 	CODE = 0x0a | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bytes toBytes(Section _s) | ||||||
|  | { | ||||||
|  | 	return toBytes(uint8_t(_s)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum class ValueType: uint8_t | ||||||
|  | { | ||||||
|  | 	Void = 0x40, | ||||||
|  | 	Function = 0x60, | ||||||
|  | 	I64 = 0x7e, | ||||||
|  | 	I32 = 0x7f | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bytes toBytes(ValueType _vt) | ||||||
|  | { | ||||||
|  | 	return toBytes(uint8_t(_vt)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum class Export: uint8_t | ||||||
|  | { | ||||||
|  | 	Function = 0x0, | ||||||
|  | 	Memory = 0x2 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bytes toBytes(Export _export) | ||||||
|  | { | ||||||
|  | 	return toBytes(uint8_t(_export)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum class Opcode: uint8_t | ||||||
|  | { | ||||||
|  | 	Unreachable = 0x00, | ||||||
|  | 	Nop = 0x01, | ||||||
|  | 	Block = 0x02, | ||||||
|  | 	Loop = 0x03, | ||||||
|  | 	If = 0x04, | ||||||
|  | 	Else = 0x05, | ||||||
|  | 	Try = 0x06, | ||||||
|  | 	Catch = 0x07, | ||||||
|  | 	Throw = 0x08, | ||||||
|  | 	Rethrow = 0x09, | ||||||
|  | 	BrOnExn = 0x0a, | ||||||
|  | 	End = 0x0b, | ||||||
|  | 	Br = 0x0c, | ||||||
|  | 	BrIf = 0x0d, | ||||||
|  | 	BrTable = 0x0e, | ||||||
|  | 	Return = 0x0f, | ||||||
|  | 	Call = 0x10, | ||||||
|  | 	CallIndirect = 0x11, | ||||||
|  | 	ReturnCall = 0x12, | ||||||
|  | 	ReturnCallIndirect = 0x13, | ||||||
|  | 	Drop = 0x1a, | ||||||
|  | 	Select = 0x1b, | ||||||
|  | 	LocalGet = 0x20, | ||||||
|  | 	LocalSet = 0x21, | ||||||
|  | 	LocalTee = 0x22, | ||||||
|  | 	GlobalGet = 0x23, | ||||||
|  | 	GlobalSet = 0x24, | ||||||
|  | 	I32Const = 0x41, | ||||||
|  | 	I64Const = 0x42, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bytes toBytes(Opcode _o) | ||||||
|  | { | ||||||
|  | 	return toBytes(uint8_t(_o)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static std::map<string, uint8_t> const builtins = { | ||||||
|  | 	{"i32.load", 0x28}, | ||||||
|  | 	{"i64.load", 0x29}, | ||||||
|  | 	{"i32.load8_s", 0x2c}, | ||||||
|  | 	{"i32.load8_u", 0x2d}, | ||||||
|  | 	{"i32.load16_s", 0x2e}, | ||||||
|  | 	{"i32.load16_u", 0x2f}, | ||||||
|  | 	{"i64.load8_s", 0x30}, | ||||||
|  | 	{"i64.load8_u", 0x31}, | ||||||
|  | 	{"i64.load16_s", 0x32}, | ||||||
|  | 	{"i64.load16_u", 0x33}, | ||||||
|  | 	{"i64.load32_s", 0x34}, | ||||||
|  | 	{"i64.load32_u", 0x35}, | ||||||
|  | 	{"i32.store", 0x36}, | ||||||
|  | 	{"i64.store", 0x37}, | ||||||
|  | 	{"i32.store8", 0x3a}, | ||||||
|  | 	{"i32.store16", 0x3b}, | ||||||
|  | 	{"i64.store8", 0x3c}, | ||||||
|  | 	{"i64.store16", 0x3d}, | ||||||
|  | 	{"i64.store32", 0x3e}, | ||||||
|  | 	{"memory.size", 0x3f}, | ||||||
|  | 	{"memory.grow", 0x40}, | ||||||
|  | 	{"i32.eqz", 0x45}, | ||||||
|  | 	{"i32.eq", 0x46}, | ||||||
|  | 	{"i32.ne", 0x47}, | ||||||
|  | 	{"i32.lt_s", 0x48}, | ||||||
|  | 	{"i32.lt_u", 0x49}, | ||||||
|  | 	{"i32.gt_s", 0x4a}, | ||||||
|  | 	{"i32.gt_u", 0x4b}, | ||||||
|  | 	{"i32.le_s", 0x4c}, | ||||||
|  | 	{"i32.le_u", 0x4d}, | ||||||
|  | 	{"i32.ge_s", 0x4e}, | ||||||
|  | 	{"i32.ge_u", 0x4f}, | ||||||
|  | 	{"i64.eqz", 0x50}, | ||||||
|  | 	{"i64.eq", 0x51}, | ||||||
|  | 	{"i64.ne", 0x52}, | ||||||
|  | 	{"i64.lt_s", 0x53}, | ||||||
|  | 	{"i64.lt_u", 0x54}, | ||||||
|  | 	{"i64.gt_s", 0x55}, | ||||||
|  | 	{"i64.gt_u", 0x56}, | ||||||
|  | 	{"i64.le_s", 0x57}, | ||||||
|  | 	{"i64.le_u", 0x58}, | ||||||
|  | 	{"i64.ge_s", 0x59}, | ||||||
|  | 	{"i64.ge_u", 0x5a}, | ||||||
|  | 	{"i32.clz", 0x67}, | ||||||
|  | 	{"i32.ctz", 0x68}, | ||||||
|  | 	{"i32.popcnt", 0x69}, | ||||||
|  | 	{"i32.add", 0x6a}, | ||||||
|  | 	{"i32.sub", 0x6b}, | ||||||
|  | 	{"i32.mul", 0x6c}, | ||||||
|  | 	{"i32.div_s", 0x6d}, | ||||||
|  | 	{"i32.div_u", 0x6e}, | ||||||
|  | 	{"i32.rem_s", 0x6f}, | ||||||
|  | 	{"i32.rem_u", 0x70}, | ||||||
|  | 	{"i32.and", 0x71}, | ||||||
|  | 	{"i32.or", 0x72}, | ||||||
|  | 	{"i32.xor", 0x73}, | ||||||
|  | 	{"i32.shl", 0x74}, | ||||||
|  | 	{"i32.shr_s", 0x75}, | ||||||
|  | 	{"i32.shr_u", 0x76}, | ||||||
|  | 	{"i32.rotl", 0x77}, | ||||||
|  | 	{"i32.rotr", 0x78}, | ||||||
|  | 	{"i64.clz", 0x79}, | ||||||
|  | 	{"i64.ctz", 0x7a}, | ||||||
|  | 	{"i64.popcnt", 0x7b}, | ||||||
|  | 	{"i64.add", 0x7c}, | ||||||
|  | 	{"i64.sub", 0x7d}, | ||||||
|  | 	{"i64.mul", 0x7e}, | ||||||
|  | 	{"i64.div_s", 0x7f}, | ||||||
|  | 	{"i64.div_u", 0x80}, | ||||||
|  | 	{"i64.rem_s", 0x81}, | ||||||
|  | 	{"i64.rem_u", 0x82}, | ||||||
|  | 	{"i64.and", 0x83}, | ||||||
|  | 	{"i64.or", 0x84}, | ||||||
|  | 	{"i64.xor", 0x85}, | ||||||
|  | 	{"i64.shl", 0x86}, | ||||||
|  | 	{"i64.shr_s", 0x87}, | ||||||
|  | 	{"i64.shr_u", 0x88}, | ||||||
|  | 	{"i64.rotl", 0x89}, | ||||||
|  | 	{"i64.rotr", 0x8a}, | ||||||
|  | 	{"i32.wrap_i64", 0xa7}, | ||||||
|  | 	{"i64.extend_i32_s", 0xac}, | ||||||
|  | 	{"i64.extend_i32_u", 0xad}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bytes lebEncode(uint64_t _n) | ||||||
|  | { | ||||||
|  | 	bytes encoded; | ||||||
|  | 	while (_n > 0x7f) | ||||||
|  | 	{ | ||||||
|  | 		encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f))); | ||||||
|  | 		_n >>= 7; | ||||||
|  | 	} | ||||||
|  | 	encoded.emplace_back(_n); | ||||||
|  | 	return encoded; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes lebEncodeSigned(int64_t _n) | ||||||
|  | { | ||||||
|  | 	if (_n >= 0 && _n < 0x40) | ||||||
|  | 		return toBytes(uint8_t(uint64_t(_n) & 0xff)); | ||||||
|  | 	else if (-_n > 0 && -_n < 0x40) | ||||||
|  | 		return toBytes(uint8_t(uint64_t(_n + 0x80) & 0xff)); | ||||||
|  | 	else | ||||||
|  | 		return toBytes(uint8_t(0x80 | uint8_t(_n & 0x7f))) + lebEncodeSigned(_n / 0x80); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes prefixSize(bytes _data) | ||||||
|  | { | ||||||
|  | 	size_t size = _data.size(); | ||||||
|  | 	return lebEncode(size) + std::move(_data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes makeSection(Section _section, bytes _data) | ||||||
|  | { | ||||||
|  | 	return toBytes(_section) + prefixSize(std::move(_data)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::run(Module const& _module) | ||||||
|  | { | ||||||
|  | 	BinaryTransform bt; | ||||||
|  | 
 | ||||||
|  | 	for (size_t i = 0; i < _module.globals.size(); ++i) | ||||||
|  | 		bt.m_globals[_module.globals[i].variableName] = i; | ||||||
|  | 
 | ||||||
|  | 	size_t funID = 0; | ||||||
|  | 	for (FunctionImport const& fun: _module.imports) | ||||||
|  | 		bt.m_functions[fun.internalName] = funID++; | ||||||
|  | 	for (FunctionDefinition const& fun: _module.functions) | ||||||
|  | 		bt.m_functions[fun.name] = funID++; | ||||||
|  | 
 | ||||||
|  | 	bytes ret{0, 'a', 's', 'm'}; | ||||||
|  | 	// version
 | ||||||
|  | 	ret += bytes{1, 0, 0, 0}; | ||||||
|  | 	ret += bt.typeSection(_module.imports, _module.functions); | ||||||
|  | 	ret += bt.importSection(_module.imports); | ||||||
|  | 	ret += bt.functionSection(_module.functions); | ||||||
|  | 	ret += bt.memorySection(); | ||||||
|  | 	ret += bt.globalSection(); | ||||||
|  | 	ret += bt.exportSection(); | ||||||
|  | 	for (auto const& sub: _module.subModules) | ||||||
|  | 	{ | ||||||
|  | 		// TODO should we prefix and / or shorten the name?
 | ||||||
|  | 		bytes data = BinaryTransform::run(sub.second); | ||||||
|  | 		size_t length = data.size(); | ||||||
|  | 		ret += bt.customSection(sub.first, std::move(data)); | ||||||
|  | 		bt.m_subModulePosAndSize[sub.first] = {ret.size() - length, length}; | ||||||
|  | 	} | ||||||
|  | 	ret += bt.codeSection(_module.functions); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(Literal const& _literal) | ||||||
|  | { | ||||||
|  | 	return toBytes(Opcode::I64Const) + lebEncodeSigned(_literal.value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(StringLiteral const&) | ||||||
|  | { | ||||||
|  | 	// TODO is this used?
 | ||||||
|  | 	yulAssert(false, "String literals not yet implemented"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(LocalVariable const& _variable) | ||||||
|  | { | ||||||
|  | 	return toBytes(Opcode::LocalGet) + lebEncode(m_locals.at(_variable.name)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(GlobalVariable const& _variable) | ||||||
|  | { | ||||||
|  | 	return toBytes(Opcode::GlobalGet) + lebEncode(m_globals.at(_variable.name)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(BuiltinCall const& _call) | ||||||
|  | { | ||||||
|  | 	// We need to avoid visiting the arguments of `dataoffset` and `datasize` because
 | ||||||
|  | 	// they are references to object names that should not end up in the code.
 | ||||||
|  | 	if (_call.functionName == "dataoffset") | ||||||
|  | 	{ | ||||||
|  | 		string name = boost::get<StringLiteral>(_call.arguments.at(0)).value; | ||||||
|  | 		return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first); | ||||||
|  | 	} | ||||||
|  | 	else if (_call.functionName == "datasize") | ||||||
|  | 	{ | ||||||
|  | 		string name = boost::get<StringLiteral>(_call.arguments.at(0)).value; | ||||||
|  | 		return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bytes args = visit(_call.arguments); | ||||||
|  | 
 | ||||||
|  | 	if (_call.functionName == "unreachable") | ||||||
|  | 			return toBytes(Opcode::Unreachable); | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found"); | ||||||
|  | 		bytes ret = std::move(args) + toBytes(builtins.at(_call.functionName)); | ||||||
|  | 		if ( | ||||||
|  | 			_call.functionName.find(".load") != string::npos || | ||||||
|  | 			_call.functionName.find(".store") != string::npos | ||||||
|  | 		) | ||||||
|  | 			// alignment and offset
 | ||||||
|  | 			ret += bytes{{3, 0}}; | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(FunctionCall const& _call) | ||||||
|  | { | ||||||
|  | 	return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functions.at(_call.functionName)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(LocalAssignment const& _assignment) | ||||||
|  | { | ||||||
|  | 	return | ||||||
|  | 		boost::apply_visitor(*this, *_assignment.value) + | ||||||
|  | 		toBytes(Opcode::LocalSet) + | ||||||
|  | 		lebEncode(m_locals.at(_assignment.variableName)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(GlobalAssignment const& _assignment) | ||||||
|  | { | ||||||
|  | 	return | ||||||
|  | 		boost::apply_visitor(*this, *_assignment.value) + | ||||||
|  | 		toBytes(Opcode::GlobalSet) + | ||||||
|  | 		lebEncode(m_globals.at(_assignment.variableName)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(If const& _if) | ||||||
|  | { | ||||||
|  | 	bytes result = | ||||||
|  | 		boost::apply_visitor(*this, *_if.condition) + | ||||||
|  | 		toBytes(Opcode::If) + | ||||||
|  | 		toBytes(ValueType::Void); | ||||||
|  | 
 | ||||||
|  | 	m_labels.push({}); | ||||||
|  | 
 | ||||||
|  | 	result += visit(_if.statements); | ||||||
|  | 	if (_if.elseStatements) | ||||||
|  | 		result += toBytes(Opcode::Else) + visit(*_if.elseStatements); | ||||||
|  | 
 | ||||||
|  | 	m_labels.pop(); | ||||||
|  | 
 | ||||||
|  | 	result += toBytes(Opcode::End); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(Loop const& _loop) | ||||||
|  | { | ||||||
|  | 	bytes result = toBytes(Opcode::Loop) + toBytes(ValueType::Void); | ||||||
|  | 
 | ||||||
|  | 	m_labels.push(_loop.labelName); | ||||||
|  | 	result += visit(_loop.statements); | ||||||
|  | 	m_labels.pop(); | ||||||
|  | 
 | ||||||
|  | 	result += toBytes(Opcode::End); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(Break const&) | ||||||
|  | { | ||||||
|  | 	yulAssert(false, "br not yet implemented."); | ||||||
|  | 	// TODO the index is just the nesting depth.
 | ||||||
|  | 	return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(BreakIf const&) | ||||||
|  | { | ||||||
|  | 	yulAssert(false, "br_if not yet implemented."); | ||||||
|  | 	// TODO the index is just the nesting depth.
 | ||||||
|  | 	return {}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(Block const& _block) | ||||||
|  | { | ||||||
|  | 	return | ||||||
|  | 		toBytes(Opcode::Block) + | ||||||
|  | 		toBytes(ValueType::Void) + | ||||||
|  | 		visit(_block.statements) + | ||||||
|  | 		toBytes(Opcode::End); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::operator()(FunctionDefinition const& _function) | ||||||
|  | { | ||||||
|  | 	bytes ret; | ||||||
|  | 
 | ||||||
|  | 	// This is a kind of run-length-encoding of local types. Has to be adapted once
 | ||||||
|  | 	// we have locals of different types.
 | ||||||
|  | 	ret += lebEncode(1); // number of locals groups
 | ||||||
|  | 	ret += lebEncode(_function.locals.size()); | ||||||
|  | 	ret += toBytes(ValueType::I64); | ||||||
|  | 
 | ||||||
|  | 	m_locals.clear(); | ||||||
|  | 	size_t varIdx = 0; | ||||||
|  | 	for (size_t i = 0; i < _function.parameterNames.size(); ++i) | ||||||
|  | 		m_locals[_function.parameterNames[i]] = varIdx++; | ||||||
|  | 	for (size_t i = 0; i < _function.locals.size(); ++i) | ||||||
|  | 		m_locals[_function.locals[i].variableName] = varIdx++; | ||||||
|  | 
 | ||||||
|  | 	ret += visit(_function.body); | ||||||
|  | 	ret += toBytes(Opcode::End); | ||||||
|  | 
 | ||||||
|  | 	return prefixSize(std::move(ret)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import) | ||||||
|  | { | ||||||
|  | 	return { | ||||||
|  | 		encodeTypes(_import.paramTypes), | ||||||
|  | 		encodeTypes(_import.returnType ? vector<string>(1, *_import.returnType) : vector<string>()) | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BinaryTransform::Type BinaryTransform::typeOf(FunctionDefinition const& _funDef) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 	return { | ||||||
|  | 		encodeTypes(vector<string>(_funDef.parameterNames.size(), "i64")), | ||||||
|  | 		encodeTypes(vector<string>(_funDef.returns ? 1 : 0, "i64")) | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t BinaryTransform::encodeType(string const& _typeName) | ||||||
|  | { | ||||||
|  | 	if (_typeName == "i32") | ||||||
|  | 		return uint8_t(ValueType::I32); | ||||||
|  | 	else if (_typeName == "i64") | ||||||
|  | 		return uint8_t(ValueType::I64); | ||||||
|  | 	else | ||||||
|  | 		yulAssert(false, ""); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vector<uint8_t> BinaryTransform::encodeTypes(vector<string> const& _typeNames) | ||||||
|  | { | ||||||
|  | 	vector<uint8_t> result; | ||||||
|  | 	for (auto const& t: _typeNames) | ||||||
|  | 		result.emplace_back(encodeType(t)); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::typeSection( | ||||||
|  | 	vector<FunctionImport> const& _imports, | ||||||
|  | 	vector<FunctionDefinition> const& _functions | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	map<Type, vector<string>> types; | ||||||
|  | 	for (auto const& import: _imports) | ||||||
|  | 		types[typeOf(import)].emplace_back(import.internalName); | ||||||
|  | 	for (auto const& fun: _functions) | ||||||
|  | 		types[typeOf(fun)].emplace_back(fun.name); | ||||||
|  | 
 | ||||||
|  | 	bytes result; | ||||||
|  | 	size_t index = 0; | ||||||
|  | 	for (auto const& [type, funNames]: types) | ||||||
|  | 	{ | ||||||
|  | 		for (string const& name: funNames) | ||||||
|  | 			m_functionTypes[name] = index; | ||||||
|  | 		result += toBytes(ValueType::Function); | ||||||
|  | 		result += lebEncode(type.first.size()) + type.first; | ||||||
|  | 		result += lebEncode(type.second.size()) + type.second; | ||||||
|  | 
 | ||||||
|  | 		index++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return makeSection(Section::TYPE, lebEncode(index) + std::move(result)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::importSection( | ||||||
|  | 	vector<FunctionImport> const& _imports | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	bytes result = lebEncode(_imports.size()); | ||||||
|  | 	for (FunctionImport const& import: _imports) | ||||||
|  | 	{ | ||||||
|  | 		uint8_t importKind = 0; // function
 | ||||||
|  | 		result += | ||||||
|  | 			encodeName(import.module) + | ||||||
|  | 			encodeName(import.externalName) + | ||||||
|  | 			toBytes(importKind) + | ||||||
|  | 			lebEncode(m_functionTypes[import.internalName]); | ||||||
|  | 	} | ||||||
|  | 	return makeSection(Section::IMPORT, std::move(result)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::functionSection(vector<FunctionDefinition> const& _functions) | ||||||
|  | { | ||||||
|  | 	bytes result = lebEncode(_functions.size()); | ||||||
|  | 	for (auto const& fun: _functions) | ||||||
|  | 		result += lebEncode(m_functionTypes.at(fun.name)); | ||||||
|  | 	return makeSection(Section::FUNCTION, std::move(result)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::memorySection() | ||||||
|  | { | ||||||
|  | 	bytes result = lebEncode(1); | ||||||
|  | 	result.push_back(0); // flags
 | ||||||
|  | 	result.push_back(1); // initial
 | ||||||
|  | 	return makeSection(Section::MEMORY, std::move(result)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::globalSection() | ||||||
|  | { | ||||||
|  | 	bytes result = lebEncode(m_globals.size()); | ||||||
|  | 	for (size_t i = 0; i < m_globals.size(); ++i) | ||||||
|  | 		result += | ||||||
|  | 			// mutable i64
 | ||||||
|  | 			bytes{uint8_t(ValueType::I64), 1} + | ||||||
|  | 			toBytes(Opcode::I64Const) + | ||||||
|  | 			lebEncodeSigned(0) + | ||||||
|  | 			toBytes(Opcode::End); | ||||||
|  | 
 | ||||||
|  | 	return makeSection(Section::GLOBAL, std::move(result)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::exportSection() | ||||||
|  | { | ||||||
|  | 	bytes result = lebEncode(2); | ||||||
|  | 	result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0); | ||||||
|  | 	result += encodeName("main") + toBytes(Export::Function) + lebEncode(m_functions.at("main")); | ||||||
|  | 	return makeSection(Section::EXPORT, std::move(result)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::customSection(string const& _name, bytes _data) | ||||||
|  | { | ||||||
|  | 	bytes result = encodeName(_name) + std::move(_data); | ||||||
|  | 	return makeSection(Section::CUSTOM, std::move(result)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions) | ||||||
|  | { | ||||||
|  | 	bytes result = lebEncode(_functions.size()); | ||||||
|  | 	for (FunctionDefinition const& fun: _functions) | ||||||
|  | 		result += (*this)(fun); | ||||||
|  | 	return makeSection(Section::CODE, std::move(result)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::visit(vector<Expression> const& _expressions) | ||||||
|  | { | ||||||
|  | 	bytes result; | ||||||
|  | 	for (auto const& expr: _expressions) | ||||||
|  | 		result += boost::apply_visitor(*this, expr); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::visitReversed(vector<Expression> const& _expressions) | ||||||
|  | { | ||||||
|  | 	bytes result; | ||||||
|  | 	for (auto const& expr: _expressions | boost::adaptors::reversed) | ||||||
|  | 		result += boost::apply_visitor(*this, expr); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bytes BinaryTransform::encodeName(std::string const& _name) | ||||||
|  | { | ||||||
|  | 	// UTF-8 is allowed here by the Wasm spec, but since all names here should stem from
 | ||||||
|  | 	// Solidity or Yul identifiers or similar, non-ascii characters ending up here
 | ||||||
|  | 	// is a very bad sign.
 | ||||||
|  | 	for (char c: _name) | ||||||
|  | 		yulAssert(uint8_t(c) <= 0x7f, "Non-ascii character found."); | ||||||
|  | 	return lebEncode(_name.size()) + asBytes(_name); | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								libyul/backends/wasm/BinaryTransform.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								libyul/backends/wasm/BinaryTransform.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | /**
 | ||||||
|  |  * EWasm to binary encoder. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <libyul/backends/wasm/EWasmAST.h> | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/Common.h> | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | #include <stack> | ||||||
|  | 
 | ||||||
|  | namespace yul | ||||||
|  | { | ||||||
|  | namespace wasm | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Web assembly to binary transform. | ||||||
|  |  */ | ||||||
|  | class BinaryTransform: public boost::static_visitor<dev::bytes> | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	static dev::bytes run(Module const& _module); | ||||||
|  | 
 | ||||||
|  | 	dev::bytes operator()(wasm::Literal const& _literal); | ||||||
|  | 	dev::bytes operator()(wasm::StringLiteral const& _literal); | ||||||
|  | 	dev::bytes operator()(wasm::LocalVariable const& _identifier); | ||||||
|  | 	dev::bytes operator()(wasm::GlobalVariable const& _identifier); | ||||||
|  | 	dev::bytes operator()(wasm::BuiltinCall const& _builinCall); | ||||||
|  | 	dev::bytes operator()(wasm::FunctionCall const& _functionCall); | ||||||
|  | 	dev::bytes operator()(wasm::LocalAssignment const& _assignment); | ||||||
|  | 	dev::bytes operator()(wasm::GlobalAssignment const& _assignment); | ||||||
|  | 	dev::bytes operator()(wasm::If const& _if); | ||||||
|  | 	dev::bytes operator()(wasm::Loop const& _loop); | ||||||
|  | 	dev::bytes operator()(wasm::Break const& _break); | ||||||
|  | 	dev::bytes operator()(wasm::BreakIf const& _break); | ||||||
|  | 	dev::bytes operator()(wasm::Block const& _block); | ||||||
|  | 	dev::bytes operator()(wasm::FunctionDefinition const& _function); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>; | ||||||
|  | 	static Type typeOf(wasm::FunctionImport const& _import); | ||||||
|  | 	static Type typeOf(wasm::FunctionDefinition const& _funDef); | ||||||
|  | 
 | ||||||
|  | 	static uint8_t encodeType(std::string const& _typeName); | ||||||
|  | 	static std::vector<uint8_t> encodeTypes(std::vector<std::string> const& _typeNames); | ||||||
|  | 	dev::bytes typeSection( | ||||||
|  | 		std::vector<wasm::FunctionImport> const& _imports, | ||||||
|  | 		std::vector<wasm::FunctionDefinition> const& _functions | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	dev::bytes importSection(std::vector<wasm::FunctionImport> const& _imports); | ||||||
|  | 	dev::bytes functionSection(std::vector<wasm::FunctionDefinition> const& _functions); | ||||||
|  | 	dev::bytes memorySection(); | ||||||
|  | 	dev::bytes globalSection(); | ||||||
|  | 	dev::bytes exportSection(); | ||||||
|  | 	dev::bytes customSection(std::string const& _name, dev::bytes _data); | ||||||
|  | 	dev::bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions); | ||||||
|  | 
 | ||||||
|  | 	dev::bytes visit(std::vector<wasm::Expression> const& _expressions); | ||||||
|  | 	dev::bytes visitReversed(std::vector<wasm::Expression> const& _expressions); | ||||||
|  | 
 | ||||||
|  | 	static dev::bytes encodeName(std::string const& _name); | ||||||
|  | 
 | ||||||
|  | 	std::map<std::string, size_t> m_locals; | ||||||
|  | 	std::map<std::string, size_t> m_globals; | ||||||
|  | 	std::map<std::string, size_t> m_functions; | ||||||
|  | 	std::map<std::string, size_t> m_functionTypes; | ||||||
|  | 	std::stack<std::string> m_labels; | ||||||
|  | 	std::map<std::string, std::pair<size_t, size_t>> m_subModulePosAndSize; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @ -23,6 +23,8 @@ | |||||||
| #include <boost/variant.hpp> | #include <boost/variant.hpp> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <map> | ||||||
|  | #include <memory> | ||||||
| 
 | 
 | ||||||
| namespace yul | namespace yul | ||||||
| { | { | ||||||
| @ -86,6 +88,16 @@ struct FunctionDefinition | |||||||
| 	std::vector<Expression> body; | 	std::vector<Expression> body; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Abstract representation of a wasm module. | ||||||
|  |  */ | ||||||
|  | struct Module | ||||||
|  | { | ||||||
|  | 	std::vector<GlobalVariableDeclaration> globals; | ||||||
|  | 	std::vector<FunctionImport> imports; | ||||||
|  | 	std::vector<FunctionDefinition> functions; | ||||||
|  | 	std::map<std::string, Module> subModules; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,7 +20,6 @@ | |||||||
| 
 | 
 | ||||||
| #include <libyul/backends/wasm/EWasmCodeTransform.h> | #include <libyul/backends/wasm/EWasmCodeTransform.h> | ||||||
| 
 | 
 | ||||||
| #include <libyul/backends/wasm/EWasmToText.h> |  | ||||||
| #include <libyul/optimiser/NameCollector.h> | #include <libyul/optimiser/NameCollector.h> | ||||||
| 
 | 
 | ||||||
| #include <libyul/AsmData.h> | #include <libyul/AsmData.h> | ||||||
| @ -37,10 +36,11 @@ using namespace std; | |||||||
| using namespace dev; | using namespace dev; | ||||||
| using namespace yul; | using namespace yul; | ||||||
| 
 | 
 | ||||||
| string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) | wasm::Module EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) | ||||||
| { | { | ||||||
|  | 	wasm::Module module; | ||||||
|  | 
 | ||||||
| 	EWasmCodeTransform transform(_dialect, _ast); | 	EWasmCodeTransform transform(_dialect, _ast); | ||||||
| 	vector<wasm::FunctionDefinition> functions; |  | ||||||
| 
 | 
 | ||||||
| 	for (auto const& statement: _ast.statements) | 	for (auto const& statement: _ast.statements) | ||||||
| 	{ | 	{ | ||||||
| @ -49,17 +49,14 @@ string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast) | |||||||
| 			"Expected only function definitions at the highest level." | 			"Expected only function definitions at the highest level." | ||||||
| 		); | 		); | ||||||
| 		if (statement.type() == typeid(yul::FunctionDefinition)) | 		if (statement.type() == typeid(yul::FunctionDefinition)) | ||||||
| 			functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement))); | 			module.functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	std::vector<wasm::FunctionImport> imports; |  | ||||||
| 	for (auto& imp: transform.m_functionsToImport) | 	for (auto& imp: transform.m_functionsToImport) | ||||||
| 		imports.emplace_back(std::move(imp.second)); | 		module.imports.emplace_back(std::move(imp.second)); | ||||||
| 	return EWasmToText().run( | 	module.globals = transform.m_globalVariables; | ||||||
| 		transform.m_globalVariables, | 
 | ||||||
| 		imports, | 	return module; | ||||||
| 		functions |  | ||||||
| 	); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| wasm::Expression EWasmCodeTransform::generateMultiAssignment( | wasm::Expression EWasmCodeTransform::generateMultiAssignment( | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ struct AsmAnalysisInfo; | |||||||
| class EWasmCodeTransform: public boost::static_visitor<wasm::Expression> | class EWasmCodeTransform: public boost::static_visitor<wasm::Expression> | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	static std::string run(Dialect const& _dialect, yul::Block const& _ast); | 	static wasm::Module run(Dialect const& _dialect, yul::Block const& _ast); | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| 	wasm::Expression operator()(yul::Instruction const& _instruction); | 	wasm::Expression operator()(yul::Instruction const& _instruction); | ||||||
|  | |||||||
| @ -21,32 +21,36 @@ | |||||||
| #include <libyul/backends/wasm/EWasmObjectCompiler.h> | #include <libyul/backends/wasm/EWasmObjectCompiler.h> | ||||||
| 
 | 
 | ||||||
| #include <libyul/backends/wasm/EWasmCodeTransform.h> | #include <libyul/backends/wasm/EWasmCodeTransform.h> | ||||||
|  | #include <libyul/backends/wasm/BinaryTransform.h> | ||||||
|  | #include <libyul/backends/wasm/EWasmToText.h> | ||||||
| 
 | 
 | ||||||
| #include <libyul/Object.h> | #include <libyul/Object.h> | ||||||
| #include <libyul/Exceptions.h> | #include <libyul/Exceptions.h> | ||||||
| 
 | 
 | ||||||
|  | #include <libdevcore/CommonData.h> | ||||||
|  | 
 | ||||||
| using namespace yul; | using namespace yul; | ||||||
| using namespace std; | using namespace std; | ||||||
| 
 | 
 | ||||||
| string EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect) | pair<string, dev::bytes> EWasmObjectCompiler::compile(Object& _object, Dialect const& _dialect) | ||||||
| { | { | ||||||
| 	EWasmObjectCompiler compiler(_dialect); | 	EWasmObjectCompiler compiler(_dialect); | ||||||
| 	return compiler.run(_object); | 	wasm::Module module = compiler.run(_object); | ||||||
|  | 	return {EWasmToText().run(module), wasm::BinaryTransform::run(module)}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| string EWasmObjectCompiler::run(Object& _object) | wasm::Module EWasmObjectCompiler::run(Object& _object) | ||||||
| { | { | ||||||
| 	string ret; | 	yulAssert(_object.analysisInfo, "No analysis info."); | ||||||
|  | 	yulAssert(_object.code, "No code."); | ||||||
|  | 
 | ||||||
|  | 	wasm::Module module = EWasmCodeTransform::run(m_dialect, *_object.code); | ||||||
| 
 | 
 | ||||||
| 	for (auto& subNode: _object.subObjects) | 	for (auto& subNode: _object.subObjects) | ||||||
| 		if (Object* subObject = dynamic_cast<Object*>(subNode.get())) | 		if (Object* subObject = dynamic_cast<Object*>(subNode.get())) | ||||||
| 			ret += compile(*subObject, m_dialect); | 			module.subModules[subObject->name.str()] = run(*subObject); | ||||||
| 		else | 		else | ||||||
| 			yulAssert(false, "Data is not yet supported for EWasm."); | 			yulAssert(false, "Data is not yet supported for EWasm."); | ||||||
| 
 | 
 | ||||||
| 	yulAssert(_object.analysisInfo, "No analysis info."); | 	return module; | ||||||
| 	yulAssert(_object.code, "No code."); |  | ||||||
| 	ret += EWasmCodeTransform::run(m_dialect, *_object.code); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -21,22 +21,33 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <tuple> | ||||||
| 
 | 
 | ||||||
|  | namespace dev | ||||||
|  | { | ||||||
|  | using bytes = std::vector<uint8_t>; | ||||||
|  | } | ||||||
| namespace yul | namespace yul | ||||||
| { | { | ||||||
| struct Object; | struct Object; | ||||||
| struct Dialect; | struct Dialect; | ||||||
|  | namespace wasm | ||||||
|  | { | ||||||
|  | struct Module; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| class EWasmObjectCompiler | class EWasmObjectCompiler | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	static std::string compile(Object& _object, Dialect const& _dialect); | 	/// Compiles the given object and returns the WAST and the binary representation.
 | ||||||
|  | 	static std::pair<std::string, dev::bytes> compile(Object& _object, Dialect const& _dialect); | ||||||
| private: | private: | ||||||
| 	EWasmObjectCompiler(Dialect const& _dialect): | 	EWasmObjectCompiler(Dialect const& _dialect): | ||||||
| 		m_dialect(_dialect) | 		m_dialect(_dialect) | ||||||
| 	{} | 	{} | ||||||
| 
 | 
 | ||||||
| 	std::string run(Object& _object); | 	wasm::Module run(Object& _object); | ||||||
| 
 | 
 | ||||||
| 	Dialect const& m_dialect; | 	Dialect const& m_dialect; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -30,14 +30,15 @@ using namespace std; | |||||||
| using namespace yul; | using namespace yul; | ||||||
| using namespace dev; | using namespace dev; | ||||||
| 
 | 
 | ||||||
| string EWasmToText::run( | string EWasmToText::run(wasm::Module const& _module) | ||||||
| 	vector<wasm::GlobalVariableDeclaration> const& _globals, |  | ||||||
| 	vector<wasm::FunctionImport> const& _imports, |  | ||||||
| 	vector<wasm::FunctionDefinition> const& _functions |  | ||||||
| ) |  | ||||||
| { | { | ||||||
| 	string ret = "(module\n"; | 	string ret = "(module\n"; | ||||||
| 	for (wasm::FunctionImport const& imp: _imports) | 	for (auto const& sub: _module.subModules) | ||||||
|  | 		ret += | ||||||
|  | 			"    ;; sub-module \"" + | ||||||
|  | 			sub.first + | ||||||
|  | 			"\" will be encoded as custom section in binary here, but is skipped in text mode.\n"; | ||||||
|  | 	for (wasm::FunctionImport const& imp: _module.imports) | ||||||
| 	{ | 	{ | ||||||
| 		ret += "    (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName; | 		ret += "    (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName; | ||||||
| 		if (!imp.paramTypes.empty()) | 		if (!imp.paramTypes.empty()) | ||||||
| @ -52,10 +53,10 @@ string EWasmToText::run( | |||||||
| 	// export the main function
 | 	// export the main function
 | ||||||
| 	ret += "    (export \"main\" (func $main))\n"; | 	ret += "    (export \"main\" (func $main))\n"; | ||||||
| 
 | 
 | ||||||
| 	for (auto const& g: _globals) | 	for (auto const& g: _module.globals) | ||||||
| 		ret += "    (global $" + g.variableName + " (mut i64) (i64.const 0))\n"; | 		ret += "    (global $" + g.variableName + " (mut i64) (i64.const 0))\n"; | ||||||
| 	ret += "\n"; | 	ret += "\n"; | ||||||
| 	for (auto const& f: _functions) | 	for (auto const& f: _module.functions) | ||||||
| 		ret += transform(f) + "\n"; | 		ret += transform(f) + "\n"; | ||||||
| 	return move(ret) + ")\n"; | 	return move(ret) + ")\n"; | ||||||
| } | } | ||||||
|  | |||||||
| @ -31,11 +31,7 @@ struct AsmAnalysisInfo; | |||||||
| class EWasmToText: public boost::static_visitor<std::string> | class EWasmToText: public boost::static_visitor<std::string> | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	std::string run( | 	std::string run(wasm::Module const& _module); | ||||||
| 		std::vector<wasm::GlobalVariableDeclaration> const& _globals, |  | ||||||
| 		std::vector<wasm::FunctionImport> const& _imports, |  | ||||||
| 		std::vector<wasm::FunctionDefinition> const& _functions |  | ||||||
| 	); |  | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| 	std::string operator()(wasm::Literal const& _literal); | 	std::string operator()(wasm::Literal const& _literal); | ||||||
|  | |||||||
| @ -1,63 +1,5 @@ | |||||||
| {"contracts":{"A":{"C":{"ewasm":{"wast":"(module | {"contracts":{"A":{"C":{"ewasm":{"wast":"(module | ||||||
|     (import \"ethereum\" \"revert\" (func $eth.revert (param i32 i32))) |     ;; sub-module \"C_2_deployed\" will be encoded as custom section in binary here, but is skipped in text mode. | ||||||
|     (memory $memory (export \"memory\") 1) |  | ||||||
|     (export \"main\" (func $main)) |  | ||||||
| 
 |  | ||||||
| (func $main |  | ||||||
|     (local $_1 i64) |  | ||||||
|     (local $_2 i64) |  | ||||||
|     (local $_3 i64) |  | ||||||
|     (local $hi i64) |  | ||||||
|     (local $y i64) |  | ||||||
|     (local $hi_1 i64) |  | ||||||
|     (local.set $_1 (i64.const 0)) |  | ||||||
|     (local.set $_2 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64)) (i64.const 64))) |  | ||||||
|     (local.set $_3 (i64.const 65280)) |  | ||||||
|     (local.set $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (local.get $_1) (i64.const 8)) (local.get $_3)) (i64.and (i64.shr_u (local.get $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) |  | ||||||
|     (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) |  | ||||||
|     (i64.store (i32.wrap_i64 (local.get $_2)) (local.get $y)) |  | ||||||
|     (i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 8))) (local.get $y)) |  | ||||||
|     (i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 16))) (local.get $y)) |  | ||||||
|     (local.set $hi_1 (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (i64.const 128) (i64.const 8)) (local.get $_3)) (i64.and (i64.shr_u (i64.const 128) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (i64.const 128) (i64.const 16)))) (i64.const 32))) |  | ||||||
|     (i64.store (i32.wrap_i64 (i64.add (local.get $_2) (i64.const 24))) (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) |  | ||||||
|     (call $eth.revert (i32.wrap_i64 (i64.add (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (i64.const 64))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)))) |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| (func $u256_to_i32 |  | ||||||
|     (param $x1 i64) |  | ||||||
|     (param $x2 i64) |  | ||||||
|     (param $x3 i64) |  | ||||||
|     (param $x4 i64) |  | ||||||
|     (result i64) |  | ||||||
|     (local $v i64) |  | ||||||
|     (if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then |  | ||||||
|         (unreachable))) |  | ||||||
|     (if (i64.ne (i64.extend_i32_u (i64.ne (local.get $v) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then |  | ||||||
|         (unreachable))) |  | ||||||
|     (local.set $v (local.get $x4)) |  | ||||||
|     (local.get $v) |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| (func $endian_swap_16 |  | ||||||
|     (param $x i64) |  | ||||||
|     (result i64) |  | ||||||
|     (local $y i64) |  | ||||||
|     (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) |  | ||||||
|     (local.get $y) |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| (func $endian_swap_32 |  | ||||||
|     (param $x i64) |  | ||||||
|     (result i64) |  | ||||||
|     (local $y i64) |  | ||||||
|     (local $hi i64) |  | ||||||
|     (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) |  | ||||||
|     (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) |  | ||||||
|     (local.get $y) |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| ) |  | ||||||
| (module |  | ||||||
|     (import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32))) |     (import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32))) | ||||||
|     (import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32))) |     (import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32))) | ||||||
|     (memory $memory (export \"memory\") 1) |     (memory $memory (export \"memory\") 1) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user