1/*
2 * Copyright (C) 2016-2017 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#include "config.h"
27#include "AirCustom.h"
28
29#if ENABLE(B3_JIT)
30
31#include "AirInstInlines.h"
32#include "B3CCallValue.h"
33#include "B3ValueInlines.h"
34
35namespace JSC { namespace B3 { namespace Air {
36
37bool PatchCustom::isValidForm(Inst& inst)
38{
39 if (inst.args.size() < 1)
40 return false;
41 if (!inst.args[0].isSpecial())
42 return false;
43 if (!inst.args[0].special()->isValid(inst))
44 return false;
45 RegisterSet clobberedEarly = inst.extraEarlyClobberedRegs();
46 RegisterSet clobberedLate = inst.extraClobberedRegs();
47 bool ok = true;
48 inst.forEachTmp(
49 [&] (Tmp& tmp, Arg::Role role, Bank, Width) {
50 if (!tmp.isReg())
51 return;
52 if (Arg::isLateDef(role) || Arg::isLateUse(role))
53 ok &= !clobberedLate.get(tmp.reg());
54 else
55 ok &= !clobberedEarly.get(tmp.reg());
56 });
57 return ok;
58}
59
60bool CCallCustom::isValidForm(Inst& inst)
61{
62 CCallValue* value = inst.origin->as<CCallValue>();
63 if (!value)
64 return false;
65
66 if (inst.args.size() != (value->type() == Void ? 0 : 1) + value->numChildren())
67 return false;
68
69 // The arguments can only refer to the stack, tmps, or immediates.
70 for (Arg& arg : inst.args) {
71 if (!arg.isTmp() && !arg.isStackMemory() && !arg.isSomeImm())
72 return false;
73 }
74
75 unsigned offset = 0;
76
77 if (!inst.args[0].isGP())
78 return false;
79
80 // If there is a result then it cannot be an immediate.
81 if (value->type() != Void) {
82 if (inst.args[1].isSomeImm())
83 return false;
84 if (!inst.args[1].canRepresent(value))
85 return false;
86 offset++;
87 }
88
89 for (unsigned i = value->numChildren(); i-- > 1;) {
90 Value* child = value->child(i);
91 Arg arg = inst.args[offset + i];
92 if (!arg.canRepresent(child))
93 return false;
94 }
95
96 return true;
97}
98
99CCallHelpers::Jump CCallCustom::generate(Inst& inst, CCallHelpers&, GenerationContext&)
100{
101 dataLog("FATAL: Unlowered C call: ", inst, "\n");
102 UNREACHABLE_FOR_PLATFORM();
103 return CCallHelpers::Jump();
104}
105
106bool ShuffleCustom::isValidForm(Inst& inst)
107{
108 if (inst.args.size() % 3)
109 return false;
110
111 // A destination may only appear once. This requirement allows us to avoid the undefined behavior
112 // of having a destination that is supposed to get multiple inputs simultaneously. It also
113 // imposes some interesting constraints on the "shape" of the shuffle. If we treat a shuffle pair
114 // as an edge and the Args as nodes, then the single-destination requirement means that the
115 // shuffle graph consists of two kinds of subgraphs:
116 //
117 // - Spanning trees. We call these shifts. They can be executed as a sequence of Move
118 // instructions and don't usually require scratch registers.
119 //
120 // - Closed loops. These loops consist of nodes that have one successor and one predecessor, so
121 // there is no way to "get into" the loop from outside of it. These can be executed using swaps
122 // or by saving one of the Args to a scratch register and executing it as a shift.
123 HashSet<Arg> dsts;
124
125 for (unsigned i = 0; i < inst.args.size(); ++i) {
126 Arg arg = inst.args[i];
127 unsigned mode = i % 3;
128
129 if (mode == 2) {
130 // It's the width.
131 if (!arg.isWidthArg())
132 return false;
133 continue;
134 }
135
136 // The source can be an immediate.
137 if (!mode) {
138 if (arg.isSomeImm())
139 continue;
140
141 if (!arg.isCompatibleBank(inst.args[i + 1]))
142 return false;
143 } else {
144 ASSERT(mode == 1);
145 if (!dsts.add(arg).isNewEntry)
146 return false;
147 }
148
149 if (arg.isTmp() || arg.isMemory())
150 continue;
151
152 return false;
153 }
154
155 // No destination register may appear in any address expressions. The lowering can't handle it
156 // and it's not useful for the way we end up using Shuffles. Normally, Shuffles only used for
157 // stack addresses and non-stack registers.
158 for (Arg& arg : inst.args) {
159 if (!arg.isMemory())
160 continue;
161 bool ok = true;
162 arg.forEachTmpFast(
163 [&] (Tmp tmp) {
164 if (dsts.contains(tmp))
165 ok = false;
166 });
167 if (!ok)
168 return false;
169 }
170
171 return true;
172}
173
174CCallHelpers::Jump ShuffleCustom::generate(Inst& inst, CCallHelpers&, GenerationContext&)
175{
176 dataLog("FATAL: Unlowered shuffle: ", inst, "\n");
177 UNREACHABLE_FOR_PLATFORM();
178 return CCallHelpers::Jump();
179}
180
181bool WasmBoundsCheckCustom::isValidForm(Inst& inst)
182{
183 if (inst.args.size() != 2)
184 return false;
185 if (!inst.args[0].isTmp() && !inst.args[0].isSomeImm())
186 return false;
187
188 return inst.args[1].isReg() || inst.args[1].isTmp() || inst.args[1].isSomeImm();
189}
190
191
192} } } // namespace JSC::B3::Air
193
194#endif // ENABLE(B3_JIT)
195
196