summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/wasm-binary.h6
-rw-r--r--src/wasm-builder.h29
-rw-r--r--src/wasm/wasm-binary.cpp31
-rw-r--r--src/wasm/wasm-s-parser.cpp35
4 files changed, 83 insertions, 18 deletions
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index acde624f7..eb8e176ec 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1094,7 +1094,8 @@ enum ASTNodes {
// typed function references opcodes
- CallRef = 0x14,
+ CallRefUnannotated = 0x14,
+ CallRef = 0x17,
RetCallRef = 0x15,
// gc opcodes
@@ -1742,7 +1743,8 @@ public:
void visitTryOrTryInBlock(Expression*& out);
void visitThrow(Throw* curr);
void visitRethrow(Rethrow* curr);
- void visitCallRef(CallRef* curr);
+ void visitCallRef(CallRef* curr,
+ std::optional<HeapType> maybeType = std::nullopt);
void visitRefAs(RefAs* curr, uint8_t code);
[[noreturn]] void throwError(std::string text);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index a29867e55..1eb31157d 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -1342,6 +1342,35 @@ public:
}
return makeBrOn(op, name, ref);
}
+
+ template<typename T>
+ Expression* validateAndMakeCallRef(Expression* target,
+ const T& args,
+ bool isReturn = false) {
+ if (target->type != Type::unreachable && !target->type.isRef()) {
+ throw ParseException("Non-reference type for a call_ref", line, col);
+ }
+ // TODO: This won't be necessary once type annotations are mandatory on
+ // call_ref.
+ if (target->type == Type::unreachable ||
+ target->type.getHeapType() == HeapType::nofunc) {
+ // An unreachable target is not supported. Similiar to br_on_cast, just
+ // emit an unreachable sequence, since we don't have enough information
+ // to create a full call_ref.
+ std::vector<Expression*> children;
+ for (auto* arg : args) {
+ children.push_back(makeDrop(arg));
+ }
+ children.push_back(makeDrop(target));
+ children.push_back(makeUnreachable());
+ return makeBlock(children, Type::unreachable);
+ }
+ auto heapType = target->type.getHeapType();
+ if (!heapType.isSignature()) {
+ throw ParseException("Invalid reference type for a call_ref", line, col);
+ }
+ return makeCallRef(target, args, heapType.getSignature().results, isReturn);
+ }
};
} // namespace wasm
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 782979520..e2bb0075b 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -3846,12 +3846,15 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
visitMemoryGrow(grow);
break;
}
+ case BinaryConsts::CallRefUnannotated:
+ visitCallRef((curr = allocator.alloc<CallRef>())->cast<CallRef>());
+ break;
case BinaryConsts::CallRef:
case BinaryConsts::RetCallRef: {
auto call = allocator.alloc<CallRef>();
call->isReturn = code == BinaryConsts::RetCallRef;
curr = call;
- visitCallRef(call);
+ visitCallRef(call, getTypeByIndex(getU32LEB()));
break;
}
case BinaryConsts::AtomicPrefix: {
@@ -6848,13 +6851,29 @@ void WasmBinaryBuilder::visitRethrow(Rethrow* curr) {
curr->finalize();
}
-void WasmBinaryBuilder::visitCallRef(CallRef* curr) {
+void WasmBinaryBuilder::visitCallRef(CallRef* curr,
+ std::optional<HeapType> maybeType) {
BYN_TRACE("zz node: CallRef\n");
curr->target = popNonVoidExpression();
- HeapType heapType = getTypeByIndex(getU32LEB());
- if (!Type::isSubType(curr->target->type, Type(heapType, Nullable))) {
- throwError("Call target has invalid type: " +
- curr->target->type.toString());
+ HeapType heapType;
+ if (maybeType) {
+ heapType = *maybeType;
+ if (!Type::isSubType(curr->target->type, Type(heapType, Nullable))) {
+ throwError("Call target has invalid type: " +
+ curr->target->type.toString());
+ }
+ } else {
+ auto type = curr->target->type;
+ if (type == Type::unreachable) {
+ // If our input is unreachable, then we cannot even find out how many
+ // inputs we have, and just set ourselves to unreachable as well.
+ curr->finalize(type);
+ return;
+ }
+ if (!type.isRef()) {
+ throwError("Non-ref type for a call_ref: " + type.toString());
+ }
+ heapType = type.getHeapType();
}
if (!heapType.isSignature()) {
throwError("Invalid reference type for a call_ref: " + heapType.toString());
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index ab6230442..a9fd2de77 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2744,20 +2744,35 @@ Expression* SExpressionWasmBuilder::makeTupleExtract(Element& s) {
}
Expression* SExpressionWasmBuilder::makeCallRef(Element& s, bool isReturn) {
- HeapType sigType = parseHeapType(*s[1]);
+ Index operandsStart = 1;
+ std::optional<HeapType> sigType;
+ try {
+ sigType = parseHeapType(*s[1]);
+ operandsStart = 2;
+ } catch (ParseException& p) {
+ // The type annotation is required for return_call_ref but temporarily
+ // optional for call_ref.
+ if (isReturn) {
+ throw;
+ }
+ }
std::vector<Expression*> operands;
- parseOperands(s, 2, s.size() - 1, operands);
+ parseOperands(s, operandsStart, s.size() - 1, operands);
auto* target = parseExpression(s[s.size() - 1]);
- if (!sigType.isSignature()) {
- throw ParseException(
- std::string(isReturn ? "return_call_ref" : "call_ref") +
- " type annotation should be a signature",
- s.line,
- s.col);
+ if (sigType) {
+ if (!sigType->isSignature()) {
+ throw ParseException(
+ std::string(isReturn ? "return_call_ref" : "call_ref") +
+ " type annotation should be a signature",
+ s.line,
+ s.col);
+ }
+ return Builder(wasm).makeCallRef(
+ target, operands, sigType->getSignature().results, isReturn);
}
- return Builder(wasm).makeCallRef(
- target, operands, sigType.getSignature().results, isReturn);
+ return ValidatingBuilder(wasm, s.line, s.col)
+ .validateAndMakeCallRef(target, operands, isReturn);
}
Expression* SExpressionWasmBuilder::makeI31New(Element& s) {