@@ -1515,7 +1515,9 @@ class BaseStackFrame final : public BaseStackFrameAllocator {
1515
1515
Local (MIRType type, int32_t offs) : type(type), offs(offs) {}
1516
1516
};
1517
1517
1518
- using LocalVector = Vector<Local, 8 , SystemAllocPolicy>;
1518
+ // Profiling shows that the number of parameters and locals frequently
1519
+ // touches or exceeds 8. So 16 seems like a reasonable starting point.
1520
+ using LocalVector = Vector<Local, 16 , SystemAllocPolicy>;
1519
1521
1520
1522
// Initialize `localInfo` based on the types of `locals` and `args`.
1521
1523
bool setupLocals (const ValTypeVector& locals, const ValTypeVector& args,
@@ -1990,7 +1992,7 @@ struct Stk {
1990
1992
}
1991
1993
};
1992
1994
1993
- typedef Vector<Stk, 8 , SystemAllocPolicy> StkVector;
1995
+ typedef Vector<Stk, 0 , SystemAllocPolicy> StkVector;
1994
1996
1995
1997
// MachineStackTracker, used for stack-slot pointerness tracking.
1996
1998
@@ -2565,8 +2567,9 @@ class BaseCompiler final : public BaseCompilerInterface {
2565
2567
BaseCompiler (const ModuleEnvironment& env, const FuncCompileInput& input,
2566
2568
const ValTypeVector& locals, const MachineState& trapExitLayout,
2567
2569
size_t trapExitLayoutNumWords, Decoder& decoder,
2568
- TempAllocator* alloc, MacroAssembler* masm,
2570
+ StkVector& stkSource, TempAllocator* alloc, MacroAssembler* masm,
2569
2571
StackMaps* stackMaps);
2572
+ ~BaseCompiler ();
2570
2573
2571
2574
MOZ_MUST_USE bool init ();
2572
2575
@@ -2865,8 +2868,22 @@ class BaseCompiler final : public BaseCompilerInterface {
2865
2868
// to avoid problems with control flow and messy register usage
2866
2869
// patterns.
2867
2870
2871
+ // This is the value stack actually used during compilation. It is a
2872
+ // StkVector rather than a StkVector& since constantly dereferencing a
2873
+ // StkVector& adds about 0.5% or more to the compiler's dynamic instruction
2874
+ // count.
2868
2875
StkVector stk_;
2869
2876
2877
+ // BaselineCompileFunctions() "lends" us the StkVector to use in this
2878
+ // BaseCompiler object, and that is installed in |stk_| in our constructor.
2879
+ // This is so as to avoid having to malloc/free the vector's contents at
2880
+ // each creation/destruction of a BaseCompiler object. It does however mean
2881
+ // that we need to hold on to a reference to BaselineCompileFunctions()'s
2882
+ // vector, so we can swap (give) its contents back when this BaseCompiler
2883
+ // object is destroyed. This significantly reduces the heap turnover of the
2884
+ // baseline compiler. See bug 1532592.
2885
+ StkVector& stkSource_;
2886
+
2870
2887
#ifdef DEBUG
2871
2888
size_t countMemRefsOnStk () {
2872
2889
size_t nRefs = 0 ;
@@ -11776,8 +11793,8 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
11776
11793
const ValTypeVector& locals,
11777
11794
const MachineState& trapExitLayout,
11778
11795
size_t trapExitLayoutNumWords, Decoder& decoder,
11779
- TempAllocator* alloc, MacroAssembler* masm ,
11780
- StackMaps* stackMaps)
11796
+ StkVector& stkSource, TempAllocator* alloc ,
11797
+ MacroAssembler* masm, StackMaps* stackMaps)
11781
11798
: env_(env),
11782
11799
iter_(env, decoder),
11783
11800
func_(func),
@@ -11799,7 +11816,28 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
11799
11816
joinRegI64_(RegI64(ReturnReg64)),
11800
11817
joinRegPtr_(RegPtr(ReturnReg)),
11801
11818
joinRegF32_(RegF32(ReturnFloat32Reg)),
11802
- joinRegF64_(RegF64(ReturnDoubleReg)) {}
11819
+ joinRegF64_(RegF64(ReturnDoubleReg)),
11820
+ stkSource_(stkSource) {
11821
+ // Our caller, BaselineCompileFunctions, will lend us the vector contents to
11822
+ // use for the eval stack. To get hold of those contents, we'll temporarily
11823
+ // installing an empty one in its place.
11824
+ MOZ_ASSERT (stk_.empty ());
11825
+ stk_.swap (stkSource_);
11826
+
11827
+ // Assuming that previously processed wasm functions are well formed, the
11828
+ // eval stack should now be empty. But empty it anyway; any non-emptyness
11829
+ // at this point will cause chaos.
11830
+ stk_.clear ();
11831
+ }
11832
+
11833
+ BaseCompiler::~BaseCompiler () {
11834
+ stk_.swap (stkSource_);
11835
+ // We've returned the eval stack vector contents to our caller,
11836
+ // BaselineCompileFunctions. We expect the vector we get in return to be
11837
+ // empty since that's what we swapped for the stack vector in our
11838
+ // constructor.
11839
+ MOZ_ASSERT (stk_.empty ());
11840
+ }
11803
11841
11804
11842
bool BaseCompiler::init () {
11805
11843
if (!SigD_.append (ValType::F64)) {
@@ -11880,6 +11918,14 @@ bool js::wasm::BaselineCompileFunctions(const ModuleEnvironment& env,
11880
11918
size_t trapExitLayoutNumWords;
11881
11919
GenerateTrapExitMachineState (&trapExitLayout, &trapExitLayoutNumWords);
11882
11920
11921
+ // The compiler's operand stack. We reuse it across all functions so as to
11922
+ // avoid malloc/free. Presize it to 128 elements in the hope of avoiding
11923
+ // reallocation later.
11924
+ StkVector stk;
11925
+ if (!stk.reserve (128 )) {
11926
+ return false ;
11927
+ }
11928
+
11883
11929
for (const FuncCompileInput& func : inputs) {
11884
11930
Decoder d (func.begin , func.end , func.lineOrBytecode , error);
11885
11931
@@ -11896,7 +11942,7 @@ bool js::wasm::BaselineCompileFunctions(const ModuleEnvironment& env,
11896
11942
// One-pass baseline compilation.
11897
11943
11898
11944
BaseCompiler f (env, func, locals, trapExitLayout, trapExitLayoutNumWords, d,
11899
- &alloc, &masm, &code->stackMaps );
11945
+ stk, &alloc, &masm, &code->stackMaps );
11900
11946
if (!f.init ()) {
11901
11947
return false ;
11902
11948
}
0 commit comments