WebKit Bugzilla
Attachment 360947 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), 32.47 KB, created by
Saam Barati
on 2019-02-01 19:13:06 PST
(
hide
)
Description:
WIP
Filename:
MIME Type:
Creator:
Saam Barati
Created:
2019-02-01 19:13:06 PST
Size:
32.47 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/b3/air/AirGenerate.cpp >=================================================================== >--- Source/JavaScriptCore/b3/air/AirGenerate.cpp (revision 240697) >+++ Source/JavaScriptCore/b3/air/AirGenerate.cpp (working copy) >@@ -36,6 +36,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 +47,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" >@@ -57,6 +61,14 @@ > > namespace JSC { namespace B3 { namespace Air { > >+ALWAYS_INLINE size_t rdtsc() >+{ >+ unsigned high; >+ unsigned low; >+ asm volatile ("rdtsc" : "=a"(low), "=d"(high)); >+ return low; >+} >+ > void prepareForGeneration(Code& code) > { > TimingScope timingScope("Air::prepareForGeneration"); >@@ -73,6 +85,36 @@ void prepareForGeneration(Code& code) > if (shouldValidateIR()) > validate(code); > >+ if (code.optLevel() == 0) { >+ 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); >+ >+ /* >+ // The control flow graph can be simplified further after we have lowered EntrySwitch. >+ simplifyCFG(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); >+ } >+ >+ return; >+ } >+ > simplifyCFG(code); > > lowerMacros(code); >@@ -161,7 +203,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 +213,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 +345,523 @@ void generate(Code& code, CCallHelpers& > pcToOriginMap.appendItem(jit.labelIgnoringWatchpoints(), Origin()); > } > >+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; >+ >+ >+class GenerateAndAllocateRegisters { >+ struct TmpData { >+ StackSlot* spillSlot; >+ Reg reg; >+ }; >+ >+public: >+ >+ GenerateAndAllocateRegisters(Code& code, CCallHelpers& jit) >+ : m_code(code) >+ , m_jit(jit) >+ , m_map(code) >+ { } >+ >+ ALWAYS_INLINE void flush(Tmp tmp, Reg reg) >+ { >+ ASSERT(tmp); >+ ASSERT(m_map[tmp].reg == reg); >+ // 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 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 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 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 void 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; >+ } >+ >+ 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; >+ } >+ >+ 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; >+ } >+ >+ RELEASE_ASSERT_NOT_REACHED(); >+ } >+ >+ void run() >+ { >+ // 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"); >+ >+ DisallowMacroScratchRegisterUsage disallowScratch(m_jit); >+ >+ RegisterSet allowedRegisters; >+ >+ // 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); >+ }); >+ >+ forEachBank([&] (Bank bank) { >+ m_registers[bank] = m_code.regsInPriorityOrder(bank); >+ >+ for (Reg reg : m_registers[bank]) { >+ 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 >+ forEachBank([&] (Bank bank) { >+ ASSERT(m_allTmps[bank].size() == m_code.numTmps(bank)); >+ }); >+#endif >+ } >+ >+ UnifiedTmpLiveness liveness(m_code); >+ >+ m_liveRangeEnd = buildLiveRanges(m_code, liveness); >+ >+ // forEachTmp = ~32654956 >+ // actual alloc loop = ~196500447 >+ // last actual alloc loop = 162908252 >+ // alloc lambda = 264039289 >+ // alloc spill = 139815925 >+ // insertionSet.insert = 204328737 >+ // entire reg alloc = 703087528 >+ >+ 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); >+ }; >+ >+ 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 (Optional<unsigned> entrypointIndex = m_code.entrypointIndex(block)) { >+ ASSERT(m_code.isEntrypoint(block)); >+ m_code.prologueGeneratorForEntrypoint(*entrypointIndex)->run(m_jit, m_code); >+ } else >+ ASSERT(!m_code.isEntrypoint(block)); >+ >+ 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]) >+ RELEASE_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) { >+ 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 (!allowedRegisters.get(tmp.reg())) >+ return; >+ >+ if (Arg::isAnyUse(role)) >+ m_namedUsedRegs.set(tmp.reg()); >+ if (Arg::isAnyDef(role)) >+ m_namedDefdRegs.set(tmp.reg()); >+ >+ return; >+ } >+ }); >+ >+ { >+ 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(allowedRegisters); >+ } >+ } >+ >+ auto allocNamed = [&] (const RegisterSet& named, bool isDef) { >+ for (Reg reg : named) { >+ if (Tmp occupyingTmp = currentAllocation[reg]) { >+ // Something is in this register. >+ 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); >+ >+ inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Bank bank, Width) { >+ if (tmp.isReg()) >+ return; >+ if (Arg::isAnyUse(role)) { >+ // We do defs below since we may also use the same temp as we def it, and we need to be sure it's only a def and no use >+ bool isDef = false; >+ assignTmp(tmp, bank, isDef); >+ } >+ }); >+ >+ inst.forEachTmpFast([&] (Tmp& tmp) { >+ if (tmp.isReg()) >+ return; >+ // If we're not yet allocated, we must be a def since we handled uses above. >+ bool isDef = true; >+ assignTmp(tmp, tmp.bank(), isDef); >+ }); >+ >+ // 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)) { >+ if (tmp.isReg() && !allowedRegisters.contains(tmp.reg())) >+ continue; >+ if (Reg reg = m_map[tmp].reg) >+ flush(tmp, reg); >+ } >+ } >+ } >+ >+ // OOPS: Figure out terminals that are Patch that also def. >+ 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; >+ } >+ } >+ } >+ } >+ >+ ++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(); >+ } >+ } >+ >+ 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)); >+ >+ // 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); >+ } >+ >+private: >+ Code& m_code; >+ CCallHelpers& m_jit; >+ >+ 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; >+}; >+ >+NEVER_INLINE static void generateAndAllocateRegisters(Code& code, CCallHelpers& jit) >+{ >+ GenerateAndAllocateRegisters generator(code, jit); >+ generator.run(); >+} >+ >+void generate(Code& code, CCallHelpers& jit) >+{ >+ if (code.optLevel() > 0) >+ generateAlreadyAlloced(code, jit); >+ else >+ generateAndAllocateRegisters(code, 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/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