B3 should support multiple entrypoints
[WebKit-https.git] / Source / JavaScriptCore / b3 / air / AirCustom.h
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #ifndef AirCustom_h
27 #define AirCustom_h
28
29 #if ENABLE(B3_JIT)
30
31 #include "AirInst.h"
32 #include "AirSpecial.h"
33 #include "B3Value.h"
34
35 namespace JSC { namespace B3 { namespace Air {
36
37 // This defines the behavior of custom instructions - i.e. those whose behavior cannot be
38 // described using AirOpcode.opcodes. If you define an opcode as "custom Foo" in that file, then
39 // you will need to create a "struct FooCustom" here that implements the custom behavior
40 // methods.
41 //
42 // The customizability granted by the custom instruction mechanism is strictly less than what
43 // you get using the Patch instruction and implementing a Special. However, that path requires
44 // allocating a Special object and ensuring that it's the first operand. For many instructions,
45 // that is not as convenient as using Custom, which makes the instruction look like any other
46 // instruction. Note that both of those extra powers of the Patch instruction happen because we
47 // special-case that instruction in many phases and analyses. Non-special-cased behaviors of
48 // Patch are implemented using the custom instruction mechanism.
49 //
50 // Specials are still more flexible if you need to list extra clobbered registers and you'd like
51 // that to be expressed as a bitvector rather than an arglist. They are also more flexible if
52 // you need to carry extra state around with the instruction. Also, Specials mean that you
53 // always have access to Code& even in methods that don't take a GenerationContext.
54
55 // Definition of Patch instruction. Patch is used to delegate the behavior of the instruction to the
56 // Special object, which will be the first argument to the instruction.
57 struct PatchCustom {
58     template<typename Functor>
59     static void forEachArg(Inst& inst, const Functor& functor)
60     {
61         // This is basically bogus, but it works for analyses that model Special as an
62         // immediate.
63         functor(inst.args[0], Arg::Use, Arg::GP, Arg::pointerWidth());
64         
65         inst.args[0].special()->forEachArg(inst, scopedLambda<Inst::EachArgCallback>(functor));
66     }
67
68     template<typename... Arguments>
69     static bool isValidFormStatic(Arguments...)
70     {
71         return false;
72     }
73
74     static bool isValidForm(Inst& inst);
75
76     static bool admitsStack(Inst& inst, unsigned argIndex)
77     {
78         if (!argIndex)
79             return false;
80         return inst.args[0].special()->admitsStack(inst, argIndex);
81     }
82
83     static Optional<unsigned> shouldTryAliasingDef(Inst& inst)
84     {
85         return inst.args[0].special()->shouldTryAliasingDef(inst);
86     }
87     
88     static bool isTerminal(Inst& inst)
89     {
90         return inst.args[0].special()->isTerminal(inst);
91     }
92
93     static bool hasNonArgEffects(Inst& inst)
94     {
95         return inst.args[0].special()->hasNonArgEffects(inst);
96     }
97
98     static bool hasNonArgNonControlEffects(Inst& inst)
99     {
100         return inst.args[0].special()->hasNonArgNonControlEffects(inst);
101     }
102
103     static CCallHelpers::Jump generate(
104         Inst& inst, CCallHelpers& jit, GenerationContext& context)
105     {
106         return inst.args[0].special()->generate(inst, jit, context);
107     }
108 };
109
110 template<typename Subtype>
111 struct CommonCustomBase {
112     static bool hasNonArgEffects(Inst& inst)
113     {
114         return Subtype::isTerminal(inst) || Subtype::hasNonArgNonControlEffects(inst);
115     }
116 };
117
118 // Definition of CCall instruction. CCall is used for hot path C function calls. It's lowered to a
119 // Patch with an Air CCallSpecial along with code to marshal instructions. The lowering happens
120 // before register allocation, so that the register allocator sees the clobbers.
121 struct CCallCustom : public CommonCustomBase<CCallCustom> {
122     template<typename Functor>
123     static void forEachArg(Inst& inst, const Functor& functor)
124     {
125         Value* value = inst.origin;
126
127         unsigned index = 0;
128
129         functor(inst.args[index++], Arg::Use, Arg::GP, Arg::pointerWidth()); // callee
130         
131         if (value->type() != Void) {
132             functor(
133                 inst.args[index++], Arg::Def,
134                 Arg::typeForB3Type(value->type()),
135                 Arg::widthForB3Type(value->type()));
136         }
137
138         for (unsigned i = 1; i < value->numChildren(); ++i) {
139             Value* child = value->child(i);
140             functor(
141                 inst.args[index++], Arg::Use,
142                 Arg::typeForB3Type(child->type()),
143                 Arg::widthForB3Type(child->type()));
144         }
145     }
146
147     template<typename... Arguments>
148     static bool isValidFormStatic(Arguments...)
149     {
150         return false;
151     }
152
153     static bool isValidForm(Inst&);
154
155     static bool admitsStack(Inst&, unsigned)
156     {
157         return true;
158     }
159     
160     static bool isTerminal(Inst&)
161     {
162         return false;
163     }
164
165     static bool hasNonArgNonControlEffects(Inst&)
166     {
167         return true;
168     }
169
170     // This just crashes, since we expect C calls to be lowered before generation.
171     static CCallHelpers::Jump generate(Inst&, CCallHelpers&, GenerationContext&);
172 };
173
174 struct ColdCCallCustom : CCallCustom {
175     template<typename Functor>
176     static void forEachArg(Inst& inst, const Functor& functor)
177     {
178         // This is just like a call, but uses become cold.
179         CCallCustom::forEachArg(
180             inst,
181             [&] (Arg& arg, Arg::Role role, Arg::Type type, Arg::Width width) {
182                 functor(arg, Arg::cooled(role), type, width);
183             });
184     }
185 };
186
187 struct ShuffleCustom : public CommonCustomBase<ShuffleCustom> {
188     template<typename Functor>
189     static void forEachArg(Inst& inst, const Functor& functor)
190     {
191         unsigned limit = inst.args.size() / 3 * 3;
192         for (unsigned i = 0; i < limit; i += 3) {
193             Arg& src = inst.args[i + 0];
194             Arg& dst = inst.args[i + 1];
195             Arg& widthArg = inst.args[i + 2];
196             Arg::Width width = widthArg.width();
197             Arg::Type type = src.isGP() && dst.isGP() ? Arg::GP : Arg::FP;
198             functor(src, Arg::Use, type, width);
199             functor(dst, Arg::Def, type, width);
200             functor(widthArg, Arg::Use, Arg::GP, Arg::Width8);
201         }
202     }
203
204     template<typename... Arguments>
205     static bool isValidFormStatic(Arguments...)
206     {
207         return false;
208     }
209
210     static bool isValidForm(Inst&);
211     
212     static bool admitsStack(Inst&, unsigned index)
213     {
214         switch (index % 3) {
215         case 0:
216         case 1:
217             return true;
218         default:
219             return false;
220         }
221     }
222
223     static bool isTerminal(Inst&)
224     {
225         return false;
226     }
227
228     static bool hasNonArgNonControlEffects(Inst&)
229     {
230         return false;
231     }
232
233     static CCallHelpers::Jump generate(Inst&, CCallHelpers&, GenerationContext&);
234 };
235
236 struct EntrySwitchCustom : public CommonCustomBase<EntrySwitchCustom> {
237     template<typename Func>
238     static void forEachArg(Inst&, const Func&)
239     {
240     }
241     
242     template<typename... Arguments>
243     static bool isValidFormStatic(Arguments...)
244     {
245         return !sizeof...(Arguments);
246     }
247     
248     static bool isValidForm(Inst& inst)
249     {
250         return inst.args.isEmpty();
251     }
252     
253     static bool admitsStack(Inst&, unsigned)
254     {
255         return false;
256     }
257     
258     static bool isTerminal(Inst&)
259     {
260         return true;
261     }
262     
263     static bool hasNonArgNonControlEffects(Inst&)
264     {
265         return false;
266     }
267
268     static CCallHelpers::Jump generate(Inst&, CCallHelpers&, GenerationContext&)
269     {
270         // This should never be reached because we should have lowered EntrySwitch before
271         // generation.
272         UNREACHABLE_FOR_PLATFORM();
273         return CCallHelpers::Jump();
274     }
275 };
276
277 } } } // namespace JSC::B3::Air
278
279 #endif // ENABLE(B3_JIT)
280
281 #endif // AirCustom_h
282