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 | |
37 | namespace JSC { namespace B3 { |
38 | |
39 | using Inst = Air::Inst; |
40 | using Arg = Air::Arg; |
41 | using GenerationContext = Air::GenerationContext; |
42 | |
43 | namespace { |
44 | |
45 | unsigned 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 | |
60 | unsigned numB3Args(Value* value) |
61 | { |
62 | return numB3Args(value->kind()); |
63 | } |
64 | |
65 | unsigned numB3Args(Inst& inst) |
66 | { |
67 | return numB3Args(inst.origin); |
68 | } |
69 | |
70 | } // anonymous namespace |
71 | |
72 | CheckSpecial::Key::Key(const Inst& inst) |
73 | { |
74 | m_kind = inst.kind; |
75 | m_numArgs = inst.args.size(); |
76 | m_stackmapRole = SameAsRep; |
77 | } |
78 | |
79 | void CheckSpecial::Key::dump(PrintStream& out) const |
80 | { |
81 | out.print(m_kind, "(" , m_numArgs, "," , m_stackmapRole, ")" ); |
82 | } |
83 | |
84 | CheckSpecial::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 | |
92 | CheckSpecial::CheckSpecial(const CheckSpecial::Key& key) |
93 | : CheckSpecial(key.kind(), key.numArgs(), key.stackmapRole()) |
94 | { |
95 | } |
96 | |
97 | CheckSpecial::~CheckSpecial() |
98 | { |
99 | } |
100 | |
101 | Inst 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 | |
111 | void 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 | |
132 | bool 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 | |
139 | bool 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 | |
146 | bool 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 | |
153 | Optional<unsigned> CheckSpecial::shouldTryAliasingDef(Inst& inst) |
154 | { |
155 | if (Optional<unsigned> branchDef = hiddenBranch(inst).shouldTryAliasingDef()) |
156 | return *branchDef + 1; |
157 | return WTF::nullopt; |
158 | } |
159 | |
160 | CCallHelpers::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 | |
252 | void CheckSpecial::dumpImpl(PrintStream& out) const |
253 | { |
254 | out.print(m_checkKind, "(" , m_numCheckArgs, "," , m_stackmapRole, ")" ); |
255 | } |
256 | |
257 | void 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 | |