WasmMetadata.cpp (11466B)
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: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "wasm/WasmMetadata.h" 8 9 #include "mozilla/BinarySearch.h" 10 #include "mozilla/CheckedInt.h" 11 12 #include "jsnum.h" // Int32ToCStringBuf 13 14 #include "vm/Logging.h" 15 16 using mozilla::CheckedInt; 17 18 using namespace js; 19 using namespace js::wasm; 20 21 // CodeMetadata helpers -- computing the Instance layout. 22 23 bool CodeMetadata::allocateInstanceDataBytes(uint32_t bytes, uint32_t align, 24 uint32_t* assignedOffset) { 25 // Assert that this offset hasn't already been computed. 26 MOZ_ASSERT(*assignedOffset == UINT32_MAX); 27 28 CheckedInt<uint32_t> newInstanceDataLength(instanceDataLength); 29 30 // Adjust the current global data length so that it's aligned to `align` 31 newInstanceDataLength += 32 ComputeByteAlignment(newInstanceDataLength.value(), align); 33 if (!newInstanceDataLength.isValid()) { 34 return false; 35 } 36 37 // The allocated data is given by the aligned length 38 *assignedOffset = newInstanceDataLength.value(); 39 40 // Advance the length for `bytes` being allocated 41 newInstanceDataLength += bytes; 42 if (!newInstanceDataLength.isValid()) { 43 return false; 44 } 45 46 // This is the highest offset into Instance::globalArea that will not 47 // overflow a signed 32-bit integer. 48 const uint32_t maxInstanceDataOffset = 49 uint32_t(INT32_MAX) - uint32_t(Instance::offsetOfData()); 50 51 // Check that the highest offset into this allocated space would not overflow 52 // a signed 32-bit integer. 53 if (newInstanceDataLength.value() > maxInstanceDataOffset + 1) { 54 return false; 55 } 56 57 instanceDataLength = newInstanceDataLength.value(); 58 return true; 59 } 60 61 bool CodeMetadata::allocateInstanceDataBytesN(uint32_t bytes, uint32_t align, 62 uint32_t count, 63 uint32_t* assignedOffset) { 64 // The size of each allocation should be a multiple of alignment so that a 65 // contiguous array of allocations will be aligned 66 MOZ_ASSERT(bytes % align == 0); 67 68 // Compute the total bytes being allocated 69 CheckedInt<uint32_t> totalBytes = bytes; 70 totalBytes *= count; 71 if (!totalBytes.isValid()) { 72 return false; 73 } 74 75 // Allocate the bytes 76 return allocateInstanceDataBytes(totalBytes.value(), align, assignedOffset); 77 } 78 79 bool CodeMetadata::prepareForCompile(CompileMode mode) { 80 MOZ_ASSERT(!isPreparedForCompile()); 81 82 // Find every function that is exported from this module and give it an 83 // implicit index 84 uint32_t exportedFuncCount = 0; 85 for (uint32_t funcIndex = 0; funcIndex < funcs.length(); funcIndex++) { 86 const FuncDesc& func = funcs[funcIndex]; 87 if (func.isExported()) { 88 exportedFuncCount++; 89 } 90 } 91 92 if (!exportedFuncIndices.reserve(exportedFuncCount)) { 93 return false; 94 } 95 for (uint32_t funcIndex = 0; funcIndex < funcs.length(); funcIndex++) { 96 const FuncDesc& func = funcs[funcIndex]; 97 if (!func.isExported()) { 98 continue; 99 } 100 exportedFuncIndices.infallibleEmplaceBack(funcIndex); 101 } 102 103 // Allocate the layout for instance data 104 instanceDataLength = 0; 105 106 // Allocate space for function counters, if we have them 107 if (mode == CompileMode::LazyTiering) { 108 if (!allocateInstanceDataBytesN(sizeof(FuncDefInstanceData), 109 alignof(FuncDefInstanceData), numFuncDefs(), 110 &funcDefsOffsetStart)) { 111 return false; 112 } 113 } 114 115 // Allocate space for type definitions 116 if (!allocateInstanceDataBytesN(sizeof(TypeDefInstanceData), 117 alignof(TypeDefInstanceData), types->length(), 118 &typeDefsOffsetStart)) { 119 return false; 120 } 121 122 // Allocate space for every function import 123 if (!allocateInstanceDataBytesN(sizeof(FuncImportInstanceData), 124 alignof(FuncImportInstanceData), 125 numFuncImports, &funcImportsOffsetStart)) { 126 return false; 127 } 128 129 // Allocate space for every function export 130 if (!allocateInstanceDataBytesN( 131 sizeof(FuncExportInstanceData), alignof(FuncExportInstanceData), 132 numExportedFuncs(), &funcExportsOffsetStart)) { 133 return false; 134 } 135 136 // Allocate space for every memory 137 if (!allocateInstanceDataBytesN(sizeof(MemoryInstanceData), 138 alignof(MemoryInstanceData), 139 memories.length(), &memoriesOffsetStart)) { 140 return false; 141 } 142 143 // Allocate space for every table 144 if (!allocateInstanceDataBytesN(sizeof(TableInstanceData), 145 alignof(TableInstanceData), tables.length(), 146 &tablesOffsetStart)) { 147 return false; 148 } 149 150 // Allocate space for every tag 151 if (!allocateInstanceDataBytesN(sizeof(TagInstanceData), 152 alignof(TagInstanceData), tags.length(), 153 &tagsOffsetStart)) { 154 return false; 155 } 156 157 // Allocate space for every global that requires it 158 for (GlobalDesc& global : globals) { 159 if (global.isConstant()) { 160 continue; 161 } 162 163 uint32_t width = global.isIndirect() ? sizeof(void*) : global.type().size(); 164 165 uint32_t assignedOffset = UINT32_MAX; 166 if (!allocateInstanceDataBytes(width, width, &assignedOffset)) { 167 return false; 168 } 169 170 global.setOffset(assignedOffset); 171 } 172 173 return true; 174 } 175 176 uint32_t CodeMetadata::findFuncExportIndex(uint32_t funcIndex) const { 177 MOZ_ASSERT(funcs[funcIndex].isExported()); 178 179 size_t match; 180 if (!mozilla::BinarySearch(exportedFuncIndices, 0, 181 exportedFuncIndices.length(), funcIndex, &match)) { 182 MOZ_CRASH("missing function export"); 183 } 184 return (uint32_t)match; 185 } 186 187 uint32_t CodeTailMetadata::findFuncIndex(uint32_t bytecodeOffset) const { 188 size_t funcDefIndex; 189 if (!mozilla::BinarySearchIf( 190 funcDefRanges, 0, funcDefRanges.length(), 191 [bytecodeOffset](const BytecodeRange& range) { 192 return range.compareOffset(bytecodeOffset); 193 }, 194 &funcDefIndex)) { 195 MOZ_CRASH("missing function definition"); 196 } 197 return codeMeta->numFuncImports + funcDefIndex; 198 } 199 200 // CodeMetadata helpers -- getting function names. 201 202 static bool AppendName(const Bytes& namePayload, const Name& name, 203 UTF8Bytes* bytes) { 204 MOZ_RELEASE_ASSERT(name.offsetInNamePayload <= namePayload.length()); 205 MOZ_RELEASE_ASSERT(name.length <= 206 namePayload.length() - name.offsetInNamePayload); 207 return bytes->append( 208 (const char*)namePayload.begin() + name.offsetInNamePayload, name.length); 209 } 210 211 static bool AppendFunctionIndexName(uint32_t funcIndex, UTF8Bytes* bytes) { 212 const char beforeFuncIndex[] = "wasm-function["; 213 const char afterFuncIndex[] = "]"; 214 215 Int32ToCStringBuf cbuf; 216 size_t funcIndexStrLen; 217 const char* funcIndexStr = 218 Uint32ToCString(&cbuf, funcIndex, &funcIndexStrLen); 219 MOZ_ASSERT(funcIndexStr); 220 221 return bytes->append(beforeFuncIndex, strlen(beforeFuncIndex)) && 222 bytes->append(funcIndexStr, funcIndexStrLen) && 223 bytes->append(afterFuncIndex, strlen(afterFuncIndex)); 224 } 225 226 bool CodeMetadata::getFuncNameForWasm(NameContext ctx, uint32_t funcIndex, 227 const ShareableBytes* nameSectionPayload, 228 UTF8Bytes* name) const { 229 if (nameSection && nameSection->moduleName.length != 0) { 230 if (!AppendName(nameSectionPayload->vector, nameSection->moduleName, 231 name)) { 232 return false; 233 } 234 if (!name->append('.')) { 235 return false; 236 } 237 } 238 239 if (nameSection && funcIndex < nameSection->funcNames.length() && 240 nameSection->funcNames[funcIndex].length != 0) { 241 return AppendName(nameSectionPayload->vector, 242 nameSection->funcNames[funcIndex], name); 243 } 244 245 if (ctx == NameContext::BeforeLocation) { 246 return true; 247 } 248 249 return AppendFunctionIndexName(funcIndex, name); 250 } 251 252 // CodeMetadata helpers -- memory accounting. 253 254 size_t CodeMetadata::sizeOfExcludingThis( 255 mozilla::MallocSizeOf mallocSizeOf) const { 256 return memories.sizeOfExcludingThis(mallocSizeOf) + 257 types->sizeOfExcludingThis(mallocSizeOf) + 258 globals.sizeOfExcludingThis(mallocSizeOf) + 259 tags.sizeOfExcludingThis(mallocSizeOf) + 260 tables.sizeOfExcludingThis(mallocSizeOf) + 261 SizeOfMaybeExcludingThis(nameSection, mallocSizeOf) + 262 funcs.sizeOfExcludingThis(mallocSizeOf) + 263 elemSegmentTypes.sizeOfExcludingThis(mallocSizeOf) + 264 asmJSSigToTableIndex.sizeOfExcludingThis(mallocSizeOf) + 265 customSectionRanges.sizeOfExcludingThis(mallocSizeOf); 266 } 267 268 CodeTailMetadata::CodeTailMetadata() 269 : codeMeta(nullptr), 270 debugEnabled(false), 271 debugHash(), 272 inliningBudget(mutexid::WasmInliningBudget, 0), 273 callRefHints(nullptr), 274 numCallRefMetrics(UINT32_MAX), 275 numAllocSites(UINT32_MAX) {} 276 277 CodeTailMetadata::CodeTailMetadata(const CodeMetadata& codeMeta) 278 : js::wasm::CodeTailMetadata() { 279 this->codeMeta = &codeMeta; 280 inliningBudget.lock().get() = 281 InliningHeuristics::moduleInliningBudget(codeMeta.codeSectionSize()); 282 } 283 284 // ModuleMetadata helpers -- memory accounting. 285 286 size_t ModuleMetadata::sizeOfExcludingThis( 287 mozilla::MallocSizeOf mallocSizeOf) const { 288 return imports.sizeOfExcludingThis(mallocSizeOf) + 289 exports.sizeOfExcludingThis(mallocSizeOf) + 290 elemSegments.sizeOfExcludingThis(mallocSizeOf) + 291 dataSegmentRanges.sizeOfExcludingThis(mallocSizeOf) + 292 dataSegments.sizeOfExcludingThis(mallocSizeOf) + 293 customSections.sizeOfExcludingThis(mallocSizeOf); 294 } 295 296 bool ModuleMetadata::addDefinedFunc( 297 ValTypeVector&& params, ValTypeVector&& results, bool declareForRef, 298 mozilla::Maybe<CacheableName>&& optionalExportedName) { 299 uint32_t typeIndex = codeMeta->types->length(); 300 FuncType funcType(std::move(params), std::move(results)); 301 if (!codeMeta->types->addType(std::move(funcType))) { 302 return false; 303 } 304 305 FuncDesc funcDesc = FuncDesc(typeIndex); 306 uint32_t funcIndex = codeMeta->funcs.length(); 307 if (!codeMeta->funcs.append(funcDesc)) { 308 return false; 309 } 310 if (declareForRef) { 311 codeMeta->funcs[funcIndex].declareFuncExported(true, true); 312 } 313 if (optionalExportedName.isSome()) { 314 if (!exports.emplaceBack(std::move(optionalExportedName.ref()), funcIndex, 315 DefinitionKind::Function)) { 316 return false; 317 } 318 } 319 return true; 320 } 321 322 bool ModuleMetadata::addImportedFunc(ValTypeVector&& params, 323 ValTypeVector&& results, 324 CacheableName&& importModName, 325 CacheableName&& importFieldName) { 326 MOZ_ASSERT(codeMeta->numFuncImports == codeMeta->funcs.length()); 327 if (!addDefinedFunc(std::move(params), std::move(results), false, 328 mozilla::Nothing())) { 329 return false; 330 } 331 codeMeta->numFuncImports++; 332 return imports.emplaceBack(std::move(importModName), 333 std::move(importFieldName), 334 DefinitionKind::Function); 335 }