1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
4 * Copyright 2021 Mozilla Foundation
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 #include "wasm/WasmBuiltinModule.h"
21 #include "util/Text.h"
22 #include "vm/GlobalObject.h"
24 #include "wasm/WasmBuiltinModuleGenerated.h"
25 #include "wasm/WasmFeatures.h"
26 #include "wasm/WasmGenerator.h"
27 #include "wasm/WasmJS.h"
28 #include "wasm/WasmModule.h"
29 #include "wasm/WasmOpIter.h"
30 #include "wasm/WasmStaticTypeDefs.h"
31 #include "wasm/WasmValidate.h"
34 using namespace js::wasm
;
36 BuiltinModuleFuncs
* BuiltinModuleFuncs::singleton_
= nullptr;
38 [[nodiscard
]] bool BuiltinModuleFunc::init(const RefPtr
<TypeContext
>& types
,
39 mozilla::Span
<const ValType
> params
,
40 Maybe
<ValType
> result
,
42 const SymbolicAddressSignature
* sig
,
43 const char* exportName
) {
44 // This builtin must not have been initialized yet.
45 MOZ_ASSERT(!recGroup_
);
47 // Initialize the basic fields
48 exportName_
= exportName
;
50 usesMemory_
= usesMemory
;
52 // Create a function type for the given params and result
53 ValTypeVector paramVec
;
54 if (!paramVec
.append(params
.data(), params
.data() + params
.size())) {
57 ValTypeVector resultVec
;
58 if (result
.isSome() && !resultVec
.append(*result
)) {
61 const TypeDef
* typeDef
=
62 types
->addType(FuncType(std::move(paramVec
), std::move(resultVec
)));
66 recGroup_
= &typeDef
->recGroup();
70 bool BuiltinModuleFuncs::init() {
71 singleton_
= js_new
<BuiltinModuleFuncs
>();
76 RefPtr
<TypeContext
> types
= js_new
<TypeContext
>();
81 #define VISIT_BUILTIN_FUNC(op, export, sa_name, abitype, entry, uses_memory, \
83 const ValType op##Params[] = \
84 DECLARE_BUILTIN_MODULE_FUNC_PARAM_VALTYPES_##op; \
85 Maybe<ValType> op##Result = DECLARE_BUILTIN_MODULE_FUNC_RESULT_VALTYPE_##op; \
86 if (!singleton_->funcs_[BuiltinModuleFuncId::op].init( \
87 types, mozilla::Span<const ValType>(op##Params), op##Result, \
88 uses_memory, &SASig##sa_name, export)) { \
91 FOR_EACH_BUILTIN_MODULE_FUNC(VISIT_BUILTIN_FUNC
)
92 #undef VISIT_BUILTIN_FUNC
97 void BuiltinModuleFuncs::destroy() {
101 js_delete(singleton_
);
102 singleton_
= nullptr;
105 bool EncodeFuncBody(const BuiltinModuleFunc
& builtinModuleFunc
,
106 BuiltinModuleFuncId id
, Bytes
* body
) {
107 Encoder
encoder(*body
);
108 if (!EncodeLocalEntries(encoder
, ValTypeVector())) {
111 const FuncType
* funcType
= builtinModuleFunc
.funcType();
112 for (uint32_t i
= 0; i
< funcType
->args().length(); i
++) {
113 if (!encoder
.writeOp(Op::LocalGet
) || !encoder
.writeVarU32(i
)) {
117 if (!encoder
.writeOp(MozOp::CallBuiltinModuleFunc
)) {
120 if (!encoder
.writeVarU32(uint32_t(id
))) {
123 return encoder
.writeOp(Op::End
);
126 bool CompileBuiltinModule(JSContext
* cx
,
127 const mozilla::Span
<BuiltinModuleFuncId
> ids
,
128 mozilla::Maybe
<Shareable
> memory
,
129 MutableHandle
<WasmModuleObject
*> result
) {
130 // Create the options manually, enabling intrinsics
131 FeatureOptions featureOptions
;
132 featureOptions
.isBuiltinModule
= true;
134 // Initialize the compiler environment, choosing the best tier possible
135 SharedCompileArgs compileArgs
= CompileArgs::buildAndReport(
136 cx
, ScriptedCaller(), featureOptions
, /* reportOOM */ true);
140 CompilerEnvironment
compilerEnv(
141 CompileMode::Once
, IonAvailable(cx
) ? Tier::Optimized
: Tier::Baseline
,
142 DebugEnabled::False
);
143 compilerEnv
.computeParameters();
145 // Build a module environment
146 ModuleEnvironment
moduleEnv(compileArgs
->features
);
147 if (!moduleEnv
.init()) {
148 ReportOutOfMemory(cx
);
152 if (memory
.isSome()) {
153 // Add (import (memory 0))
154 CacheableName emptyString
;
155 CacheableName memoryString
;
156 if (!CacheableName::fromUTF8Chars("memory", &memoryString
)) {
157 ReportOutOfMemory(cx
);
160 if (!moduleEnv
.imports
.append(Import(std::move(emptyString
),
161 std::move(memoryString
),
162 DefinitionKind::Memory
))) {
163 ReportOutOfMemory(cx
);
166 if (!moduleEnv
.memories
.append(MemoryDesc(Limits(0, Nothing(), *memory
)))) {
167 ReportOutOfMemory(cx
);
172 // Add (type (func (params ...))) for each func. The function types will
173 // be deduplicated by the runtime
174 for (uint32_t funcIndex
= 0; funcIndex
< ids
.size(); funcIndex
++) {
175 const BuiltinModuleFuncId
& id
= ids
[funcIndex
];
176 const BuiltinModuleFunc
& builtinModuleFunc
=
177 BuiltinModuleFuncs::getFromId(id
);
179 SharedRecGroup recGroup
= builtinModuleFunc
.recGroup();
180 MOZ_ASSERT(recGroup
->numTypes() == 1);
181 if (!moduleEnv
.types
->addRecGroup(recGroup
)) {
182 ReportOutOfMemory(cx
);
187 // Add (func (type $i)) declarations. Do this after all types have been added
188 // as the function declaration metadata uses pointers into the type vectors
189 // that must be stable.
190 for (uint32_t funcIndex
= 0; funcIndex
< ids
.size(); funcIndex
++) {
191 FuncDesc
decl(&(*moduleEnv
.types
)[funcIndex
].funcType(), funcIndex
);
192 if (!moduleEnv
.funcs
.append(decl
)) {
193 ReportOutOfMemory(cx
);
196 moduleEnv
.declareFuncExported(funcIndex
, true, false);
199 // Add (export "$name" (func $i)) declarations.
200 for (uint32_t funcIndex
= 0; funcIndex
< ids
.size(); funcIndex
++) {
201 const BuiltinModuleFunc
& builtinModuleFunc
=
202 BuiltinModuleFuncs::getFromId(ids
[funcIndex
]);
204 CacheableName exportName
;
205 if (!CacheableName::fromUTF8Chars(builtinModuleFunc
.exportName(),
207 !moduleEnv
.exports
.append(Export(std::move(exportName
), funcIndex
,
208 DefinitionKind::Function
))) {
209 ReportOutOfMemory(cx
);
214 // Compile the module functions
216 ModuleGenerator
mg(*compileArgs
, &moduleEnv
, &compilerEnv
, nullptr, &error
,
218 if (!mg
.init(nullptr)) {
219 ReportOutOfMemory(cx
);
223 // Prepare and compile function bodies
224 Vector
<Bytes
, 1, SystemAllocPolicy
> bodies
;
225 if (!bodies
.reserve(ids
.size())) {
226 ReportOutOfMemory(cx
);
229 for (uint32_t funcIndex
= 0; funcIndex
< ids
.size(); funcIndex
++) {
230 BuiltinModuleFuncId id
= ids
[funcIndex
];
231 const BuiltinModuleFunc
& builtinModuleFunc
=
232 BuiltinModuleFuncs::getFromId(ids
[funcIndex
]);
234 // Compilation may be done using other threads, ModuleGenerator requires
235 // that function bodies live until after finishFuncDefs().
236 bodies
.infallibleAppend(Bytes());
237 Bytes
& bytecode
= bodies
.back();
239 // Encode function body that will call the builtinModuleFunc using our
240 // builtin opcode, and launch a compile task
241 if (!EncodeFuncBody(builtinModuleFunc
, id
, &bytecode
) ||
242 !mg
.compileFuncDef(funcIndex
, 0, bytecode
.begin(),
243 bytecode
.begin() + bytecode
.length())) {
244 // This must be an OOM and will be reported by the caller
246 ReportOutOfMemory(cx
);
251 // Finish and block on function compilation
252 if (!mg
.finishFuncDefs()) {
253 // This must be an OOM and will be reported by the caller
255 ReportOutOfMemory(cx
);
259 // Create a dummy bytecode vector, that will not be used
260 SharedBytes bytecode
= js_new
<ShareableBytes
>();
262 ReportOutOfMemory(cx
);
267 SharedModule module
= mg
.finishModule(*bytecode
, nullptr);
269 ReportOutOfMemory(cx
);
273 // Create a WasmModuleObject for the module, and return it
275 cx
, GlobalObject::getOrCreatePrototype(cx
, JSProto_WasmModule
));
277 ReportOutOfMemory(cx
);
280 result
.set(WasmModuleObject::create(cx
, *module
, proto
));
284 static BuiltinModuleFuncId SelfTestFuncs
[] = {BuiltinModuleFuncId::I8VecMul
};
286 #ifdef ENABLE_WASM_MOZ_INTGEMM
287 static BuiltinModuleFuncId IntGemmFuncs
[] = {
288 BuiltinModuleFuncId::I8PrepareB
,
289 BuiltinModuleFuncId::I8PrepareBFromTransposed
,
290 BuiltinModuleFuncId::I8PrepareBFromQuantizedTransposed
,
291 BuiltinModuleFuncId::I8PrepareA
,
292 BuiltinModuleFuncId::I8PrepareBias
,
293 BuiltinModuleFuncId::I8MultiplyAndAddBias
,
294 BuiltinModuleFuncId::I8SelectColumnsOfB
};
295 #endif // ENABLE_WASM_MOZ_INTGEMM
297 #ifdef ENABLE_WASM_JS_STRING_BUILTINS
298 static BuiltinModuleFuncId JSStringFuncs
[] = {
299 BuiltinModuleFuncId::StringTest
,
300 BuiltinModuleFuncId::StringCast
,
301 BuiltinModuleFuncId::StringFromCharCodeArray
,
302 BuiltinModuleFuncId::StringIntoCharCodeArray
,
303 BuiltinModuleFuncId::StringFromCharCode
,
304 BuiltinModuleFuncId::StringFromCodePoint
,
305 BuiltinModuleFuncId::StringCharCodeAt
,
306 BuiltinModuleFuncId::StringCodePointAt
,
307 BuiltinModuleFuncId::StringLength
,
308 BuiltinModuleFuncId::StringConcat
,
309 BuiltinModuleFuncId::StringSubstring
,
310 BuiltinModuleFuncId::StringEquals
,
311 BuiltinModuleFuncId::StringCompare
};
312 static const char* JSStringModuleName
= "wasm:js-string";
313 #endif // ENABLE_WASM_JS_STRING_BUILTINS
315 Maybe
<BuiltinModuleId
> wasm::ImportMatchesBuiltinModule(
316 Span
<const char> importName
, BuiltinModuleIds enabledBuiltins
) {
317 #ifdef ENABLE_WASM_JS_STRING_BUILTINS
318 if (enabledBuiltins
.jsString
&&
319 importName
== mozilla::MakeStringSpan(JSStringModuleName
)) {
320 return Some(BuiltinModuleId::JSString
);
322 #endif // ENABLE_WASM_JS_STRING_BUILTINS
323 // Not supported for implicit instantiation yet
324 MOZ_RELEASE_ASSERT(!enabledBuiltins
.selfTest
&& !enabledBuiltins
.intGemm
);
328 Maybe
<const BuiltinModuleFunc
*> wasm::ImportMatchesBuiltinModuleFunc(
329 mozilla::Span
<const char> importName
, BuiltinModuleId module
) {
330 #ifdef ENABLE_WASM_JS_STRING_BUILTINS
331 // Not supported for implicit instantiation yet
332 MOZ_RELEASE_ASSERT(module
== BuiltinModuleId::JSString
);
333 for (BuiltinModuleFuncId funcId
: JSStringFuncs
) {
334 const BuiltinModuleFunc
& func
= BuiltinModuleFuncs::getFromId(funcId
);
335 if (importName
== mozilla::MakeStringSpan(func
.exportName())) {
339 #endif // ENABLE_WASM_JS_STRING_BUILTINS
343 bool wasm::CompileBuiltinModule(JSContext
* cx
, BuiltinModuleId module
,
344 MutableHandle
<WasmModuleObject
*> result
) {
346 case BuiltinModuleId::SelfTest
:
347 return CompileBuiltinModule(cx
, SelfTestFuncs
, Some(Shareable::False
),
349 #ifdef ENABLE_WASM_MOZ_INTGEMM
350 case BuiltinModuleId::IntGemm
:
351 return CompileBuiltinModule(cx
, IntGemmFuncs
, Some(Shareable::False
),
353 #endif // ENABLE_WASM_MOZ_INTGEMM
354 #ifdef ENABLE_WASM_JS_STRING_BUILTINS
355 case BuiltinModuleId::JSString
:
356 return CompileBuiltinModule(cx
, JSStringFuncs
, Nothing(), result
);
357 #endif // ENABLE_WASM_JS_STRING_BUILTINS
363 bool wasm::InstantiateBuiltinModule(JSContext
* cx
, BuiltinModuleId module
,
364 MutableHandleObject result
) {
365 Rooted
<WasmModuleObject
*> moduleObj(cx
);
366 if (!CompileBuiltinModule(cx
, module
, &moduleObj
)) {
367 ReportOutOfMemory(cx
);
370 ImportValues imports
;
371 Rooted
<WasmInstanceObject
*> instanceObj(cx
);
372 RootedObject
instanceProto(cx
);
373 if (!moduleObj
->module().instantiate(cx
, imports
, instanceProto
,
375 MOZ_RELEASE_ASSERT(cx
->isThrowingOutOfMemory());
378 result
.set(&instanceObj
->exportsObj());