Add support for generating code with i32 variables in text and binary wasm

This commit is contained in:
Kamil Śliwak 2020-05-28 21:40:44 +02:00
parent 6a82d32ef6
commit e67f5072df
7 changed files with 177 additions and 29 deletions

View File

@ -22,6 +22,7 @@
#include <libyul/Exceptions.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Visitor.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/map.hpp>
@ -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<string, uint8_t> 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<pair<size_t, ValueType>> groupLocalVariables(vector<VariableDeclaration> _localVariables)
{
vector<pair<size_t, ValueType>> 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<uint64_t>(_literal.value), "");
return toBytes(Opcode::I64Const) + lebEncodeSigned(get<uint64_t>(_literal.value));
return std::visit(GenericVisitor{
[&](uint32_t _value) -> bytes { return toBytes(Opcode::I32Const) + lebEncodeSigned(static_cast<int32_t>(_value)); },
[&](uint64_t _value) -> bytes { return toBytes(Opcode::I64Const) + lebEncodeSigned(static_cast<int64_t>(_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<pair<size_t, ValueType>> localEntries = groupLocalVariables(_function.locals);
ret += lebEncode(localEntries.size());
for (pair<size_t, ValueType> 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<wasm::Type>(_funDef.parameters.size(), wasm::Type::i64)),
encodeTypes(vector<wasm::Type>(_funDef.returnType.has_value() ? 1 : 0, wasm::Type::i64))
encodeTypes(_funDef.parameters),
encodeTypes(_funDef.returnType ? vector<wasm::Type>(1, *_funDef.returnType) : vector<wasm::Type>())
};
}
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<uint8_t> BinaryTransform::encodeTypes(vector<wasm::Type> const& _types)
@ -509,6 +550,14 @@ vector<uint8_t> BinaryTransform::encodeTypes(vector<wasm::Type> const& _types)
return result;
}
vector<uint8_t> BinaryTransform::encodeTypes(wasm::TypedNameList const& _typedNameList)
{
vector<uint8_t> result;
for (TypedName const& typedName: _typedNameList)
result.emplace_back(encodeType(typedName.type));
return result;
}
map<BinaryTransform::Type, vector<string>> BinaryTransform::typeToFunctionMap(
vector<wasm::FunctionImport> const& _imports,
vector<wasm::FunctionDefinition> const& _functions
@ -614,13 +663,16 @@ bytes BinaryTransform::memorySection()
bytes BinaryTransform::globalSection(vector<wasm::GlobalVariableDeclaration> 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<uint8_t>(Mutability::Var)) +
toBytes(Opcode::I64Const) +
toBytes(constOpcodeFor(globalType)) +
lebEncodeSigned(0) +
toBytes(Opcode::End);
}
return makeSection(Section::GLOBAL, move(result));
}

View File

@ -73,6 +73,7 @@ private:
static uint8_t encodeType(wasm::Type _type);
static std::vector<uint8_t> encodeTypes(std::vector<wasm::Type> const& _types);
static std::vector<uint8_t> encodeTypes(wasm::TypedNameList const& _typedNameList);
static std::map<Type, std::vector<std::string>> typeToFunctionMap(
std::vector<wasm::FunctionImport> const& _imports,

View File

@ -23,6 +23,7 @@
#include <libyul/Exceptions.h>
#include <libsolutil/StringUtils.h>
#include <libsolutil/Visitor.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
@ -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<uint64_t>(_literal.value), "");
return "(i64.const " + to_string(get<uint64_t>(_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';

View File

@ -0,0 +1 @@
--yul --yul-dialect ewasm --machine ewasm

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -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
}
}
}

View File

@ -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)
)
)