summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2020-04-02 10:36:19 -0700
committerGitHub <noreply@github.com>2020-04-02 10:36:19 -0700
commit80c5164e1f50ed26e6fab373a563fd7a84135429 (patch)
treeac944407ac6bf1b26e524d31a3db8c410e4599e7
parent702b99508487bc5f56c409d890644b3b9212ae81 (diff)
downloadbinaryen-80c5164e1f50ed26e6fab373a563fd7a84135429.tar.gz
binaryen-80c5164e1f50ed26e6fab373a563fd7a84135429.tar.bz2
binaryen-80c5164e1f50ed26e6fab373a563fd7a84135429.zip
Tuple globals (#2718)
Since it wasn't easy to support tuples in Asyncify's call support using temporary functions, we decided to allow tuple-typed globals after all. This PR adds support for parsing, printing, lowering, and interpreting tuple globals and also adds validation ensuring that imported and exported globals do not have tuple types.
-rw-r--r--src/ir/global-utils.h9
-rw-r--r--src/ir/module-utils.h23
-rw-r--r--src/passes/Print.cpp12
-rw-r--r--src/passes/SimplifyGlobals.cpp8
-rw-r--r--src/shell-interface.h12
-rw-r--r--src/tools/wasm-ctor-eval.cpp20
-rw-r--r--src/wasm-interpreter.h10
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm/wasm-binary.cpp21
-rw-r--r--src/wasm/wasm-s-parser.cpp28
-rw-r--r--src/wasm/wasm-stack.cpp16
-rw-r--r--src/wasm/wasm-validator.cpp21
-rw-r--r--test/multivalue.wast16
-rw-r--r--test/multivalue.wast.from-wast37
-rw-r--r--test/multivalue.wast.fromBinary42
-rw-r--r--test/multivalue.wast.fromBinary.noDebugInfo42
-rw-r--r--test/spec/multivalue.wast15
17 files changed, 240 insertions, 93 deletions
diff --git a/src/ir/global-utils.h b/src/ir/global-utils.h
index e096aec8c..b575d6952 100644
--- a/src/ir/global-utils.h
+++ b/src/ir/global-utils.h
@@ -54,6 +54,15 @@ getGlobalInitializedToImport(Module& wasm, Name module, Name base) {
}
inline bool canInitializeGlobal(const Expression* curr) {
+ if (auto* tuple = curr->dynCast<TupleMake>()) {
+ for (auto* op : tuple->operands) {
+ if (!op->is<Const>() && !op->is<RefNull>() &&
+ !op->is<RefFunc>() & !op->is<GlobalGet>()) {
+ return false;
+ }
+ }
+ return true;
+ }
return curr->is<Const>() || curr->is<RefNull>() || curr->is<RefFunc>() ||
curr->is<GlobalGet>();
}
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h
index be924f742..485bc4833 100644
--- a/src/ir/module-utils.h
+++ b/src/ir/module-utils.h
@@ -35,8 +35,8 @@ namespace ModuleUtils {
// just use them directly).
struct BinaryIndexes {
std::unordered_map<Name, Index> functionIndexes;
- std::unordered_map<Name, Index> globalIndexes;
std::unordered_map<Name, Index> eventIndexes;
+ std::unordered_map<Name, Index> globalIndexes;
BinaryIndexes(Module& wasm) {
auto addIndexes = [&](auto& source, auto& indexes) {
@@ -56,8 +56,27 @@ struct BinaryIndexes {
}
};
addIndexes(wasm.functions, functionIndexes);
- addIndexes(wasm.globals, globalIndexes);
addIndexes(wasm.events, eventIndexes);
+
+ // Globals may have tuple types in the IR, in which case they lower to
+ // multiple globals, one for each tuple element, in the binary. Tuple
+ // globals therefore occupy multiple binary indices, and we have to take
+ // that into account when calculating indices.
+ Index globalCount = 0;
+ auto addGlobal = [&](auto* curr) {
+ globalIndexes[curr->name] = globalCount;
+ globalCount += curr->type.size();
+ };
+ for (auto& curr : wasm.globals) {
+ if (curr->imported()) {
+ addGlobal(curr.get());
+ }
+ }
+ for (auto& curr : wasm.globals) {
+ if (!curr->imported()) {
+ addGlobal(curr.get());
+ }
+ }
}
};
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index d6dbb3300..18f2beb43 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -59,12 +59,12 @@ static std::ostream& printLocal(Index index, Function* func, std::ostream& o) {
// Unlike the default format, tuple types in s-expressions should not have
// commas.
-struct LocalType {
+struct SExprType {
Type type;
- LocalType(Type type) : type(type){};
+ SExprType(Type type) : type(type){};
};
-static std::ostream& operator<<(std::ostream& o, const LocalType& localType) {
+static std::ostream& operator<<(std::ostream& o, const SExprType& localType) {
Type type = localType.type;
if (type.isMulti()) {
const std::vector<Type>& types = type.expand();
@@ -2072,9 +2072,9 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
}
void emitGlobalType(Global* curr) {
if (curr->mutable_) {
- o << "(mut " << curr->type << ')';
+ o << "(mut " << SExprType(curr->type) << ')';
} else {
- o << curr->type;
+ o << SExprType(curr->type);
}
}
void visitImportedGlobal(Global* curr) {
@@ -2155,7 +2155,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << '(';
printMinor(o, "local ");
printLocal(i, currFunction, o)
- << ' ' << LocalType(curr->getLocalType(i)) << ')';
+ << ' ' << SExprType(curr->getLocalType(i)) << ')';
o << maybeNewLine;
}
// Print the body.
diff --git a/src/passes/SimplifyGlobals.cpp b/src/passes/SimplifyGlobals.cpp
index 55c16910a..5fa1686fc 100644
--- a/src/passes/SimplifyGlobals.cpp
+++ b/src/passes/SimplifyGlobals.cpp
@@ -109,7 +109,7 @@ struct ConstantGlobalApplier
if (auto* set = curr->dynCast<GlobalSet>()) {
if (Properties::isConstantExpression(set->value)) {
currConstantGlobals[set->name] =
- getSingleLiteralFromConstExpression(set->value);
+ getLiteralsFromConstExpression(set->value);
} else {
currConstantGlobals.erase(set->name);
}
@@ -160,7 +160,7 @@ private:
bool replaced = false;
// The globals currently constant in the linear trace.
- std::map<Name, Literal> currConstantGlobals;
+ std::map<Name, Literals> currConstantGlobals;
};
} // anonymous namespace
@@ -248,12 +248,12 @@ struct SimplifyGlobals : public Pass {
void propagateConstantsToGlobals() {
// Go over the list of globals in order, which is the order of
// initialization as well, tracking their constant values.
- std::map<Name, Literal> constantGlobals;
+ std::map<Name, Literals> constantGlobals;
for (auto& global : module->globals) {
if (!global->imported()) {
if (Properties::isConstantExpression(global->init)) {
constantGlobals[global->name] =
- getSingleLiteralFromConstExpression(global->init);
+ getLiteralsFromConstExpression(global->init);
} else if (auto* get = global->init->dynCast<GlobalGet>()) {
auto iter = constantGlobals.find(get->name);
if (iter != constantGlobals.end()) {
diff --git a/src/shell-interface.h b/src/shell-interface.h
index de4fe357a..94ecd61c3 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -95,22 +95,22 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
table.resize(wasm.table.initial);
}
- void importGlobals(std::map<Name, Literal>& globals, Module& wasm) override {
+ void importGlobals(std::map<Name, Literals>& globals, Module& wasm) override {
// add spectest globals
ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) {
if (import->module == SPECTEST && import->base.startsWith(GLOBAL)) {
switch (import->type.getSingle()) {
case Type::i32:
- globals[import->name] = Literal(int32_t(666));
+ globals[import->name] = {Literal(int32_t(666))};
break;
case Type::i64:
- globals[import->name] = Literal(int64_t(666));
+ globals[import->name] = {Literal(int64_t(666))};
break;
case Type::f32:
- globals[import->name] = Literal(float(666.6));
+ globals[import->name] = {Literal(float(666.6))};
break;
case Type::f64:
- globals[import->name] = Literal(double(666.6));
+ globals[import->name] = {Literal(double(666.6))};
break;
case Type::v128:
assert(false && "v128 not implemented yet");
@@ -118,7 +118,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
case Type::anyref:
case Type::nullref:
case Type::exnref:
- globals[import->name] = Literal::makeNullref();
+ globals[import->name] = {Literal::makeNullref()};
break;
case Type::none:
case Type::unreachable:
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 512c47396..ca0dafd8e 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -48,7 +48,7 @@ struct FailToEvalException {
// We do not have access to imported globals
class EvallingGlobalManager {
// values of globals
- std::map<Name, Literal> globals;
+ std::map<Name, Literals> globals;
// globals that are dangerous to modify in the module
std::set<Name> dangerousGlobals;
@@ -70,7 +70,7 @@ public:
return !(*this == other);
}
- Literal& operator[](Name name) {
+ Literals& operator[](Name name) {
if (dangerousGlobals.count(name) > 0) {
std::string extra;
if (name == "___dso_handle") {
@@ -87,11 +87,11 @@ public:
struct Iterator {
Name first;
- Literal second;
+ Literals second;
bool found;
Iterator() : found(false) {}
- Iterator(Name name, Literal value)
+ Iterator(Name name, Literals value)
: first(name), second(value), found(true) {}
bool operator==(const Iterator& other) {
@@ -177,28 +177,28 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
// fill usable values for stack imports, and globals initialized to them
ImportInfo imports(wasm_);
if (auto* stackTop = imports.getImportedGlobal(ENV, STACKTOP)) {
- globals[stackTop->name] = Literal(int32_t(STACK_START));
+ globals[stackTop->name] = {Literal(int32_t(STACK_START))};
if (auto* stackTop =
GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACKTOP)) {
- globals[stackTop->name] = Literal(int32_t(STACK_START));
+ globals[stackTop->name] = {Literal(int32_t(STACK_START))};
}
}
if (auto* stackMax = imports.getImportedGlobal(ENV, STACK_MAX)) {
- globals[stackMax->name] = Literal(int32_t(STACK_START));
+ globals[stackMax->name] = {Literal(int32_t(STACK_START))};
if (auto* stackMax =
GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACK_MAX)) {
- globals[stackMax->name] = Literal(int32_t(STACK_START));
+ globals[stackMax->name] = {Literal(int32_t(STACK_START))};
}
}
// fill in fake values for everything else, which is dangerous to use
ModuleUtils::iterDefinedGlobals(wasm_, [&](Global* defined) {
if (globals.find(defined->name) == globals.end()) {
- globals[defined->name] = Literal::makeSingleZero(defined->type);
+ globals[defined->name] = Literal::makeZero(defined->type);
}
});
ModuleUtils::iterImportedGlobals(wasm_, [&](Global* import) {
if (globals.find(import->name) == globals.end()) {
- globals[import->name] = Literal::makeSingleZero(import->type);
+ globals[import->name] = Literal::makeZero(import->type);
}
});
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 4765efa0e..ca8041d5a 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1432,7 +1432,7 @@ public:
globals[global->name] =
ConstantExpressionRunner<GlobalManager>(globals, maxDepth)
.visit(global->init)
- .getSingleValue();
+ .values;
});
// initialize the rest of the external interface
@@ -1457,10 +1457,10 @@ public:
return callFunction(export_->value, arguments);
}
- Literal callExport(Name name) { return callExport(name, LiteralList()); }
+ Literals callExport(Name name) { return callExport(name, LiteralList()); }
// get an exported global
- Literal getExport(Name name) {
+ Literals getExport(Name name) {
Export* export_ = wasm.getExportOrNull(name);
if (!export_) {
externalInterface->trap("getExport external not found");
@@ -1677,7 +1677,7 @@ private:
}
NOTE_EVAL1(name);
NOTE_EVAL1(flow.getSingleValue());
- instance.globals[name] = flow.getSingleValue();
+ instance.globals[name] = flow.values;
return Flow();
}
@@ -2232,7 +2232,7 @@ protected:
};
// The default ModuleInstance uses a trivial global manager
-using TrivialGlobalManager = std::map<Name, Literal>;
+using TrivialGlobalManager = std::map<Name, Literals>;
class ModuleInstance
: public ModuleInstanceBase<TrivialGlobalManager, ModuleInstance> {
public:
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 420692808..24cb7914a 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -160,6 +160,7 @@ private:
}
Type
stringToType(const char* str, bool allowError = false, bool prefix = false);
+ Type elementToType(Element& s);
Type stringToLaneType(const char* str);
bool isType(cashew::IString str) {
return stringToType(str, true) != Type::none;
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index c36789828..3b8649d29 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -376,14 +376,25 @@ void WasmBinaryWriter::writeGlobals() {
}
BYN_TRACE("== writeglobals\n");
auto start = startSection(BinaryConsts::Section::Global);
- auto num = importInfo->getNumDefinedGlobals();
+ // Count and emit the total number of globals after tuple globals have been
+ // expanded into their constituent parts.
+ Index num = 0;
+ ModuleUtils::iterDefinedGlobals(
+ *wasm, [&num](Global* global) { num += global->type.size(); });
o << U32LEB(num);
ModuleUtils::iterDefinedGlobals(*wasm, [&](Global* global) {
BYN_TRACE("write one\n");
- o << binaryType(global->type);
- o << U32LEB(global->mutable_);
- writeExpression(global->init);
- o << int8_t(BinaryConsts::End);
+ const auto& types = global->type.expand();
+ for (size_t i = 0; i < types.size(); ++i) {
+ o << binaryType(types[i]);
+ o << U32LEB(global->mutable_);
+ if (types.size() == 1) {
+ writeExpression(global->init);
+ } else {
+ writeExpression(global->init->cast<TupleMake>()->operands[i]);
+ }
+ o << int8_t(BinaryConsts::End);
+ }
});
finishSection(start);
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 4354b747a..a39e52b6d 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -546,12 +546,7 @@ SExpressionWasmBuilder::parseParamOrLocal(Element& s, size_t& localIndex) {
throw ParseException(
"params may not have tuple types", s[i]->line, s[i]->col);
}
- auto& tuple = s[i]->list();
- std::vector<Type> types;
- for (size_t j = 0; j < s[i]->size(); ++j) {
- types.push_back(stringToType(tuple[j]->str()));
- }
- type = Type(types);
+ type = elementToType(*s[i]);
}
namedParams.emplace_back(name, type);
}
@@ -886,6 +881,18 @@ Type SExpressionWasmBuilder::stringToType(const char* str,
throw ParseException(std::string("invalid wasm type: ") + str);
}
+Type SExpressionWasmBuilder::elementToType(Element& s) {
+ if (s.isStr()) {
+ return stringToType(s.str(), false, false);
+ }
+ auto& tuple = s.list();
+ std::vector<Type> types;
+ for (size_t i = 0; i < s.size(); ++i) {
+ types.push_back(stringToType(tuple[i]->str()));
+ }
+ return Type(types);
+}
+
Type SExpressionWasmBuilder::stringToLaneType(const char* str) {
if (strcmp(str, "i8x16") == 0) {
return Type::i32;
@@ -2278,7 +2285,7 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
bool exported = false;
Name importModule, importBase;
while (i < s.size() && s[i]->isList()) {
- auto& inner = *s[i];
+ auto& inner = *s[i++];
if (elementStartsWith(inner, EXPORT)) {
auto ex = make_unique<Export>();
ex->name = inner[1]->str();
@@ -2289,16 +2296,15 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
}
wasm.addExport(ex.release());
exported = true;
- i++;
} else if (elementStartsWith(inner, IMPORT)) {
importModule = inner[1]->str();
importBase = inner[2]->str();
- i++;
} else if (elementStartsWith(inner, MUT)) {
mutable_ = true;
- type = stringToType(inner[1]->str());
- i++;
+ type = elementToType(*inner[1]);
+ break;
} else {
+ type = elementToType(inner);
break;
}
}
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 9b5dc3f22..48fe79fa1 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -114,13 +114,21 @@ void BinaryInstWriter::visitLocalSet(LocalSet* curr) {
}
void BinaryInstWriter::visitGlobalGet(GlobalGet* curr) {
- o << int8_t(BinaryConsts::GlobalGet)
- << U32LEB(parent.getGlobalIndex(curr->name));
+ // Emit a global.get for each element if this is a tuple global
+ Index index = parent.getGlobalIndex(curr->name);
+ size_t numValues = curr->type.size();
+ for (Index i = 0; i < numValues; ++i) {
+ o << int8_t(BinaryConsts::GlobalGet) << U32LEB(index + i);
+ }
}
void BinaryInstWriter::visitGlobalSet(GlobalSet* curr) {
- o << int8_t(BinaryConsts::GlobalSet)
- << U32LEB(parent.getGlobalIndex(curr->name));
+ // Emit a global.set for each element if this is a tuple global
+ Index index = parent.getGlobalIndex(curr->name);
+ size_t numValues = parent.getModule()->getGlobal(curr->name)->type.size();
+ for (int i = numValues - 1; i >= 0; --i) {
+ o << int8_t(BinaryConsts::GlobalSet) << U32LEB(index + i);
+ }
}
void BinaryInstWriter::visitLoad(Load* curr) {
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 368c01bdc..c152e4b98 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2137,12 +2137,14 @@ static void validateImports(Module& module, ValidationInfo& info) {
}
}
});
- if (!module.features.hasMutableGlobals()) {
- ModuleUtils::iterImportedGlobals(module, [&](Global* curr) {
+ ModuleUtils::iterImportedGlobals(module, [&](Global* curr) {
+ if (!module.features.hasMutableGlobals()) {
info.shouldBeFalse(
curr->mutable_, curr->name, "Imported global cannot be mutable");
- });
- }
+ }
+ info.shouldBeTrue(
+ curr->type.isSingle(), curr->name, "Imported global cannot be tuple");
+ });
}
static void validateExports(Module& module, ValidationInfo& info) {
@@ -2164,11 +2166,14 @@ static void validateExports(Module& module, ValidationInfo& info) {
"Exported function must not have i64 results");
}
}
- } else if (curr->kind == ExternalKind::Global &&
- !module.features.hasMutableGlobals()) {
+ } else if (curr->kind == ExternalKind::Global) {
if (Global* g = module.getGlobalOrNull(curr->value)) {
- info.shouldBeFalse(
- g->mutable_, g->name, "Exported global cannot be mutable");
+ if (!module.features.hasMutableGlobals()) {
+ info.shouldBeFalse(
+ g->mutable_, g->name, "Exported global cannot be mutable");
+ }
+ info.shouldBeTrue(
+ g->type.isSingle(), g->name, "Exported global cannot be tuple");
}
}
}
diff --git a/test/multivalue.wast b/test/multivalue.wast
index 08e23f62a..d740d384f 100644
--- a/test/multivalue.wast
+++ b/test/multivalue.wast
@@ -1,7 +1,8 @@
(module
(import "env" "pair" (func $pair (result i32 i64)))
-
- ;; Test basic lowering of tuple.make, tuple.extract, and tuple locals
+ (global $g1 (mut (i32 i64)) (tuple.make (i32.const 0) (i64.const 0)))
+ (global $g2 (i32 i64) (tuple.make (i32.const 0) (i64.const 0)))
+ ;; Test basic lowering of tuple.make, tuple.extract, and tuple variables
(func $triple (result i32 i64 f32)
(tuple.make
(i32.const 42)
@@ -51,6 +52,17 @@
)
)
+ ;; Test multivalue globals
+ (func $global (result i32 i64)
+ (global.set $g1
+ (tuple.make
+ (i32.const 42)
+ (i64.const 7)
+ )
+ )
+ (global.get $g2)
+ )
+
;; Test lowering of multivalue drops
(func $drop-call
(drop
diff --git a/test/multivalue.wast.from-wast b/test/multivalue.wast.from-wast
index a918979f5..d23198e94 100644
--- a/test/multivalue.wast.from-wast
+++ b/test/multivalue.wast.from-wast
@@ -9,6 +9,14 @@
(type $none_=>_i32_i64_nullref (func (result i32 i64 nullref)))
(type $none_=>_f32 (func (result f32)))
(import "env" "pair" (func $pair (result i32 i64)))
+ (global $g1 (mut (i32 i64)) (tuple.make
+ (i32.const 0)
+ (i64.const 0)
+ ))
+ (global $g2 (i32 i64) (tuple.make
+ (i32.const 0)
+ (i64.const 0)
+ ))
(func $triple (; 1 ;) (result i32 i64 f32)
(tuple.make
(i32.const 42)
@@ -57,12 +65,21 @@
)
)
)
- (func $drop-call (; 7 ;)
+ (func $global (; 7 ;) (result i32 i64)
+ (global.set $g1
+ (tuple.make
+ (i32.const 42)
+ (i64.const 7)
+ )
+ )
+ (global.get $g2)
+ )
+ (func $drop-call (; 8 ;)
(drop
(call $pair)
)
)
- (func $drop-tuple-make (; 8 ;)
+ (func $drop-tuple-make (; 9 ;)
(drop
(tuple.make
(i32.const 42)
@@ -70,7 +87,7 @@
)
)
)
- (func $drop-block (; 9 ;)
+ (func $drop-block (; 10 ;)
(drop
(block $block (result i32 i64)
(tuple.make
@@ -80,7 +97,7 @@
)
)
)
- (func $mv-return (; 10 ;) (result i32 i64)
+ (func $mv-return (; 11 ;) (result i32 i64)
(return
(tuple.make
(i32.const 42)
@@ -88,7 +105,7 @@
)
)
)
- (func $mv-return-in-block (; 11 ;) (result i32 i64)
+ (func $mv-return-in-block (; 12 ;) (result i32 i64)
(block $block (result i32 i64)
(return
(tuple.make
@@ -98,7 +115,7 @@
)
)
)
- (func $mv-block-break (; 12 ;) (result i32 i64)
+ (func $mv-block-break (; 13 ;) (result i32 i64)
(block $l (result i32 i64)
(br $l
(tuple.make
@@ -108,7 +125,7 @@
)
)
)
- (func $mv-block-br-if (; 13 ;) (result i32 i64)
+ (func $mv-block-br-if (; 14 ;) (result i32 i64)
(block $l (result i32 i64)
(br_if $l
(tuple.make
@@ -119,7 +136,7 @@
)
)
)
- (func $mv-if (; 14 ;) (result i32 i64 anyref)
+ (func $mv-if (; 15 ;) (result i32 i64 anyref)
(if (result i32 i64 nullref)
(i32.const 1)
(tuple.make
@@ -134,7 +151,7 @@
)
)
)
- (func $mv-loop (; 15 ;) (result i32 i64)
+ (func $mv-loop (; 16 ;) (result i32 i64)
(loop $loop-in (result i32 i64)
(tuple.make
(i32.const 42)
@@ -142,7 +159,7 @@
)
)
)
- (func $mv-switch (; 16 ;) (result i32 i64)
+ (func $mv-switch (; 17 ;) (result i32 i64)
(block $a (result i32 i64)
(block $b (result i32 i64)
(br_table $a $b
diff --git a/test/multivalue.wast.fromBinary b/test/multivalue.wast.fromBinary
index 32f744c46..2a1bd095e 100644
--- a/test/multivalue.wast.fromBinary
+++ b/test/multivalue.wast.fromBinary
@@ -9,6 +9,10 @@
(type $none_=>_i32_i64_nullref (func (result i32 i64 nullref)))
(type $none_=>_f32 (func (result f32)))
(import "env" "pair" (func $pair (result i32 i64)))
+ (global $global$0 (mut i32) (i32.const 0))
+ (global $global$1 (mut i64) (i64.const 0))
+ (global $global$2 i32 (i32.const 0))
+ (global $global$3 i64 (i64.const 0))
(func $triple (; 1 ;) (result i32 i64 f32)
(tuple.make
(i32.const 42)
@@ -235,7 +239,25 @@
)
(unreachable)
)
- (func $drop-call (; 7 ;)
+ (func $global (; 7 ;) (result i32 i64)
+ (local $0 i32)
+ (global.set $global$0
+ (block (result i32)
+ (local.set $0
+ (i32.const 42)
+ )
+ (global.set $global$1
+ (i64.const 7)
+ )
+ (local.get $0)
+ )
+ )
+ (tuple.make
+ (global.get $global$2)
+ (global.get $global$3)
+ )
+ )
+ (func $drop-call (; 8 ;)
(local $0 (i32 i64))
(local $1 i32)
(local.set $0
@@ -257,7 +279,7 @@
)
)
)
- (func $drop-tuple-make (; 8 ;)
+ (func $drop-tuple-make (; 9 ;)
(local $0 i32)
(drop
(block (result i32)
@@ -271,7 +293,7 @@
)
)
)
- (func $drop-block (; 9 ;)
+ (func $drop-block (; 10 ;)
(local $0 (i32 i64))
(local $1 i32)
(local.set $0
@@ -298,7 +320,7 @@
)
)
)
- (func $mv-return (; 10 ;) (result i32 i64)
+ (func $mv-return (; 11 ;) (result i32 i64)
(return
(tuple.make
(i32.const 42)
@@ -306,7 +328,7 @@
)
)
)
- (func $mv-return-in-block (; 11 ;) (result i32 i64)
+ (func $mv-return-in-block (; 12 ;) (result i32 i64)
(return
(tuple.make
(i32.const 42)
@@ -314,7 +336,7 @@
)
)
)
- (func $mv-block-break (; 12 ;) (result i32 i64)
+ (func $mv-block-break (; 13 ;) (result i32 i64)
(local $0 (i32 i64))
(local.set $0
(block $label$1 (result i32 i64)
@@ -335,7 +357,7 @@
)
)
)
- (func $mv-block-br-if (; 13 ;) (result i32 i64)
+ (func $mv-block-br-if (; 14 ;) (result i32 i64)
(local $0 (i32 i64))
(local $1 (i32 i64))
(local.set $1
@@ -368,7 +390,7 @@
)
)
)
- (func $mv-if (; 14 ;) (result i32 i64 anyref)
+ (func $mv-if (; 15 ;) (result i32 i64 anyref)
(local $0 (i32 i64 nullref))
(local.set $0
(if (result i32 i64 nullref)
@@ -397,7 +419,7 @@
)
)
)
- (func $mv-loop (; 15 ;) (result i32 i64)
+ (func $mv-loop (; 16 ;) (result i32 i64)
(local $0 (i32 i64))
(local.set $0
(loop $label$1 (result i32 i64)
@@ -416,7 +438,7 @@
)
)
)
- (func $mv-switch (; 16 ;) (result i32 i64)
+ (func $mv-switch (; 17 ;) (result i32 i64)
(local $0 (i32 i64))
(local $1 (i32 i64))
(local.set $1
diff --git a/test/multivalue.wast.fromBinary.noDebugInfo b/test/multivalue.wast.fromBinary.noDebugInfo
index c105df989..1682e3144 100644
--- a/test/multivalue.wast.fromBinary.noDebugInfo
+++ b/test/multivalue.wast.fromBinary.noDebugInfo
@@ -9,6 +9,10 @@
(type $none_=>_i32_i64_nullref (func (result i32 i64 nullref)))
(type $none_=>_f32 (func (result f32)))
(import "env" "pair" (func $fimport$0 (result i32 i64)))
+ (global $global$0 (mut i32) (i32.const 0))
+ (global $global$1 (mut i64) (i64.const 0))
+ (global $global$2 i32 (i32.const 0))
+ (global $global$3 i64 (i64.const 0))
(func $0 (; 1 ;) (result i32 i64 f32)
(tuple.make
(i32.const 42)
@@ -235,7 +239,25 @@
)
(unreachable)
)
- (func $6 (; 7 ;)
+ (func $6 (; 7 ;) (result i32 i64)
+ (local $0 i32)
+ (global.set $global$0
+ (block (result i32)
+ (local.set $0
+ (i32.const 42)
+ )
+ (global.set $global$1
+ (i64.const 7)
+ )
+ (local.get $0)
+ )
+ )
+ (tuple.make
+ (global.get $global$2)
+ (global.get $global$3)
+ )
+ )
+ (func $7 (; 8 ;)
(local $0 (i32 i64))
(local $1 i32)
(local.set $0
@@ -257,7 +279,7 @@
)
)
)
- (func $7 (; 8 ;)
+ (func $8 (; 9 ;)
(local $0 i32)
(drop
(block (result i32)
@@ -271,7 +293,7 @@
)
)
)
- (func $8 (; 9 ;)
+ (func $9 (; 10 ;)
(local $0 (i32 i64))
(local $1 i32)
(local.set $0
@@ -298,7 +320,7 @@
)
)
)
- (func $9 (; 10 ;) (result i32 i64)
+ (func $10 (; 11 ;) (result i32 i64)
(return
(tuple.make
(i32.const 42)
@@ -306,7 +328,7 @@
)
)
)
- (func $10 (; 11 ;) (result i32 i64)
+ (func $11 (; 12 ;) (result i32 i64)
(return
(tuple.make
(i32.const 42)
@@ -314,7 +336,7 @@
)
)
)
- (func $11 (; 12 ;) (result i32 i64)
+ (func $12 (; 13 ;) (result i32 i64)
(local $0 (i32 i64))
(local.set $0
(block $label$1 (result i32 i64)
@@ -335,7 +357,7 @@
)
)
)
- (func $12 (; 13 ;) (result i32 i64)
+ (func $13 (; 14 ;) (result i32 i64)
(local $0 (i32 i64))
(local $1 (i32 i64))
(local.set $1
@@ -368,7 +390,7 @@
)
)
)
- (func $13 (; 14 ;) (result i32 i64 anyref)
+ (func $14 (; 15 ;) (result i32 i64 anyref)
(local $0 (i32 i64 nullref))
(local.set $0
(if (result i32 i64 nullref)
@@ -397,7 +419,7 @@
)
)
)
- (func $14 (; 15 ;) (result i32 i64)
+ (func $15 (; 16 ;) (result i32 i64)
(local $0 (i32 i64))
(local.set $0
(loop $label$1 (result i32 i64)
@@ -416,7 +438,7 @@
)
)
)
- (func $15 (; 16 ;) (result i32 i64)
+ (func $16 (; 17 ;) (result i32 i64)
(local $0 (i32 i64))
(local $1 (i32 i64))
(local.set $1
diff --git a/test/spec/multivalue.wast b/test/spec/multivalue.wast
index d6089721d..3891d6983 100644
--- a/test/spec/multivalue.wast
+++ b/test/spec/multivalue.wast
@@ -1,4 +1,5 @@
(module
+ (global $global_pair (mut (i32 i64)) (tuple.make (i32.const 0) (i64.const 0)))
(func $pair (export "pair") (result i32 i64)
(tuple.make
(i32.const 42)
@@ -9,6 +10,17 @@
(local $x (i32 i64))
(local.get $x)
)
+ (func (export "tuple-global-get") (result i32 i64)
+ (global.get $global_pair)
+ )
+ (func (export "tuple-global-set")
+ (global.set $global_pair
+ (tuple.make
+ (i32.const 42)
+ (i64.const 7)
+ )
+ )
+ )
(func (export "tail-call") (result i32 i64)
(return_call $pair)
(unreachable)
@@ -17,4 +29,7 @@
(assert_return (invoke "pair") (tuple.make (i32.const 42) (i64.const 7)))
(assert_return (invoke "tuple-local") (tuple.make (i32.const 0) (i64.const 0)))
+(assert_return (invoke "tuple-global-get") (tuple.make (i32.const 0) (i64.const 0)))
+(assert_return (invoke "tuple-global-set"))
+(assert_return (invoke "tuple-global-get") (tuple.make (i32.const 42) (i64.const 7)))
(assert_return (invoke "tail-call") (tuple.make (i32.const 42) (i64.const 7)))