Changeset 215533 in webkit
- Timestamp:
- Apr 19, 2017 3:05:51 PM (7 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r215531 r215533 1 2017-04-19 JF Bastien <jfbastien@apple.com> 2 3 WebAssembly: fast memory cleanups 4 https://bugs.webkit.org/show_bug.cgi?id=170909 5 6 Reviewed by Saam Barati. 7 8 * b3/B3LowerToAir.cpp: correct comment, and make wasm-independent 9 (JSC::B3::Air::LowerToAir::lower): 10 * b3/B3Procedure.h: 11 * b3/B3Validate.cpp: 12 * b3/B3Value.cpp: 13 (JSC::B3::Value::effects): 14 * b3/B3WasmBoundsCheckValue.cpp: have the creator pass in a 15 maximum, so we don't have to know so much about wasm here 16 (JSC::B3::WasmBoundsCheckValue::WasmBoundsCheckValue): 17 (JSC::B3::WasmBoundsCheckValue::cloneImpl): 18 (JSC::B3::WasmBoundsCheckValue::dumpMeta): 19 * b3/B3WasmBoundsCheckValue.h: 20 (JSC::B3::WasmBoundsCheckValue::boundsType): 21 (JSC::B3::WasmBoundsCheckValue::bounds): 22 * b3/air/AirCode.h: 23 * b3/air/AirCustom.h: 24 (JSC::B3::Air::WasmBoundsCheckCustom::generate): 25 * b3/testb3.cpp: 26 (JSC::B3::testWasmBoundsCheck): 27 * wasm/WasmB3IRGenerator.cpp: 28 (JSC::Wasm::B3IRGenerator::B3IRGenerator): 29 (JSC::Wasm::B3IRGenerator::emitCheckAndPreparePointer): 30 (JSC::Wasm::createJSToWasmWrapper): remove dead code 31 * wasm/WasmMemory.cpp: don't GC if no memory could possibly be free'd 32 (JSC::Wasm::Memory::initializePreallocations): verbose-only code, 33 and copy-pasta bug 34 1 35 2017-04-19 Mark Lam <mark.lam@apple.com> 2 36 -
trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp
r215407 r215533 3147 3147 3148 3148 case B3::WasmBoundsCheck: { 3149 #if ENABLE(WEBASSEMBLY)3150 3149 WasmBoundsCheckValue* value = m_value->as<WasmBoundsCheckValue>(); 3151 3150 … … 3165 3164 3166 3165 Arg limit; 3167 if (value->pinnedGPR() != InvalidGPRReg) 3168 limit = Arg(value->pinnedGPR()); 3169 else { 3170 // Signaling memories don't pin a register because only the accesses whose reg+imm could ever overflow 4GiB+redzone need to be checked, 3171 // and we don't think these will be frequent. All other accesses will trap due to PROT_NONE pages. 3172 // 3173 // If we got here it's because a memory access had a very large offset. We could check that it doesn't exceed 4GiB+redzone since that's 3174 // technically the limit we need to avoid overflowing, but it's better if we use a smaller immediate which codegens more easily. 3175 // We know that anything above the declared 'maximum' will trap, so we can compare against that number. If there was no declared 3176 // 'maximum' then we still know that any access above 4GiB will trap, no need to add the redzone. 3166 switch (value->boundsType()) { 3167 case WasmBoundsCheckValue::Type::Pinned: 3168 limit = Arg(value->bounds().pinned); 3169 break; 3170 3171 case WasmBoundsCheckValue::Type::Maximum: 3177 3172 limit = m_code.newTmp(GP); 3178 size_t limitValue = value->maximum() ? value->maximum().bytes() : std::numeric_limits<uint32_t>::max(); 3179 ASSERT(limitValue <= value->redzoneLimit()); 3180 if (imm(limitValue)) 3181 append(Move, imm(limitValue), limit); 3173 if (imm(value->bounds().maximum)) 3174 append(Move, imm(value->bounds().maximum), limit); 3182 3175 else 3183 append(Move, Arg::bigImm(limitValue), limit); 3184 } 3176 append(Move, Arg::bigImm(value->bounds().maximum), limit); 3177 break; 3178 } 3179 3185 3180 append(Inst(Air::WasmBoundsCheck, value, ptrPlusImm, limit)); 3186 #else3187 append(Air::Oops);3188 #endif // ENABLE(WEBASSEMBLY)3189 3181 return; 3190 3182 } -
trunk/Source/JavaScriptCore/b3/B3Procedure.h
r215292 r215533 59 59 namespace Air { class Code; } 60 60 61 typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg , unsigned);61 typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg); 62 62 typedef SharedTask<WasmBoundsCheckGeneratorFunction> WasmBoundsCheckGenerator; 63 63 -
trunk/Source/JavaScriptCore/b3/B3Validate.cpp
r215340 r215533 471 471 VALIDATE(value->numChildren() == 1, ("At ", *value)); 472 472 VALIDATE(value->child(0)->type() == Int32, ("At ", *value)); 473 if (value->as<WasmBoundsCheckValue>()->pinnedGPR() != InvalidGPRReg) 474 VALIDATE(m_procedure.code().isPinned(value->as<WasmBoundsCheckValue>()->pinnedGPR()), ("At ", *value)); 473 switch (value->as<WasmBoundsCheckValue>()->boundsType()) { 474 case WasmBoundsCheckValue::Type::Pinned: 475 VALIDATE(m_procedure.code().isPinned(value->as<WasmBoundsCheckValue>()->bounds().pinned), ("At ", *value)); 476 break; 477 case WasmBoundsCheckValue::Type::Maximum: 478 break; 479 } 475 480 VALIDATE(m_procedure.code().wasmBoundsCheckGenerator(), ("At ", *value)); 476 481 break; -
trunk/Source/JavaScriptCore/b3/B3Value.cpp
r215340 r215533 666 666 break; 667 667 case WasmBoundsCheck: 668 if (as<WasmBoundsCheckValue>()->pinnedGPR() != InvalidGPRReg) 668 switch (as<WasmBoundsCheckValue>()->boundsType()) { 669 case WasmBoundsCheckValue::Type::Pinned: 669 670 result.readsPinned = true; 671 break; 672 case WasmBoundsCheckValue::Type::Maximum: 673 break; 674 } 670 675 result.exitsSideways = true; 671 676 break; -
trunk/Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.cpp
r215340 r215533 36 36 } 37 37 38 WasmBoundsCheckValue::WasmBoundsCheckValue(Origin origin, Value* ptr, GPRReg pinnedGPR, unsigned offset, PageCount maximum)38 WasmBoundsCheckValue::WasmBoundsCheckValue(Origin origin, Value* ptr, unsigned offset, GPRReg pinnedGPR) 39 39 : Value(CheckedOpcode, WasmBoundsCheck, origin, ptr) 40 , m_pinnedGPR(pinnedGPR)41 40 , m_offset(offset) 42 , m_ maximum(maximum)41 , m_boundsType(Type::Pinned) 43 42 { 43 m_bounds.pinned = pinnedGPR; 44 } 45 46 WasmBoundsCheckValue::WasmBoundsCheckValue(Origin origin, Value* ptr, unsigned offset, size_t maximum) 47 : Value(CheckedOpcode, WasmBoundsCheck, origin, ptr) 48 , m_offset(offset) 49 , m_boundsType(Type::Maximum) 50 { 51 #if ENABLE(WEBASSEMBLY) 52 size_t redzoneLimit = static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + Wasm::Memory::fastMappedRedzoneBytes(); 53 ASSERT_UNUSED(redzoneLimit, maximum <= redzoneLimit); 54 #endif 55 m_bounds.maximum = maximum; 44 56 } 45 57 … … 49 61 } 50 62 51 size_t WasmBoundsCheckValue::redzoneLimit() const52 {53 ASSERT(m_pinnedGPR == InvalidGPRReg);54 #if ENABLE(WEBASSEMBLY)55 return static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + Wasm::Memory::fastMappedRedzoneBytes();56 #else57 RELEASE_ASSERT_NOT_REACHED();58 #endif59 }60 61 63 void WasmBoundsCheckValue::dumpMeta(CommaPrinter& comma, PrintStream& out) const 62 64 { 63 if (m_pinnedGPR == InvalidGPRReg) 64 out.print(comma, "redzoneLimit = ", redzoneLimit(), ", offset = ", m_offset); 65 else 66 out.print(comma, "sizeRegister = ", m_pinnedGPR, ", offset = ", m_offset); 65 switch (m_boundsType) { 66 case Type::Pinned: 67 out.print(comma, "offset = ", m_offset, ", pinned = ", m_bounds.pinned); 68 break; 69 case Type::Maximum: 70 out.print(comma, "offset = ", m_offset, ", maximum = ", m_bounds.maximum); 71 break; 72 } 67 73 } 68 74 -
trunk/Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.h
r215340 r215533 30 30 #include "B3Value.h" 31 31 #include "CCallHelpers.h" 32 #include "WasmPageCount.h"33 32 34 33 namespace JSC { namespace B3 { … … 48 47 ~WasmBoundsCheckValue(); 49 48 50 #if ENABLE(WEBASSEMBLY) 51 typedef Wasm::PageCount PageCount; 52 #else 53 typedef char PageCount; 54 #endif 49 enum class Type { 50 Pinned, 51 Maximum, 52 }; 55 53 56 GPRReg pinnedGPR() const { return m_pinnedGPR; } 54 union Bounds { 55 GPRReg pinned; 56 size_t maximum; 57 }; 58 57 59 unsigned offset() const { return m_offset; } 58 size_t redzoneLimit() const;59 PageCount maximum() const { return m_maximum; }60 Type boundsType() const { return m_boundsType; } 61 Bounds bounds() const { return m_bounds; } 60 62 61 63 protected: … … 67 69 friend class Procedure; 68 70 69 JS_EXPORT_PRIVATE WasmBoundsCheckValue(Origin, Value* ptr, GPRReg pinnedGPR, unsigned offset, PageCount maximum); 71 JS_EXPORT_PRIVATE WasmBoundsCheckValue(Origin, Value* ptr, unsigned offset, GPRReg pinnedGPR); 72 JS_EXPORT_PRIVATE WasmBoundsCheckValue(Origin, Value* ptr, unsigned offset, size_t maximum); 70 73 71 GPRReg m_pinnedGPR;72 74 unsigned m_offset; 73 PageCount m_maximum; 75 Type m_boundsType; 76 Bounds m_bounds; 74 77 }; 75 78 -
trunk/Source/JavaScriptCore/b3/air/AirCode.h
r215292 r215533 56 56 class Disassembler; 57 57 58 typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg , unsigned);58 typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg); 59 59 typedef SharedTask<WasmBoundsCheckGeneratorFunction> WasmBoundsCheckGenerator; 60 60 -
trunk/Source/JavaScriptCore/b3/air/AirCustom.h
r214827 r215533 315 315 [outOfBounds, value] (CCallHelpers& jit, Air::GenerationContext& context) { 316 316 outOfBounds.link(&jit); 317 context.code->wasmBoundsCheckGenerator()->run(jit, value->pinnedGPR(), value->offset()); 317 switch (value->boundsType()) { 318 case WasmBoundsCheckValue::Type::Pinned: 319 context.code->wasmBoundsCheckGenerator()->run(jit, value->bounds().pinned); 320 break; 321 322 case WasmBoundsCheckValue::Type::Maximum: 323 context.code->wasmBoundsCheckGenerator()->run(jit, InvalidGPRReg); 324 break; 325 } 318 326 })); 319 327 -
trunk/Source/JavaScriptCore/b3/testb3.cpp
r215407 r215533 15171 15171 proc.pinRegister(pinned); 15172 15172 15173 proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR , unsigned actualOffset) {15173 proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR) { 15174 15174 CHECK_EQ(pinnedGPR, pinned); 15175 CHECK_EQ(actualOffset, offset);15176 15175 15177 15176 // This should always work because a function this simple should never have callee … … 15186 15185 if (pointerType() != Int32) 15187 15186 left = root->appendNew<Value>(proc, Trunc, Origin(), left); 15188 Wasm::PageCount maximum; 15189 root->appendNew<WasmBoundsCheckValue>(proc, Origin(), left, pinned, offset, maximum); 15187 root->appendNew<WasmBoundsCheckValue>(proc, Origin(), left, pinned, offset); 15190 15188 Value* result = root->appendNew<Const32Value>(proc, Origin(), 0x42); 15191 15189 root->appendNewControlValue(proc, Return, Origin(), result); -
trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
r215407 r215533 60 60 #include "WasmOpcodeOrigin.h" 61 61 #include "WasmThunks.h" 62 #include <limits> 62 63 #include <wtf/Optional.h> 63 64 #include <wtf/StdLibExtras.h> … … 353 354 354 355 if (info.memory) { 355 m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR , unsigned) {356 m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR) { 356 357 AllowMacroScratchRegisterUsage allowScratch(jit); 357 358 switch (m_mode) { … … 550 551 { 551 552 ASSERT(m_memoryBaseGPR); 553 552 554 switch (m_mode) { 553 555 case MemoryMode::BoundsChecking: … … 555 557 ASSERT(m_memorySizeGPR); 556 558 ASSERT(sizeOfOperation + offset > offset); 557 m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, m_memorySizeGPR, sizeOfOperation + offset - 1, m_info.memory.maximum());559 m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, sizeOfOperation + offset - 1, m_memorySizeGPR); 558 560 break; 561 559 562 case MemoryMode::Signaling: 560 // We've virtually mapped 4GiB+redzone for this memory. Only the user-allocated pages are addressable, contiguously in range [0, current], and everything above is mapped PROT_NONE. We don't need to perform any explicit bounds check in the 4GiB range because WebAssembly register memory accesses are 32-bit. However WebAssembly register+immediate accesses perform the addition in 64-bit which can push an access above the 32-bit limit. The redzone will catch most small immediates, and we'll explicitly bounds check any register + large immediate access. 561 if (offset >= Memory::fastMappedRedzoneBytes()) 562 m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, InvalidGPRReg, sizeOfOperation + offset - 1, m_info.memory.maximum()); 563 // We've virtually mapped 4GiB+redzone for this memory. Only the user-allocated pages are addressable, contiguously in range [0, current], 564 // and everything above is mapped PROT_NONE. We don't need to perform any explicit bounds check in the 4GiB range because WebAssembly register 565 // memory accesses are 32-bit. However WebAssembly register + offset accesses perform the addition in 64-bit which can push an access above 566 // the 32-bit limit (the offset is unsigned 32-bit). The redzone will catch most small offsets, and we'll explicitly bounds check any 567 // register + large offset access. We don't think this will be generated frequently. 568 // 569 // We could check that register + large offset doesn't exceed 4GiB+redzone since that's technically the limit we need to avoid overflowing the 570 // PROT_NONE region, but it's better if we use a smaller immediate because it can codegens better. We know that anything equal to or greater 571 // than the declared 'maximum' will trap, so we can compare against that number. If there was no declared 'maximum' then we still know that 572 // any access equal to or greater than 4GiB will trap, no need to add the redzone. 573 if (offset >= Memory::fastMappedRedzoneBytes()) { 574 size_t maximum = m_info.memory.maximum() ? m_info.memory.maximum().bytes() : std::numeric_limits<uint32_t>::max(); 575 m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, sizeOfOperation + offset - 1, maximum); 576 } 563 577 break; 578 564 579 case MemoryMode::NumberOfMemoryModes: 565 580 RELEASE_ASSERT_NOT_REACHED(); … … 1301 1316 compilationContext.jsEntrypointToWasmEntrypointCall = jit.call(); 1302 1317 1303 if (!!info.memory) {1304 // Resetting the register prevents the GC from mistakenly thinking that the context is still live.1305 GPRReg baseMemory = pinnedRegs.baseMemoryPointer;1306 jit.move(CCallHelpers::TrustedImm32(0), baseMemory);1307 }1308 1309 1318 for (const RegisterAtOffset& regAtOffset : registersToSpill) { 1310 1319 GPRReg reg = regAtOffset.reg().gpr(); -
trunk/Source/JavaScriptCore/wasm/WasmMemory.cpp
r215525 r215533 49 49 50 50 NEVER_INLINE NO_RETURN_DUE_TO_CRASH void webAssemblyCouldntGetFastMemory() { CRASH(); } 51 NEVER_INLINE NO_RETURN_DUE_TO_CRASH void webAssemblyCouldntUnmapMemory Bytes() { CRASH(); }51 NEVER_INLINE NO_RETURN_DUE_TO_CRASH void webAssemblyCouldntUnmapMemory() { CRASH(); } 52 52 NEVER_INLINE NO_RETURN_DUE_TO_CRASH void webAssemblyCouldntUnprotectMemory() { CRASH(); } 53 53 … … 67 67 { 68 68 if (UNLIKELY(munmap(memory, size))) 69 webAssemblyCouldntUnmapMemory Bytes();69 webAssemblyCouldntUnmapMemory(); 70 70 } 71 71 … … 150 150 if (memory) 151 151 dataLogLnIf(verbose, "tryGetFastMemory re-using ", RawPointer(memory)); 152 else {153 // No memory was available in the cache . Maybe waiting onGC will find a free one.152 else if (currentlyAllocatedFastMemories.load(std::memory_order_acquire) >= 1) { 153 // No memory was available in the cache, but we know there's at least one currently live. Maybe GC will find a free one. 154 154 // FIXME collectSync(Full) and custom eager destruction of wasm memories could be better. For now use collectAllGarbage. Also, nothing tells us the current VM is holding onto fast memories. https://bugs.webkit.org/show_bug.cgi?id=170748 155 155 dataLogLnIf(verbose, "tryGetFastMemory waiting on GC and retrying"); … … 299 299 // Races cannot occur in this function: it is only called at program initialization, before WebAssembly can be invoked. 300 300 301 const auto startTime = MonotonicTime::now(); 301 MonotonicTime startTime; 302 if (verbose) 303 startTime = MonotonicTime::now(); 304 302 305 const size_t desiredFastMemories = std::min<size_t>(Options::webAssemblyFastMemoryPreallocateCount(), fastMemoryCacheHardLimit); 303 306 … … 306 309 if (void *memory = mmapBytes(Memory::fastMappedBytes() * numContiguous)) { 307 310 for (size_t subMemory = 0; subMemory < numContiguous; ++subMemory) { 308 void* startAddress = reinterpret_cast<char*>(memory) + Memory::fastMappedBytes() * subMemory + subMemory;311 void* startAddress = reinterpret_cast<char*>(memory) + Memory::fastMappedBytes() * subMemory; 309 312 bool inserted = false; 310 313 for (size_t cacheEntry = 0; cacheEntry < fastMemoryCacheHardLimit; ++cacheEntry) { … … 338 341 observedMaximumFastMemory.store(fastMemoryPreallocateCount, std::memory_order_relaxed); 339 342 340 const auto endTime = MonotonicTime::now(); 341 342 for (size_t cacheEntry = 0; cacheEntry < fastMemoryPreallocateCount; ++cacheEntry) { 343 void* startAddress = fastMemoryCache[cacheEntry].load(std::memory_order_relaxed); 344 ASSERT(startAddress); 345 dataLogLnIf(verbose, "Pre-allocation of WebAssembly fast memory at ", RawPointer(startAddress)); 346 } 347 dataLogLnIf(verbose, "Pre-allocated ", fastMemoryPreallocateCount, " WebAssembly fast memories in ", fastMemoryPreallocateCount == 0 ? 0 : fragments, fragments == 1 ? " fragment, took " : " fragments, took ", endTime - startTime); 343 if (verbose) { 344 MonotonicTime endTime = MonotonicTime::now(); 345 346 for (size_t cacheEntry = 0; cacheEntry < fastMemoryPreallocateCount; ++cacheEntry) { 347 void* startAddress = fastMemoryCache[cacheEntry].load(std::memory_order_relaxed); 348 ASSERT(startAddress); 349 dataLogLn("Pre-allocation of WebAssembly fast memory at ", RawPointer(startAddress)); 350 } 351 352 dataLogLn("Pre-allocated ", fastMemoryPreallocateCount, " WebAssembly fast memories in ", fastMemoryPreallocateCount == 0 ? 0 : fragments, fragments == 1 ? " fragment, took " : " fragments, took ", endTime - startTime); 353 } 348 354 } 349 355
Note: See TracChangeset
for help on using the changeset viewer.