1/*
2 * Copyright (C) 2015-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 "B3CheckSpecial.h"
28
29#if ENABLE(B3_JIT)
30
31#include "AirCode.h"
32#include "AirGenerationContext.h"
33#include "AirInstInlines.h"
34#include "B3StackmapGenerationParams.h"
35#include "B3ValueInlines.h"
36
37namespace JSC { namespace B3 {
38
39using Inst = Air::Inst;
40using Arg = Air::Arg;
41using GenerationContext = Air::GenerationContext;
42
43namespace {
44
45unsigned numB3Args(Kind kind)
46{
47 switch (kind.opcode()) {
48 case CheckAdd:
49 case CheckSub:
50 case CheckMul:
51 return 2;
52 case Check:
53 return 1;
54 default:
55 RELEASE_ASSERT_NOT_REACHED();
56 return 0;
57 }
58}
59
60unsigned numB3Args(Value* value)
61{
62 return numB3Args(value->kind());
63}
64
65unsigned numB3Args(Inst& inst)
66{
67 return numB3Args(inst.origin);
68}
69
70} // anonymous namespace
71
72CheckSpecial::Key::Key(const Inst& inst)
73{
74 m_kind = inst.kind;
75 m_numArgs = inst.args.size();
76 m_stackmapRole = SameAsRep;
77}
78
79void CheckSpecial::Key::dump(PrintStream& out) const
80{
81 out.print(m_kind, "(", m_numArgs, ",", m_stackmapRole, ")");
82}
83
84CheckSpecial::CheckSpecial(Air::Kind kind, unsigned numArgs, RoleMode stackmapRole)
85 : m_checkKind(kind)
86 , m_stackmapRole(stackmapRole)
87 , m_numCheckArgs(numArgs)
88{
89 ASSERT(isDefinitelyTerminal(kind.opcode));
90}
91
92CheckSpecial::CheckSpecial(const CheckSpecial::Key& key)
93 : CheckSpecial(key.kind(), key.numArgs(), key.stackmapRole())
94{
95}
96
97CheckSpecial::~CheckSpecial()
98{
99}
100
101Inst CheckSpecial::hiddenBranch(const Inst& inst) const
102{
103 Inst hiddenBranch(m_checkKind, inst.origin);
104 hiddenBranch.args.reserveInitialCapacity(m_numCheckArgs);
105 for (unsigned i = 0; i < m_numCheckArgs; ++i)
106 hiddenBranch.args.append(inst.args[i + 1]);
107 ASSERT(hiddenBranch.isTerminal());
108 return hiddenBranch;
109}
110
111void CheckSpecial::forEachArg(Inst& inst, const ScopedLambda<Inst::EachArgCallback>& callback)
112{
113 using namespace Air;
114 Optional<Width> optionalDefArgWidth;
115 Inst hidden = hiddenBranch(inst);
116 hidden.forEachArg(
117 [&] (Arg& arg, Arg::Role role, Bank bank, Width width) {
118 if (Arg::isAnyDef(role) && role != Arg::Scratch) {
119 ASSERT(!optionalDefArgWidth); // There can only be one Def'ed arg.
120 optionalDefArgWidth = width;
121 }
122 unsigned index = &arg - &hidden.args[0];
123 callback(inst.args[1 + index], role, bank, width);
124 });
125
126 Optional<unsigned> firstRecoverableIndex;
127 if (m_checkKind.opcode == BranchAdd32 || m_checkKind.opcode == BranchAdd64)
128 firstRecoverableIndex = 1;
129 forEachArgImpl(numB3Args(inst), m_numCheckArgs + 1, inst, m_stackmapRole, firstRecoverableIndex, callback, optionalDefArgWidth);
130}
131
132bool CheckSpecial::isValid(Inst& inst)
133{
134 return hiddenBranch(inst).isValidForm()
135 && isValidImpl(numB3Args(inst), m_numCheckArgs + 1, inst)
136 && inst.args.size() - m_numCheckArgs - 1 == inst.origin->numChildren() - numB3Args(inst);
137}
138
139bool CheckSpecial::admitsStack(Inst& inst, unsigned argIndex)
140{
141 if (argIndex >= 1 && argIndex < 1 + m_numCheckArgs)
142 return hiddenBranch(inst).admitsStack(argIndex - 1);
143 return admitsStackImpl(numB3Args(inst), m_numCheckArgs + 1, inst, argIndex);
144}
145
146bool CheckSpecial::admitsExtendedOffsetAddr(Inst& inst, unsigned argIndex)
147{
148 if (argIndex >= 1 && argIndex < 1 + m_numCheckArgs)
149 return false;
150 return admitsStack(inst, argIndex);
151}
152
153Optional<unsigned> CheckSpecial::shouldTryAliasingDef(Inst& inst)
154{
155 if (Optional<unsigned> branchDef = hiddenBranch(inst).shouldTryAliasingDef())
156 return *branchDef + 1;
157 return WTF::nullopt;
158}
159
160CCallHelpers::Jump CheckSpecial::generate(Inst& inst, CCallHelpers& jit, GenerationContext& context)
161{
162 using namespace Air;
163 CCallHelpers::Jump fail = hiddenBranch(inst).generate(jit, context);
164 ASSERT(fail.isSet());
165
166 StackmapValue* value = inst.origin->as<StackmapValue>();
167 ASSERT(value);
168
169 Vector<ValueRep> reps = repsImpl(context, numB3Args(inst), m_numCheckArgs + 1, inst);
170
171 // Set aside the args that are relevant to undoing the operation. This is because we don't want to
172 // capture all of inst in the closure below.
173 Vector<Arg, 3> args;
174 for (unsigned i = 0; i < m_numCheckArgs; ++i)
175 args.append(inst.args[1 + i]);
176
177 context.latePaths.append(
178 createSharedTask<GenerationContext::LatePathFunction>(
179 [=] (CCallHelpers& jit, GenerationContext& context) {
180 fail.link(&jit);
181
182 // If necessary, undo the operation.
183 switch (m_checkKind.opcode) {
184 case BranchAdd32:
185 if ((m_numCheckArgs == 4 && args[1] == args[2] && args[2] == args[3])
186 || (m_numCheckArgs == 3 && args[1] == args[2])) {
187 // This is ugly, but that's fine - we won't have to do this very often.
188 ASSERT(args[1].isGPR());
189 GPRReg valueGPR = args[1].gpr();
190 GPRReg scratchGPR = CCallHelpers::selectScratchGPR(valueGPR);
191 jit.pushToSave(scratchGPR);
192 jit.setCarry(scratchGPR);
193 jit.lshift32(CCallHelpers::TrustedImm32(31), scratchGPR);
194 jit.urshift32(CCallHelpers::TrustedImm32(1), valueGPR);
195 jit.or32(scratchGPR, valueGPR);
196 jit.popToRestore(scratchGPR);
197 break;
198 }
199 if (m_numCheckArgs == 4) {
200 if (args[1] == args[3])
201 Inst(Sub32, nullptr, args[2], args[3]).generate(jit, context);
202 else if (args[2] == args[3])
203 Inst(Sub32, nullptr, args[1], args[3]).generate(jit, context);
204 } else if (m_numCheckArgs == 3)
205 Inst(Sub32, nullptr, args[1], args[2]).generate(jit, context);
206 break;
207 case BranchAdd64:
208 if ((m_numCheckArgs == 4 && args[1] == args[2] && args[2] == args[3])
209 || (m_numCheckArgs == 3 && args[1] == args[2])) {
210 // This is ugly, but that's fine - we won't have to do this very often.
211 ASSERT(args[1].isGPR());
212 GPRReg valueGPR = args[1].gpr();
213 GPRReg scratchGPR = CCallHelpers::selectScratchGPR(valueGPR);
214 jit.pushToSave(scratchGPR);
215 jit.setCarry(scratchGPR);
216 jit.lshift64(CCallHelpers::TrustedImm32(63), scratchGPR);
217 jit.urshift64(CCallHelpers::TrustedImm32(1), valueGPR);
218 jit.or64(scratchGPR, valueGPR);
219 jit.popToRestore(scratchGPR);
220 break;
221 }
222 if (m_numCheckArgs == 4) {
223 if (args[1] == args[3])
224 Inst(Sub64, nullptr, args[2], args[3]).generate(jit, context);
225 else if (args[2] == args[3])
226 Inst(Sub64, nullptr, args[1], args[3]).generate(jit, context);
227 } else if (m_numCheckArgs == 3)
228 Inst(Sub64, nullptr, args[1], args[2]).generate(jit, context);
229 break;
230 case BranchSub32:
231 Inst(Add32, nullptr, args[1], args[2]).generate(jit, context);
232 break;
233 case BranchSub64:
234 Inst(Add64, nullptr, args[1], args[2]).generate(jit, context);
235 break;
236 case BranchNeg32:
237 Inst(Neg32, nullptr, args[1]).generate(jit, context);
238 break;
239 case BranchNeg64:
240 Inst(Neg64, nullptr, args[1]).generate(jit, context);
241 break;
242 default:
243 break;
244 }
245
246 value->m_generator->run(jit, StackmapGenerationParams(value, reps, context));
247 }));
248
249 return CCallHelpers::Jump(); // As far as Air thinks, we are not a terminal.
250}
251
252void CheckSpecial::dumpImpl(PrintStream& out) const
253{
254 out.print(m_checkKind, "(", m_numCheckArgs, ",", m_stackmapRole, ")");
255}
256
257void CheckSpecial::deepDumpImpl(PrintStream& out) const
258{
259 out.print("B3::CheckValue lowered to ", m_checkKind, " with ", m_numCheckArgs, " args.");
260}
261
262} } // namespace JSC::B3
263
264#endif // ENABLE(B3_JIT)
265