diff --git a/libyul/backends/wasm/BinaryTransform.cpp b/libyul/backends/wasm/BinaryTransform.cpp index 875d93dbf..b305b9c55 100644 --- a/libyul/backends/wasm/BinaryTransform.cpp +++ b/libyul/backends/wasm/BinaryTransform.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -83,6 +84,16 @@ bytes toBytes(ValueType _vt) return toBytes(uint8_t(_vt)); } +ValueType toValueType(wasm::Type _type) +{ + if (_type == wasm::Type::i32) + return ValueType::I32; + else if (_type == wasm::Type::i64) + return ValueType::I64; + else + yulAssert(false, "Invalid wasm variable type"); +} + enum class Export: uint8_t { Function = 0x0, @@ -132,6 +143,16 @@ bytes toBytes(Opcode _o) return toBytes(uint8_t(_o)); } +Opcode constOpcodeFor(ValueType _type) +{ + if (_type == ValueType::I32) + return Opcode::I32Const; + else if (_type == ValueType::I64) + return Opcode::I64Const; + else + yulAssert(false, "Values of this type cannot be used with const opcode"); +} + static map const builtins = { {"i32.load", 0x28}, {"i64.load", 0x29}, @@ -250,6 +271,34 @@ bytes makeSection(Section _section, bytes _data) return toBytes(_section) + prefixSize(move(_data)); } +/// This is a kind of run-length-encoding of local types. +vector> groupLocalVariables(vector _localVariables) +{ + vector> localEntries; + + size_t entrySize = 0; + ValueType entryType = ValueType::I32; // Any type would work here + for (VariableDeclaration const& localVariable: _localVariables) + { + ValueType variableType = toValueType(localVariable.type); + + if (variableType != entryType) + { + if (entrySize > 0) + localEntries.emplace_back(entrySize, entryType); + + entryType = variableType; + entrySize = 0; + } + + ++entrySize; + } + if (entrySize > 0) + localEntries.emplace_back(entrySize, entryType); + + return localEntries; +} + } bytes BinaryTransform::run(Module const& _module) @@ -298,8 +347,10 @@ bytes BinaryTransform::run(Module const& _module) bytes BinaryTransform::operator()(Literal const& _literal) { - yulAssert(holds_alternative(_literal.value), ""); - return toBytes(Opcode::I64Const) + lebEncodeSigned(get(_literal.value)); + return std::visit(GenericVisitor{ + [&](uint32_t _value) -> bytes { return toBytes(Opcode::I32Const) + lebEncodeSigned(static_cast(_value)); }, + [&](uint64_t _value) -> bytes { return toBytes(Opcode::I64Const) + lebEncodeSigned(static_cast(_value)); }, + }, _literal.value); } bytes BinaryTransform::operator()(StringLiteral const&) @@ -445,15 +496,12 @@ 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. - if (_function.locals.size() == 0) - ret += lebEncode(0); // number of locals groups - else + vector> localEntries = groupLocalVariables(_function.locals); + ret += lebEncode(localEntries.size()); + for (pair const& entry: localEntries) { - ret += lebEncode(1); // number of locals groups - ret += lebEncode(_function.locals.size()); - ret += toBytes(ValueType::I64); + ret += lebEncode(entry.first); + ret += toBytes(entry.second); } m_locals.clear(); @@ -483,22 +531,15 @@ BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import) BinaryTransform::Type BinaryTransform::typeOf(FunctionDefinition const& _funDef) { - return { - encodeTypes(vector(_funDef.parameters.size(), wasm::Type::i64)), - encodeTypes(vector(_funDef.returnType.has_value() ? 1 : 0, wasm::Type::i64)) + encodeTypes(_funDef.parameters), + encodeTypes(_funDef.returnType ? vector(1, *_funDef.returnType) : vector()) }; } uint8_t BinaryTransform::encodeType(wasm::Type _type) { - if (_type == wasm::Type::i32) - return uint8_t(ValueType::I32); - else if (_type == wasm::Type::i64) - return uint8_t(ValueType::I64); - else - yulAssert(false, ""); - return 0; + return uint8_t(toValueType(_type)); } vector BinaryTransform::encodeTypes(vector const& _types) @@ -509,6 +550,14 @@ vector BinaryTransform::encodeTypes(vector const& _types) return result; } +vector BinaryTransform::encodeTypes(wasm::TypedNameList const& _typedNameList) +{ + vector result; + for (TypedName const& typedName: _typedNameList) + result.emplace_back(encodeType(typedName.type)); + return result; +} + map> BinaryTransform::typeToFunctionMap( vector const& _imports, vector const& _functions @@ -614,13 +663,16 @@ bytes BinaryTransform::memorySection() bytes BinaryTransform::globalSection(vector const& _globals) { bytes result = lebEncode(_globals.size()); - for (size_t i = 0; i < _globals.size(); ++i) + for (wasm::GlobalVariableDeclaration const& global: _globals) + { + ValueType globalType = toValueType(global.type); result += - toBytes(ValueType::I64) + + toBytes(globalType) + lebEncode(static_cast(Mutability::Var)) + - toBytes(Opcode::I64Const) + + toBytes(constOpcodeFor(globalType)) + lebEncodeSigned(0) + toBytes(Opcode::End); + } return makeSection(Section::GLOBAL, move(result)); } diff --git a/libyul/backends/wasm/BinaryTransform.h b/libyul/backends/wasm/BinaryTransform.h index 1f4ecf663..c505fe2dc 100644 --- a/libyul/backends/wasm/BinaryTransform.h +++ b/libyul/backends/wasm/BinaryTransform.h @@ -73,6 +73,7 @@ private: static uint8_t encodeType(wasm::Type _type); static std::vector encodeTypes(std::vector const& _types); + static std::vector encodeTypes(wasm::TypedNameList const& _typedNameList); static std::map> typeToFunctionMap( std::vector const& _imports, diff --git a/libyul/backends/wasm/TextTransform.cpp b/libyul/backends/wasm/TextTransform.cpp index 84f2ce972..b7f1e81ea 100644 --- a/libyul/backends/wasm/TextTransform.cpp +++ b/libyul/backends/wasm/TextTransform.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -58,7 +59,7 @@ string TextTransform::run(wasm::Module const& _module) ret += " (export \"main\" (func $main))\n"; for (auto const& g: _module.globals) - ret += " (global $" + g.variableName + " (mut i64) (i64.const 0))\n"; + ret += " (global $" + g.variableName + " (mut " + encodeType(g.type) + ") (" + encodeType(g.type) + ".const 0))\n"; ret += "\n"; for (auto const& f: _module.functions) ret += transform(f) + "\n"; @@ -67,8 +68,10 @@ string TextTransform::run(wasm::Module const& _module) string TextTransform::operator()(wasm::Literal const& _literal) { - yulAssert(holds_alternative(_literal.value), ""); - return "(i64.const " + to_string(get(_literal.value)) + ")"; + return std::visit(GenericVisitor{ + [&](uint32_t _value) -> string { return "(i32.const " + to_string(_value) + ")"; }, + [&](uint64_t _value) -> string { return "(i64.const " + to_string(_value) + ")"; }, + }, _literal.value); } string TextTransform::operator()(wasm::StringLiteral const& _literal) @@ -166,11 +169,11 @@ string TextTransform::transform(wasm::FunctionDefinition const& _function) { string ret = "(func $" + _function.name + "\n"; for (auto const& param: _function.parameters) - ret += " (param $" + param.name + " i64)\n"; + ret += " (param $" + param.name + " " + encodeType(param.type) + ")\n"; if (_function.returnType.has_value()) - ret += " (result i64)\n"; + ret += " (result " + encodeType(_function.returnType.value()) + ")\n"; for (auto const& local: _function.locals) - ret += " (local $" + local.variableName + " i64)\n"; + ret += " (local $" + local.variableName + " " + encodeType(local.type) + ")\n"; ret += indented(joinTransformed(_function.body, '\n')); if (ret.back() != '\n') ret += '\n'; diff --git a/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/args b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/args new file mode 100644 index 000000000..04cd5f05b --- /dev/null +++ b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/args @@ -0,0 +1 @@ +--yul --yul-dialect ewasm --machine ewasm diff --git a/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/err b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/input.yul b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/input.yul new file mode 100644 index 000000000..2b1a6d960 --- /dev/null +++ b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/input.yul @@ -0,0 +1,17 @@ +object "object" { + code { + function main() + { + let m:i64, n:i32, p:i32, q:i64 := multireturn(1:i32, 2:i64, 3:i64, 4:i32) + } + + function multireturn(a:i32, b:i64, c:i64, d:i32) -> x:i64, y:i32, z:i32, w:i64 + { + x := b + w := c + + y := a + z := d + } + } +} diff --git a/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/output b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/output new file mode 100644 index 000000000..a99808601 --- /dev/null +++ b/test/cmdlineTests/wasm_to_wasm_function_returning_multiple_values/output @@ -0,0 +1,73 @@ + +======= wasm_to_wasm_function_returning_multiple_values/input.yul (Ewasm) ======= + +Pretty printed source: +object "object" { + code { + function main() + { + let m, n:i32, p:i32, q := multireturn(1:i32, 2, 3, 4:i32) + } + function multireturn(a:i32, b, c, d:i32) -> x, y:i32, z:i32, w + { + x := b + w := c + y := a + z := d + } + } +} + + +Binary representation: +0061736d01000000010c0260000060047e7e7e7e017e020100030302000105030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00000a4a022201047e024002404201420242034204100121002300210123012102230221030b0b0b2501047e0240200121042002210720002105200321060b20052400200624012007240220040b + +Text representation: +(module + (memory $memory (export "memory") 1) + (export "main" (func $main)) + (global $global_ (mut i64) (i64.const 0)) + (global $global__1 (mut i64) (i64.const 0)) + (global $global__2 (mut i64) (i64.const 0)) + +(func $main + (local $m i64) + (local $n i64) + (local $p i64) + (local $q i64) + (block $label_ + (block + (local.set $m (call $multireturn (i64.const 1) (i64.const 2) (i64.const 3) (i64.const 4))) + (local.set $n (global.get $global_)) + (local.set $p (global.get $global__1)) + (local.set $q (global.get $global__2)) + + ) + + ) +) + +(func $multireturn + (param $a i64) + (param $b i64) + (param $c i64) + (param $d i64) + (result i64) + (local $x i64) + (local $y i64) + (local $z i64) + (local $w i64) + (block $label__3 + (local.set $x (local.get $b)) + (local.set $w (local.get $c)) + (local.set $y (local.get $a)) + (local.set $z (local.get $d)) + + ) + (global.set $global_ (local.get $y)) + (global.set $global__1 (local.get $z)) + (global.set $global__2 (local.get $w)) + (local.get $x) +) + +)