WebKit Bugzilla
Attachment 361989 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
c-backup.diff (text/plain), 86.73 KB, created by
Saam Barati
on 2019-02-13 20:51:17 PST
(
hide
)
Description:
WIP
Filename:
MIME Type:
Creator:
Saam Barati
Created:
2019-02-13 20:51:17 PST
Size:
86.73 KB
patch
obsolete
>Index: JSTests/stress/tail-call-many-arguments.js >=================================================================== >--- JSTests/stress/tail-call-many-arguments.js (nonexistent) >+++ JSTests/stress/tail-call-many-arguments.js (working copy) >@@ -0,0 +1,21 @@ >+"use strict"; >+ >+function foo(...args) { >+ return args; >+} >+noInline(foo); >+ >+function bar(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35) >+{ >+ return foo(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35); >+} >+noInline(bar); >+ >+let args = []; >+for (let i = 0; i < 35; ++i) { >+ args.push(i); >+} >+ >+for (let i = 0; i < 100000; ++i) { >+ bar(...args); >+} >Index: Source/JavaScriptCore/Sources.txt >=================================================================== >--- Source/JavaScriptCore/Sources.txt (revision 241482) >+++ 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 241482) >+++ 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 */; }; >@@ -3353,6 +3354,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>"; }; >@@ -5435,6 +5438,8 @@ > 0FEC84B31BDACD880080FF74 /* air */ = { > isa = PBXGroup; > children = ( >+ 524E9D7122092B4500A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.cpp */, >+ 524E9D7222092B4600A6BEEE /* AirAllocateRegistersAndStackAndGenerateCode.h */, > 0F2AC5681E8A0BD10001EE3F /* AirAllocateRegistersAndStackByLinearScan.cpp */, > 0F2AC5691E8A0BD10001EE3F /* AirAllocateRegistersAndStackByLinearScan.h */, > 7965C2141E5D799600B7591D /* AirAllocateRegistersByGraphColoring.cpp */, >@@ -8852,6 +8857,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/testb3.cpp >=================================================================== >--- Source/JavaScriptCore/b3/testb3.cpp (revision 241482) >+++ Source/JavaScriptCore/b3/testb3.cpp (working copy) >@@ -3534,7 +3534,7 @@ void testBitNotOnBooleanAndBranch32(int6 > root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)); > Value* argsAreEqual = root->appendNew<Value>(proc, Equal, Origin(), arg1, arg2); > Value* argsAreNotEqual = root->appendNew<Value>(proc, BitXor, Origin(), >- root->appendNew<Const32Value>(proc, Origin(), -1), >+ root->appendNew<Const32Value>(proc, Origin(), 1), > argsAreEqual); > > root->appendNewControlValue( >@@ -8257,6 +8257,13 @@ void testSimplePatchpointWithOuputClobbe > // spill everything else. > > Procedure proc; >+ if (proc.optLevel() < 1) { >+ // FIXME: Air O0 allocator can't handle such programs. We rely on WasmAirIRGenerator >+ // to not use any such constructs where the register allocator is cornered in such >+ // a way. >+ // https://bugs.webkit.org/show_bug.cgi?id=194633 >+ return; >+ } > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); >@@ -8330,6 +8337,13 @@ void testSimplePatchpointWithoutOuputClo > void testSimplePatchpointWithOuputClobbersFPArgs() > { > Procedure proc; >+ if (proc.optLevel() < 1) { >+ // FIXME: Air O0 allocator can't handle such programs. We rely on WasmAirIRGenerator >+ // to not use any such constructs where the register allocator is cornered in such >+ // a way. >+ // https://bugs.webkit.org/show_bug.cgi?id=194633 >+ return; >+ } > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0); > Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1); >@@ -8380,10 +8394,13 @@ void testPatchpointWithEarlyClobber() > patchpoint->append(ConstrainedValue(arg1, ValueRep::SomeRegister)); > patchpoint->append(ConstrainedValue(arg2, ValueRep::SomeRegister)); > patchpoint->clobberEarly(RegisterSet(registerToClobber)); >+ unsigned optLevel = proc.optLevel(); > patchpoint->setGenerator( > [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { >- CHECK((params[1].gpr() == GPRInfo::argumentGPR0) == arg1InArgGPR); >- CHECK((params[2].gpr() == GPRInfo::argumentGPR1) == arg2InArgGPR); >+ if (optLevel > 0) { >+ CHECK((params[1].gpr() == GPRInfo::argumentGPR0) == arg1InArgGPR); >+ CHECK((params[2].gpr() == GPRInfo::argumentGPR1) == arg2InArgGPR); >+ } > > add32(jit, params[1].gpr(), params[2].gpr(), params[0].gpr()); > }); >@@ -8719,6 +8736,8 @@ void testPatchpointWithAnyResult() > void testSimpleCheck() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > CheckValue* check = root->appendNew<CheckValue>(proc, Check, Origin(), arg); >@@ -8748,9 +8767,11 @@ void testCheckFalse() > BasicBlock* root = proc.addBlock(); > CheckValue* check = root->appendNew<CheckValue>( > proc, Check, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0)); >+ unsigned optLevel = proc.optLevel(); > check->setGenerator( > [&] (CCallHelpers&, const StackmapGenerationParams&) { >- CHECK(!"This should not have executed"); >+ if (optLevel > 1) >+ CHECK(!"This should not have executed"); > }); > root->appendNewControlValue( > proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0)); >@@ -8763,13 +8784,17 @@ void testCheckFalse() > void testCheckTrue() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > CheckValue* check = root->appendNew<CheckValue>( > proc, Check, Origin(), root->appendNew<Const32Value>(proc, Origin(), 1)); >+ unsigned optLevel = proc.optLevel(); > check->setGenerator( > [&] (CCallHelpers& jit, const StackmapGenerationParams& params) { > AllowMacroScratchRegisterUsage allowScratch(jit); >- CHECK(params.value()->opcode() == Patchpoint); >+ if (optLevel > 1) >+ CHECK(params.value()->opcode() == Patchpoint); > CHECK(!params.size()); > > // This should always work because a function this simple should never have callee >@@ -8789,6 +8814,8 @@ void testCheckTrue() > void testCheckLessThan() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -8824,6 +8851,8 @@ void testCheckLessThan() > void testCheckMegaCombo() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > Value* index = root->appendNew<Value>( >@@ -8876,6 +8905,8 @@ void testCheckMegaCombo() > void testCheckTrickyMegaCombo() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > Value* index = root->appendNew<Value>( >@@ -8931,6 +8962,8 @@ void testCheckTrickyMegaCombo() > void testCheckTwoMegaCombos() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* base = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > Value* index = root->appendNew<Value>( >@@ -8995,6 +9028,8 @@ void testCheckTwoMegaCombos() > void testCheckTwoNonRedundantMegaCombos() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > > BasicBlock* root = proc.addBlock(); > BasicBlock* thenCase = proc.addBlock(); >@@ -9088,6 +9123,8 @@ void testCheckTwoNonRedundantMegaCombos( > void testCheckAddImm() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9124,6 +9161,8 @@ void testCheckAddImm() > void testCheckAddImmCommute() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9160,6 +9199,8 @@ void testCheckAddImmCommute() > void testCheckAddImmSomeRegister() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9195,6 +9236,8 @@ void testCheckAddImmSomeRegister() > void testCheckAdd() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9232,6 +9275,8 @@ void testCheckAdd() > void testCheckAdd64() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); >@@ -9269,9 +9314,11 @@ void testCheckAddFold(int a, int b) > Value* arg1 = root->appendNew<Const32Value>(proc, Origin(), a); > Value* arg2 = root->appendNew<Const32Value>(proc, Origin(), b); > CheckValue* checkAdd = root->appendNew<CheckValue>(proc, CheckAdd, Origin(), arg1, arg2); >+ unsigned optLevel = proc.optLevel(); > checkAdd->setGenerator( > [&] (CCallHelpers&, const StackmapGenerationParams&) { >- CHECK(!"Should have been folded"); >+ if (optLevel > 1) >+ CHECK(!"Should have been folded"); > }); > root->appendNewControlValue(proc, Return, Origin(), checkAdd); > >@@ -9283,6 +9330,8 @@ void testCheckAddFold(int a, int b) > void testCheckAddFoldFail(int a, int b) > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Const32Value>(proc, Origin(), a); > Value* arg2 = root->appendNew<Const32Value>(proc, Origin(), b); >@@ -9384,6 +9433,8 @@ void testCheckAddArgumentAliasing32() > void testCheckAddSelfOverflow64() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > CheckValue* checkAdd = root->appendNew<CheckValue>(proc, CheckAdd, Origin(), arg, arg); >@@ -9413,6 +9464,8 @@ void testCheckAddSelfOverflow64() > void testCheckAddSelfOverflow32() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9444,6 +9497,8 @@ void testCheckAddSelfOverflow32() > void testCheckSubImm() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9480,6 +9535,8 @@ void testCheckSubImm() > void testCheckSubBadImm() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9522,6 +9579,8 @@ void testCheckSubBadImm() > void testCheckSub() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9564,6 +9623,8 @@ NEVER_INLINE double doubleSub(double a, > void testCheckSub64() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); >@@ -9601,9 +9662,11 @@ void testCheckSubFold(int a, int b) > Value* arg1 = root->appendNew<Const32Value>(proc, Origin(), a); > Value* arg2 = root->appendNew<Const32Value>(proc, Origin(), b); > CheckValue* checkSub = root->appendNew<CheckValue>(proc, CheckSub, Origin(), arg1, arg2); >+ unsigned optLevel = proc.optLevel(); > checkSub->setGenerator( > [&] (CCallHelpers&, const StackmapGenerationParams&) { >- CHECK(!"Should have been folded"); >+ if (optLevel > 1) >+ CHECK(!"Should have been folded"); > }); > root->appendNewControlValue(proc, Return, Origin(), checkSub); > >@@ -9615,6 +9678,8 @@ void testCheckSubFold(int a, int b) > void testCheckSubFoldFail(int a, int b) > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Const32Value>(proc, Origin(), a); > Value* arg2 = root->appendNew<Const32Value>(proc, Origin(), b); >@@ -9636,6 +9701,8 @@ void testCheckSubFoldFail(int a, int b) > void testCheckNeg() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Const32Value>(proc, Origin(), 0); > Value* arg2 = root->appendNew<Value>( >@@ -9668,6 +9735,8 @@ void testCheckNeg() > void testCheckNeg64() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Const64Value>(proc, Origin(), 0); > Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); >@@ -9698,6 +9767,8 @@ void testCheckNeg64() > void testCheckMul() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9735,6 +9806,8 @@ void testCheckMul() > void testCheckMulMemory() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > > int left; >@@ -9787,6 +9860,8 @@ void testCheckMulMemory() > void testCheckMul2() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, Trunc, Origin(), >@@ -9823,6 +9898,8 @@ void testCheckMul2() > void testCheckMul64() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0); > Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1); >@@ -9860,9 +9937,11 @@ void testCheckMulFold(int a, int b) > Value* arg1 = root->appendNew<Const32Value>(proc, Origin(), a); > Value* arg2 = root->appendNew<Const32Value>(proc, Origin(), b); > CheckValue* checkMul = root->appendNew<CheckValue>(proc, CheckMul, Origin(), arg1, arg2); >+ unsigned optLevel = proc.optLevel(); > checkMul->setGenerator( > [&] (CCallHelpers&, const StackmapGenerationParams&) { >- CHECK(!"Should have been folded"); >+ if (optLevel > 1) >+ CHECK(!"Should have been folded"); > }); > root->appendNewControlValue(proc, Return, Origin(), checkMul); > >@@ -9874,6 +9953,8 @@ void testCheckMulFold(int a, int b) > void testCheckMulFoldFail(int a, int b) > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Const32Value>(proc, Origin(), a); > Value* arg2 = root->appendNew<Const32Value>(proc, Origin(), b); >@@ -9975,6 +10056,8 @@ void testCheckMulArgumentAliasing32() > void testCheckMul64SShr() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > Value* arg1 = root->appendNew<Value>( > proc, SShr, Origin(), >@@ -12180,6 +12263,8 @@ void testSelectInvert() > void testCheckSelect() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > > CheckValue* check = root->appendNew<CheckValue>( >@@ -12222,6 +12307,8 @@ void testCheckSelect() > void testCheckSelectCheckSelect() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > > CheckValue* check = root->appendNew<CheckValue>( >@@ -12295,6 +12382,8 @@ void testCheckSelectCheckSelect() > void testCheckSelectAndCSE() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > BasicBlock* root = proc.addBlock(); > > auto* selectValue = root->appendNew<Value>( >@@ -13029,6 +13118,13 @@ void testSpillUseLargerThanDef() > void testLateRegister() > { > Procedure proc; >+ >+ if (!proc.optLevel()) { >+ // FIXME: Make O0 handle such situations: >+ // https://bugs.webkit.org/show_bug.cgi?id=194633 >+ return; >+ } >+ > BasicBlock* root = proc.addBlock(); > > // This works by making all but 1 register be input to the first patchpoint as LateRegister. >@@ -13340,6 +13436,8 @@ void testInterpreter() > void testReduceStrengthCheckBottomUseInAnotherBlock() > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > > BasicBlock* one = proc.addBlock(); > BasicBlock* two = proc.addBlock(); >@@ -13434,6 +13532,10 @@ void testEntrySwitchSimple() > CodeLocationLabel<B3CompilationPtrTag> labelTwo = linkBuffer.locationOf<B3CompilationPtrTag>(proc.entrypointLabel(1)); > CodeLocationLabel<B3CompilationPtrTag> labelThree = linkBuffer.locationOf<B3CompilationPtrTag>(proc.entrypointLabel(2)); > >+ dataLogLn("label one=", RawPointer(labelOne.executableAddress())); >+ dataLogLn("label two=", RawPointer(labelTwo.executableAddress())); >+ dataLogLn("label three=", RawPointer(labelThree.executableAddress())); >+ > MacroAssemblerCodeRef<B3CompilationPtrTag> codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation"); > > CHECK(invoke<int>(labelOne, 1, 2) == 3); >@@ -13782,12 +13884,15 @@ void testSomeEarlyRegister() > if (succeed) > patchpoint->resultConstraint = ValueRep::SomeEarlyRegister; > bool ranSecondPatchpoint = false; >+ unsigned optLevel = proc.optLevel(); > patchpoint->setGenerator( > [&] (CCallHelpers&, const StackmapGenerationParams& params) { > if (succeed) > CHECK(params[0].gpr() != params[1].gpr()); >- else >- CHECK(params[0].gpr() == params[1].gpr()); >+ else { >+ if (optLevel > 1) >+ CHECK(params[0].gpr() == params[1].gpr()); >+ } > ranSecondPatchpoint = true; > }); > >@@ -14372,9 +14477,11 @@ void testPinRegisters() > a, b, c); > PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Void, Origin()); > patchpoint->appendSomeRegister(d); >+ unsigned optLevel = proc.optLevel(); > patchpoint->setGenerator( > [&] (CCallHelpers&, const StackmapGenerationParams& params) { >- CHECK_EQ(params[0].gpr(), GPRInfo::regCS0); >+ if (optLevel > 1) >+ CHECK_EQ(params[0].gpr(), GPRInfo::regCS0); > }); > root->appendNew<Value>(proc, Return, Origin()); > auto code = compileProc(proc); >@@ -14390,6 +14497,11 @@ void testPinRegisters() > }); > } > } >+ if (proc.optLevel() < 2) { >+ // Our less good register allocators may use the >+ // pinned CSRs in a move. >+ usesCSRs = false; >+ } > for (const RegisterAtOffset& regAtOffset : proc.calleeSaveRegisterAtOffsetList()) > usesCSRs |= csrs.get(regAtOffset.reg()); > CHECK_EQ(usesCSRs, !pin); >@@ -14417,7 +14529,10 @@ void testX86LeaAddAddShlLeft() > root->appendNew<Value>(proc, Return, Origin(), result); > > auto code = compileProc(proc); >- checkUsesInstruction(*code, "lea 0x64(%rdi,%rsi,4), %rax"); >+ if (proc.optLevel() > 1) >+ checkUsesInstruction(*code, "lea 0x64(%rdi,%rsi,4), %rax"); >+ else >+ checkUsesInstruction(*code, "lea"); > CHECK_EQ(invoke<intptr_t>(*code, 1, 2), (1 + (2 << 2)) + 100); > } > >@@ -14439,7 +14554,10 @@ void testX86LeaAddAddShlRight() > root->appendNew<Value>(proc, Return, Origin(), result); > > auto code = compileProc(proc); >- checkUsesInstruction(*code, "lea 0x64(%rdi,%rsi,4), %rax"); >+ if (proc.optLevel() > 1) >+ checkUsesInstruction(*code, "lea 0x64(%rdi,%rsi,4), %rax"); >+ else >+ checkUsesInstruction(*code, "lea"); > CHECK_EQ(invoke<intptr_t>(*code, 1, 2), (1 + (2 << 2)) + 100); > } > >@@ -14459,13 +14577,15 @@ void testX86LeaAddAdd() > > auto code = compileProc(proc); > CHECK_EQ(invoke<intptr_t>(*code, 1, 2), (1 + 2) + 100); >- checkDisassembly( >- *code, >- [&] (const char* disassembly) -> bool { >- return strstr(disassembly, "lea 0x64(%rdi,%rsi), %rax") >- || strstr(disassembly, "lea 0x64(%rsi,%rdi), %rax"); >- }, >- "Expected to find something like lea 0x64(%rdi,%rsi), %rax but didn't!"); >+ if (proc.optLevel() > 1) { >+ checkDisassembly( >+ *code, >+ [&] (const char* disassembly) -> bool { >+ return strstr(disassembly, "lea 0x64(%rdi,%rsi), %rax") >+ || strstr(disassembly, "lea 0x64(%rsi,%rdi), %rax"); >+ }, >+ "Expected to find something like lea 0x64(%rdi,%rsi), %rax but didn't!"); >+ } > } > > void testX86LeaAddShlRight() >@@ -14483,7 +14603,10 @@ void testX86LeaAddShlRight() > root->appendNew<Value>(proc, Return, Origin(), result); > > auto code = compileProc(proc); >- checkUsesInstruction(*code, "lea (%rdi,%rsi,4), %rax"); >+ if (proc.optLevel() > 1) >+ checkUsesInstruction(*code, "lea (%rdi,%rsi,4), %rax"); >+ else >+ checkUsesInstruction(*code, "lea"); > CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 2)); > } > >@@ -14503,13 +14626,15 @@ void testX86LeaAddShlLeftScale1() > > auto code = compileProc(proc); > CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + 2); >- checkDisassembly( >- *code, >- [&] (const char* disassembly) -> bool { >- return strstr(disassembly, "lea (%rdi,%rsi), %rax") >- || strstr(disassembly, "lea (%rsi,%rdi), %rax"); >- }, >- "Expected to find something like lea (%rdi,%rsi), %rax but didn't!"); >+ if (proc.optLevel() > 1) { >+ checkDisassembly( >+ *code, >+ [&] (const char* disassembly) -> bool { >+ return strstr(disassembly, "lea (%rdi,%rsi), %rax") >+ || strstr(disassembly, "lea (%rsi,%rdi), %rax"); >+ }, >+ "Expected to find something like lea (%rdi,%rsi), %rax but didn't!"); >+ } > } > > void testX86LeaAddShlLeftScale2() >@@ -14527,7 +14652,10 @@ void testX86LeaAddShlLeftScale2() > root->appendNew<Value>(proc, Return, Origin(), result); > > auto code = compileProc(proc); >- checkUsesInstruction(*code, "lea (%rdi,%rsi,2), %rax"); >+ if (proc.optLevel() > 1) >+ checkUsesInstruction(*code, "lea (%rdi,%rsi,2), %rax"); >+ else >+ checkUsesInstruction(*code, "lea"); > CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 1)); > } > >@@ -14546,7 +14674,10 @@ void testX86LeaAddShlLeftScale4() > root->appendNew<Value>(proc, Return, Origin(), result); > > auto code = compileProc(proc); >- checkUsesInstruction(*code, "lea (%rdi,%rsi,4), %rax"); >+ if (proc.optLevel() > 1) >+ checkUsesInstruction(*code, "lea (%rdi,%rsi,4), %rax"); >+ else >+ checkUsesInstruction(*code, "lea"); > CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 2)); > } > >@@ -14565,7 +14696,10 @@ void testX86LeaAddShlLeftScale8() > root->appendNew<Value>(proc, Return, Origin(), result); > > auto code = compileProc(proc); >- checkUsesInstruction(*code, "lea (%rdi,%rsi,8), %rax"); >+ if (proc.optLevel() > 1) >+ checkUsesInstruction(*code, "lea (%rdi,%rsi,8), %rax"); >+ else >+ checkUsesInstruction(*code, "lea"); > CHECK_EQ(invoke<intptr_t>(*code, 1, 2), 1 + (2 << 3)); > } > >@@ -14684,7 +14818,7 @@ void testLoadBaseIndexShift2() > root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1), > root->appendNew<Const32Value>(proc, Origin(), 2))))); > auto code = compileProc(proc); >- if (isX86()) >+ if (isX86() && proc.optLevel() > 1) > checkUsesInstruction(*code, "(%rdi,%rsi,4)"); > int32_t value = 12341234; > char* ptr = bitwise_cast<char*>(&value); >@@ -14719,6 +14853,9 @@ void testLoadBaseIndexShift32() > void testOptimizeMaterialization() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; >+ > BasicBlock* root = proc.addBlock(); > root->appendNew<CCallValue>( > proc, Void, Origin(), >@@ -14847,6 +14984,10 @@ static void noOpFunction() > void testLICMPure() > { > Procedure proc; >+ >+ if (proc.optLevel() < 2) >+ return; >+ > generateLoop( > proc, > [&] (BasicBlock* loop, Value*) -> Value* { >@@ -14864,6 +15005,8 @@ void testLICMPure() > void testLICMPureSideExits() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > generateLoop( > proc, > [&] (BasicBlock* loop, Value*) -> Value* { >@@ -14887,6 +15030,8 @@ void testLICMPureSideExits() > void testLICMPureWritesPinned() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > generateLoop( > proc, > [&] (BasicBlock* loop, Value*) -> Value* { >@@ -14910,6 +15055,8 @@ void testLICMPureWritesPinned() > void testLICMPureWrites() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > generateLoop( > proc, > [&] (BasicBlock* loop, Value*) -> Value* { >@@ -14952,6 +15099,8 @@ void testLICMReadsLocalState() > void testLICMReadsPinned() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > generateLoop( > proc, > [&] (BasicBlock* loop, Value*) -> Value* { >@@ -14971,6 +15120,8 @@ void testLICMReadsPinned() > void testLICMReads() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > generateLoop( > proc, > [&] (BasicBlock* loop, Value*) -> Value* { >@@ -14990,6 +15141,8 @@ void testLICMReads() > void testLICMPureNotBackwardsDominant() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > auto array = makeArrayForLoops(); > generateLoopNotBackwardsDominant( > proc, array, >@@ -15026,6 +15179,8 @@ void testLICMPureFoiledByChild() > void testLICMPureNotBackwardsDominantFoiledByChild() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > auto array = makeArrayForLoops(); > generateLoopNotBackwardsDominant( > proc, array, >@@ -15140,6 +15295,8 @@ void testLICMWritesPinned() > void testLICMControlDependent() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > generateLoop( > proc, > [&] (BasicBlock* loop, Value*) -> Value* { >@@ -15159,6 +15316,8 @@ void testLICMControlDependent() > void testLICMControlDependentNotBackwardsDominant() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > auto array = makeArrayForLoops(); > generateLoopNotBackwardsDominant( > proc, array, >@@ -15229,6 +15388,8 @@ void testLICMReadsPinnedWritesPinned() > void testLICMReadsWritesDifferentHeaps() > { > Procedure proc; >+ if (proc.optLevel() < 2) >+ return; > generateLoop( > proc, > [&] (BasicBlock* loop, Value*) -> Value* { >@@ -16010,6 +16171,8 @@ void testDepend64() > void testWasmBoundsCheck(unsigned offset) > { > Procedure proc; >+ if (proc.optLevel() < 1) >+ return; > GPRReg pinned = GPRInfo::argumentGPR1; > proc.pinRegister(pinned); > >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,710 @@ >+/* >+ * 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 "AirLowerStackArgs.h" >+#include "AirStackAllocation.h" >+#include "AirTmpMap.h" >+#include "CCallHelpers.h" >+#include "DisallowMacroScratchRegisterUsage.h" >+ >+namespace JSC { namespace B3 { namespace Air { >+ >+GenerateAndAllocateRegisters::GenerateAndAllocateRegisters(Code& code) >+ : m_code(code) >+ , m_map(code) >+{ } >+ >+void GenerateAndAllocateRegisters::buildLiveRanges(UnifiedTmpLiveness& liveness) >+{ >+ m_liveRangeEnd = TmpMap<size_t>(m_code, 0); >+ >+ m_globalInstIndex = 0; >+ for (BasicBlock* block : m_code) { >+ for (Tmp tmp : liveness.liveAtHead(block)) { >+ if (!tmp.isReg()) >+ m_liveRangeEnd[tmp] = m_globalInstIndex; >+ } >+ for (Inst& inst : *block) { >+ inst.forEachTmpFast([&] (Tmp tmp) { >+ if (!tmp.isReg()) >+ m_liveRangeEnd[tmp] = m_globalInstIndex; >+ }); >+ ++m_globalInstIndex; >+ } >+ for (Tmp tmp : liveness.liveAtTail(block)) { >+ if (!tmp.isReg()) >+ m_liveRangeEnd[tmp] = m_globalInstIndex; >+ } >+ } >+} >+ >+void GenerateAndAllocateRegisters::insertBlocksForFlushAfterTerminalPatchpoints() >+{ >+ BlockInsertionSet blockInsertionSet(m_code); >+ for (BasicBlock* block : m_code) { >+ Inst& inst = block->last(); >+ if (inst.kind.opcode != Patch) >+ continue; >+ >+ HashMap<Tmp, Arg*> needToDef; >+ >+ inst.forEachArg([&] (Arg& arg, Arg::Role role, Bank, Width) { >+ if (!arg.isTmp()) >+ return; >+ Tmp tmp = arg.tmp(); >+ if (Arg::isAnyDef(role) && !tmp.isReg()) >+ needToDef.add(tmp, &arg); >+ }); >+ >+ 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_blocksAfterTerminalPatchForSpilling.add(newBlock, PatchSpillData { CCallHelpers::Jump(), CCallHelpers::Label(), needToDef }); >+ } >+ } >+ >+ blockInsertionSet.execute(); >+} >+ >+static ALWAYS_INLINE CCallHelpers::Address callFrameAddr(CCallHelpers& jit, intptr_t offsetFromFP) >+{ >+ if (isX86()) { >+ ASSERT(Arg::addr(Air::Tmp(GPRInfo::callFrameRegister), offsetFromFP).isValidForm(Width64)); >+ return CCallHelpers::Address(GPRInfo::callFrameRegister, offsetFromFP); >+ } >+ >+ ASSERT(pinnedExtendedOffsetAddrRegister()); >+ auto addr = Arg::addr(Air::Tmp(GPRInfo::callFrameRegister), offsetFromFP); >+ if (addr.isValidForm(Width64)) >+ return CCallHelpers::Address(GPRInfo::callFrameRegister, offsetFromFP); >+ GPRReg reg = *pinnedExtendedOffsetAddrRegister(); >+ jit.move(CCallHelpers::TrustedImmPtr(offsetFromFP), reg); >+ jit.add64(GPRInfo::callFrameRegister, reg); >+ return CCallHelpers::Address(reg); >+} >+ >+ALWAYS_INLINE void GenerateAndAllocateRegisters::flush(Tmp tmp, Reg reg) >+{ >+ ASSERT(tmp); >+ intptr_t offset = m_map[tmp].spillSlot->offsetFromFP(); >+ if (tmp.isGP()) >+ m_jit->store64(reg.gpr(), callFrameAddr(*m_jit, offset)); >+ else >+ m_jit->storeDouble(reg.fpr(), callFrameAddr(*m_jit, offset)); >+} >+ >+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->at(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->at(reg)) >+ spill(occupyingTmp, reg); >+ else { >+ ASSERT(!m_currentAllocation->at(reg)); >+ ASSERT(m_availableRegs[tmp.bank()].get(reg)); >+ } >+ >+ m_map[tmp].reg = reg; >+ m_availableRegs[tmp.bank()].clear(reg); >+ m_currentAllocation->at(reg) = tmp; >+ >+ if (!isDef) { >+ intptr_t offset = m_map[tmp].spillSlot->offsetFromFP(); >+ if (tmp.bank() == GP) >+ m_jit->load64(callFrameAddr(*m_jit, offset), reg.gpr()); >+ else >+ m_jit->loadDouble(callFrameAddr(*m_jit, offset), reg.fpr()); >+ } >+} >+ >+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->at(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->at(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 (Reg reg : m_registers[bank]) { >+ 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 > #Available registers >+ return false; >+} >+ >+ALWAYS_INLINE bool GenerateAndAllocateRegisters::isDisallowedRegister(Reg reg) >+{ >+ return !m_allowedRegisters.get(reg); >+} >+ >+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) { >+ intptr_t offset = -static_cast<intptr_t>(m_code.frameSize()) - static_cast<intptr_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 passes add any tmps. >+#if !ASSERT_DISABLED >+ forEachBank([&] (Bank bank) { >+ ASSERT(m_allTmps[bank].size() - m_registers[bank].size() == m_code.numTmps(bank)); >+ }); >+#endif >+} >+ >+void GenerateAndAllocateRegisters::generate(CCallHelpers& jit) >+{ >+ m_jit = &jit; >+ >+ TimingScope timingScope("Air::generateAndAllocateRegisters"); >+ >+ insertBlocksForFlushAfterTerminalPatchpoints(); >+ >+ DisallowMacroScratchRegisterUsage disallowScratch(*m_jit); >+ >+ UnifiedTmpLiveness liveness(m_code); >+ buildLiveRanges(liveness); >+ >+ IndexMap<BasicBlock*, IndexMap<Reg, Tmp>> currentAllocationMap(m_code.size()); >+ { >+ IndexMap<Reg, Tmp> defaultCurrentAllocation(Reg::maxIndex() + 1); >+ for (BasicBlock* block : m_code) >+ currentAllocationMap[block] = defaultCurrentAllocation; >+ >+ // The only things live that are in registers at the root blocks are >+ // the explicitly named registers that are live. >+ >+ for (unsigned i = m_code.numEntrypoints(); i--;) { >+ BasicBlock* entrypoint = m_code.entrypoint(i).block(); >+ for (Tmp tmp : liveness.liveAtHead(entrypoint)) { >+ if (tmp.isReg()) >+ currentAllocationMap[entrypoint][tmp.reg()] = tmp; >+ } >+ } >+ } >+ >+ // 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_blocksAfterTerminalPatchForSpilling.find(block); >+ if (iter != m_blocksAfterTerminalPatchForSpilling.end()) { >+ auto& data = iter->value; >+ data.jump = m_jit->jump(); >+ data.continueLabel = m_jit->label(); >+ } >+ } >+ >+ 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); >+ } >+ >+ bool isReplayingSameInst = false; >+ for (size_t instIndex = 0; instIndex < block->size(); ++instIndex) { >+ if (instIndex && !isReplayingSameInst) >+ startLabel = m_jit->labelIgnoringWatchpoints(); >+ >+ context.indexInBlock = instIndex; >+ >+ Inst& inst = block->at(instIndex); >+ >+ m_didAlreadyFreeDeadSlots = false; >+ >+ m_namedUsedRegs = RegisterSet(); >+ m_namedDefdRegs = RegisterSet(); >+ >+ inst.forEachArg([&] (Arg& arg, Arg::Role role, Bank, Width) { >+ if (!arg.isTmp()) >+ return; >+ >+ Tmp tmp = arg.tmp(); >+ if (tmp.isReg() && isDisallowedRegister(tmp.reg())) >+ return; >+ >+ if (tmp.isReg()) { >+ if (Arg::isAnyUse(role)) >+ m_namedUsedRegs.set(tmp.reg()); >+ if (Arg::isAnyDef(role)) >+ m_namedDefdRegs.set(tmp.reg()); >+ } >+ >+ // We convert any cold uses that are already in the stack to just point to >+ // the canonical stack location. >+ if (!Arg::isColdUse(role)) >+ return; >+ >+ if (!inst.admitsStack(arg)) >+ return; >+ >+ auto& entry = m_map[tmp]; >+ if (!entry.reg) { >+ // We're a cold use, and our current location is already on the stack. Just use that. >+ arg = Arg::addr(Tmp(GPRInfo::callFrameRegister), entry.spillSlot->offsetFromFP()); >+ } >+ }); >+ >+ RegisterSet clobberedRegisters; >+ { >+ Inst* nextInst = block->get(instIndex + 1); >+ if (inst.kind.opcode == Patch || (nextInst && nextInst->kind.opcode == Patch)) { >+ if (inst.kind.opcode == Patch) >+ clobberedRegisters.merge(inst.extraClobberedRegs()); >+ if (nextInst && nextInst->kind.opcode == Patch) >+ clobberedRegisters.merge(nextInst->extraEarlyClobberedRegs()); >+ >+ clobberedRegisters.filter(m_allowedRegisters); >+ clobberedRegisters.exclude(m_namedDefdRegs); >+ >+ m_namedDefdRegs.merge(clobberedRegisters); >+ } >+ } >+ >+ 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); >+ >+ { >+ auto tryAllocate = [&] { >+ Vector<Tmp*, 8> usesToAlloc; >+ Vector<Tmp*, 8> defsToAlloc; >+ >+ inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Bank, Width) { >+ if (tmp.isReg()) >+ return; >+ >+ // We treat Use+Def as a use. >+ if (Arg::isAnyUse(role)) >+ usesToAlloc.append(&tmp); >+ else if (Arg::isAnyDef(role)) >+ defsToAlloc.append(&tmp); >+ }); >+ >+ auto tryAllocateTmps = [&] (auto& vector, bool isDef) { >+ bool success = true; >+ for (Tmp* tmp : vector) >+ 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 &= tryAllocateTmps(usesToAlloc, false); >+ success &= tryAllocateTmps(defsToAlloc, true); >+ return success; >+ }; >+ >+ // We first allocate trying to give any Tmp a register. If that makes us exhaust the >+ // available registers, we convert anything that accepts stack to be a stack addr >+ // instead. This can happen for programs Insts that take in many args, but most >+ // args can just be stack values. >+ //dataLogLn("Before allocate: ", inst); >+ bool success = tryAllocate(); >+ //dataLogLn("After allocate: ", inst); >+ if (!success) { >+ RELEASE_ASSERT(!isReplayingSameInst); // We should only need to do the below at most once per inst. >+ >+ // We need to capture the register state before we start spilling things >+ // since we may have multiple arguments that are the same register. >+ IndexMap<Reg, Tmp> allocationSnapshot = currentAllocation; >+ >+ // We rewind this Inst to be in its previous state, however, if any arg admits stack, >+ // we move to providing that arg in stack form. This will allow us to fully allocate >+ // this inst when we rewind. >+ inst.forEachArg([&] (Arg& arg, Arg::Role, Bank, Width) { >+ if (!arg.isTmp()) >+ return; >+ >+ Tmp tmp = arg.tmp(); >+ if (tmp.isReg() && isDisallowedRegister(tmp.reg())) >+ return; >+ >+ if (tmp.isReg()) { >+ Tmp originalTmp = allocationSnapshot[tmp.reg()]; >+ if (originalTmp.isReg()) { >+ ASSERT(tmp.reg() == originalTmp.reg()); >+ // This means this Inst referred to this reg directly. We leave these as is. >+ return; >+ } >+ tmp = originalTmp; >+ } >+ >+ if (!inst.admitsStack(arg)) { >+ arg = tmp; >+ return; >+ } >+ >+ auto& entry = m_map[tmp]; >+ if (Reg reg = entry.reg) >+ spill(tmp, reg); >+ >+ arg = Arg::addr(Tmp(GPRInfo::callFrameRegister), entry.spillSlot->offsetFromFP()); >+ }); >+ >+ --instIndex; >+ isReplayingSameInst = true; >+ continue; >+ } >+ >+ isReplayingSameInst = false; >+ } >+ >+ 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); >+ } >+ >+ if (inst.isTerminal() && block->numSuccessors()) { >+ // By default, we spill everything between block boundaries. However, we have a small >+ // heuristic to pass along register state. We should eventually make this better. >+ // What we do now is if we have a successor with a single predecessor (us), and we >+ // haven't yet generated code for it, we give it our register state. If all our successors >+ // can take on our register state, we don't flush at the end of this block. >+ >+ bool everySuccessorGetsOurRegisterState = true; >+ for (unsigned i = 0; i < block->numSuccessors(); ++i) { >+ BasicBlock* successor = block->successorBlock(i); >+ if (successor->numPredecessors() == 1 && !context.blockLabels[successor]->isSet()) >+ currentAllocationMap[successor] = currentAllocation; >+ else >+ everySuccessorGetsOurRegisterState = false; >+ } >+ if (!everySuccessorGetsOurRegisterState) { >+ for (Tmp tmp : liveness.liveAtTail(block)) { >+ if (tmp.isReg() && isDisallowedRegister(tmp.reg())) >+ continue; >+ if (Reg reg = m_map[tmp].reg) >+ flush(tmp, reg); >+ } >+ } >+ } >+ >+ if (!inst.isTerminal()) { >+ CCallHelpers::Jump jump = inst.generate(*m_jit, context); >+ ASSERT_UNUSED(jump, !jump.isSet()); >+ >+ for (Reg reg : clobberedRegisters) { >+ Tmp tmp(reg); >+ ASSERT(currentAllocation[reg] == tmp); >+ m_availableRegs[tmp.bank()].set(reg); >+ m_currentAllocation->at(reg) = Tmp(); >+ m_map[tmp].reg = Reg(); >+ } >+ } 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 = inst.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_blocksAfterTerminalPatchForSpilling) { >+ entry.value.jump.linkTo(m_jit->label(), m_jit); >+ const HashMap<Tmp, Arg*>& spills = entry.value.defdTmps; >+ for (auto& entry : spills) { >+ Arg* arg = entry.value; >+ if (!arg->isTmp()) >+ continue; >+ Tmp originalTmp = entry.key; >+ Tmp currentTmp = arg->tmp(); >+ ASSERT_WITH_MESSAGE(currentTmp.isReg(), "We already did register allocation so we should have assigned this Tmp to a register."); >+ flush(originalTmp, currentTmp.reg()); >+ } >+ m_jit->jump().linkTo(entry.value.continueLabel, 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,93 @@ >+/* >+ * 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 "AirLiveness.h" >+#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 prepareForGeneration(); >+ void generate(CCallHelpers&); >+ >+private: >+ void insertBlocksForFlushAfterTerminalPatchpoints(); >+ void flush(Tmp, Reg); >+ void spill(Tmp, Reg); >+ void alloc(Tmp, Reg, bool isDef); >+ void freeDeadTmpsIfNeeded(); >+ bool assignTmp(Tmp&, Bank, bool isDef); >+ void buildLiveRanges(UnifiedTmpLiveness&); >+ bool isDisallowedRegister(Reg); >+ >+ 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; >+ >+ struct PatchSpillData { >+ CCallHelpers::Jump jump; >+ CCallHelpers::Label continueLabel; >+ HashMap<Tmp, Arg*> defdTmps; >+ }; >+ >+ HashMap<BasicBlock*, PatchSpillData> m_blocksAfterTerminalPatchForSpilling; >+}; >+ >+} } } // namespace JSC::B3::Air >+ >+#endif // ENABLE(B3_JIT) >Index: Source/JavaScriptCore/b3/air/AirCode.cpp >=================================================================== >--- Source/JavaScriptCore/b3/air/AirCode.cpp (revision 241482) >+++ 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 241482) >+++ 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 241482) >+++ 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()) { >+ lowerMacros(code); >+ >+ // FIXME: The name of this phase doesn't make much sense in O0 since we do this before >+ // register allocation. >+ 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); >@@ -83,7 +116,7 @@ void prepareForGeneration(Code& code) > > eliminateDeadCode(code); > >- if (code.optLevel() <= 1) { >+ if (code.optLevel() == 1) { > // When we're compiling quickly, we do register and stack allocation in one linear scan > // phase. It's fast because it computes liveness only once. > allocateRegistersAndStackByLinearScan(code); >@@ -161,7 +194,7 @@ void prepareForGeneration(Code& code) > } > } > >-void generate(Code& code, CCallHelpers& jit) >+static void generateWithAlreadyAllocatedRegisters(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()) >+ generateWithAlreadyAllocatedRegisters(code, jit); >+ else >+ code.m_generateAndAllocateRegisters->generate(jit); >+} >+ > } } } // namespace JSC::B3::Air > > #endif // ENABLE(B3_JIT) >Index: Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.cpp >=================================================================== >--- Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.cpp (revision 241482) >+++ 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 241482) >+++ 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/b3/air/AirTmpMap.h >=================================================================== >--- Source/JavaScriptCore/b3/air/AirTmpMap.h (revision 241482) >+++ Source/JavaScriptCore/b3/air/AirTmpMap.h (working copy) >@@ -39,9 +39,9 @@ namespace JSC { namespace B3 { namespace > template<typename Value> > class TmpMap { > public: >- TmpMap() >- { >- } >+ TmpMap() = default; >+ TmpMap(TmpMap&&) = default; >+ TmpMap& operator=(TmpMap&&) = default; > > template<typename... Args> > TmpMap(Code& code, const Args&... args) >Index: Source/JavaScriptCore/runtime/Options.h >=================================================================== >--- Source/JavaScriptCore/runtime/Options.h (revision 241482) >+++ Source/JavaScriptCore/runtime/Options.h (working copy) >@@ -480,7 +480,7 @@ constexpr bool enableWebAssemblyStreamin > \ > v(bool, failToCompileWebAssemblyCode, false, Normal, "If true, no Wasm::Plan will sucessfully compile a function.") \ > v(size, webAssemblyPartialCompileLimit, 5000, Normal, "Limit on the number of bytes a Wasm::Plan::compile should attempt before checking for other work.") \ >- v(unsigned, webAssemblyBBQOptimizationLevel, 1, Normal, "B3 Optimization level for BBQ Web Assembly module compilations.") \ >+ v(unsigned, webAssemblyBBQOptimizationLevel, 0, Normal, "B3 Optimization level for BBQ Web Assembly module compilations.") \ > v(unsigned, webAssemblyOMGOptimizationLevel, Options::defaultB3OptLevel(), Normal, "B3 Optimization level for OMG Web Assembly module compilations.") \ > \ > v(bool, useBBQTierUpChecks, true, Normal, "Enables tier up checks for our BBQ code.") \ >Index: Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >=================================================================== >--- Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp (revision 241482) >+++ Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp (working copy) >@@ -222,7 +222,7 @@ public: > return fail(__VA_ARGS__); \ > } while (0) > >- AirIRGenerator(const ModuleInformation&, B3::Procedure&, InternalFunction*, Vector<UnlinkedWasmToWasmCall>&, MemoryMode, CompilationMode, unsigned functionIndex, TierUpCount*, ThrowWasmException, const Signature&); >+ AirIRGenerator(const ModuleInformation&, B3::Procedure&, InternalFunction*, Vector<UnlinkedWasmToWasmCall>&, MemoryMode, unsigned functionIndex, TierUpCount*, ThrowWasmException, const Signature&); > > PartialResult WARN_UNUSED_RETURN addArguments(const Signature&); > PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t); >@@ -285,6 +285,17 @@ public: > return result; > } > >+ ALWAYS_INLINE void didKill(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) > { >@@ -324,6 +335,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); > } > >@@ -559,7 +580,6 @@ private: > FunctionParser<AirIRGenerator>* m_parser { nullptr }; > const ModuleInformation& m_info; > const MemoryMode m_mode { MemoryMode::BoundsChecking }; >- const CompilationMode m_compilationMode { CompilationMode::BBQMode }; > const unsigned m_functionIndex { UINT_MAX }; > const TierUpCount* m_tierUp { nullptr }; > >@@ -574,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() >@@ -630,10 +653,9 @@ void AirIRGenerator::restoreWasmContextI > emitPatchpoint(block, patchpoint, Tmp(), instance); > } > >-AirIRGenerator::AirIRGenerator(const ModuleInformation& info, B3::Procedure& procedure, InternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, MemoryMode mode, CompilationMode compilationMode, unsigned functionIndex, TierUpCount* tierUp, ThrowWasmException throwWasmException, const Signature& signature) >+AirIRGenerator::AirIRGenerator(const ModuleInformation& info, B3::Procedure& procedure, InternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, MemoryMode mode, unsigned functionIndex, TierUpCount* tierUp, ThrowWasmException throwWasmException, const Signature& signature) > : m_info(info) > , m_mode(mode) >- , m_compilationMode(compilationMode) > , m_functionIndex(functionIndex) > , m_tierUp(tierUp) > , m_proc(procedure) >@@ -719,7 +741,6 @@ AirIRGenerator::AirIRGenerator(const Mod > // This allows leaf functions to not do stack checks if their frame size is within > // certain limits since their caller would have already done the check. > if (needsOverflowCheck) { >- AllowMacroScratchRegisterUsage allowScratch(jit); > GPRReg scratch = wasmCallingConventionAir().prologueScratch(0); > > if (Context::useFastTLS()) >@@ -735,7 +756,6 @@ AirIRGenerator::AirIRGenerator(const Mod > }); > } else if (m_usesInstanceValue && Context::useFastTLS()) { > // No overflow check is needed, but the instance values still needs to be correct. >- AllowMacroScratchRegisterUsageIf allowScratch(jit, CCallHelpers::loadWasmContextInstanceNeedsMacroScratchRegister()); > jit.loadWasmContextInstance(contextInstance); > } > } >@@ -1912,7 +1932,7 @@ auto AirIRGenerator::origin() -> B3::Ori > return B3::Origin(); > } > >-Expected<std::unique_ptr<InternalFunction>, String> parseAndCompileAir(CompilationContext& compilationContext, const uint8_t* functionStart, size_t functionLength, const Signature& signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ModuleInformation& info, MemoryMode mode, CompilationMode compilationMode, uint32_t functionIndex, TierUpCount* tierUp, ThrowWasmException throwWasmException) >+Expected<std::unique_ptr<InternalFunction>, String> parseAndCompileAir(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>(); > >@@ -1933,11 +1953,9 @@ Expected<std::unique_ptr<InternalFunctio > // optLevel=1. > procedure.setNeedsUsedRegisters(false); > >- procedure.setOptLevel(compilationMode == CompilationMode::BBQMode >- ? Options::webAssemblyBBQOptimizationLevel() >- : Options::webAssemblyOMGOptimizationLevel()); >+ procedure.setOptLevel(Options::webAssemblyBBQOptimizationLevel()); > >- AirIRGenerator irGenerator(info, procedure, result.get(), unlinkedWasmToWasmCalls, mode, compilationMode, functionIndex, tierUp, throwWasmException, signature); >+ AirIRGenerator irGenerator(info, procedure, result.get(), unlinkedWasmToWasmCalls, mode, functionIndex, tierUp, throwWasmException, signature); > FunctionParser<AirIRGenerator> parser(irGenerator, functionStart, functionLength, signature, info); > WASM_FAIL_IF_HELPER_FAILS(parser.parse()); > >@@ -2504,6 +2522,7 @@ auto AirIRGenerator::addOp<OpType::I64Tr > Vector<ConstrainedTmp> args; > auto* patchpoint = addPatchpoint(B3::Int64); > patchpoint->effects = B3::Effects::none(); >+ patchpoint->clobber(RegisterSet::macroScratchRegisters()); > args.append(arg); > if (isX86()) { > args.append(signBitConstant); >@@ -2577,6 +2596,7 @@ auto AirIRGenerator::addOp<OpType::I64Tr > > auto* patchpoint = addPatchpoint(B3::Int64); > patchpoint->effects = B3::Effects::none(); >+ patchpoint->clobber(RegisterSet::macroScratchRegisters()); > Vector<ConstrainedTmp> args; > args.append(arg); > if (isX86()) { >Index: Source/JavaScriptCore/wasm/WasmAirIRGenerator.h >=================================================================== >--- Source/JavaScriptCore/wasm/WasmAirIRGenerator.h (revision 241482) >+++ Source/JavaScriptCore/wasm/WasmAirIRGenerator.h (working copy) >@@ -31,7 +31,7 @@ > > namespace JSC { namespace Wasm { > >-Expected<std::unique_ptr<InternalFunction>, String> parseAndCompileAir(CompilationContext&, const uint8_t*, size_t, const Signature&, Vector<UnlinkedWasmToWasmCall>&, const ModuleInformation&, MemoryMode, CompilationMode, uint32_t functionIndex, TierUpCount* = nullptr, ThrowWasmException = nullptr); >+Expected<std::unique_ptr<InternalFunction>, String> parseAndCompileAir(CompilationContext&, const uint8_t*, size_t, const Signature&, Vector<UnlinkedWasmToWasmCall>&, const ModuleInformation&, MemoryMode, uint32_t functionIndex, TierUpCount* = nullptr, ThrowWasmException = nullptr); > > } } // namespace JSC::Wasm > >Index: Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >=================================================================== >--- Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp (revision 241482) >+++ 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 didKill(ExpressionType) { } >+ > private: > void emitExceptionCheck(CCallHelpers&, ExceptionType); > >Index: Source/JavaScriptCore/wasm/WasmBBQPlan.cpp >=================================================================== >--- Source/JavaScriptCore/wasm/WasmBBQPlan.cpp (revision 241482) >+++ Source/JavaScriptCore/wasm/WasmBBQPlan.cpp (working copy) >@@ -274,7 +274,7 @@ void BBQPlan::compileFunctions(Compilati > TierUpCount* tierUp = Options::useBBQTierUpChecks() ? &m_tierUpCounts[functionIndex] : nullptr; > Expected<std::unique_ptr<InternalFunction>, String> parseAndCompileResult; > if (Options::wasmBBQUsesAir()) >- parseAndCompileResult = parseAndCompileAir(m_compilationContexts[functionIndex], function.data.data(), function.data.size(), signature, m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, CompilationMode::BBQMode, functionIndex, tierUp, m_throwWasmException); >+ parseAndCompileResult = parseAndCompileAir(m_compilationContexts[functionIndex], function.data.data(), function.data.size(), signature, m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, functionIndex, tierUp, m_throwWasmException); > else > parseAndCompileResult = parseAndCompile(m_compilationContexts[functionIndex], function.data.data(), function.data.size(), signature, m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, CompilationMode::BBQMode, functionIndex, tierUp, m_throwWasmException); > >Index: Source/JavaScriptCore/wasm/WasmFunctionParser.h >=================================================================== >--- Source/JavaScriptCore/wasm/WasmFunctionParser.h (revision 241482) >+++ Source/JavaScriptCore/wasm/WasmFunctionParser.h (working copy) >@@ -70,6 +70,7 @@ private: > #define WASM_TRY_POP_EXPRESSION_STACK_INTO(result, what) do { \ > WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't pop empty stack in " what); \ > result = m_expressionStack.takeLast(); \ >+ m_toKillAfterExpression.append(result); \ > } while (0) > > template<OpType> >@@ -91,6 +92,8 @@ private: > OpType m_currentOpcode; > size_t m_currentOpcodeStartingOffset { 0 }; > >+ Vector<ExpressionType, 8> m_toKillAfterExpression; >+ > unsigned m_unreachableBlocks { 0 }; > }; > >@@ -136,6 +139,8 @@ auto FunctionParser<Context>::parseBody( > m_controlStack.append({ ExpressionList(), m_context.addTopLevel(m_signature.returnType()) }); > uint8_t op; > while (m_controlStack.size()) { >+ ASSERT(m_toKillAfterExpression.isEmpty()); >+ > m_currentOpcodeStartingOffset = m_offset; > WASM_PARSER_FAIL_IF(!parseUInt8(op), "can't decode opcode"); > WASM_PARSER_FAIL_IF(!isValidOpType(op), "invalid opcode ", op); >@@ -149,8 +154,11 @@ auto FunctionParser<Context>::parseBody( > > if (m_unreachableBlocks) > WASM_FAIL_IF_HELPER_FAILS(parseUnreachableExpression()); >- else >+ else { > WASM_FAIL_IF_HELPER_FAILS(parseExpression()); >+ while (m_toKillAfterExpression.size()) >+ m_context.didKill(m_toKillAfterExpression.takeLast()); >+ } > } > > ASSERT(op == OpType::End); >@@ -420,6 +428,7 @@ auto FunctionParser<Context>::parseExpre > ControlType& data = m_controlStack[m_controlStack.size() - 1 - target].controlData; > > WASM_TRY_ADD_TO_CONTEXT(addBranch(data, condition, m_expressionStack)); >+ > return { }; > } > >@@ -481,7 +490,8 @@ auto FunctionParser<Context>::parseExpre > > case Drop: { > WASM_PARSER_FAIL_IF(!m_expressionStack.size(), "can't drop on empty stack"); >- m_expressionStack.takeLast(); >+ auto expression = m_expressionStack.takeLast(); >+ m_toKillAfterExpression.append(expression); > return { }; > } > >Index: Source/JavaScriptCore/wasm/WasmValidate.cpp >=================================================================== >--- Source/JavaScriptCore/wasm/WasmValidate.cpp (revision 241482) >+++ 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 didKill(ExpressionType) { } >+ > bool hasMemory() const { return !!m_module.memory; } > > Validate(const ModuleInformation& module) >Index: Source/WTF/wtf/IndexMap.h >=================================================================== >--- Source/WTF/wtf/IndexMap.h (revision 241482) >+++ Source/WTF/wtf/IndexMap.h (working copy) >@@ -37,9 +37,11 @@ namespace WTF { > template<typename Key, typename Value> > class IndexMap { > public: >- IndexMap() >- { >- } >+ IndexMap() = default; >+ IndexMap(IndexMap&&) = default; >+ IndexMap& operator=(IndexMap&&) = default; >+ IndexMap(const IndexMap&) = default; >+ IndexMap& operator=(const IndexMap&) = default; > > template<typename... Args> > explicit IndexMap(size_t size, Args&&... args) >@@ -61,25 +63,30 @@ public: > > size_t size() const { return m_vector.size(); } > >- Value& operator[](size_t index) >+ Value& at(const Key& key) > { >- return m_vector[index]; >+ return m_vector[IndexKeyType<Key>::index(key)]; >+ } >+ >+ const Value& at(const Key& key) const >+ { >+ return m_vector[IndexKeyType<Key>::index(key)]; > } > >- const Value& operator[](size_t index) const >+ Value& at(size_t index) > { > return m_vector[index]; > } >- >- Value& operator[](const Key& key) >+ >+ const Value& at(size_t index) const > { >- return m_vector[IndexKeyType<Key>::index(key)]; >+ return m_vector[index]; > } > >- const Value& operator[](const Key& key) const >- { >- return m_vector[IndexKeyType<Key>::index(key)]; >- } >+ Value& operator[](size_t index) { return at(index); } >+ const Value& operator[](size_t index) const { return at(index); } >+ Value& operator[](const Key& key) { return at(key); } >+ const Value& operator[](const Key& key) const { return at(key); } > > template<typename PassedValue> > void append(const Key& key, PassedValue&& value) >Index: Tools/Scripts/run-jsc-stress-tests >=================================================================== >--- Tools/Scripts/run-jsc-stress-tests (revision 241482) >+++ Tools/Scripts/run-jsc-stress-tests (working copy) >@@ -486,6 +486,7 @@ EAGER_OPTIONS = ["--thresholdForJITAfter > # NOTE: Tests rely on this using scribbleFreeCells. > NO_CJIT_OPTIONS = ["--useConcurrentJIT=false", "--thresholdForJITAfterWarmUp=100", "--scribbleFreeCells=true"] > B3O1_OPTIONS = ["--defaultB3OptLevel=1"] >+B3O0_OPTIONS = ["--defaultB3OptLevel=0"] > FTL_OPTIONS = ["--useFTLJIT=true"] > PROBE_OSR_EXIT_OPTION = ["--useProbeOSRExit=true"] > >@@ -678,8 +679,8 @@ def runFTLNoCJIT(*optionalTestSpecificOp > run("misc-ftl-no-cjit", *(FTL_OPTIONS + NO_CJIT_OPTIONS + optionalTestSpecificOptions)) > end > >-def runFTLNoCJITB3O1(*optionalTestSpecificOptions) >- run("ftl-no-cjit-b3o1", "--useArrayAllocationProfiling=false", "--forcePolyProto=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS + B3O1_OPTIONS + optionalTestSpecificOptions)) >+def runFTLNoCJITB3O0(*optionalTestSpecificOptions) >+ run("ftl-no-cjit-b3o0", "--useArrayAllocationProfiling=false", "--forcePolyProto=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS + B3O0_OPTIONS + optionalTestSpecificOptions)) > end > > def runFTLNoCJITValidate(*optionalTestSpecificOptions) >@@ -781,7 +782,7 @@ def defaultRun > return if $mode == "basic" > > runFTLNoCJITValidate >- runFTLNoCJITB3O1 >+ runFTLNoCJITB3O0 > runFTLNoCJITNoPutStackValidate > runFTLNoCJITNoInlineValidate > runFTLEagerNoCJITB3O1 >@@ -811,7 +812,7 @@ def defaultNoNoLLIntRun > > return if $mode == "basic" > >- runFTLNoCJITB3O1 >+ runFTLNoCJITB3O0 > runFTLNoCJITNoPutStackValidate > runFTLNoCJITNoInlineValidate > runFTLEager >@@ -840,7 +841,7 @@ def defaultSpotCheckNoMaximalFlush > > runFTLNoCJITOSRValidation > runFTLNoCJITNoAccessInlining >- runFTLNoCJITB3O1 >+ runFTLNoCJITB3O0 > end > > def defaultSpotCheck >@@ -866,7 +867,7 @@ def defaultNoEagerRun > return if $mode == "basic" > > runFTLNoCJITNoInlineValidate >- runFTLNoCJITB3O1 >+ runFTLNoCJITB3O0 > end > end >
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