WebKit Bugzilla
Attachment 357110 Details for
Bug 191802
: [WebAssembly] Change BBQ to generate Air IR
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
WIP
bug-191802-20181212164841.patch (text/plain), 79.57 KB, created by
Yusuke Suzuki
on 2018-12-11 23:48:42 PST
(
hide
)
Description:
WIP
Filename:
MIME Type:
Creator:
Yusuke Suzuki
Created:
2018-12-11 23:48:42 PST
Size:
79.57 KB
patch
obsolete
>Subversion Revision: 239098 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 8e34e8ef639f091d61ffce89f531ed213c57b52b..58c44b373ad9ac71a18c811c38df7ab0bc01aac4 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,85 @@ >+2018-12-11 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [WebAssembly] Add template JIT >+ https://bugs.webkit.org/show_bug.cgi?id=191802 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ * Sources.txt: >+ * dfg/DFGJITCompiler.cpp: >+ * dfg/DFGSpeculativeJIT.h: >+ * jit/RegisterBank.h: Renamed from Source/JavaScriptCore/dfg/DFGRegisterBank.h. >+ * wasm/WasmB3IRGenerator.cpp: >+ (JSC::Wasm::B3IRGenerator::teeLocal): >+ (JSC::Wasm::B3IRGenerator::addBranch): >+ (JSC::Wasm::B3IRGenerator::addCall): >+ (JSC::Wasm::B3IRGenerator::addCallIndirect): >+ * wasm/WasmFunctionParser.h: >+ (JSC::Wasm::FunctionParser<Context>::binaryCase): >+ (JSC::Wasm::FunctionParser<Context>::unaryCase): >+ (JSC::Wasm::FunctionParser<Context>::parseExpression): >+ * wasm/WasmRailgunJIT.cpp: Added. >+ (JSC::Wasm::StackValue::data const): >+ (JSC::Wasm::StackValue::type const): >+ (JSC::Wasm::StackValue::location const): >+ (JSC::Wasm::StackValue::StackValue): >+ (JSC::Wasm::StackValue::operator=): >+ (JSC::Wasm::StackValue::swap): >+ (JSC::Wasm::ControlData::ControlData): >+ (JSC::Wasm::ControlData::dump const): >+ (JSC::Wasm::ControlData::type const): >+ (JSC::Wasm::ControlData::hasNonVoidSignature const): >+ (JSC::Wasm::ControlData::targetBlockForBranch): >+ (JSC::Wasm::ControlData::convertIfToBlock): >+ (JSC::Wasm::ControlData::resultForBranch const): >+ (JSC::Wasm::RailgunJIT::setParser): >+ (JSC::Wasm::RailgunJIT::lock): >+ (JSC::Wasm::RailgunJIT::unlock): >+ (JSC::Wasm::RailgunJIT::allocateGPR): >+ (JSC::Wasm::RailgunJIT::fail const): >+ (JSC::Wasm::GPROperand::GPROperand): >+ (JSC::Wasm::GPROperand::~GPROperand): >+ (JSC::Wasm::GPROperand::gpr): >+ (JSC::Wasm::StackValue::~StackValue): >+ (JSC::Wasm::RailgunJIT::RailgunJIT): >+ (JSC::Wasm::RailgunJIT::addArguments): >+ (JSC::Wasm::RailgunJIT::addLocal): >+ (JSC::Wasm::RailgunJIT::addConstant): >+ (JSC::Wasm::RailgunJIT::getLocal): >+ (JSC::Wasm::RailgunJIT::setLocal): >+ (JSC::Wasm::RailgunJIT::teeLocal): >+ (JSC::Wasm::RailgunJIT::getGlobal): >+ (JSC::Wasm::RailgunJIT::setGlobal): >+ (JSC::Wasm::RailgunJIT::load): >+ (JSC::Wasm::RailgunJIT::store): >+ (JSC::Wasm::RailgunJIT::addGrowMemory): >+ (JSC::Wasm::RailgunJIT::addCurrentMemory): >+ (JSC::Wasm::RailgunJIT::addOp): >+ (JSC::Wasm::RailgunJIT::addSelect): >+ (JSC::Wasm::RailgunJIT::addTopLevel): >+ (JSC::Wasm::RailgunJIT::addBlock): >+ (JSC::Wasm::RailgunJIT::addLoop): >+ (JSC::Wasm::RailgunJIT::addIf): >+ (JSC::Wasm::RailgunJIT::addElse): >+ (JSC::Wasm::RailgunJIT::addElseToUnreachable): >+ (JSC::Wasm::RailgunJIT::addReturn): >+ (JSC::Wasm::RailgunJIT::addBranch): >+ (JSC::Wasm::RailgunJIT::addSwitch): >+ (JSC::Wasm::RailgunJIT::endBlock): >+ (JSC::Wasm::RailgunJIT::addEndToUnreachable): >+ (JSC::Wasm::RailgunJIT::addCall): >+ (JSC::Wasm::RailgunJIT::addCallIndirect): >+ (JSC::Wasm::RailgunJIT::addUnreachable): >+ (JSC::Wasm::RailgunJIT::dump): >+ (JSC::Wasm::parseAndCompile): >+ * wasm/WasmRailgunJIT.h: Added. >+ * wasm/WasmValidate.cpp: >+ (JSC::Wasm::Validate::teeLocal): >+ (JSC::Wasm::Validate::addBranch): >+ (JSC::Wasm::Validate::addCall): >+ (JSC::Wasm::Validate::addCallIndirect): >+ > 2018-12-11 Justin Michaud <justin_michaud@apple.com> > > Implement feature flag for CSS Typed OM >diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >index f3ef1c42380b3cef2f00fac658e92e9d6e799f03..3aa02d3c4829ffd9b200fd4632037af6416e616f 100644 >--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >@@ -1226,7 +1226,7 @@ > 86EC9DC81328DF82002B2AD7 /* DFGGraph.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EC9DB81328DF82002B2AD7 /* DFGGraph.h */; }; > 86EC9DCC1328DF82002B2AD7 /* DFGJITCompiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EC9DBC1328DF82002B2AD7 /* DFGJITCompiler.h */; }; > 86EC9DD01328DF82002B2AD7 /* DFGOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EC9DC01328DF82002B2AD7 /* DFGOperations.h */; }; >- 86EC9DD11328DF82002B2AD7 /* DFGRegisterBank.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EC9DC11328DF82002B2AD7 /* DFGRegisterBank.h */; }; >+ 86EC9DD11328DF82002B2AD7 /* RegisterBank.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EC9DC11328DF82002B2AD7 /* RegisterBank.h */; }; > 86EC9DD31328DF82002B2AD7 /* DFGSpeculativeJIT.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EC9DC31328DF82002B2AD7 /* DFGSpeculativeJIT.h */; }; > 86ECA3EA132DEF1C002B2AD7 /* DFGNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ECA3E9132DEF1C002B2AD7 /* DFGNode.h */; }; > 86ECA3FA132DF25A002B2AD7 /* DFGScoreBoard.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ECA3F9132DF25A002B2AD7 /* DFGScoreBoard.h */; }; >@@ -3899,7 +3899,7 @@ > 86EC9DBC1328DF82002B2AD7 /* DFGJITCompiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGJITCompiler.h; path = dfg/DFGJITCompiler.h; sourceTree = "<group>"; }; > 86EC9DBF1328DF82002B2AD7 /* DFGOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGOperations.cpp; path = dfg/DFGOperations.cpp; sourceTree = "<group>"; }; > 86EC9DC01328DF82002B2AD7 /* DFGOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGOperations.h; path = dfg/DFGOperations.h; sourceTree = "<group>"; }; >- 86EC9DC11328DF82002B2AD7 /* DFGRegisterBank.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGRegisterBank.h; path = dfg/DFGRegisterBank.h; sourceTree = "<group>"; }; >+ 86EC9DC11328DF82002B2AD7 /* RegisterBank.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterBank.h; path = jit/RegisterBank.h; sourceTree = "<group>"; }; > 86EC9DC21328DF82002B2AD7 /* DFGSpeculativeJIT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGSpeculativeJIT.cpp; path = dfg/DFGSpeculativeJIT.cpp; sourceTree = "<group>"; }; > 86EC9DC31328DF82002B2AD7 /* DFGSpeculativeJIT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGSpeculativeJIT.h; path = dfg/DFGSpeculativeJIT.h; sourceTree = "<group>"; }; > 86ECA3E9132DEF1C002B2AD7 /* DFGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNode.h; path = dfg/DFGNode.h; sourceTree = "<group>"; }; >@@ -5713,6 +5713,7 @@ > 6540C79F1B82D9CE000F6B79 /* RegisterAtOffset.h */, > 6540C79C1B82D99D000F6B79 /* RegisterAtOffsetList.cpp */, > 6540C79D1B82D99D000F6B79 /* RegisterAtOffsetList.h */, >+ 86EC9DC11328DF82002B2AD7 /* RegisterBank.h */, > 623A37EB1B87A7BD00754209 /* RegisterMap.h */, > 0FC3141418146D7000033232 /* RegisterSet.cpp */, > 0FC314101814559100033232 /* RegisterSet.h */, >@@ -7488,7 +7489,6 @@ > 0FB1765F196B8F9E0091052A /* DFGPureValue.h */, > 0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */, > 0F3A1BF81A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h */, >- 86EC9DC11328DF82002B2AD7 /* DFGRegisterBank.h */, > 79FC8A071E32E9F000D88F0E /* DFGRegisteredStructure.h */, > 7980C16A1E3A940E00B71615 /* DFGRegisteredStructureSet.cpp */, > 7980C16B1E3A940E00B71615 /* DFGRegisteredStructureSet.h */, >@@ -8809,7 +8809,6 @@ > 0FFC92161B94FB3E0071DD66 /* DFGPropertyTypeKey.h in Headers */, > 0FB17663196B8F9E0091052A /* DFGPureValue.h in Headers */, > 0F3A1BFA1A9ECB7D000DE01A /* DFGPutStackSinkingPhase.h in Headers */, >- 86EC9DD11328DF82002B2AD7 /* DFGRegisterBank.h in Headers */, > 79FC8A081E32E9F000D88F0E /* DFGRegisteredStructure.h in Headers */, > 7980C16D1E3A940E00B71615 /* DFGRegisteredStructureSet.h in Headers */, > 0F2FCCFC18A60070001A27F8 /* DFGSafepoint.h in Headers */, >@@ -9532,6 +9531,7 @@ > BC18C45D0E16F5CD00B34460 /* Register.h in Headers */, > E328C6C91DA432F900D255FD /* RegisterAtOffset.h in Headers */, > E328C6C81DA4306100D255FD /* RegisterAtOffsetList.h in Headers */, >+ 86EC9DD11328DF82002B2AD7 /* RegisterBank.h in Headers */, > 969A072B0ED1CE6900F1F681 /* RegisterID.h in Headers */, > 623A37EC1B87A7C000754209 /* RegisterMap.h in Headers */, > 0FC314121814559100033232 /* RegisterSet.h in Headers */, >diff --git a/Source/JavaScriptCore/Sources.txt b/Source/JavaScriptCore/Sources.txt >index cba4c4ba6004f930d5ca8563310a6243e36449e5..a106508f5975adf9663a70d96301dadbb62b0aed 100644 >--- a/Source/JavaScriptCore/Sources.txt >+++ b/Source/JavaScriptCore/Sources.txt >@@ -993,6 +993,7 @@ wasm/WasmOMGPlan.cpp > wasm/WasmOpcodeOrigin.cpp > wasm/WasmPageCount.cpp > wasm/WasmPlan.cpp >+wasm/WasmRailgunJIT.cpp > wasm/WasmSectionParser.cpp > wasm/WasmSignature.cpp > wasm/WasmStreamingParser.cpp >diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp >index 43376421756a95e00dfa073da87f7c15517b663f..512fea0af1729b21c23a4d1f6e5538b8495244ec 100644 >--- a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp >+++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp >@@ -35,7 +35,6 @@ > #include "DFGJITFinalizer.h" > #include "DFGOSRExit.h" > #include "DFGOperations.h" >-#include "DFGRegisterBank.h" > #include "DFGSlowPathGenerator.h" > #include "DFGSpeculativeJIT.h" > #include "DFGThunks.h" >@@ -43,6 +42,7 @@ > #include "JSCJSValueInlines.h" > #include "LinkBuffer.h" > #include "MaxFrameExtentForSlowPathCall.h" >+#include "RegisterBank.h" > #include "StructureStubInfo.h" > #include "ThunkGenerators.h" > #include "VM.h" >diff --git a/Source/JavaScriptCore/dfg/DFGRegisterBank.h b/Source/JavaScriptCore/dfg/DFGRegisterBank.h >deleted file mode 100644 >index e04f3cadb04b745d2c570e58aefccc983e2c4dd9..0000000000000000000000000000000000000000 >--- a/Source/JavaScriptCore/dfg/DFGRegisterBank.h >+++ /dev/null >@@ -1,375 +0,0 @@ >-/* >- * Copyright (C) 2011 Apple Inc. All rights reserved. >- * >- * Redistribution and use in source and binary forms, with or without >- * modification, are permitted provided that the following conditions >- * are met: >- * 1. Redistributions of source code must retain the above copyright >- * notice, this list of conditions and the following disclaimer. >- * 2. Redistributions in binary form must reproduce the above copyright >- * notice, this list of conditions and the following disclaimer in the >- * documentation and/or other materials provided with the distribution. >- * >- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >- */ >- >-#pragma once >- >-#if ENABLE(DFG_JIT) >- >-#include "DFGCommon.h" >-#include "FPRInfo.h" >-#include "GPRInfo.h" >- >-namespace JSC { namespace DFG { >- >-// === RegisterBank === >-// >-// This class is used to implement the GPR and FPR register banks. >-// All registers have two pieces of state associated with them: >-// a lock count (used to indicate this register is already in use >-// in code generation of the current node, and cannot be spilled or >-// allocated as a temporary), and VirtualRegister 'name', recording >-// which value (if any) a machine register currently holds. >-// Either or both of these pieces of information may be valid for a >-// given register. A register may be: >-// >-// - unlocked, and unnamed: Available for allocation. >-// - locked, but unnamed: Already allocated as a temporary or >-// result for the current node. >-// - unlocked, but named: Contains the result of a prior operation, >-// not yet in use for this node, >-// - locked, but named: Contains the result of a prior operation, >-// already allocated as a operand to the >-// current operation. >-// >-// For every named register we also record a hint value indicating >-// the order in which registers should be selected to be spilled; >-// registers that can be more cheaply spilled and/or filled should >-// be selected first. >-// >-// Locking register is a strong retention mechanism; a locked register >-// will never be reallocated (this is used to ensure the operands to >-// the current node are in registers). Naming, conversely, in a weak >-// retention mechanism - allocating a register may force a named value >-// to be spilled. >-// >-// All named values must be given a hint that is greater than Min and >-// less than Max. >-template<class BankInfo> >-class RegisterBank { >- typedef typename BankInfo::RegisterType RegID; >- static const size_t NUM_REGS = BankInfo::numberOfRegisters; >- >- typedef uint32_t SpillHint; >- static const SpillHint SpillHintInvalid = 0xffffffff; >- >-public: >- RegisterBank() >- { >- } >- >- // Attempt to allocate a register - this function finds an unlocked >- // register, locks it, and returns it. If none can be found, this >- // returns -1 (InvalidGPRReg or InvalidFPRReg). >- RegID tryAllocate() >- { >- VirtualRegister ignored = VirtualRegister(); >- >- for (uint32_t i = 0; i < NUM_REGS; ++i) { >- if (!m_data[i].lockCount && !m_data[i].name.isValid()) >- return allocateInternal(i, ignored); >- } >- >- return (RegID)-1; >- } >- >- // Allocate a register - this function finds an unlocked register, >- // locks it, and returns it. If any named registers exist, one >- // of these should be selected to be allocated. If all unlocked >- // registers are named, then one of the named registers will need >- // to be spilled. In this case the register selected to be spilled >- // will be one of the registers that has the lowest 'spillOrder' >- // cost associated with it. >- // >- // This method select the register to be allocated, and calls the >- // private 'allocateInternal' method to update internal data >- // structures accordingly. >- RegID allocate(VirtualRegister &spillMe) >- { >- uint32_t currentLowest = NUM_REGS; >- SpillHint currentSpillOrder = SpillHintInvalid; >- >- // This loop is broken into two halves, looping from the last allocated >- // register (the register returned last time this method was called) to >- // the maximum register value, then from 0 to the last allocated. >- // This implements a simple round-robin like approach to try to reduce >- // thrash, and minimize time spent scanning locked registers in allocation. >- // If a unlocked and unnamed register is found return it immediately. >- // Otherwise, find the first unlocked register with the lowest spillOrder. >- for (uint32_t i = 0 ; i < NUM_REGS; ++i) { >- // (1) If the current register is locked, it is not a candidate. >- if (m_data[i].lockCount) >- continue; >- // (2) If the current register's spill order is 0, pick this! â unassigned registers have spill order 0. >- SpillHint spillOrder = m_data[i].spillOrder; >- if (spillOrder == SpillHintInvalid) >- return allocateInternal(i, spillMe); >- // If this register is better (has a lower spill order value) than any prior >- // candidate, then record it. >- if (spillOrder < currentSpillOrder) { >- currentSpillOrder = spillOrder; >- currentLowest = i; >- } >- } >- >- // Deadlock check - this could only occur is all registers are locked! >- ASSERT(currentLowest != NUM_REGS && currentSpillOrder != SpillHintInvalid); >- // There were no available registers; currentLowest will need to be spilled. >- return allocateInternal(currentLowest, spillMe); >- } >- >- uint32_t lockedCount() const >- { >- uint32_t count = 0; >- for (uint32_t i = 0 ; i < NUM_REGS; ++i) { >- if (m_data[i].lockCount) >- ++count; >- } >- return count; >- } >- >- // Allocates the given register, even if this will force a spill. >- VirtualRegister allocateSpecific(RegID reg) >- { >- unsigned index = BankInfo::toIndex(reg); >- >- ++m_data[index].lockCount; >- VirtualRegister name = nameAtIndex(index); >- if (name.isValid()) >- releaseAtIndex(index); >- >- return name; >- } >- >- // retain/release - these methods are used to associate/disassociate names >- // with values in registers. retain should only be called on locked registers. >- void retain(RegID reg, VirtualRegister name, SpillHint spillOrder) >- { >- unsigned index = BankInfo::toIndex(reg); >- >- // SpillHint must be valid. >- ASSERT(spillOrder != SpillHintInvalid); >- // 'index' must be a valid, locked register. >- ASSERT(index < NUM_REGS); >- ASSERT(m_data[index].lockCount); >- // 'index' should not currently be named, the new name must be valid. >- ASSERT(!m_data[index].name.isValid()); >- ASSERT(name.isValid()); >- // 'index' should not currently have a spillOrder. >- ASSERT(m_data[index].spillOrder == SpillHintInvalid); >- >- m_data[index].name = name; >- m_data[index].spillOrder = spillOrder; >- } >- void release(RegID reg) >- { >- releaseAtIndex(BankInfo::toIndex(reg)); >- } >- >- // lock/unlock register, ensures that they are not spilled. >- void lock(RegID reg) >- { >- unsigned index = BankInfo::toIndex(reg); >- >- ASSERT(index < NUM_REGS); >- ++m_data[index].lockCount; >- ASSERT(m_data[index].lockCount); >- } >- void unlock(RegID reg) >- { >- unsigned index = BankInfo::toIndex(reg); >- >- ASSERT(index < NUM_REGS); >- ASSERT(m_data[index].lockCount); >- --m_data[index].lockCount; >- } >- bool isLocked(RegID reg) const >- { >- return isLockedAtIndex(BankInfo::toIndex(reg)); >- } >- >- // Get the name (VirtualRegister) associated with the >- // given register (or default VirtualRegister() for none). >- VirtualRegister name(RegID reg) const >- { >- return nameAtIndex(BankInfo::toIndex(reg)); >- } >- >- bool isInUse(RegID reg) const >- { >- return isLocked(reg) || name(reg).isValid(); >- } >- >- void dump() >- { >- // For each register, print the VirtualRegister 'name'. >- for (uint32_t i =0; i < NUM_REGS; ++i) { >- if (m_data[i].name.isValid()) >- dataLogF("[%02d]", m_data[i].name.offset()); >- else >- dataLogF("[--]"); >- } >- dataLogF("\n"); >- } >- >- class iterator { >- friend class RegisterBank<BankInfo>; >- public: >- VirtualRegister name() const >- { >- return m_bank->nameAtIndex(m_index); >- } >- >- bool isLocked() const >- { >- return m_bank->isLockedAtIndex(m_index); >- } >- >- void release() const >- { >- m_bank->releaseAtIndex(m_index); >- } >- >- RegID regID() const >- { >- return BankInfo::toRegister(m_index); >- } >- >-#ifndef NDEBUG >- const char* debugName() const >- { >- return BankInfo::debugName(regID()); >- } >-#endif >- >- iterator& operator++() >- { >- ++m_index; >- return *this; >- } >- >- bool operator!=(const iterator& other) const >- { >- ASSERT(m_bank == other.m_bank); >- return m_index != other.m_index; >- } >- >- unsigned index() const >- { >- return m_index; >- } >- >- private: >- iterator(RegisterBank<BankInfo>* bank, unsigned index) >- : m_bank(bank) >- , m_index(index) >- { >- } >- >- RegisterBank<BankInfo>* m_bank; >- unsigned m_index; >- }; >- >- iterator begin() >- { >- return iterator(this, 0); >- } >- >- iterator end() >- { >- return iterator(this, NUM_REGS); >- } >- >-private: >- bool isLockedAtIndex(unsigned index) const >- { >- ASSERT(index < NUM_REGS); >- return m_data[index].lockCount; >- } >- >- VirtualRegister nameAtIndex(unsigned index) const >- { >- ASSERT(index < NUM_REGS); >- return m_data[index].name; >- } >- >- void releaseAtIndex(unsigned index) >- { >- // 'index' must be a valid register. >- ASSERT(index < NUM_REGS); >- // 'index' should currently be named. >- ASSERT(m_data[index].name.isValid()); >- // 'index' should currently have a valid spill order. >- ASSERT(m_data[index].spillOrder != SpillHintInvalid); >- >- m_data[index].name = VirtualRegister(); >- m_data[index].spillOrder = SpillHintInvalid; >- } >- >- // Used by 'allocate', above, to update inforamtion in the map. >- RegID allocateInternal(uint32_t i, VirtualRegister &spillMe) >- { >- // 'i' must be a valid, unlocked register. >- ASSERT(i < NUM_REGS && !m_data[i].lockCount); >- >- // Return the VirtualRegister of the named value currently stored in >- // the register being returned - or default VirtualRegister() if none. >- spillMe = m_data[i].name; >- >- // Clear any name/spillOrder currently associated with the register, >- m_data[i] = MapEntry(); >- // Mark the register as locked (with a lock count of 1). >- m_data[i].lockCount = 1; >- >- return BankInfo::toRegister(i); >- } >- >- // === MapEntry === >- // >- // This structure provides information for an individual machine register >- // being managed by the RegisterBank. For each register we track a lock >- // count, name and spillOrder hint. >- struct MapEntry { >- MapEntry() >- : name(VirtualRegister()) >- , spillOrder(SpillHintInvalid) >- , lockCount(0) >- { >- } >- >- VirtualRegister name; >- SpillHint spillOrder; >- uint32_t lockCount; >- }; >- >- // Holds the current status of all registers. >- MapEntry m_data[NUM_REGS]; >-}; >- >-typedef RegisterBank<GPRInfo>::iterator gpr_iterator; >-typedef RegisterBank<FPRInfo>::iterator fpr_iterator; >- >-} } // namespace JSC::DFG >- >-#endif >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >index a20e39a83afe8cbb2929271d3ce25afe33e7ea87..27c1d4c011d4278110c36f44b4355d11c860d1d8 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >@@ -34,11 +34,11 @@ > #include "DFGJITCompiler.h" > #include "DFGOSRExit.h" > #include "DFGOSRExitJumpPlaceholder.h" >-#include "DFGRegisterBank.h" > #include "DFGSilentRegisterSavePlan.h" > #include "JITMathIC.h" > #include "JITOperations.h" > #include "PutKind.h" >+#include "RegisterBank.h" > #include "SpillRegistersMode.h" > #include "StructureStubInfo.h" > #include "ValueRecovery.h" >diff --git a/Source/JavaScriptCore/jit/RegisterBank.h b/Source/JavaScriptCore/jit/RegisterBank.h >new file mode 100644 >index 0000000000000000000000000000000000000000..f3743b9a9b69df9f90425b3b8090940d47f2eecc >--- /dev/null >+++ b/Source/JavaScriptCore/jit/RegisterBank.h >@@ -0,0 +1,374 @@ >+/* >+ * Copyright (C) 2011 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#if ENABLE(JIT) >+ >+#include "FPRInfo.h" >+#include "GPRInfo.h" >+ >+namespace JSC { >+ >+// === RegisterBank === >+// >+// This class is used to implement the GPR and FPR register banks. >+// All registers have two pieces of state associated with them: >+// a lock count (used to indicate this register is already in use >+// in code generation of the current node, and cannot be spilled or >+// allocated as a temporary), and VirtualRegister 'name', recording >+// which value (if any) a machine register currently holds. >+// Either or both of these pieces of information may be valid for a >+// given register. A register may be: >+// >+// - unlocked, and unnamed: Available for allocation. >+// - locked, but unnamed: Already allocated as a temporary or >+// result for the current node. >+// - unlocked, but named: Contains the result of a prior operation, >+// not yet in use for this node, >+// - locked, but named: Contains the result of a prior operation, >+// already allocated as a operand to the >+// current operation. >+// >+// For every named register we also record a hint value indicating >+// the order in which registers should be selected to be spilled; >+// registers that can be more cheaply spilled and/or filled should >+// be selected first. >+// >+// Locking register is a strong retention mechanism; a locked register >+// will never be reallocated (this is used to ensure the operands to >+// the current node are in registers). Naming, conversely, in a weak >+// retention mechanism - allocating a register may force a named value >+// to be spilled. >+// >+// All named values must be given a hint that is greater than Min and >+// less than Max. >+template<class BankInfo> >+class RegisterBank { >+ typedef typename BankInfo::RegisterType RegID; >+ static const size_t NUM_REGS = BankInfo::numberOfRegisters; >+ >+ typedef uint32_t SpillHint; >+ static const SpillHint SpillHintInvalid = 0xffffffff; >+ >+public: >+ RegisterBank() >+ { >+ } >+ >+ // Attempt to allocate a register - this function finds an unlocked >+ // register, locks it, and returns it. If none can be found, this >+ // returns -1 (InvalidGPRReg or InvalidFPRReg). >+ RegID tryAllocate() >+ { >+ VirtualRegister ignored = VirtualRegister(); >+ >+ for (uint32_t i = 0; i < NUM_REGS; ++i) { >+ if (!m_data[i].lockCount && !m_data[i].name.isValid()) >+ return allocateInternal(i, ignored); >+ } >+ >+ return (RegID)-1; >+ } >+ >+ // Allocate a register - this function finds an unlocked register, >+ // locks it, and returns it. If any named registers exist, one >+ // of these should be selected to be allocated. If all unlocked >+ // registers are named, then one of the named registers will need >+ // to be spilled. In this case the register selected to be spilled >+ // will be one of the registers that has the lowest 'spillOrder' >+ // cost associated with it. >+ // >+ // This method select the register to be allocated, and calls the >+ // private 'allocateInternal' method to update internal data >+ // structures accordingly. >+ RegID allocate(VirtualRegister &spillMe) >+ { >+ uint32_t currentLowest = NUM_REGS; >+ SpillHint currentSpillOrder = SpillHintInvalid; >+ >+ // This loop is broken into two halves, looping from the last allocated >+ // register (the register returned last time this method was called) to >+ // the maximum register value, then from 0 to the last allocated. >+ // This implements a simple round-robin like approach to try to reduce >+ // thrash, and minimize time spent scanning locked registers in allocation. >+ // If a unlocked and unnamed register is found return it immediately. >+ // Otherwise, find the first unlocked register with the lowest spillOrder. >+ for (uint32_t i = 0 ; i < NUM_REGS; ++i) { >+ // (1) If the current register is locked, it is not a candidate. >+ if (m_data[i].lockCount) >+ continue; >+ // (2) If the current register's spill order is 0, pick this! â unassigned registers have spill order 0. >+ SpillHint spillOrder = m_data[i].spillOrder; >+ if (spillOrder == SpillHintInvalid) >+ return allocateInternal(i, spillMe); >+ // If this register is better (has a lower spill order value) than any prior >+ // candidate, then record it. >+ if (spillOrder < currentSpillOrder) { >+ currentSpillOrder = spillOrder; >+ currentLowest = i; >+ } >+ } >+ >+ // Deadlock check - this could only occur is all registers are locked! >+ ASSERT(currentLowest != NUM_REGS && currentSpillOrder != SpillHintInvalid); >+ // There were no available registers; currentLowest will need to be spilled. >+ return allocateInternal(currentLowest, spillMe); >+ } >+ >+ uint32_t lockedCount() const >+ { >+ uint32_t count = 0; >+ for (uint32_t i = 0 ; i < NUM_REGS; ++i) { >+ if (m_data[i].lockCount) >+ ++count; >+ } >+ return count; >+ } >+ >+ // Allocates the given register, even if this will force a spill. >+ VirtualRegister allocateSpecific(RegID reg) >+ { >+ unsigned index = BankInfo::toIndex(reg); >+ >+ ++m_data[index].lockCount; >+ VirtualRegister name = nameAtIndex(index); >+ if (name.isValid()) >+ releaseAtIndex(index); >+ >+ return name; >+ } >+ >+ // retain/release - these methods are used to associate/disassociate names >+ // with values in registers. retain should only be called on locked registers. >+ void retain(RegID reg, VirtualRegister name, SpillHint spillOrder) >+ { >+ unsigned index = BankInfo::toIndex(reg); >+ >+ // SpillHint must be valid. >+ ASSERT(spillOrder != SpillHintInvalid); >+ // 'index' must be a valid, locked register. >+ ASSERT(index < NUM_REGS); >+ ASSERT(m_data[index].lockCount); >+ // 'index' should not currently be named, the new name must be valid. >+ ASSERT(!m_data[index].name.isValid()); >+ ASSERT(name.isValid()); >+ // 'index' should not currently have a spillOrder. >+ ASSERT(m_data[index].spillOrder == SpillHintInvalid); >+ >+ m_data[index].name = name; >+ m_data[index].spillOrder = spillOrder; >+ } >+ void release(RegID reg) >+ { >+ releaseAtIndex(BankInfo::toIndex(reg)); >+ } >+ >+ // lock/unlock register, ensures that they are not spilled. >+ void lock(RegID reg) >+ { >+ unsigned index = BankInfo::toIndex(reg); >+ >+ ASSERT(index < NUM_REGS); >+ ++m_data[index].lockCount; >+ ASSERT(m_data[index].lockCount); >+ } >+ void unlock(RegID reg) >+ { >+ unsigned index = BankInfo::toIndex(reg); >+ >+ ASSERT(index < NUM_REGS); >+ ASSERT(m_data[index].lockCount); >+ --m_data[index].lockCount; >+ } >+ bool isLocked(RegID reg) const >+ { >+ return isLockedAtIndex(BankInfo::toIndex(reg)); >+ } >+ >+ // Get the name (VirtualRegister) associated with the >+ // given register (or default VirtualRegister() for none). >+ VirtualRegister name(RegID reg) const >+ { >+ return nameAtIndex(BankInfo::toIndex(reg)); >+ } >+ >+ bool isInUse(RegID reg) const >+ { >+ return isLocked(reg) || name(reg).isValid(); >+ } >+ >+ void dump() >+ { >+ // For each register, print the VirtualRegister 'name'. >+ for (uint32_t i =0; i < NUM_REGS; ++i) { >+ if (m_data[i].name.isValid()) >+ dataLogF("[%02d]", m_data[i].name.offset()); >+ else >+ dataLogF("[--]"); >+ } >+ dataLogF("\n"); >+ } >+ >+ class iterator { >+ friend class RegisterBank<BankInfo>; >+ public: >+ VirtualRegister name() const >+ { >+ return m_bank->nameAtIndex(m_index); >+ } >+ >+ bool isLocked() const >+ { >+ return m_bank->isLockedAtIndex(m_index); >+ } >+ >+ void release() const >+ { >+ m_bank->releaseAtIndex(m_index); >+ } >+ >+ RegID regID() const >+ { >+ return BankInfo::toRegister(m_index); >+ } >+ >+#ifndef NDEBUG >+ const char* debugName() const >+ { >+ return BankInfo::debugName(regID()); >+ } >+#endif >+ >+ iterator& operator++() >+ { >+ ++m_index; >+ return *this; >+ } >+ >+ bool operator!=(const iterator& other) const >+ { >+ ASSERT(m_bank == other.m_bank); >+ return m_index != other.m_index; >+ } >+ >+ unsigned index() const >+ { >+ return m_index; >+ } >+ >+ private: >+ iterator(RegisterBank<BankInfo>* bank, unsigned index) >+ : m_bank(bank) >+ , m_index(index) >+ { >+ } >+ >+ RegisterBank<BankInfo>* m_bank; >+ unsigned m_index; >+ }; >+ >+ iterator begin() >+ { >+ return iterator(this, 0); >+ } >+ >+ iterator end() >+ { >+ return iterator(this, NUM_REGS); >+ } >+ >+private: >+ bool isLockedAtIndex(unsigned index) const >+ { >+ ASSERT(index < NUM_REGS); >+ return m_data[index].lockCount; >+ } >+ >+ VirtualRegister nameAtIndex(unsigned index) const >+ { >+ ASSERT(index < NUM_REGS); >+ return m_data[index].name; >+ } >+ >+ void releaseAtIndex(unsigned index) >+ { >+ // 'index' must be a valid register. >+ ASSERT(index < NUM_REGS); >+ // 'index' should currently be named. >+ ASSERT(m_data[index].name.isValid()); >+ // 'index' should currently have a valid spill order. >+ ASSERT(m_data[index].spillOrder != SpillHintInvalid); >+ >+ m_data[index].name = VirtualRegister(); >+ m_data[index].spillOrder = SpillHintInvalid; >+ } >+ >+ // Used by 'allocate', above, to update inforamtion in the map. >+ RegID allocateInternal(uint32_t i, VirtualRegister &spillMe) >+ { >+ // 'i' must be a valid, unlocked register. >+ ASSERT(i < NUM_REGS && !m_data[i].lockCount); >+ >+ // Return the VirtualRegister of the named value currently stored in >+ // the register being returned - or default VirtualRegister() if none. >+ spillMe = m_data[i].name; >+ >+ // Clear any name/spillOrder currently associated with the register, >+ m_data[i] = MapEntry(); >+ // Mark the register as locked (with a lock count of 1). >+ m_data[i].lockCount = 1; >+ >+ return BankInfo::toRegister(i); >+ } >+ >+ // === MapEntry === >+ // >+ // This structure provides information for an individual machine register >+ // being managed by the RegisterBank. For each register we track a lock >+ // count, name and spillOrder hint. >+ struct MapEntry { >+ MapEntry() >+ : name(VirtualRegister()) >+ , spillOrder(SpillHintInvalid) >+ , lockCount(0) >+ { >+ } >+ >+ VirtualRegister name; >+ SpillHint spillOrder; >+ uint32_t lockCount; >+ }; >+ >+ // Holds the current status of all registers. >+ MapEntry m_data[NUM_REGS]; >+}; >+ >+typedef RegisterBank<GPRInfo>::iterator gpr_iterator; >+typedef RegisterBank<FPRInfo>::iterator fpr_iterator; >+ >+} // namespace JSC >+ >+#endif >diff --git a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >index 86c3a8f8298d834d3311fc5de55d192b30d04c61..a55022dd012a9e9ccd37cf5f819298c5ba099a16 100644 >--- a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >@@ -161,8 +161,6 @@ class B3IRGenerator { > typedef ControlData::ResultList ResultList; > typedef FunctionParser<B3IRGenerator>::ControlEntry ControlEntry; > >- static constexpr ExpressionType emptyExpression = nullptr; >- > typedef String ErrorType; > typedef Unexpected<ErrorType> UnexpectedResult; > typedef Expected<std::unique_ptr<InternalFunction>, ErrorType> Result; >@@ -187,6 +185,7 @@ class B3IRGenerator { > // Locals > PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); > PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); >+ PartialResult WARN_UNUSED_RETURN teeLocal(uint32_t index, ExpressionType& value); > > // Globals > PartialResult WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result); >@@ -214,14 +213,14 @@ class B3IRGenerator { > PartialResult WARN_UNUSED_RETURN addElseToUnreachable(ControlData&); > > PartialResult WARN_UNUSED_RETURN addReturn(const ControlData&, const ExpressionList& returnValues); >- PartialResult WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues); >+ PartialResult WARN_UNUSED_RETURN addBranch(ControlData&, std::optional<ExpressionType> condition, const ExpressionList& returnValues); > PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack); > PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack); > PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&); > > // Calls >- PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result); >- PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature&, Vector<ExpressionType>& args, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, std::optional<ExpressionType>& result); >+ PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature&, Vector<ExpressionType>& args, std::optional<ExpressionType>& result); > PartialResult WARN_UNUSED_RETURN addUnreachable(); > > void dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack); >@@ -619,6 +618,11 @@ auto B3IRGenerator::setLocal(uint32_t index, ExpressionType value) -> PartialRes > return { }; > } > >+auto B3IRGenerator::teeLocal(uint32_t index, ExpressionType& value) -> PartialResult >+{ >+ return setLocal(index, value); >+} >+ > auto B3IRGenerator::getGlobal(uint32_t index, ExpressionType& result) -> PartialResult > { > Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfGlobals())); >@@ -1011,14 +1015,14 @@ auto B3IRGenerator::addReturn(const ControlData&, const ExpressionList& returnVa > return { }; > } > >-auto B3IRGenerator::addBranch(ControlData& data, ExpressionType condition, const ExpressionList& returnValues) -> PartialResult >+auto B3IRGenerator::addBranch(ControlData& data, std::optional<ExpressionType> condition, const ExpressionList& returnValues) -> PartialResult > { > unifyValuesWithBlock(returnValues, data.resultForBranch()); > > BasicBlock* target = data.targetBlockForBranch(); > if (condition) { > BasicBlock* continuation = m_proc.addBlock(); >- m_currentBlock->appendNew<Value>(m_proc, B3::Branch, origin(), condition); >+ m_currentBlock->appendNew<Value>(m_proc, B3::Branch, origin(), condition.value()); > m_currentBlock->setSuccessors(FrequentedBlock(target), FrequentedBlock(continuation)); > target->addPredecessor(m_currentBlock); > continuation->addPredecessor(m_currentBlock); >@@ -1079,7 +1083,7 @@ auto B3IRGenerator::addEndToUnreachable(ControlEntry& entry) -> PartialResult > return { }; > } > >-auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult >+auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature& signature, Vector<ExpressionType>& args, std::optional<ExpressionType>& result) -> PartialResult > { > ASSERT(signature.argumentCount() == args.size()); > >@@ -1148,18 +1152,17 @@ auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature& signature, > > m_currentBlock = continuation; > >- if (returnType == Void) >- result = nullptr; >- else { >- result = continuation->appendNew<Value>(m_proc, Phi, toB3Type(returnType), origin()); >- wasmCallResultUpsilon->setPhi(result); >- embedderCallResultUpsilon->setPhi(result); >+ if (returnType != Void) { >+ ExpressionType resultValue = continuation->appendNew<Value>(m_proc, Phi, toB3Type(returnType), origin()); >+ wasmCallResultUpsilon->setPhi(resultValue); >+ embedderCallResultUpsilon->setPhi(resultValue); >+ result = resultValue; > } > > // The call could have been to another WebAssembly instance, and / or could have modified our Memory. > restoreWebAssemblyGlobalState(RestoreCachedStackLimit::Yes, m_info.memory, instanceValue(), m_proc, continuation); > } else { >- result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, origin(), args, toB3Type(returnType), >+ ExpressionType resultValue = wasmCallingConvention().setupCall(m_proc, m_currentBlock, origin(), args, toB3Type(returnType), > [=] (PatchpointValue* patchpoint) { > patchpoint->effects.writesPinned = true; > patchpoint->effects.readsPinned = true; >@@ -1172,12 +1175,14 @@ auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature& signature, > }); > }); > }); >+ if (resultValue) >+ result = resultValue; > } > > return { }; > } > >-auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult >+auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<ExpressionType>& args, std::optional<ExpressionType>& result) -> PartialResult > { > ExpressionType calleeIndex = args.takeLast(); > ASSERT(signature.argumentCount() == args.size()); >@@ -1310,7 +1315,7 @@ auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<Expressio > calleeCode = m_currentBlock->appendNew<Value>(m_proc, BitXor, origin(), calleeCode, m_currentBlock->appendNew<Const64Value>(m_proc, origin(), g_JITCodePoison)); > > Type returnType = signature.returnType(); >- result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, origin(), args, toB3Type(returnType), >+ auto* resultValue = wasmCallingConvention().setupCall(m_proc, m_currentBlock, origin(), args, toB3Type(returnType), > [=] (PatchpointValue* patchpoint) { > patchpoint->effects.writesPinned = true; > patchpoint->effects.readsPinned = true; >@@ -1327,6 +1332,8 @@ auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<Expressio > jit.call(params[returnType == Void ? 0 : 1].gpr(), WasmEntryPtrTag); > }); > }); >+ if (resultValue) >+ result = resultValue; > > // The call could have been to another WebAssembly instance, and / or could have modified our Memory. > restoreWebAssemblyGlobalState(RestoreCachedStackLimit::Yes, m_info.memory, instanceValue(), m_proc, m_currentBlock); >diff --git a/Source/JavaScriptCore/wasm/WasmFunctionParser.h b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >index 088e728ce4cc54ce3c606b40f255a609d57b3b91..702c6b896e20efb5cfbfc428b159bbf0e6b75f18 100644 >--- a/Source/JavaScriptCore/wasm/WasmFunctionParser.h >+++ b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >@@ -167,9 +167,9 @@ auto FunctionParser<Context>::binaryCase() -> PartialResult > > WASM_TRY_POP_EXPRESSION_STACK_INTO(right, "binary right"); > WASM_TRY_POP_EXPRESSION_STACK_INTO(left, "binary left"); >- WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(left, right, result)); >+ WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(WTFMove(left), WTFMove(right), result)); > >- m_expressionStack.append(result); >+ m_expressionStack.append(WTFMove(result)); > return { }; > } > >@@ -181,9 +181,9 @@ auto FunctionParser<Context>::unaryCase() -> PartialResult > ExpressionType result; > > WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "unary"); >- WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(value, result)); >+ WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(WTFMove(value), result)); > >- m_expressionStack.append(result); >+ m_expressionStack.append(WTFMove(result)); > return { }; > } > >@@ -209,9 +209,9 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > WASM_TRY_POP_EXPRESSION_STACK_INTO(nonZero, "select non-zero"); > > ExpressionType result; >- WASM_TRY_ADD_TO_CONTEXT(addSelect(condition, nonZero, zero, result)); >+ WASM_TRY_ADD_TO_CONTEXT(addSelect(WTFMove(condition), WTFMove(nonZero), WTFMove(zero), result)); > >- m_expressionStack.append(result); >+ m_expressionStack.append(WTFMove(result)); > return { }; > } > >@@ -225,8 +225,8 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > WASM_PARSER_FAIL_IF(alignment > memoryLog2Alignment(m_currentOpcode), "byte alignment ", 1ull << alignment, " exceeds load's natural alignment ", 1ull << memoryLog2Alignment(m_currentOpcode)); > WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get load offset"); > WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "load pointer"); >- WASM_TRY_ADD_TO_CONTEXT(load(static_cast<LoadOpType>(m_currentOpcode), pointer, result, offset)); >- m_expressionStack.append(result); >+ WASM_TRY_ADD_TO_CONTEXT(load(static_cast<LoadOpType>(m_currentOpcode), WTFMove(pointer), result, offset)); >+ m_expressionStack.append(WTFMove(result)); > return { }; > } > >@@ -240,7 +240,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get store offset"); > WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "store value"); > WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "store pointer"); >- WASM_TRY_ADD_TO_CONTEXT(store(static_cast<StoreOpType>(m_currentOpcode), pointer, value, offset)); >+ WASM_TRY_ADD_TO_CONTEXT(store(static_cast<StoreOpType>(m_currentOpcode), WTFMove(pointer), WTFMove(value), offset)); > return { }; > } > #undef CREATE_CASE >@@ -278,7 +278,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > ExpressionType result; > WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for get_local"); > WASM_TRY_ADD_TO_CONTEXT(getLocal(index, result)); >- m_expressionStack.append(result); >+ m_expressionStack.append(WTFMove(result)); > return { }; > } > >@@ -287,7 +287,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > ExpressionType value; > WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for set_local"); > WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_local"); >- WASM_TRY_ADD_TO_CONTEXT(setLocal(index, value)); >+ WASM_TRY_ADD_TO_CONTEXT(setLocal(index, WTFMove(value))); > return { }; > } > >@@ -295,7 +295,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > uint32_t index; > WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for tee_local"); > WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't tee_local on empty expression stack"); >- WASM_TRY_ADD_TO_CONTEXT(setLocal(index, m_expressionStack.last())); >+ WASM_TRY_ADD_TO_CONTEXT(teeLocal(index, m_expressionStack.last())); > return { }; > } > >@@ -304,7 +304,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > ExpressionType result; > WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index"); > WASM_TRY_ADD_TO_CONTEXT(getGlobal(index, result)); >- m_expressionStack.append(result); >+ m_expressionStack.append(WTFMove(result)); > return { }; > } > >@@ -313,7 +313,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > ExpressionType value; > WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get set_global's index"); > WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_global value"); >- WASM_TRY_ADD_TO_CONTEXT(setGlobal(index, value)); >+ WASM_TRY_ADD_TO_CONTEXT(setGlobal(index, WTFMove(value))); > return { }; > } > >@@ -330,14 +330,14 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > Vector<ExpressionType> args; > WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(calleeSignature.argumentCount()), "can't allocate enough memory for call's ", calleeSignature.argumentCount(), " arguments"); > for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) >- args.uncheckedAppend(m_expressionStack[i]); >+ args.uncheckedAppend(WTFMove(m_expressionStack[i])); > m_expressionStack.shrink(firstArgumentIndex); > >- ExpressionType result = Context::emptyExpression; >+ std::optional<ExpressionType> result; > WASM_TRY_ADD_TO_CONTEXT(addCall(functionIndex, calleeSignature, args, result)); > >- if (result != Context::emptyExpression) >- m_expressionStack.append(result); >+ if (result) >+ m_expressionStack.append(WTFMove(result.value())); > > return { }; > } >@@ -359,14 +359,14 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(argumentCount), "can't allocate enough memory for ", argumentCount, " call_indirect arguments"); > size_t firstArgumentIndex = m_expressionStack.size() - argumentCount; > for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) >- args.uncheckedAppend(m_expressionStack[i]); >+ args.uncheckedAppend(WTFMove(m_expressionStack[i])); > m_expressionStack.shrink(firstArgumentIndex); > >- ExpressionType result = Context::emptyExpression; >+ std::optional<ExpressionType> result; > WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(calleeSignature, args, result)); > >- if (result != Context::emptyExpression) >- m_expressionStack.append(result); >+ if (result) >+ m_expressionStack.append(WTFMove(result.value())); > > return { }; > } >@@ -393,8 +393,8 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > ControlType control; > WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get if's inline signature"); > WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "if condition"); >- WASM_TRY_ADD_TO_CONTEXT(addIf(condition, inlineSignature, control)); >- m_controlStack.append({ WTFMove(m_expressionStack), control }); >+ WASM_TRY_ADD_TO_CONTEXT(addIf(WTFMove(condition), inlineSignature, control)); >+ m_controlStack.append({ WTFMove(m_expressionStack), WTFMove(control) }); > m_expressionStack = ExpressionList(); > return { }; > } >@@ -409,7 +409,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > case Br: > case BrIf: { > uint32_t target; >- ExpressionType condition = Context::emptyExpression; >+ std::optional<ExpressionType> condition; > WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get br / br_if's target"); > WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br / br_if's target ", target, " exceeds control stack size ", m_controlStack.size()); > if (m_currentOpcode == BrIf) >@@ -419,7 +419,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > > ControlType& data = m_controlStack[m_controlStack.size() - 1 - target].controlData; > >- WASM_TRY_ADD_TO_CONTEXT(addBranch(data, condition, m_expressionStack)); >+ WASM_TRY_ADD_TO_CONTEXT(addBranch(data, WTFMove(condition), m_expressionStack)); > return { }; > } > >@@ -444,7 +444,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > WASM_PARSER_FAIL_IF(defaultTarget >= m_controlStack.size(), "br_table's default target ", defaultTarget, " exceeds control stack size ", m_controlStack.size()); > > WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br_table condition"); >- WASM_TRY_ADD_TO_CONTEXT(addSwitch(condition, targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack)); >+ WASM_TRY_ADD_TO_CONTEXT(addSwitch(WTFMove(condition), targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack)); > > m_unreachableBlocks = 1; > return { }; >@@ -455,7 +455,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > if (m_signature.returnType() != Void) { > ExpressionType returnValue; > WASM_TRY_POP_EXPRESSION_STACK_INTO(returnValue, "return"); >- returnValues.append(returnValue); >+ returnValues.append(WTFMove(returnValue)); > } > > WASM_TRY_ADD_TO_CONTEXT(addReturn(m_controlStack[0].controlData, returnValues)); >@@ -500,8 +500,8 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > WASM_TRY_POP_EXPRESSION_STACK_INTO(delta, "expect an i32 argument to grow_memory on the stack"); > > ExpressionType result; >- WASM_TRY_ADD_TO_CONTEXT(addGrowMemory(delta, result)); >- m_expressionStack.append(result); >+ WASM_TRY_ADD_TO_CONTEXT(addGrowMemory(WTFMove(delta), result)); >+ m_expressionStack.append(WTFMove(result)); > > return { }; > } >@@ -515,7 +515,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > > ExpressionType result; > WASM_TRY_ADD_TO_CONTEXT(addCurrentMemory(result)); >- m_expressionStack.append(result); >+ m_expressionStack.append(WTFMove(result)); > > return { }; > } >diff --git a/Source/JavaScriptCore/wasm/WasmRailgunJIT.cpp b/Source/JavaScriptCore/wasm/WasmRailgunJIT.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..8b85a7b8cbccedf6539c1d117a75d2be6dfee798 >--- /dev/null >+++ b/Source/JavaScriptCore/wasm/WasmRailgunJIT.cpp >@@ -0,0 +1,580 @@ >+/* >+ * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "WasmRailgunJIT.h" >+ >+#include "RegisterBank.h" >+#include "WasmFunctionParser.h" >+#include <wtf/Variant.h> >+ >+#if ENABLE(WEBASSEMBLY) >+ >+namespace JSC { namespace Wasm { >+namespace WasmRailgunJITInternal { >+static constexpr bool verbose = false; >+} >+ >+class RailgunJIT; >+ >+class StackValue { >+ WTF_MAKE_NONCOPYABLE(StackValue); >+ WTF_MAKE_FAST_ALLOCATED; >+public: >+ struct Local { >+ uint32_t slot { 0 }; >+ }; >+ >+ struct Constant { >+ uint64_t bits { 0 }; >+ }; >+ >+ struct Register { >+ Reg reg; >+ }; >+ >+ struct Stack { >+ uint32_t slot { 0 }; >+ }; >+ >+ // Keep the order in sync with the order in Variant. >+ enum class Location : uint8_t { >+ Local = 0, >+ Constant, >+ Register, >+ Stack, >+ }; >+ using Data = Variant<Local, Constant, Register, Stack>; >+ >+ Data data() const >+ { >+ return m_data; >+ } >+ >+ Type type() const >+ { >+ return m_type; >+ } >+ >+ Location location() const >+ { >+ return static_cast<Location>(m_data.index()); >+ } >+ >+ ~StackValue(); >+ >+ StackValue() = default; >+ >+ StackValue(StackValue&& value) >+ { >+ swap(value); >+ } >+ >+ StackValue& operator=(StackValue&& value) >+ { >+ swap(value); >+ return *this; >+ } >+ >+ void swap(StackValue& value) >+ { >+ std::swap(m_jit, value.m_jit); >+ std::swap(m_type, value.m_type); >+ std::swap(m_data, value.m_data); >+ } >+ >+ StackValue(RailgunJIT* jit, Type type, Local local) >+ : m_jit(jit) >+ , m_type(type) >+ , m_data(local) >+ { >+ } >+ >+ StackValue(RailgunJIT* jit, Type type, Constant constant) >+ : m_jit(jit) >+ , m_type(type) >+ , m_data(constant) >+ { >+ } >+ >+private: >+ RailgunJIT* m_jit { nullptr }; >+ Type m_type { Void }; >+ Data m_data { Constant { 0 } }; >+}; >+ >+class BasicBlock { >+}; >+ >+class ControlData { >+public: >+ ControlData(BlockType type, BasicBlock* continuation) >+ : blockType(type) >+ , continuation(continuation) >+ { >+ } >+ >+ ControlData() = default; >+ >+ void dump(PrintStream& out) const >+ { >+ switch (type()) { >+ case BlockType::If: >+ out.print("If: "); >+ break; >+ case BlockType::Block: >+ out.print("Block: "); >+ break; >+ case BlockType::Loop: >+ out.print("Loop: "); >+ break; >+ case BlockType::TopLevel: >+ out.print("TopLevel: "); >+ break; >+ } >+ } >+ >+ BlockType type() const { return blockType; } >+ >+ bool hasNonVoidSignature() const { return result.size(); } >+ >+ BasicBlock* targetBlockForBranch() >+ { >+ if (type() == BlockType::Loop) >+ return special; >+ return continuation; >+ } >+ >+ void convertIfToBlock() >+ { >+ ASSERT(type() == BlockType::If); >+ blockType = BlockType::Block; >+ special = nullptr; >+ } >+ >+ using ResultList = Vector<StackValue>; // StackValue must be flushed. >+ >+ const ResultList* resultForBranch() const >+ { >+ if (type() == BlockType::Loop) >+ return nullptr; >+ return &result; >+ } >+ >+private: >+ friend class B3IRGenerator; >+ BlockType blockType; >+ BasicBlock* continuation; >+ BasicBlock* special; >+ ResultList result; >+}; >+ >+ >+class RailgunJIT { >+ WTF_MAKE_NONCOPYABLE(RailgunJIT); >+public: >+ RailgunJIT(const ModuleInformation&, InternalFunction*, Vector<UnlinkedWasmToWasmCall>&, MemoryMode, unsigned functionIndex, TierUpCount*, ThrowWasmException); >+ >+ using ExpressionType = StackValue; >+ using ControlType = ControlData; >+ using ExpressionList = Vector<StackValue>; >+ using ControlEntry = FunctionParser<RailgunJIT>::ControlEntry; >+ >+ using ErrorType = String; >+ using UnexpectedResult = Unexpected<ErrorType>; >+ using Result = Expected<std::unique_ptr<InternalFunction>, ErrorType>; >+ using PartialResult = Expected<void, ErrorType>; >+ >+ void setParser(FunctionParser<RailgunJIT>* parser) { m_parser = parser; }; >+ >+ // lock and unlock GPR & FPR registers. >+ void lock(GPRReg reg) >+ { >+ m_gprs.lock(reg); >+ } >+ void lock(FPRReg reg) >+ { >+ m_fprs.lock(reg); >+ } >+ void unlock(GPRReg reg) >+ { >+ m_gprs.unlock(reg); >+ } >+ void unlock(FPRReg reg) >+ { >+ m_fprs.unlock(reg); >+ } >+ >+ GPRReg allocateGPR(StackValue&) >+ { >+ return InvalidGPRReg; >+ } >+ >+ template <typename ...Args> >+ NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const >+ { >+ using namespace FailureHelper; // See ADL comment in WasmParser.h. >+ return UnexpectedResult(makeString("WebAssembly.Module failed compiling: "_s, makeString(args)...)); >+ } >+#define WASM_COMPILE_FAIL_IF(condition, ...) do { \ >+ if (UNLIKELY(condition)) \ >+ return fail(__VA_ARGS__); \ >+ } while (0) >+ >+ // Functions invoked by FunctionParser. >+ PartialResult WARN_UNUSED_RETURN addArguments(const Signature&); >+ PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t); >+ ExpressionType addConstant(Type, uint64_t); >+ >+ // Locals >+ PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); >+ PartialResult WARN_UNUSED_RETURN teeLocal(uint32_t index, ExpressionType& value); >+ >+ // Globals >+ PartialResult WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value); >+ >+ // Memory >+ PartialResult WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset); >+ PartialResult WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset); >+ PartialResult WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result); >+ >+ // Basic operators >+ template<OpType> >+ PartialResult WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result); >+ template<OpType> >+ PartialResult WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result); >+ >+ // Control flow >+ ControlData WARN_UNUSED_RETURN addTopLevel(Type signature); >+ ControlData WARN_UNUSED_RETURN addBlock(Type signature); >+ ControlData WARN_UNUSED_RETURN addLoop(Type signature); >+ PartialResult WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result); >+ PartialResult WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&); >+ PartialResult WARN_UNUSED_RETURN addElseToUnreachable(ControlData&); >+ >+ PartialResult WARN_UNUSED_RETURN addReturn(const ControlData&, const ExpressionList& returnValues); >+ PartialResult WARN_UNUSED_RETURN addBranch(ControlData&, std::optional<ExpressionType> condition, const ExpressionList& returnValues); >+ PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack); >+ PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack); >+ PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&); >+ >+ // Calls >+ PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, std::optional<ExpressionType>& result); >+ PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature&, Vector<ExpressionType>& args, std::optional<ExpressionType>& result); >+ PartialResult WARN_UNUSED_RETURN addUnreachable(); >+ >+ void dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack); >+ >+private: >+ void spill(uint32_t stackSlot, StackValue&); >+ >+ CCallHelpers m_jit; >+ RegisterBank<GPRInfo> m_gprs; >+ RegisterBank<FPRInfo> m_fprs; >+ Vector<Type> m_locals; >+ FunctionParser<RailgunJIT>* m_parser { nullptr }; >+ const ModuleInformation& m_info; >+ const MemoryMode m_mode { MemoryMode::BoundsChecking }; >+ const unsigned m_functionIndex { UINT_MAX }; >+ const TierUpCount* m_tierUp { nullptr }; >+ ThrowWasmException m_throwWasmException { nullptr }; >+}; >+ >+class GPROperand { >+ WTF_MAKE_NONCOPYABLE(GPROperand); >+public: >+ GPROperand(RailgunJIT* jit, StackValue&& value) >+ : m_jit(jit) >+ , m_value(WTFMove(value)) >+ { >+ if (m_value.location() == StackValue::Location::Register) { >+ m_gpr = WTF::get<StackValue::Register>(m_value.data()).reg.gpr(); >+ m_jit->lock(m_gpr); >+ } else { >+ m_gpr = m_jit->allocateGPR(m_value); >+ } >+ } >+ >+ ~GPROperand() >+ { >+ m_jit->unlock(m_gpr); >+ } >+ >+ GPRReg gpr() >+ { >+ return m_gpr; >+ } >+ >+private: >+ RailgunJIT* m_jit; >+ StackValue m_value; >+ GPRReg m_gpr { InvalidGPRReg }; >+}; >+ >+StackValue::~StackValue() >+{ >+ if (m_jit) { >+ } >+} >+ >+RailgunJIT::RailgunJIT(const ModuleInformation& info, InternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, MemoryMode mode, unsigned functionIndex, TierUpCount* tierUp, ThrowWasmException throwWasmException) >+ : m_info(info) >+ , m_mode(mode) >+ , m_functionIndex(functionIndex) >+ , m_tierUp(tierUp) >+ , m_throwWasmException(throwWasmException) >+{ >+ UNUSED_PARAM(compilation); >+} >+ >+auto RailgunJIT::addArguments(const Signature& signature) -> PartialResult >+{ >+ ASSERT(!m_locals.size()); >+ WASM_COMPILE_FAIL_IF(!m_locals.tryReserveCapacity(signature.argumentCount()), "can't allocate memory for ", signature.argumentCount(), " arguments"); >+ >+ m_locals.grow(signature.argumentCount()); >+ >+ // FIXME: Fix the code. >+ >+ return { }; >+} >+ >+auto RailgunJIT::addLocal(Type type, uint32_t count) -> PartialResult >+{ >+ Checked<uint32_t, RecordOverflow> totalBytesChecked = count; >+ totalBytesChecked += m_locals.size(); >+ uint32_t totalBytes; >+ WASM_COMPILE_FAIL_IF((totalBytesChecked.safeGet(totalBytes) == CheckedState::DidOverflow) || !m_locals.tryReserveCapacity(totalBytes), "can't allocate memory for ", totalBytes, " locals"); >+ >+ for (uint32_t i = 0; i < count; ++i) { >+ int32_t stackSlot = m_locals.size(); >+ m_locals.uncheckedAppend(type); >+ switch (type) { >+ case Type::Anyfunc: >+ case Type::Func: >+ case Type::Void: >+ RELEASE_ASSERT_NOT_REACHED(); >+ return { }; >+ // FIXME: Address offset overflow. >+ case Type::I32: >+ case Type::F32: >+ m_jit.store32(CCallHelpers::TrustedImm32(0), CCallHelpers::Address(GPRInfo::callFrameRegister, -(stackSlot * 8))); >+ return { }; >+ case Type::I64: >+ case Type::F64: >+ m_jit.store64(CCallHelpers::TrustedImm64(0), CCallHelpers::Address(GPRInfo::callFrameRegister, -(stackSlot * 8))); >+ return { }; >+ } >+ } >+ return { }; >+} >+ >+auto RailgunJIT::addConstant(Type type, uint64_t bits) -> ExpressionType >+{ >+ switch (type) { >+ case Type::Anyfunc: >+ case Type::Func: >+ case Type::Void: >+ RELEASE_ASSERT_NOT_REACHED(); >+ return { }; >+ >+ case Type::I32: >+ case Type::I64: >+ case Type::F32: >+ case Type::F64: >+ return StackValue { this, type, StackValue::Constant { bits } }; >+ } >+ return { }; >+} >+ >+auto RailgunJIT::getLocal(uint32_t index, ExpressionType& result) -> PartialResult >+{ >+ ASSERT(m_locals[index]); >+ result = StackValue { this, m_locals[index], StackValue::Local { index } }; >+ return { }; >+} >+ >+auto RailgunJIT::setLocal(uint32_t index, ExpressionType value) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::teeLocal(uint32_t index, ExpressionType& value) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::getGlobal(uint32_t index, ExpressionType& result) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::setGlobal(uint32_t index, ExpressionType value) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addGrowMemory(ExpressionType delta, ExpressionType& result) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addCurrentMemory(ExpressionType& result) -> PartialResult >+{ >+ return { }; >+} >+ >+template<OpType> >+auto RailgunJIT::addOp(ExpressionType arg, ExpressionType& result) -> PartialResult >+{ >+ return { }; >+} >+ >+template<OpType> >+auto RailgunJIT::addOp(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult >+{ >+ GPROperand leftOperand(this, WTFMove(left)); >+ GPROperand rightOperand(this, WTFMove(right)); >+ >+ GPRReg leftGPR = leftOperand.gpr(); >+ GPRReg rightGPR = rightOperand.gpr(); >+ >+ m_jit.add32(leftGPR, rightGPR); >+ >+ return { }; >+} >+ >+auto RailgunJIT::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addTopLevel(Type signature) -> ControlData >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addBlock(Type signature) -> ControlData >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addLoop(Type signature) -> ControlData >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addIf(ExpressionType condition, Type signature, ControlData& result) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addElse(ControlData&, const ExpressionList&) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addElseToUnreachable(ControlData&) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addReturn(const ControlData&, const ExpressionList& returnValues) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addBranch(ControlData&, std::optional<ExpressionType> condition, const ExpressionList& returnValues) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::endBlock(ControlEntry&, ExpressionList& expressionStack) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addEndToUnreachable(ControlEntry&) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, std::optional<ExpressionType>& result) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addCallIndirect(const Signature&, Vector<ExpressionType>& args, std::optional<ExpressionType>& result) -> PartialResult >+{ >+ return { }; >+} >+ >+auto RailgunJIT::addUnreachable() -> PartialResult >+{ >+ return { }; >+} >+ >+void RailgunJIT::dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack) >+{ >+} >+ >+Expected<std::unique_ptr<InternalFunction>, String> parseAndCompile(CompilationContext& compilationContext, const uint8_t* functionStart, size_t functionLength, const Signature& signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ModuleInformation& info, MemoryMode mode, uint32_t functionIndex, TierUpCount* tierUp, ThrowWasmException throwWasmException) >+{ >+ auto result = std::make_unique<InternalFunction>(); >+ >+ compilationContext.embedderEntrypointJIT = std::make_unique<CCallHelpers>(); >+ compilationContext.wasmEntrypointJIT = std::make_unique<CCallHelpers>(); >+ >+ RailgunJIT compiler(info, result.get(), unlinkedWasmToWasmCalls, mode, functionIndex, tierUp, throwWasmException); >+ FunctionParser<RailgunJIT> parser(compiler, functionStart, functionLength, signature, info); >+ WASM_FAIL_IF_HELPER_FAILS(parser.parse()); >+ >+ return WTFMove(result); >+} >+ >+ >+ >+} } // namespace JSC::Wasm >+ >+#endif // ENABLE(WEBASSEMBLY) >diff --git a/Source/JavaScriptCore/wasm/WasmRailgunJIT.h b/Source/JavaScriptCore/wasm/WasmRailgunJIT.h >new file mode 100644 >index 0000000000000000000000000000000000000000..526dd2451770793cc484eb74e59b5b5c531be322 >--- /dev/null >+++ b/Source/JavaScriptCore/wasm/WasmRailgunJIT.h >@@ -0,0 +1,34 @@ >+/* >+ * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#if ENABLE(WEBASSEMBLY) >+ >+namespace JSC { namespace Wasm { >+ >+} } // namespace JSC::Wasm >+ >+#endif // ENABLE(WEBASSEMBLY) >diff --git a/Source/JavaScriptCore/wasm/WasmValidate.cpp b/Source/JavaScriptCore/wasm/WasmValidate.cpp >index ea4a7598fa049544f87684a50e3cfcb913108eaf..87669ea08e3865c39af1af6296a8039ffed4eb73 100644 >--- a/Source/JavaScriptCore/wasm/WasmValidate.cpp >+++ b/Source/JavaScriptCore/wasm/WasmValidate.cpp >@@ -83,8 +83,6 @@ class Validate { > typedef Vector<ExpressionType, 1> ExpressionList; > typedef FunctionParser<Validate>::ControlEntry ControlEntry; > >- static const ExpressionType emptyExpression = Void; >- > template <typename ...Args> > NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const > { >@@ -103,6 +101,7 @@ class Validate { > // Locals > Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); > Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); >+ Result WARN_UNUSED_RETURN teeLocal(uint32_t index, ExpressionType& value); > > // Globals > Result WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result); >@@ -128,7 +127,7 @@ class Validate { > Result WARN_UNUSED_RETURN addElseToUnreachable(ControlData&); > > Result WARN_UNUSED_RETURN addReturn(ControlData& topLevel, const ExpressionList& returnValues); >- Result WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& expressionStack); >+ Result WARN_UNUSED_RETURN addBranch(ControlData&, std::optional<ExpressionType> condition, const ExpressionList& expressionStack); > Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack); > Result WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack); > Result WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&); >@@ -138,8 +137,8 @@ class Validate { > Result WARN_UNUSED_RETURN addUnreachable() { return { }; } > > // Calls >- Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature&, const Vector<ExpressionType>& args, ExpressionType& result); >- Result WARN_UNUSED_RETURN addCallIndirect(const Signature&, const Vector<ExpressionType>& args, ExpressionType& result); >+ Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature&, const Vector<ExpressionType>& args, std::optional<ExpressionType>& result); >+ Result WARN_UNUSED_RETURN addCallIndirect(const Signature&, const Vector<ExpressionType>& args, std::optional<ExpressionType>& result); > > bool hasMemory() const { return !!m_module.memory; } > >@@ -192,6 +191,11 @@ auto Validate::setLocal(uint32_t index, ExpressionType value) -> Result > return { }; > } > >+auto Validate::teeLocal(uint32_t index, ExpressionType& value) -> Result >+{ >+ return setLocal(index, value); >+} >+ > auto Validate::getGlobal(uint32_t index, ExpressionType& result) -> Result > { > WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "get_global ", index, " of unknown global, limit is ", m_module.globals.size()); >@@ -275,10 +279,10 @@ auto Validate::checkBranchTarget(ControlType& target, const ExpressionList& expr > return { }; > } > >-auto Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack) -> Result >+auto Validate::addBranch(ControlType& target, std::optional<ExpressionType> condition, const ExpressionList& stack) -> Result > { > // Void means this is an unconditional branch. >- WASM_VALIDATOR_FAIL_IF(condition != Void && condition != I32, "conditional branch with non-i32 condition ", condition); >+ WASM_VALIDATOR_FAIL_IF(condition && condition.value() != I32, "conditional branch with non-i32 condition ", condition.value()); > return checkBranchTarget(target, stack); > } > >@@ -321,18 +325,19 @@ auto Validate::addEndToUnreachable(ControlEntry& entry) -> Result > return { }; > } > >-auto Validate::addCall(unsigned, const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result >+auto Validate::addCall(unsigned, const Signature& signature, const Vector<ExpressionType>& args, std::optional<ExpressionType>& result) -> Result > { > WASM_VALIDATOR_FAIL_IF(signature.argumentCount() != args.size(), "arity mismatch in call, got ", args.size(), " arguments, expected ", signature.argumentCount()); > > for (unsigned i = 0; i < args.size(); ++i) > WASM_VALIDATOR_FAIL_IF(args[i] != signature.argument(i), "argument type mismatch in call, got ", args[i], ", expected ", signature.argument(i)); > >- result = signature.returnType(); >+ if (signature.returnType() != Void) >+ result = signature.returnType(); > return { }; > } > >-auto Validate::addCallIndirect(const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result >+auto Validate::addCallIndirect(const Signature& signature, const Vector<ExpressionType>& args, std::optional<ExpressionType>& result) -> Result > { > const auto argumentCount = signature.argumentCount(); > WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount); >@@ -342,7 +347,8 @@ auto Validate::addCallIndirect(const Signature& signature, const Vector<Expressi > > WASM_VALIDATOR_FAIL_IF(args.last() != I32, "non-i32 call_indirect index ", args.last()); > >- result = signature.returnType(); >+ if (signature.returnType() != Void) >+ result = signature.returnType(); > return { }; > } >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 191802
:
356297
|
357108
|
357110
|
360063
|
360163
|
360171
|
360204
|
360205
|
360309
|
360310
|
360328
|
360374
|
360382
|
360400
|
360415
|
360420
|
360421
|
360423
|
360444
|
360452
|
360495
|
360498
|
360531
|
360648
|
360652
|
360655
|
360669