1/*
2 * Copyright (C) 2015-2018 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 "B3StackmapSpecial.h"
28
29#if ENABLE(B3_JIT)
30
31#include "AirCode.h"
32#include "AirGenerationContext.h"
33#include "B3ValueInlines.h"
34
35namespace JSC { namespace B3 {
36
37using Arg = Air::Arg;
38using Inst = Air::Inst;
39using Tmp = Air::Tmp;
40
41StackmapSpecial::StackmapSpecial()
42{
43}
44
45StackmapSpecial::~StackmapSpecial()
46{
47}
48
49void StackmapSpecial::reportUsedRegisters(Inst& inst, const RegisterSet& usedRegisters)
50{
51 StackmapValue* value = inst.origin->as<StackmapValue>();
52 ASSERT(value);
53
54 // FIXME: If the Inst that uses the StackmapSpecial gets duplicated, then we end up merging used
55 // register sets from multiple places. This currently won't happen since Air doesn't have taildup
56 // or things like that. But maybe eventually it could be a problem.
57 value->m_usedRegisters.merge(usedRegisters);
58}
59
60RegisterSet StackmapSpecial::extraClobberedRegs(Inst& inst)
61{
62 StackmapValue* value = inst.origin->as<StackmapValue>();
63 ASSERT(value);
64
65 return value->lateClobbered();
66}
67
68RegisterSet StackmapSpecial::extraEarlyClobberedRegs(Inst& inst)
69{
70 StackmapValue* value = inst.origin->as<StackmapValue>();
71 ASSERT(value);
72
73 return value->earlyClobbered();
74}
75
76void StackmapSpecial::forEachArgImpl(
77 unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs,
78 Inst& inst, RoleMode roleMode, Optional<unsigned> firstRecoverableIndex,
79 const ScopedLambda<Inst::EachArgCallback>& callback, Optional<Width> optionalDefArgWidth)
80{
81 StackmapValue* value = inst.origin->as<StackmapValue>();
82 ASSERT(value);
83
84 // Check that insane things have not happened.
85 ASSERT(inst.args.size() >= numIgnoredAirArgs);
86 ASSERT(value->children().size() >= numIgnoredB3Args);
87 ASSERT(inst.args.size() - numIgnoredAirArgs >= value->children().size() - numIgnoredB3Args);
88 ASSERT(inst.args[0].kind() == Arg::Kind::Special);
89
90 for (unsigned i = 0; i < value->children().size() - numIgnoredB3Args; ++i) {
91 Arg& arg = inst.args[i + numIgnoredAirArgs];
92 ConstrainedValue child = value->constrainedChild(i + numIgnoredB3Args);
93
94 Arg::Role role;
95 switch (roleMode) {
96 case ForceLateUseUnlessRecoverable:
97 ASSERT(firstRecoverableIndex);
98 if (arg != inst.args[*firstRecoverableIndex] && arg != inst.args[*firstRecoverableIndex + 1]) {
99 role = Arg::LateColdUse;
100 break;
101 }
102 FALLTHROUGH;
103 case SameAsRep:
104 switch (child.rep().kind()) {
105 case ValueRep::WarmAny:
106 case ValueRep::SomeRegister:
107 case ValueRep::Register:
108 case ValueRep::Stack:
109 case ValueRep::StackArgument:
110 case ValueRep::Constant:
111 role = Arg::Use;
112 break;
113 case ValueRep::SomeRegisterWithClobber:
114 role = Arg::UseDef;
115 break;
116 case ValueRep::LateRegister:
117 role = Arg::LateUse;
118 break;
119 case ValueRep::ColdAny:
120 role = Arg::ColdUse;
121 break;
122 case ValueRep::LateColdAny:
123 role = Arg::LateColdUse;
124 break;
125 default:
126 RELEASE_ASSERT_NOT_REACHED();
127 break;
128 }
129
130 // If the Def'ed arg has a smaller width than the a stackmap value, then we may not
131 // be able to recover the stackmap value. So, force LateColdUse to preserve the
132 // original stackmap value across the Special operation.
133 if (!Arg::isLateUse(role) && optionalDefArgWidth && *optionalDefArgWidth < child.value()->resultWidth()) {
134 // The role can only be some kind of def if we did SomeRegisterWithClobber, which is
135 // only allowed for patchpoints. Patchpoints don't use the defArgWidth feature.
136 RELEASE_ASSERT(!Arg::isAnyDef(role));
137
138 if (Arg::isWarmUse(role))
139 role = Arg::LateUse;
140 else
141 role = Arg::LateColdUse;
142 }
143 break;
144 case ForceLateUse:
145 role = Arg::LateColdUse;
146 break;
147 }
148
149 Type type = child.value()->type();
150 callback(arg, role, bankForType(type), widthForType(type));
151 }
152}
153
154bool StackmapSpecial::isValidImpl(
155 unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs,
156 Inst& inst)
157{
158 StackmapValue* value = inst.origin->as<StackmapValue>();
159 ASSERT(value);
160
161 // Check that insane things have not happened.
162 ASSERT(inst.args.size() >= numIgnoredAirArgs);
163 ASSERT(value->children().size() >= numIgnoredB3Args);
164
165 // For the Inst to be valid, it needs to have the right number of arguments.
166 if (inst.args.size() - numIgnoredAirArgs < value->children().size() - numIgnoredB3Args)
167 return false;
168
169 // Regardless of constraints, stackmaps have some basic requirements for their arguments. For
170 // example, you can't have a non-FP-offset address. This verifies those conditions as well as the
171 // argument types.
172 for (unsigned i = 0; i < value->children().size() - numIgnoredB3Args; ++i) {
173 Value* child = value->child(i + numIgnoredB3Args);
174 Arg& arg = inst.args[i + numIgnoredAirArgs];
175
176 if (!isArgValidForValue(arg, child))
177 return false;
178 }
179
180 // The number of constraints has to be no greater than the number of B3 children.
181 ASSERT(value->m_reps.size() <= value->children().size());
182
183 // Verify any explicitly supplied constraints.
184 for (unsigned i = numIgnoredB3Args; i < value->m_reps.size(); ++i) {
185 ValueRep& rep = value->m_reps[i];
186 Arg& arg = inst.args[i - numIgnoredB3Args + numIgnoredAirArgs];
187
188 if (!isArgValidForRep(code(), arg, rep))
189 return false;
190 }
191
192 return true;
193}
194
195bool StackmapSpecial::admitsStackImpl(
196 unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs,
197 Inst& inst, unsigned argIndex)
198{
199 StackmapValue* value = inst.origin->as<StackmapValue>();
200 ASSERT(value);
201
202 unsigned stackmapArgIndex = argIndex - numIgnoredAirArgs + numIgnoredB3Args;
203
204 if (stackmapArgIndex >= value->numChildren()) {
205 // It's not a stackmap argument, so as far as we are concerned, it doesn't admit stack.
206 return false;
207 }
208
209 if (stackmapArgIndex >= value->m_reps.size()) {
210 // This means that there was no constraint.
211 return true;
212 }
213
214 // We only admit stack for Any's, since Stack is not a valid input constraint, and StackArgument
215 // translates to a CallArg in Air.
216 if (value->m_reps[stackmapArgIndex].isAny())
217 return true;
218
219 return false;
220}
221
222Vector<ValueRep> StackmapSpecial::repsImpl(Air::GenerationContext& context, unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs, Inst& inst)
223{
224 Vector<ValueRep> result;
225 for (unsigned i = 0; i < inst.origin->numChildren() - numIgnoredB3Args; ++i)
226 result.append(repForArg(*context.code, inst.args[i + numIgnoredAirArgs]));
227 return result;
228}
229
230bool StackmapSpecial::isArgValidForValue(const Air::Arg& arg, Value* value)
231{
232 switch (arg.kind()) {
233 case Arg::Tmp:
234 case Arg::Imm:
235 case Arg::BigImm:
236 break;
237 default:
238 if (!arg.isStackMemory())
239 return false;
240 break;
241 }
242
243 return arg.canRepresent(value);
244}
245
246bool StackmapSpecial::isArgValidForRep(Air::Code& code, const Air::Arg& arg, const ValueRep& rep)
247{
248 switch (rep.kind()) {
249 case ValueRep::WarmAny:
250 case ValueRep::ColdAny:
251 case ValueRep::LateColdAny:
252 // We already verified by isArgValidForValue().
253 return true;
254 case ValueRep::SomeRegister:
255 case ValueRep::SomeRegisterWithClobber:
256 case ValueRep::SomeEarlyRegister:
257 return arg.isTmp();
258 case ValueRep::LateRegister:
259 case ValueRep::Register:
260 return arg == Tmp(rep.reg());
261 case ValueRep::StackArgument:
262 if (arg == Arg::callArg(rep.offsetFromSP()))
263 return true;
264 if ((arg.isAddr() || arg.isExtendedOffsetAddr()) && code.frameSize()) {
265 if (arg.base() == Tmp(GPRInfo::callFrameRegister)
266 && arg.offset() == static_cast<int64_t>(rep.offsetFromSP()) - code.frameSize())
267 return true;
268 if (arg.base() == Tmp(MacroAssembler::stackPointerRegister)
269 && arg.offset() == rep.offsetFromSP())
270 return true;
271 }
272 return false;
273 default:
274 RELEASE_ASSERT_NOT_REACHED();
275 return false;
276 }
277}
278
279ValueRep StackmapSpecial::repForArg(Air::Code& code, const Arg& arg)
280{
281 switch (arg.kind()) {
282 case Arg::Tmp:
283 return ValueRep::reg(arg.reg());
284 break;
285 case Arg::Imm:
286 case Arg::BigImm:
287 return ValueRep::constant(arg.value());
288 break;
289 case Arg::ExtendedOffsetAddr:
290 ASSERT(arg.base() == Tmp(GPRInfo::callFrameRegister));
291 FALLTHROUGH;
292 case Arg::Addr:
293 if (arg.base() == Tmp(GPRInfo::callFrameRegister))
294 return ValueRep::stack(arg.offset());
295 ASSERT(arg.base() == Tmp(MacroAssembler::stackPointerRegister));
296 return ValueRep::stack(arg.offset() - safeCast<Value::OffsetType>(code.frameSize()));
297 default:
298 ASSERT_NOT_REACHED();
299 return ValueRep();
300 }
301}
302
303} } // namespace JSC::B3
304
305namespace WTF {
306
307using namespace JSC::B3;
308
309void printInternal(PrintStream& out, StackmapSpecial::RoleMode mode)
310{
311 switch (mode) {
312 case StackmapSpecial::SameAsRep:
313 out.print("SameAsRep");
314 return;
315 case StackmapSpecial::ForceLateUseUnlessRecoverable:
316 out.print("ForceLateUseUnlessRecoverable");
317 return;
318 case StackmapSpecial::ForceLateUse:
319 out.print("ForceLateUse");
320 return;
321 }
322 RELEASE_ASSERT_NOT_REACHED();
323}
324
325} // namespace WTF
326
327#endif // ENABLE(B3_JIT)
328