summaryrefslogtreecommitdiffstats
path: root/js/src/jit/TrialInlining.h
blob: e3d6d83305956ba05d569f8c36ffe2d01cc603af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_TrialInlining_h
#define jit_TrialInlining_h

#include "jit/CacheIR.h"
#include "jit/ICStubSpace.h"
#include "vm/BytecodeLocation.h"

/*
 * [SMDOC] Trial Inlining
 *
 * WarpBuilder relies on transpiling CacheIR. When inlining scripted
 * functions in WarpBuilder, we want our ICs to be as monomorphic as
 * possible. Functions with multiple callers complicate this. An IC in
 * such a function might be monomorphic for any given caller, but
 * polymorphic overall. This make the input to WarpBuilder less precise.
 *
 * To solve this problem, we do trial inlining. During baseline
 * execution, we identify call sites for which it would be useful to
 * have more precise inlining data. For each such call site, we
 * allocate a fresh ICScript and replace the existing call IC with a
 * new specialized IC that invokes the callee using the new
 * ICScript. Other callers of the callee will continue using the
 * default ICScript. When we eventually Warp-compile the script, we
 * can generate code for the callee using the IC information in our
 * private ICScript, which is specialized for its caller.
 *
 * The same approach can be used to inline recursively.
 */

namespace js {
namespace jit {

class BaselineFrame;
class ICEntry;
class ICScript;

/*
 * An InliningRoot is owned by a JitScript. In turn, it owns the set
 * of ICScripts that are candidates for being inlined in that JitScript.
 */
class InliningRoot {
 public:
  explicit InliningRoot(JSContext* cx, JSScript* owningScript)
      : owningScript_(owningScript),
        inlinedScripts_(cx),
        totalBytecodeSize_(owningScript->length()) {}

  FallbackICStubSpace* fallbackStubSpace() { return &fallbackStubSpace_; }

  void trace(JSTracer* trc);

  bool addInlinedScript(js::UniquePtr<ICScript> icScript);
  void removeInlinedScript(ICScript* icScript);

  uint32_t numInlinedScripts() const { return inlinedScripts_.length(); }

  void purgeOptimizedStubs(Zone* zone);
  void resetWarmUpCounts(uint32_t count);

  JSScript* owningScript() const { return owningScript_; }

  size_t totalBytecodeSize() const { return totalBytecodeSize_; }

  void addToTotalBytecodeSize(size_t size) { totalBytecodeSize_ += size; }

 private:
  FallbackICStubSpace fallbackStubSpace_ = {};
  HeapPtr<JSScript*> owningScript_;
  js::Vector<js::UniquePtr<ICScript>> inlinedScripts_;

  // Bytecode size of outer script and all inlined scripts.
  size_t totalBytecodeSize_;
};

class InlinableOpData {
 public:
  JSFunction* target = nullptr;
  ICScript* icScript = nullptr;
  const uint8_t* endOfSharedPrefix = nullptr;
};

class InlinableCallData : public InlinableOpData {
 public:
  ObjOperandId calleeOperand;
  CallFlags callFlags;
};

class InlinableGetterData : public InlinableOpData {
 public:
  ValOperandId receiverOperand;
  bool sameRealm = false;
};

class InlinableSetterData : public InlinableOpData {
 public:
  ObjOperandId receiverOperand;
  ValOperandId rhsOperand;
  bool sameRealm = false;
};

mozilla::Maybe<InlinableOpData> FindInlinableOpData(ICCacheIRStub* stub,
                                                    BytecodeLocation loc);

mozilla::Maybe<InlinableCallData> FindInlinableCallData(ICCacheIRStub* stub);
mozilla::Maybe<InlinableGetterData> FindInlinableGetterData(
    ICCacheIRStub* stub);
mozilla::Maybe<InlinableSetterData> FindInlinableSetterData(
    ICCacheIRStub* stub);

class MOZ_RAII TrialInliner {
 public:
  TrialInliner(JSContext* cx, HandleScript script, ICScript* icScript,
               InliningRoot* root)
      : cx_(cx), script_(script), icScript_(icScript), root_(root) {}

  JSContext* cx() { return cx_; }

  [[nodiscard]] bool tryInlining();
  [[nodiscard]] bool maybeInlineCall(const ICEntry& entry,
                                     BytecodeLocation loc);
  [[nodiscard]] bool maybeInlineGetter(const ICEntry& entry,
                                       BytecodeLocation loc);
  [[nodiscard]] bool maybeInlineSetter(const ICEntry& entry,
                                       BytecodeLocation loc);

  static bool canInline(JSFunction* target, HandleScript caller);

 private:
  ICCacheIRStub* maybeSingleStub(const ICEntry& entry);
  void cloneSharedPrefix(ICCacheIRStub* stub, const uint8_t* endOfPrefix,
                         CacheIRWriter& writer);
  ICScript* createInlinedICScript(JSFunction* target, BytecodeLocation loc);
  [[nodiscard]] bool replaceICStub(const ICEntry& entry, CacheIRWriter& writer,
                                   CacheKind kind);

  bool shouldInline(JSFunction* target, ICCacheIRStub* stub,
                    BytecodeLocation loc);

  JSContext* cx_;
  HandleScript script_;
  ICScript* icScript_;
  InliningRoot* root_;
};

bool DoTrialInlining(JSContext* cx, BaselineFrame* frame);

}  // namespace jit
}  // namespace js

#endif /* jit_TrialInlining_h */