WebKit Bugzilla
Attachment 361270 Details for
Bug 194036
: [WebAssembly] Write a new register allocator for Air O0 and make BBQ use it
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
WIP
b-backup.diff (text/plain), 51.05 KB, created by
Saam Barati
on 2019-02-05 20:02:09 PST
(
hide
)
Description:
WIP
Filename:
MIME Type:
Creator:
Saam Barati
Created:
2019-02-05 20:02:09 PST
Size:
51.05 KB
patch
obsolete
>Index: PerformanceTests/JetStream2/JetStreamDriver.js >=================================================================== >--- PerformanceTests/JetStream2/JetStreamDriver.js (revision 240697) >+++ PerformanceTests/JetStream2/JetStreamDriver.js (working copy) >@@ -1538,7 +1538,7 @@ let runSeaMonster = true; > let runCodeLoad = true; > let runWasm = true; > >-if (false) { >+if (true) { > runOctane = false; > runARES = false; > runWSL = false; >@@ -1550,7 +1550,7 @@ if (false) { > runWorkerTests = false; > runSeaMonster = false; > runCodeLoad = false; >- runWasm = false; >+ //runWasm = false; > } > > if (typeof testList !== "undefined") { >Index: Source/JavaScriptCore/Sources.txt >=================================================================== >--- Source/JavaScriptCore/Sources.txt (revision 240752) >+++ Source/JavaScriptCore/Sources.txt (working copy) >@@ -56,6 +56,7 @@ assembler/Printer.cpp > assembler/ProbeContext.cpp > assembler/ProbeStack.cpp > >+b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp > b3/air/AirAllocateRegistersAndStackByLinearScan.cpp > b3/air/AirAllocateRegistersByGraphColoring.cpp > b3/air/AirAllocateStackByGraphColoring.cpp >Index: Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (revision 240752) >+++ Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (working copy) >@@ -872,6 +872,7 @@ > 4BAA07CEB81F49A296E02203 /* WasmSignatureInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A5F403F11C4F599CD596D5 /* WasmSignatureInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 521131F71F82BF14007CCEEE /* PolyProtoAccessChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 521131F61F82BF11007CCEEE /* PolyProtoAccessChain.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 521322461ECBCE8200F65615 /* WebAssemblyFunctionBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 521322441ECBCE8200F65615 /* WebAssemblyFunctionBase.h */; }; >+ 524E9D7322092B5200A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 524E9D7222092B4600A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.h */; }; > 5250D2D21E8DA05A0029A932 /* WasmThunks.h in Headers */ = {isa = PBXBuildFile; fileRef = 5250D2D01E8DA05A0029A932 /* WasmThunks.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 525C0DDA1E935847002184CD /* WasmCallee.h in Headers */ = {isa = PBXBuildFile; fileRef = 525C0DD81E935847002184CD /* WasmCallee.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 525C9CDF220285830082DBFD /* WasmAirIRGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52847AD921FFB8630061A9DB /* WasmAirIRGenerator.cpp */; }; >@@ -3350,6 +3351,8 @@ > 521131F61F82BF11007CCEEE /* PolyProtoAccessChain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PolyProtoAccessChain.h; sourceTree = "<group>"; }; > 521322431ECBCE8200F65615 /* WebAssemblyFunctionBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyFunctionBase.cpp; path = js/WebAssemblyFunctionBase.cpp; sourceTree = "<group>"; }; > 521322441ECBCE8200F65615 /* WebAssemblyFunctionBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyFunctionBase.h; path = js/WebAssemblyFunctionBase.h; sourceTree = "<group>"; }; >+ 524E9D7122092B4500A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = AirAllocateRegistersAndStackAndGenerateCode.cpp; path = b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp; sourceTree = "<group>"; }; >+ 524E9D7222092B4600A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AirAllocateRegistersAndStackAndGenerateCode.h; path = b3/air/AirAllocateRegistersAndStackAndGenerateCode.h; sourceTree = "<group>"; }; > 5250D2CF1E8DA05A0029A932 /* WasmThunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmThunks.cpp; sourceTree = "<group>"; }; > 5250D2D01E8DA05A0029A932 /* WasmThunks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmThunks.h; sourceTree = "<group>"; }; > 525C0DD71E935847002184CD /* WasmCallee.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmCallee.cpp; sourceTree = "<group>"; }; >@@ -5429,6 +5432,8 @@ > 0FEC84B31BDACD880080FF74 /* air */ = { > isa = PBXGroup; > children = ( >+ 524E9D7122092B4500A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.cpp */, >+ 524E9D7222092B4600A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.h */, > 0F2AC5681E8A0BD10001EE3F /* AirAllocateRegistersAndStackByLinearScan.cpp */, > 0F2AC5691E8A0BD10001EE3F /* AirAllocateRegistersAndStackByLinearScan.h */, > 7965C2141E5D799600B7591D /* AirAllocateRegistersByGraphColoring.cpp */, >@@ -8843,6 +8848,7 @@ > A7D89CFE17A0B8CC00773AD8 /* DFGOSRAvailabilityAnalysisPhase.h in Headers */, > 0FD82E57141DAF1000179C94 /* DFGOSREntry.h in Headers */, > 0FD8A32617D51F5700CA2C40 /* DFGOSREntrypointCreationPhase.h in Headers */, >+ 524E9D7322092B5200A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.h in Headers */, > 0FC0976A1468A6F700CF2442 /* DFGOSRExit.h in Headers */, > 0F235BEC17178E7300690C7F /* DFGOSRExitBase.h in Headers */, > 0FFB921C16D02F110055A5DB /* DFGOSRExitCompilationInfo.h in Headers */, >Index: Source/JavaScriptCore/b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp >=================================================================== >--- Source/JavaScriptCore/b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp (nonexistent) >+++ Source/JavaScriptCore/b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp (working copy) >@@ -0,0 +1,717 @@ >+/* >+ * Copyright (C) 2019 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. >+ */ >+ >+#include "config.h" >+#include "AirAllocateRegistersAndStackAndGenerateCode.h" >+ >+#if ENABLE(B3_JIT) >+ >+#include "AirBlockInsertionSet.h" >+#include "AirCode.h" >+#include "AirHandleCalleeSaves.h" >+#include "AirLiveness.h" >+#include "AirLowerStackArgs.h" >+#include "AirStackAllocation.h" >+#include "AirTmpMap.h" >+#include "CCallHelpers.h" >+#include "DisallowMacroScratchRegisterUsage.h" >+ >+namespace JSC { namespace B3 { namespace Air { >+ >+NEVER_INLINE static TmpMap<size_t> buildLiveRanges(Code& code, UnifiedTmpLiveness& liveness) >+{ >+ TmpMap<size_t> result(code, 0); // OOPS: How to handle named Tmps? >+ >+ // Build live ranges. >+ { >+ size_t globalInstIndex = 0; >+ for (BasicBlock* block : code) { >+ for (Tmp tmp : liveness.liveAtHead(block)) { >+ if (tmp.isReg()) >+ continue; >+ result[tmp] = std::max(globalInstIndex, result[tmp]); >+ } >+ for (size_t instIndex = 0; instIndex < block->size(); ++instIndex) { >+ Inst& inst = block->at(instIndex); >+ inst.forEachTmpFast([&] (Tmp tmp) { >+ if (tmp.isReg()) >+ return; >+ result[tmp] = std::max(globalInstIndex, result[tmp]); >+ }); >+ ++globalInstIndex; >+ } >+ for (Tmp tmp : liveness.liveAtTail(block)) { >+ if (tmp.isReg()) >+ continue; >+ result[tmp] = std::max(globalInstIndex, result[tmp]); >+ } >+ } >+ } >+ >+ return result; >+} >+ >+static size_t totalMemOps; >+ >+GenerateAndAllocateRegisters::GenerateAndAllocateRegisters(Code& code) >+ : m_code(code) >+ , m_map(code) >+{ } >+ >+void GenerateAndAllocateRegisters::gatherTerminalPatchSpills() >+{ >+ BlockInsertionSet blockInsertionSet(m_code); >+ for (BasicBlock* block : m_code) { >+ Inst& inst = block->last(); >+ if (inst.kind.opcode != Patch) >+ continue; >+ >+ HashMap<Tmp, Tmp*> needToDef; >+ >+ inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Bank, Width) { >+ if (Arg::isAnyDef(role) && !tmp.isReg()) >+ needToDef.add(tmp, &tmp); >+ }); >+ >+ if (needToDef.isEmpty()) >+ continue; >+ >+ for (FrequentedBlock& frequentedSuccessor : block->successors()) { >+ BasicBlock* successor = frequentedSuccessor.block(); >+ BasicBlock* newBlock = blockInsertionSet.insertBefore(successor, successor->frequency()); >+ newBlock->appendInst(Inst(Jump, inst.origin)); >+ newBlock->setSuccessors(successor); >+ newBlock->addPredecessor(block); >+ frequentedSuccessor.block() = newBlock; >+ successor->replacePredecessor(block, newBlock); >+ >+ m_blocksForPatchSpilling.add(newBlock, std::make_tuple(&inst, CCallHelpers::Jump(), CCallHelpers::Label(), needToDef)); >+ //HashMap<BasicBlock*, std::tuple<Inst*, CCallHelpers::Jump, CCallHelpers::Label, Vector<std::pair<Tmp, Reg>>>> m_blocksForPatchSpilling; >+ } >+ } >+ >+ blockInsertionSet.execute(); >+} >+ >+ALWAYS_INLINE void GenerateAndAllocateRegisters::flush(Tmp tmp, Reg reg) >+{ >+ ASSERT(tmp); >+ // OOPS: scratch reg on arm issue w/ offset size being too big. >+ ptrdiff_t offset = m_map[tmp].spillSlot->offsetFromFP(); >+ if (tmp.bank() == GP) >+ m_jit->store64(reg.gpr(), CCallHelpers::Address(GPRInfo::callFrameRegister, offset)); >+ else >+ m_jit->storeDouble(reg.fpr(), CCallHelpers::Address(GPRInfo::callFrameRegister, offset)); >+ ++totalMemOps; >+} >+ >+ALWAYS_INLINE void GenerateAndAllocateRegisters::spill(Tmp tmp, Reg reg) >+{ >+ ASSERT(reg); >+ ASSERT(m_map[tmp].reg == reg); >+ m_availableRegs[tmp.bank()].set(reg); >+ (*m_currentAllocation)[reg] = Tmp(); >+ flush(tmp, reg); >+ m_map[tmp].reg = Reg(); >+} >+ >+ALWAYS_INLINE void GenerateAndAllocateRegisters::alloc(Tmp tmp, Reg reg, bool isDef) >+{ >+ if (Tmp occupyingTmp = (*m_currentAllocation)[reg]) >+ spill(occupyingTmp, reg); >+ else { >+ ASSERT(!(*m_currentAllocation)[reg]); >+ ASSERT(m_availableRegs[tmp.bank()].get(reg)); >+ } >+ >+ m_map[tmp].reg = reg; >+ m_availableRegs[tmp.bank()].clear(reg); >+ (*m_currentAllocation)[reg] = tmp; >+ >+ if (!isDef) { >+ ptrdiff_t offset = m_map[tmp].spillSlot->offsetFromFP(); >+ if (tmp.bank() == GP) >+ m_jit->load64(CCallHelpers::Address(GPRInfo::callFrameRegister, offset), reg.gpr()); >+ else >+ m_jit->loadDouble(CCallHelpers::Address(GPRInfo::callFrameRegister, offset), reg.fpr()); >+ >+ ++totalMemOps; >+ } >+} >+ >+ALWAYS_INLINE void GenerateAndAllocateRegisters::freeDeadTmpsIfNeeded() >+{ >+ if (m_didAlreadyFreeDeadSlots) >+ return; >+ >+ m_didAlreadyFreeDeadSlots = true; >+ for (size_t i = 0; i < m_currentAllocation->size(); ++i) { >+ Tmp tmp = (*m_currentAllocation)[i]; >+ if (!tmp) >+ continue; >+ if (tmp.isReg()) >+ continue; >+ if (m_liveRangeEnd[tmp] >= m_globalInstIndex) >+ continue; >+ >+ Reg reg = Reg::fromIndex(i); >+ m_map[tmp].reg = Reg(); >+ m_availableRegs[tmp.bank()].set(reg); >+ (*m_currentAllocation)[i] = Tmp(); >+ } >+} >+ >+ALWAYS_INLINE bool GenerateAndAllocateRegisters::assignTmp(Tmp& tmp, Bank bank, bool isDef) >+{ >+ ASSERT(!tmp.isReg()); >+ if (Reg reg = m_map[tmp].reg) { >+ ASSERT(!m_namedDefdRegs.contains(reg)); >+ tmp = Tmp(reg); >+ m_namedUsedRegs.set(reg); >+ ASSERT(!m_availableRegs[bank].get(reg)); >+ return true; >+ } >+ >+ if (!m_availableRegs[bank].numberOfSetRegisters()) >+ freeDeadTmpsIfNeeded(); >+ >+ if (m_availableRegs[bank].numberOfSetRegisters()) { >+ // We first take an available register. >+ for (Reg reg : m_registers[bank]) { >+ if (m_namedUsedRegs.contains(reg) || m_namedDefdRegs.contains(reg)) >+ continue; >+ if (!m_availableRegs[bank].contains(reg)) >+ continue; >+ m_namedUsedRegs.set(reg); // At this point, it doesn't matter if we add it to the m_namedUsedRegs or m_namedDefdRegs. We just need to mark that we can't use it again. >+ alloc(tmp, reg, isDef); >+ tmp = Tmp(reg); >+ return true; >+ } >+ >+ RELEASE_ASSERT_NOT_REACHED(); >+ } >+ >+ // Nothing was available, let's make some room. >+ for (unsigned i = 0; i < m_registers[bank].size(); ++i) { >+ Reg reg = m_registers[bank][i]; >+ if (m_namedUsedRegs.contains(reg) || m_namedDefdRegs.contains(reg)) >+ continue; >+ >+ m_namedUsedRegs.set(reg); >+ >+ alloc(tmp, reg, isDef); >+ tmp = Tmp(reg); >+ return true; >+ } >+ >+ // This can happen if we have a #WarmAnys > sizeof(registerFile). >+ return false; >+} >+ >+void GenerateAndAllocateRegisters::prepareForGeneration() >+{ >+ // We pessimistically assume we use all callee saves. >+ handleCalleeSaves(m_code, RegisterSet::calleeSaveRegisters()); >+ allocateEscapedStackSlots(m_code); >+ >+ // Each Tmp gets its own stack slot. >+ unsigned nextStackIndex = 0; >+ auto assignNextStackSlot = [&] (const Tmp& tmp) { >+ ptrdiff_t offset = -static_cast<ptrdiff_t>(m_code.frameSize()) - static_cast<ptrdiff_t>(nextStackIndex) * 8 - 8; >+ ++nextStackIndex; >+ >+ TmpData data; >+ data.spillSlot = m_code.addStackSlot(8, StackSlotKind::Spill); >+ data.spillSlot->setOffsetFromFP(offset); >+ data.reg = Reg(); >+ m_map[tmp] = data; >+#if !ASSERT_DISABLED >+ m_allTmps[tmp.bank()].append(tmp); >+#endif >+ }; >+ >+ m_code.forEachTmp([&] (Tmp tmp) { >+ ASSERT(!tmp.isReg()); >+ assignNextStackSlot(tmp); >+ }); >+ >+ m_allowedRegisters = RegisterSet(); >+ >+ forEachBank([&] (Bank bank) { >+ m_registers[bank] = m_code.regsInPriorityOrder(bank); >+ >+ for (Reg reg : m_registers[bank]) { >+ m_allowedRegisters.set(reg); >+ >+ Tmp tmp(reg); >+ assignNextStackSlot(tmp); >+ } >+ }); >+ >+ updateFrameSizeBasedOnStackSlots(m_code); >+ m_code.setStackIsAllocated(true); >+ >+ lowerStackArgs(m_code); >+ >+ // Verify none of these functions add any tmps. >+ /* >+#if !ASSERT_DISABLED >+ // OOPS: can't include named regs. find a way to add this back. >+ forEachBank([&] (Bank bank) { >+ ASSERT(m_allTmps[bank].size() == m_code.numTmps(bank)); >+ }); >+#endif >+*/ >+} >+ >+void GenerateAndAllocateRegisters::run(CCallHelpers& jit) >+{ >+ m_jit = &jit; >+ >+ // FIXME: We neither use the disassembler nor the CodeOriginMap. >+ // We could change this code to use those APIs. They're currently >+ // only used with JS code, and we don't want to any cycles calling >+ // into them. >+ >+ TimingScope timingScope("Air::generateAndAllocateRegisters"); >+ >+ gatherTerminalPatchSpills(); >+ >+ DisallowMacroScratchRegisterUsage disallowScratch(*m_jit); >+ >+ UnifiedTmpLiveness liveness(m_code); >+ m_liveRangeEnd = buildLiveRanges(m_code, liveness); >+ >+ IndexMap<BasicBlock*, IndexMap<Reg, Tmp>> currentAllocationMap(m_code.size()); >+ { >+ IndexMap<Reg, Tmp> defaultCurrentAllocation(Reg::maxIndex() + 1); >+ for (BasicBlock* block : m_code) { >+ if (block == m_code[0]) // Handled below. >+ continue; >+ currentAllocationMap[block] = defaultCurrentAllocation; >+ } >+ >+ for (Tmp tmp : liveness.liveAtHead(m_code[0])) { >+ if (!tmp.isReg()) >+ continue; >+ defaultCurrentAllocation[tmp.reg()] = tmp; >+ } >+ currentAllocationMap[m_code[0]] = defaultCurrentAllocation; >+ } >+ >+ // And now, we generate code. >+ GenerationContext context; >+ context.code = &m_code; >+ context.blockLabels.resize(m_code.size()); >+ for (BasicBlock* block : m_code) >+ context.blockLabels[block] = Box<CCallHelpers::Label>::create(); >+ IndexMap<BasicBlock*, CCallHelpers::JumpList> blockJumps(m_code.size()); >+ >+ auto link = [&] (CCallHelpers::Jump jump, BasicBlock* target) { >+ if (context.blockLabels[target]->isSet()) { >+ jump.linkTo(*context.blockLabels[target], m_jit); >+ return; >+ } >+ >+ blockJumps[target].append(jump); >+ }; >+ >+ Disassembler* disassembler = m_code.disassembler(); >+ >+ m_globalInstIndex = 0; >+ >+ for (BasicBlock* block : m_code) { >+ context.currentBlock = block; >+ context.indexInBlock = UINT_MAX; >+ blockJumps[block].link(m_jit); >+ CCallHelpers::Label label = m_jit->label(); >+ *context.blockLabels[block] = label; >+ >+ if (disassembler) >+ disassembler->startBlock(block, *m_jit); >+ >+ if (Optional<unsigned> entrypointIndex = m_code.entrypointIndex(block)) { >+ ASSERT(m_code.isEntrypoint(block)); >+ if (disassembler) >+ disassembler->startEntrypoint(*m_jit); >+ >+ m_code.prologueGeneratorForEntrypoint(*entrypointIndex)->run(*m_jit, m_code); >+ >+ if (disassembler) >+ disassembler->endEntrypoint(*m_jit); >+ } else >+ ASSERT(!m_code.isEntrypoint(block)); >+ >+ auto startLabel = m_jit->labelIgnoringWatchpoints(); >+ >+ { >+ auto iter = m_blocksForPatchSpilling.find(block); >+ if (iter != m_blocksForPatchSpilling.end()) { >+ auto& tuple = iter->value; >+ std::get<1>(tuple) = m_jit->jump(); >+ std::get<2>(tuple) = m_jit->label(); >+ } >+ } >+ //HashMap<BasicBlock*, std::tuple<Inst*, CCallHelpers::Jump, CCallHelpers::Label>> m_blocksForPatchSpilling; >+ >+ forEachBank([&] (Bank bank) { >+#if !ASSERT_DISABLED >+ // By default, everything is spilled at block boundaries. We do this after we process each block >+ // so we don't have to walk all Tmps, since #Tmps >> #Available regs. Instead, we walk the register file at >+ // each block boundary and clear entries in this map. >+ for (Tmp tmp : m_allTmps[bank]) >+ ASSERT(m_map[tmp].reg == Reg()); >+#endif >+ >+ RegisterSet availableRegisters; >+ for (Reg reg : m_registers[bank]) >+ availableRegisters.set(reg); >+ m_availableRegs[bank] = WTFMove(availableRegisters); >+ }); >+ >+ IndexMap<Reg, Tmp>& currentAllocation = currentAllocationMap[block]; >+ m_currentAllocation = ¤tAllocation; >+ >+ for (unsigned i = 0; i < currentAllocation.size(); ++i) { >+ Tmp tmp = currentAllocation[i]; >+ if (!tmp) >+ continue; >+ Reg reg = Reg::fromIndex(i); >+ m_map[tmp].reg = reg; >+ m_availableRegs[tmp.bank()].clear(reg); >+ } >+ >+ for (size_t instIndex = 0; instIndex < block->size(); ++instIndex) { >+ if (instIndex) >+ startLabel = m_jit->labelIgnoringWatchpoints(); >+ >+ context.indexInBlock = instIndex; >+ >+ Inst& inst = block->at(instIndex); >+ >+ m_didAlreadyFreeDeadSlots = false; >+ >+ m_namedUsedRegs = RegisterSet(); >+ m_namedDefdRegs = RegisterSet(); >+ >+ inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Bank, Width) { >+ if (tmp.isReg()) { >+ if (!m_allowedRegisters.get(tmp.reg())) >+ return; >+ >+ if (Arg::isAnyUse(role)) >+ m_namedUsedRegs.set(tmp.reg()); >+ if (Arg::isAnyDef(role)) >+ m_namedDefdRegs.set(tmp.reg()); >+ >+ return; >+ } >+ }); >+ >+ auto dumpState = [&] { >+ bool b = true; >+ if (b) >+ return; >+ dataLogLn("at inst: ", inst); >+ dataLogLn("namedUsed=", m_namedUsedRegs); >+ dataLogLn("namedDefd=", m_namedDefdRegs); >+ dataLog("currentAllocation={"); >+ for (size_t i = 0; i < currentAllocation.size(); ++i) { >+ if (Tmp tmp = currentAllocation[i]) { >+ dataLog(tmp, "=", Reg::fromIndex(i), ","); >+ } >+ } >+ dataLog("}\n"); >+ }; >+ >+ { >+ Inst* nextInst = block->get(instIndex + 1); >+ if (inst.kind.opcode == Patch || (nextInst && nextInst->kind.opcode == Patch)) { >+ if (inst.kind.opcode == Patch) >+ m_namedDefdRegs.merge(inst.extraClobberedRegs()); >+ if (nextInst && nextInst->kind.opcode == Patch) >+ m_namedDefdRegs.merge(nextInst->extraEarlyClobberedRegs()); >+ m_namedDefdRegs.filter(m_allowedRegisters); // OOPS: We don't really need to say that clobbered regs are in use after this inst. >+ } >+ } >+ >+ auto allocNamed = [&] (const RegisterSet& named, bool isDef) { >+ for (Reg reg : named) { >+ if (Tmp occupyingTmp = currentAllocation[reg]) { >+ if (occupyingTmp == Tmp(reg)) >+ continue; >+ } >+ >+ freeDeadTmpsIfNeeded(); // We don't want to spill a dead tmp. >+ alloc(Tmp(reg), reg, isDef); >+ } >+ }; >+ >+ allocNamed(m_namedUsedRegs, false); // Must come before the defd registers since we may use and def the same register. >+ allocNamed(m_namedDefdRegs, true); >+ //dataLogLn("state after alloc named"); >+ dumpState(); >+ >+ // OOPS: What happens when warmAny >= registerFile.... >+ inst.forEachArg([&] (Arg& arg, Arg::Role role, Bank, Width) { >+ if (!Arg::isColdUse(role)) >+ return; >+ if (!arg.isTmp()) >+ return; >+ >+ Tmp tmp = arg.tmp(); >+ if (tmp.isReg() && !m_allowedRegisters.contains(tmp.reg())) >+ return; >+ >+ if (!inst.admitsStack(arg)) >+ return; >+ >+ auto& entry = m_map[tmp]; >+ if (Reg reg = entry.reg) >+ flush(tmp, reg); >+ >+ arg = Arg::addr(Tmp(GPRInfo::callFrameRegister), entry.spillSlot->offsetFromFP()); >+ }); >+ >+ //dataLogLn("state after cold alloc"); >+ dumpState(); >+ >+ >+ { >+ auto tryAllocate = [&] { >+ Vector<Tmp*, 8> usesToAlloc; >+ Vector<Tmp*, 8> defsToAlloc; >+ >+ inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Bank, Width) { >+ if (tmp.isReg()) >+ return; >+ if (Arg::isAnyUse(role)) >+ usesToAlloc.append(&tmp); >+ if (Arg::isAnyDef(role)) >+ defsToAlloc.append(&tmp); >+ }); >+ >+ auto allocVector = [&] (auto& vector, bool isDef) { >+ bool success = true; >+ for (Tmp* tmp : vector) { >+ if (tmp->isReg()) { >+ // Use and Def could be same Tmp* >+ ASSERT(isDef); >+ continue; >+ } >+ success &= assignTmp(*tmp, tmp->bank(), isDef); >+ } >+ return success; >+ }; >+ >+ // We first handle uses, then defs. We want to be able to tell the register allocator >+ // which tmps need to be loaded from memory into their assigned register. Those such >+ // tmps are uses. Defs don't need to be reloaded since we're defining them. However, >+ // some tmps may both be used and defd. So we handle uses first since forEachTmp could >+ // walk uses/defs in any order. >+ bool success = true; >+ success &= allocVector(usesToAlloc, false); >+ success &= allocVector(defsToAlloc, true); >+ >+ return success; >+ }; >+ >+ bool success = tryAllocate(); >+ if (!success) { >+ inst.forEachArg([&] (Arg& arg, Arg::Role, Bank, Width) { >+ if (!arg.isTmp()) >+ return; >+ >+ Tmp tmp = arg.tmp(); >+ if (tmp.isReg() && !m_allowedRegisters.contains(tmp.reg())) >+ return; >+ >+ if (!inst.admitsStack(arg)) >+ return; >+ >+ if (tmp.isReg()) { >+ Tmp originalTmp = currentAllocation[tmp.reg()]; >+ if (originalTmp.isReg()) { >+ // This means this Inst referred to this reg directly. We leave these as is. >+ return; >+ } >+ tmp = originalTmp; >+ } >+ >+ auto& entry = m_map[tmp]; >+ if (Reg reg = entry.reg) >+ spill(tmp, reg); >+ >+ // OOPS: arm offset nonsense >+ arg = Arg::addr(Tmp(GPRInfo::callFrameRegister), entry.spillSlot->offsetFromFP()); >+ }); >+ >+ success = tryAllocate(); >+ RELEASE_ASSERT(success); >+ } >+ } >+ >+ //dataLogLn("state after each use"); >+ dumpState(); >+ >+ //dataLogLn("state after each def"); >+ dumpState(); >+ >+ if (m_code.needsUsedRegisters() && inst.kind.opcode == Patch) { >+ freeDeadTmpsIfNeeded(); >+ RegisterSet registerSet; >+ for (size_t i = 0; i < currentAllocation.size(); ++i) { >+ if (currentAllocation[i]) >+ registerSet.set(Reg::fromIndex(i)); >+ } >+ inst.reportUsedRegisters(registerSet); >+ } >+ >+ m_jit->nop(); >+ m_jit->nop(); >+ >+ // OOPS: Assert not terminal w/ def. If this isn't true, the below >+ // isn't our current regalloc state. If it is true, then this is our >+ // reg alloc state... >+ if (inst.isTerminal() && block->numSuccessors()) { >+ // We spill everything between block boundaries. >+ >+ bool canPassAlongRegisterState = true; >+ for (unsigned i = 0; i < block->numSuccessors(); ++i) { >+ BasicBlock* succ = block->successorBlock(i); >+ canPassAlongRegisterState &= succ->numPredecessors() == 1 && !context.blockLabels[succ]->isSet(); >+ } >+ if (canPassAlongRegisterState) { >+ for (unsigned i = 0; i < block->numSuccessors(); ++i) { >+ BasicBlock* succ = block->successorBlock(i); >+ currentAllocationMap[succ] = currentAllocation; >+ } >+ } else { >+ for (Tmp tmp : liveness.liveAtTail(block)) { >+ //dataLogLn("block: ", block->index(), " tmp is alive: ", tmp); >+ if (tmp.isReg() && !m_allowedRegisters.contains(tmp.reg())) >+ continue; >+ if (Reg reg = m_map[tmp].reg) { >+ flush(tmp, reg); >+ //dataLogLn("block: ", block->index(), " flushing ", tmp, " in ", reg); >+ } >+ } >+ } >+ } >+ >+ if (!inst.isTerminal()) { >+ CCallHelpers::Jump jump = inst.generate(*m_jit, context); >+ ASSERT_UNUSED(jump, !jump.isSet()); >+ } else { >+ bool needsToGenerate = true; >+ if (inst.kind.opcode == Jump && block->successorBlock(0) == m_code.findNextBlock(block)) >+ needsToGenerate = false; >+ >+ if (isReturn(inst.kind.opcode)) { >+ needsToGenerate = false; >+ >+ // We currently don't represent the full epilogue in Air, so we need to >+ // have this override. >+ if (m_code.frameSize()) { >+ m_jit->emitRestore(m_code.calleeSaveRegisterAtOffsetList()); >+ m_jit->emitFunctionEpilogue(); >+ } else >+ m_jit->emitFunctionEpilogueWithEmptyFrame(); >+ m_jit->ret(); >+ } >+ >+ if (needsToGenerate) { >+ CCallHelpers::Jump jump = block->last().generate(*m_jit, context); >+ >+ // The jump won't be set for patchpoints. It won't be set for Oops because then it won't have >+ // any successors. >+ if (jump.isSet()) { >+ switch (block->numSuccessors()) { >+ case 1: >+ link(jump, block->successorBlock(0)); >+ break; >+ case 2: >+ link(jump, block->successorBlock(0)); >+ if (block->successorBlock(1) != m_code.findNextBlock(block)) >+ link(m_jit->jump(), block->successorBlock(1)); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ } >+ } >+ } >+ >+ auto endLabel = m_jit->labelIgnoringWatchpoints(); >+ if (disassembler) >+ disassembler->addInst(&inst, startLabel, endLabel); >+ >+ ++m_globalInstIndex; >+ } >+ >+ // Registers usually get spilled at block boundaries. We do it this way since we don't >+ // want to iterate the entire TmpMap, since usually #Tmps >> #Regs. We may not actually spill >+ // all registers, but at the top of this loop we handle that case by pre-populating register >+ // state. Here, we just clear this map. After this loop, this map should contain only >+ // null entries. >+ for (size_t i = 0; i < currentAllocation.size(); ++i) { >+ if (Tmp tmp = currentAllocation[i]) >+ m_map[tmp].reg = Reg(); >+ } >+ } >+ >+ for (auto& entry : m_blocksForPatchSpilling) { >+ std::get<1>(entry.value).linkTo(m_jit->label(), m_jit); >+ const HashMap<Tmp, Tmp*>& spills = std::get<3>(entry.value); >+ for (auto& entry : spills) { >+ ASSERT(entry.value->isReg()); >+ flush(entry.key, entry.value->reg()); >+ } >+ m_jit->jump().linkTo(std::get<2>(entry.value), m_jit); >+ } >+ >+ context.currentBlock = nullptr; >+ context.indexInBlock = UINT_MAX; >+ >+ Vector<CCallHelpers::Label> entrypointLabels(m_code.numEntrypoints()); >+ for (unsigned i = m_code.numEntrypoints(); i--;) >+ entrypointLabels[i] = *context.blockLabels[m_code.entrypoint(i).block()]; >+ m_code.setEntrypointLabels(WTFMove(entrypointLabels)); >+ >+ if (disassembler) >+ disassembler->startLatePath(*m_jit); >+ >+ // FIXME: Make late paths have Origins: https://bugs.webkit.org/show_bug.cgi?id=153689 >+ for (auto& latePath : context.latePaths) >+ latePath->run(*m_jit, context); >+ >+ if (disassembler) >+ disassembler->endLatePath(*m_jit); >+} >+ >+} } } // namespace JSC::B3::Air >+ >+#endif // ENABLE(B3_JIT) >Index: Source/JavaScriptCore/b3/air/AirAllocateRegistersAndStackAndGenerateCode.h >=================================================================== >--- Source/JavaScriptCore/b3/air/AirAllocateRegistersAndStackAndGenerateCode.h (nonexistent) >+++ Source/JavaScriptCore/b3/air/AirAllocateRegistersAndStackAndGenerateCode.h (working copy) >@@ -0,0 +1,82 @@ >+/* >+ * Copyright (C) 2019 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(B3_JIT) >+ >+#include "AirTmpMap.h" >+ >+namespace JSC { >+ >+class CCallHelpers; >+ >+namespace B3 { namespace Air { >+ >+class Code; >+ >+class GenerateAndAllocateRegisters { >+ struct TmpData { >+ StackSlot* spillSlot; >+ Reg reg; >+ }; >+ >+public: >+ GenerateAndAllocateRegisters(Code&); >+ void gatherTerminalPatchSpills(); >+ >+ void flush(Tmp tmp, Reg reg); >+ void spill(Tmp tmp, Reg reg); >+ void alloc(Tmp tmp, Reg reg, bool isDef); >+ void freeDeadTmpsIfNeeded(); >+ bool assignTmp(Tmp& tmp, Bank bank, bool isDef); >+ void prepareForGeneration(); >+ void run(CCallHelpers&); >+ >+private: >+ Code& m_code; >+ CCallHelpers* m_jit { nullptr }; >+ >+ TmpMap<TmpData> m_map; >+ >+#if !ASSERT_DISABLED >+ Vector<Tmp> m_allTmps[numBanks]; >+#endif >+ >+ Vector<Reg> m_registers[numBanks]; >+ RegisterSet m_availableRegs[numBanks]; >+ size_t m_globalInstIndex; >+ IndexMap<Reg, Tmp>* m_currentAllocation { nullptr }; >+ bool m_didAlreadyFreeDeadSlots; >+ TmpMap<size_t> m_liveRangeEnd; >+ RegisterSet m_namedUsedRegs; >+ RegisterSet m_namedDefdRegs; >+ RegisterSet m_allowedRegisters; >+ HashMap<BasicBlock*, std::tuple<Inst*, CCallHelpers::Jump, CCallHelpers::Label, HashMap<Tmp, Tmp*>>> m_blocksForPatchSpilling; >+}; >+ >+} } } // namespace JSC::B3::Air >+ >+#endif // ENABLE(B3_JIT) >Index: Source/JavaScriptCore/b3/air/AirCode.cpp >=================================================================== >--- Source/JavaScriptCore/b3/air/AirCode.cpp (revision 240752) >+++ Source/JavaScriptCore/b3/air/AirCode.cpp (working copy) >@@ -28,6 +28,7 @@ > > #if ENABLE(B3_JIT) > >+#include "AirAllocateRegistersAndStackAndGenerateCode.h" > #include "AirCCallSpecial.h" > #include "AirCFG.h" > #include "AllowMacroScratchRegisterUsageIf.h" >Index: Source/JavaScriptCore/b3/air/AirCode.h >=================================================================== >--- Source/JavaScriptCore/b3/air/AirCode.h (revision 240752) >+++ Source/JavaScriptCore/b3/air/AirCode.h (working copy) >@@ -50,6 +50,7 @@ IGNORE_RETURN_TYPE_WARNINGS_BEGIN > > namespace Air { > >+class GenerateAndAllocateRegisters; > class BlockInsertionSet; > class CCallSpecial; > class CFG; >@@ -337,6 +338,8 @@ public: > WeakRandom& weakRandom() { return m_weakRandom; } > > void emitDefaultPrologue(CCallHelpers&); >+ >+ std::unique_ptr<GenerateAndAllocateRegisters> m_generateAndAllocateRegisters; > > private: > friend class ::JSC::B3::Procedure; >Index: Source/JavaScriptCore/b3/air/AirGenerate.cpp >=================================================================== >--- Source/JavaScriptCore/b3/air/AirGenerate.cpp (revision 240697) >+++ Source/JavaScriptCore/b3/air/AirGenerate.cpp (working copy) >@@ -28,6 +28,7 @@ > > #if ENABLE(B3_JIT) > >+#include "AirAllocateRegistersAndStackAndGenerateCode.h" > #include "AirAllocateRegistersAndStackByLinearScan.h" > #include "AirAllocateRegistersByGraphColoring.h" > #include "AirAllocateStackByGraphColoring.h" >@@ -36,6 +37,8 @@ > #include "AirFixObviousSpills.h" > #include "AirFixPartialRegisterStalls.h" > #include "AirGenerationContext.h" >+#include "AirHandleCalleeSaves.h" >+#include "AirLiveness.h" > #include "AirLogRegisterPressure.h" > #include "AirLowerAfterRegAlloc.h" > #include "AirLowerEntrySwitch.h" >@@ -45,6 +48,8 @@ > #include "AirOptimizeBlockOrder.h" > #include "AirReportUsedRegisters.h" > #include "AirSimplifyCFG.h" >+#include "AirStackAllocation.h" >+#include "AirTmpMap.h" > #include "AirValidate.h" > #include "B3Common.h" > #include "B3Procedure.h" >@@ -73,6 +78,34 @@ void prepareForGeneration(Code& code) > if (shouldValidateIR()) > validate(code); > >+ if (code.optLevel() <= 1) { >+ lowerMacros(code); >+ >+ // We may still need to do post-allocation lowering. Doing it after both register and >+ // stack allocation is less optimal, but it works fine. >+ lowerAfterRegAlloc(code); >+ >+ // Actually create entrypoints. >+ lowerEntrySwitch(code); >+ >+ // This sorts the basic blocks in Code to achieve an ordering that maximizes the likelihood that a high >+ // frequency successor is also the fall-through target. >+ optimizeBlockOrder(code); >+ >+ if (shouldValidateIR()) >+ validate(code); >+ >+ if (shouldDumpIR(AirMode)) { >+ dataLog("Air after ", code.lastPhaseName(), ", before generation:\n"); >+ dataLog(code); >+ } >+ >+ code.m_generateAndAllocateRegisters = std::make_unique<GenerateAndAllocateRegisters>(code); >+ code.m_generateAndAllocateRegisters->prepareForGeneration(); >+ >+ return; >+ } >+ > simplifyCFG(code); > > lowerMacros(code); >@@ -161,7 +194,7 @@ void prepareForGeneration(Code& code) > } > } > >-void generate(Code& code, CCallHelpers& jit) >+NEVER_INLINE static void generateAlreadyAlloced(Code& code, CCallHelpers& jit) > { > TimingScope timingScope("Air::generate"); > >@@ -171,10 +204,8 @@ void generate(Code& code, CCallHelpers& > GenerationContext context; > context.code = &code; > context.blockLabels.resize(code.size()); >- for (BasicBlock* block : code) { >- if (block) >- context.blockLabels[block] = Box<CCallHelpers::Label>::create(); >- } >+ for (BasicBlock* block : code) >+ context.blockLabels[block] = Box<CCallHelpers::Label>::create(); > IndexMap<BasicBlock*, CCallHelpers::JumpList> blockJumps(code.size()); > > auto link = [&] (CCallHelpers::Jump jump, BasicBlock* target) { >@@ -305,6 +336,14 @@ void generate(Code& code, CCallHelpers& > pcToOriginMap.appendItem(jit.labelIgnoringWatchpoints(), Origin()); > } > >+void generate(Code& code, CCallHelpers& jit) >+{ >+ if (code.optLevel() > 1) >+ generateAlreadyAlloced(code, jit); >+ else >+ code.m_generateAndAllocateRegisters->run(jit); >+} >+ > } } } // namespace JSC::B3::Air > > #endif // ENABLE(B3_JIT) >Index: Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.cpp >=================================================================== >--- Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.cpp (revision 240697) >+++ Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.cpp (working copy) >@@ -50,7 +50,12 @@ void handleCalleeSaves(Code& code) > } > } > >- // Now we filter to really get the callee saves. >+ handleCalleeSaves(code, WTFMove(usedCalleeSaves)); >+} >+ >+void handleCalleeSaves(Code& code, RegisterSet usedCalleeSaves) >+{ >+ // We filter to really get the callee saves. > usedCalleeSaves.filter(RegisterSet::calleeSaveRegisters()); > usedCalleeSaves.filter(code.mutableRegs()); > usedCalleeSaves.exclude(RegisterSet::stackRegisters()); // We don't need to save FP here. >Index: Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.h >=================================================================== >--- Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.h (revision 240697) >+++ Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.h (working copy) >@@ -41,6 +41,7 @@ class Code; > // We should make this interact with the client: https://bugs.webkit.org/show_bug.cgi?id=150459 > > void handleCalleeSaves(Code&); >+void handleCalleeSaves(Code&, RegisterSet); > > } } } // namespace JSC::B3::Air > >Index: Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (revision 240697) >+++ Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (working copy) >@@ -98,6 +98,8 @@ > #include <wtf/RecursableLambda.h> > #include <wtf/StdUnorderedSet.h> > >+#include "MacroAssemblerPrinter.h" >+ > #undef RELEASE_ASSERT > #define RELEASE_ASSERT(assertion) do { \ > if (!(assertion)) { \ >@@ -13091,10 +13093,11 @@ private: > CCallHelpers::JumpList jumpToSlowPath; > > GPRReg allocatorGPR; >- if (actualAllocator.isConstant()) >+ if (actualAllocator.isConstant()) { > allocatorGPR = params.gpScratch(1); >- else >+ } else { > allocatorGPR = params[1].gpr(); >+ } > > // We use a patchpoint to emit the allocation path because whenever we mess with > // allocation paths, we already reason about them at the machine code level. We know >Index: Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp (revision 240697) >+++ Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp (working copy) >@@ -397,6 +397,9 @@ static void compileStub( > > // Before we start messing with the frame, we need to set aside any registers that the > // FTL code was preserving. >+ jit.nop(); >+ jit.nop(); >+ jit.nop(); > for (unsigned i = codeBlock->calleeSaveRegisters()->size(); i--;) { > RegisterAtOffset entry = codeBlock->calleeSaveRegisters()->at(i); > jit.load64( >@@ -404,6 +407,9 @@ static void compileStub( > GPRInfo::regT0); > jit.store64(GPRInfo::regT0, unwindScratch + i); > } >+ jit.nop(); >+ jit.nop(); >+ jit.nop(); > > CodeBlock* baselineCodeBlock = jit.baselineCodeBlockFor(exit.m_codeOrigin); > >Index: Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >=================================================================== >--- Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp (revision 240752) >+++ Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp (working copy) >@@ -284,6 +284,17 @@ public: > return result; > } > >+ ALWAYS_INLINE void didDie(const ExpressionType& typedTmp) >+ { >+ Tmp tmp = typedTmp.tmp(); >+ if (!tmp) >+ return; >+ if (tmp.isGP()) >+ m_freeGPs.append(tmp); >+ else >+ m_freeFPs.append(tmp); >+ } >+ > private: > ALWAYS_INLINE void validateInst(Inst& inst) > { >@@ -323,6 +334,16 @@ private: > > Tmp newTmp(B3::Bank bank) > { >+ switch (bank) { >+ case B3::GP: >+ if (m_freeGPs.size()) >+ return m_freeGPs.takeLast(); >+ break; >+ case B3::FP: >+ if (m_freeFPs.size()) >+ return m_freeFPs.takeLast(); >+ break; >+ } > return m_code.newTmp(bank); > } > >@@ -573,6 +594,9 @@ private: > GPRReg m_wasmContextInstanceGPR { InvalidGPRReg }; > bool m_makesCalls { false }; > >+ Vector<Tmp, 8> m_freeGPs; >+ Vector<Tmp, 8> m_freeFPs; >+ > TypedTmp m_instanceValue; // Always use the accessor below to ensure the instance value is materialized when used. > bool m_usesInstanceValue { false }; > TypedTmp instanceValue() >@@ -1895,6 +1919,9 @@ Expected<std::unique_ptr<InternalFunctio > // optLevel=1. > procedure.setNeedsUsedRegisters(false); > >+ //procedure.setOptLevel(compilationMode == CompilationMode::BBQMode >+ // ? Options::webAssemblyBBQOptimizationLevel() >+ // : Options::webAssemblyOMGOptimizationLevel()); > procedure.setOptLevel(compilationMode == CompilationMode::BBQMode > ? Options::webAssemblyBBQOptimizationLevel() > : Options::webAssemblyOMGOptimizationLevel()); >Index: Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >=================================================================== >--- Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp (revision 240752) >+++ Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp (working copy) >@@ -230,6 +230,8 @@ public: > Value* constant(B3::Type, uint64_t bits, Optional<Origin> = WTF::nullopt); > void insertConstants(); > >+ ALWAYS_INLINE void didDie(ExpressionType) { } >+ > private: > void emitExceptionCheck(CCallHelpers&, ExceptionType); > >Index: Source/JavaScriptCore/wasm/WasmBBQPlan.h >=================================================================== >--- Source/JavaScriptCore/wasm/WasmBBQPlan.h (revision 240697) >+++ Source/JavaScriptCore/wasm/WasmBBQPlan.h (working copy) >@@ -147,6 +147,7 @@ private: > HashMap<uint32_t, std::unique_ptr<InternalFunction>, typename DefaultHash<uint32_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint32_t>> m_embedderToWasmInternalFunctions; > Vector<CompilationContext> m_compilationContexts; > Vector<TierUpCount> m_tierUpCounts; >+ Vector<std::pair<size_t, unsigned>> m_sortedFunctions; > > Vector<Vector<UnlinkedWasmToWasmCall>> m_unlinkedWasmToWasmCalls; > State m_state; >Index: Source/JavaScriptCore/wasm/WasmFunctionParser.h >=================================================================== >--- Source/JavaScriptCore/wasm/WasmFunctionParser.h (revision 240752) >+++ Source/JavaScriptCore/wasm/WasmFunctionParser.h (working copy) >@@ -168,6 +168,8 @@ auto FunctionParser<Context>::binaryCase > 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)); >+ m_context.didDie(left); >+ m_context.didDie(right); > > m_expressionStack.append(result); > return { }; >@@ -182,6 +184,7 @@ auto FunctionParser<Context>::unaryCase( > > WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "unary"); > WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(value, result)); >+ m_context.didDie(value); > > m_expressionStack.append(result); > return { }; >@@ -211,6 +214,10 @@ auto FunctionParser<Context>::parseExpre > ExpressionType result; > WASM_TRY_ADD_TO_CONTEXT(addSelect(condition, nonZero, zero, result)); > >+ m_context.didDie(condition); >+ m_context.didDie(zero); >+ m_context.didDie(nonZero); >+ > m_expressionStack.append(result); > return { }; > } >@@ -226,6 +233,7 @@ auto FunctionParser<Context>::parseExpre > 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_context.didDie(pointer); > m_expressionStack.append(result); > return { }; > } >@@ -241,6 +249,8 @@ auto FunctionParser<Context>::parseExpre > 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)); >+ m_context.didDie(value); >+ m_context.didDie(pointer); > return { }; > } > #undef CREATE_CASE >@@ -288,6 +298,7 @@ auto FunctionParser<Context>::parseExpre > 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)); >+ m_context.didDie(value); > return { }; > } > >@@ -314,6 +325,7 @@ auto FunctionParser<Context>::parseExpre > 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)); >+ m_context.didDie(value); > return { }; > } > >@@ -396,6 +408,7 @@ auto FunctionParser<Context>::parseExpre > WASM_TRY_ADD_TO_CONTEXT(addIf(condition, inlineSignature, control)); > m_controlStack.append({ WTFMove(m_expressionStack), control }); > m_expressionStack = ExpressionList(); >+ m_context.didDie(condition); > return { }; > } > >@@ -420,6 +433,9 @@ auto FunctionParser<Context>::parseExpre > ControlType& data = m_controlStack[m_controlStack.size() - 1 - target].controlData; > > WASM_TRY_ADD_TO_CONTEXT(addBranch(data, condition, m_expressionStack)); >+ >+ m_context.didDie(condition); >+ > return { }; > } > >@@ -446,6 +462,8 @@ auto FunctionParser<Context>::parseExpre > 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)); > >+ m_context.didDie(condition); >+ > m_unreachableBlocks = 1; > return { }; > } >@@ -503,6 +521,8 @@ auto FunctionParser<Context>::parseExpre > WASM_TRY_ADD_TO_CONTEXT(addGrowMemory(delta, result)); > m_expressionStack.append(result); > >+ m_context.didDie(delta); >+ > return { }; > } > >Index: Source/JavaScriptCore/wasm/WasmValidate.cpp >=================================================================== >--- Source/JavaScriptCore/wasm/WasmValidate.cpp (revision 240752) >+++ Source/JavaScriptCore/wasm/WasmValidate.cpp (working copy) >@@ -141,6 +141,8 @@ public: > 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); > >+ ALWAYS_INLINE void didDie(ExpressionType) { } >+ > bool hasMemory() const { return !!m_module.memory; } > > Validate(const ModuleInformation& module)
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 194036
:
360608
|
360647
|
360667
|
360907
|
360947
|
360948
|
361270
|
361481
|
361488
|
361496
|
361497
|
361716
|
361742
|
361989
|
361990
|
361991
|
362002
|
362055
|
362098