summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmIntrinsic.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/wasm/WasmIntrinsic.cpp239
1 files changed, 239 insertions, 0 deletions
diff --git a/js/src/wasm/WasmIntrinsic.cpp b/js/src/wasm/WasmIntrinsic.cpp
new file mode 100644
index 0000000000..cc43ed887c
--- /dev/null
+++ b/js/src/wasm/WasmIntrinsic.cpp
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ *
+ * Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wasm/WasmIntrinsic.h"
+
+#include "util/Text.h"
+#include "vm/GlobalObject.h"
+
+#include "wasm/WasmGenerator.h"
+#include "wasm/WasmIntrinsicGenerated.h"
+#include "wasm/WasmJS.h"
+#include "wasm/WasmModule.h"
+#include "wasm/WasmOpIter.h"
+#include "wasm/WasmValidate.h"
+
+using namespace js;
+using namespace js::wasm;
+
+#define INTR_DECL(op, export, sa_name, abitype, entry, idx) \
+ static const ValType Intrinsic##op##_Params[] = \
+ DECLARE_INTRINSIC_SAS_PARAM_VALTYPES_##op; \
+ \
+ const Intrinsic Intrinsic##op = { \
+ export, \
+ mozilla::Span<const ValType>(Intrinsic##op##_Params), \
+ SASig##sa_name, \
+ };
+
+FOR_EACH_INTRINSIC(INTR_DECL)
+#undef INTR_DECL
+
+bool Intrinsic::funcType(FuncType* type) const {
+ ValTypeVector paramVec;
+ if (!paramVec.append(params.data(), params.data() + params.size())) {
+ return false;
+ }
+ *type = FuncType(std::move(paramVec), ValTypeVector());
+ return true;
+}
+
+/* static */
+const Intrinsic& Intrinsic::getFromId(IntrinsicId id) {
+ switch (id) {
+#define OP(op, export, sa_name, abitype, entry, idx) \
+ case IntrinsicId::op: \
+ return Intrinsic##op;
+ FOR_EACH_INTRINSIC(OP)
+#undef OP
+ default:
+ MOZ_CRASH("unexpected intrinsic");
+ }
+}
+
+bool EncodeIntrinsicBody(const Intrinsic& intrinsic, IntrinsicId id,
+ Bytes* body) {
+ Encoder encoder(*body);
+ if (!EncodeLocalEntries(encoder, ValTypeVector())) {
+ return false;
+ }
+ for (uint32_t i = 0; i < intrinsic.params.size(); i++) {
+ if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(i)) {
+ return false;
+ }
+ }
+ if (!encoder.writeOp(MozOp::Intrinsic)) {
+ return false;
+ }
+ if (!encoder.writeVarU32(uint32_t(id))) {
+ return false;
+ }
+ if (!encoder.writeOp(Op::End)) {
+ return false;
+ }
+ return true;
+}
+
+bool wasm::CompileIntrinsicModule(JSContext* cx,
+ const mozilla::Span<IntrinsicId> ids,
+ Shareable sharedMemory,
+ MutableHandle<WasmModuleObject*> result) {
+ // Create the options manually, enabling intrinsics
+ FeatureOptions featureOptions;
+ featureOptions.intrinsics = true;
+
+ // Initialize the compiler environment, choosing the best tier possible
+ SharedCompileArgs compileArgs = CompileArgs::buildAndReport(
+ cx, ScriptedCaller(), featureOptions, /* reportOOM */ true);
+ if (!compileArgs) {
+ return false;
+ }
+ CompilerEnvironment compilerEnv(
+ CompileMode::Once, IonAvailable(cx) ? Tier::Optimized : Tier::Baseline,
+ DebugEnabled::False);
+ compilerEnv.computeParameters();
+
+ // Build a module environment
+ ModuleEnvironment moduleEnv(compileArgs->features);
+ if (!moduleEnv.init()) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ // Add (import (memory 0))
+ CacheableName emptyString;
+ CacheableName memoryString;
+ if (!CacheableName::fromUTF8Chars("memory", &memoryString)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ if (!moduleEnv.imports.append(Import(std::move(emptyString),
+ std::move(memoryString),
+ DefinitionKind::Memory))) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ moduleEnv.memory = Some(MemoryDesc(Limits(0, Nothing(), sharedMemory)));
+
+ // Add (type (func (params ...))) for each intrinsic. The function types will
+ // be deduplicated by the runtime
+ for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) {
+ const IntrinsicId& id = ids[funcIndex];
+ const Intrinsic& intrinsic = Intrinsic::getFromId(id);
+
+ FuncType type;
+ if (!intrinsic.funcType(&type) ||
+ !moduleEnv.types->addType(std::move(type))) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ // Add (func (type $i)) declarations. Do this after all types have been added
+ // as the function declaration metadata uses pointers into the type vectors
+ // that must be stable.
+ for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) {
+ FuncDesc decl(&(*moduleEnv.types)[funcIndex].funcType(), funcIndex);
+ if (!moduleEnv.funcs.append(decl)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ moduleEnv.declareFuncExported(funcIndex, true, false);
+ }
+
+ // Add (export "$name" (func $i)) declarations.
+ for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) {
+ const Intrinsic& intrinsic = Intrinsic::getFromId(ids[funcIndex]);
+
+ CacheableName exportName;
+ if (!CacheableName::fromUTF8Chars(intrinsic.exportName, &exportName) ||
+ !moduleEnv.exports.append(Export(std::move(exportName), funcIndex,
+ DefinitionKind::Function))) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ // Compile the module functions
+ UniqueChars error;
+ ModuleGenerator mg(*compileArgs, &moduleEnv, &compilerEnv, nullptr, &error,
+ nullptr);
+ if (!mg.init(nullptr)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ // Prepare and compile function bodies
+ Vector<Bytes, 1, SystemAllocPolicy> bodies;
+ if (!bodies.reserve(ids.size())) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ for (uint32_t funcIndex = 0; funcIndex < ids.size(); funcIndex++) {
+ IntrinsicId id = ids[funcIndex];
+ const Intrinsic& intrinsic = Intrinsic::getFromId(ids[funcIndex]);
+
+ // Compilation may be done using other threads, ModuleGenerator requires
+ // that function bodies live until after finishFuncDefs().
+ bodies.infallibleAppend(Bytes());
+ Bytes& bytecode = bodies.back();
+
+ // Encode function body that will call the intrinsic using our builtin
+ // opcode, and launch a compile task
+ if (!EncodeIntrinsicBody(intrinsic, id, &bytecode) ||
+ !mg.compileFuncDef(funcIndex, 0, bytecode.begin(),
+ bytecode.begin() + bytecode.length())) {
+ // This must be an OOM and will be reported by the caller
+ MOZ_ASSERT(!error);
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ // Finish and block on function compilation
+ if (!mg.finishFuncDefs()) {
+ // This must be an OOM and will be reported by the caller
+ MOZ_ASSERT(!error);
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ // Create a dummy bytecode vector, that will not be used
+ SharedBytes bytecode = js_new<ShareableBytes>();
+ if (!bytecode) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ // Finish the module
+ SharedModule module = mg.finishModule(*bytecode, nullptr);
+ if (!module) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ // Create a WasmModuleObject for the module, and return it
+ RootedObject proto(
+ cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmModule));
+ if (!proto) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ result.set(WasmModuleObject::create(cx, *module, proto));
+ return !!result;
+}