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->numChildren() >= numIgnoredB3Args);
87 ASSERT(inst.args.size() - numIgnoredAirArgs >= value->numChildren() - numIgnoredB3Args);
88 ASSERT(inst.args[0].kind() == Arg::Kind::Special);
89
90 for (unsigned i = 0; i < value->numChildren() - 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::SomeLateRegister:
117 case ValueRep::LateRegister:
118 role = Arg::LateUse;
119 break;
120 case ValueRep::ColdAny:
121 role = Arg::ColdUse;
122 break;
123 case ValueRep::LateColdAny:
124 role = Arg::LateColdUse;
125 break;
126 default:
127 RELEASE_ASSERT_NOT_REACHED();
128 break;
129 }
130
131 // If the Def'ed arg has a smaller width than the a stackmap value, then we may not
132 // be able to recover the stackmap value. So, force LateColdUse to preserve the
133 // original stackmap value across the Special operation.
134 if (!Arg::isLateUse(role) && optionalDefArgWidth && *optionalDefArgWidth < child.value()->resultWidth()) {
135 // The role can only be some kind of def if we did SomeRegisterWithClobber, which is
136 // only allowed for patchpoints. Patchpoints don't use the defArgWidth feature.
137 RELEASE_ASSERT(!Arg::isAnyDef(role));
138
139 if (Arg::isWarmUse(role))
140 role = Arg::LateUse;
141 else
142 role = Arg::LateColdUse;
143 }
144 break;
145 case ForceLateUse:
146 role = Arg::LateColdUse;
147 break;
148 }
149
150 Type type = child.value()->type();
151 callback(arg, role, bankForType(type), widthForType(type));
152 }
153}
154
155bool StackmapSpecial::isValidImpl(
156 unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs,
157 Inst& inst)
158{
159 StackmapValue* value = inst.origin->as<StackmapValue>();
160 ASSERT(value);
161
162 // Check that insane things have not happened.
163 ASSERT(inst.args.size() >= numIgnoredAirArgs);
164 ASSERT(value->numChildren() >= numIgnoredB3Args);
165
166 // For the Inst to be valid, it needs to have the right number of arguments.
167 if (inst.args.size() - numIgnoredAirArgs < value->numChildren() - numIgnoredB3Args)
168 return false;
169
170 // Regardless of constraints, stackmaps have some basic requirements for their arguments. For
171 // example, you can't have a non-FP-offset address. This verifies those conditions as well as the
172 // argument types.
173 for (unsigned i = 0; i < value->numChildren() - numIgnoredB3Args; ++i) {
174 Value* child = value->child(i + numIgnoredB3Args);
175 Arg& arg = inst.args[i + numIgnoredAirArgs];
176
177 if (!isArgValidForType(arg, child->type()))
178 return false;
179 }
180
181 // The number of constraints has to be no greater than the number of B3 children.
182 ASSERT(value->m_reps.size() <= value->numChildren());
183
184 // Verify any explicitly supplied constraints.
185 for (unsigned i = numIgnoredB3Args; i < value->m_reps.size(); ++i) {
186 ValueRep& rep = value->m_reps[i];
187 Arg& arg = inst.args[i - numIgnoredB3Args + numIgnoredAirArgs];
188
189 if (!isArgValidForRep(code(), arg, rep))
190 return false;
191 }
192
193 return true;
194}
195
196bool StackmapSpecial::admitsStackImpl(
197 unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs,
198 Inst& inst, unsigned argIndex)
199{
200 StackmapValue* value = inst.origin->as<StackmapValue>();
201 ASSERT(value);
202
203 unsigned stackmapArgIndex = argIndex - numIgnoredAirArgs + numIgnoredB3Args;
204
205 if (stackmapArgIndex >= value->numChildren()) {
206 // It's not a stackmap argument, so as far as we are concerned, it doesn't admit stack.
207 return false;
208 }
209
210 if (stackmapArgIndex >= value->m_reps.size()) {
211 // This means that there was no constraint.
212 return true;
213 }
214
215 // We only admit stack for Any's, since Stack is not a valid input constraint, and StackArgument
216 // translates to a CallArg in Air.
217 if (value->m_reps[stackmapArgIndex].isAny())
218 return true;
219
220 return false;
221}
222
223Vector<ValueRep> StackmapSpecial::repsImpl(Air::GenerationContext& context, unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs, Inst& inst)
224{
225 Vector<ValueRep> result;
226 for (unsigned i = 0; i < inst.origin->numChildren() - numIgnoredB3Args; ++i)
227 result.append(repForArg(*context.code, inst.args[i + numIgnoredAirArgs]));
228 return result;
229}
230
231bool StackmapSpecial::isArgValidForType(const Air::Arg& arg, Type type)
232{
233 switch (arg.kind()) {
234 case Arg::Tmp:
235 case Arg::Imm:
236 case Arg::BigImm:
237 break;
238 default:
239 if (!arg.isStackMemory())
240 return false;
241 break;
242 }
243
244 return arg.canRepresent(type);
245}
246
247bool StackmapSpecial::isArgValidForRep(Air::Code& code, const Air::Arg& arg, const ValueRep& rep)
248{
249 switch (rep.kind()) {
250 case ValueRep::WarmAny:
251 case ValueRep::ColdAny:
252 case ValueRep::LateColdAny:
253 // We already verified by isArgValidForType().
254 return true;
255 case ValueRep::SomeRegister:
256 case ValueRep::SomeRegisterWithClobber:
257 case ValueRep::SomeEarlyRegister:
258 case ValueRep::SomeLateRegister:
259 return arg.isTmp();
260 case ValueRep::LateRegister:
261 case ValueRep::Register:
262 return arg == Tmp(rep.reg());
263 case ValueRep::StackArgument:
264 if (arg == Arg::callArg(rep.offsetFromSP()))
265 return true;
266 if ((arg.isAddr() || arg.isExtendedOffsetAddr()) && code.frameSize()) {
267 if (arg.base() == Tmp(GPRInfo::callFrameRegister)
268 && arg.offset() == static_cast<int64_t>(rep.offsetFromSP()) - code.frameSize())
269 return true;
270 if (arg.base() == Tmp(MacroAssembler::stackPointerRegister)
271 && arg.offset() == rep.offsetFromSP())
272 return true;
273 }
274 return false;
275 default:
276 RELEASE_ASSERT_NOT_REACHED();
277 return false;
278 }
279}
280
281ValueRep StackmapSpecial::repForArg(Air::Code& code, const Arg& arg)
282{
283 switch (arg.kind()) {
284 case Arg::Tmp:
285 return ValueRep::reg(arg.reg());
286 break;
287 case Arg::Imm:
288 case Arg::BigImm:
289 return ValueRep::constant(arg.value());
290 break;
291 case Arg::ExtendedOffsetAddr:
292 ASSERT(arg.base() == Tmp(GPRInfo::callFrameRegister));
293 FALLTHROUGH;
294 case Arg::Addr:
295 if (arg.base() == Tmp(GPRInfo::callFrameRegister))
296 return ValueRep::stack(arg.offset());
297 ASSERT(arg.base() == Tmp(MacroAssembler::stackPointerRegister));
298 return ValueRep::stack(arg.offset() - safeCast<Value::OffsetType>(code.frameSize()));
299 default:
300 ASSERT_NOT_REACHED();
301 return ValueRep();
302 }
303}
304
305} } // namespace JSC::B3
306
307namespace WTF {
308
309using namespace JSC::B3;
310
311void printInternal(PrintStream& out, StackmapSpecial::RoleMode mode)
312{
313 switch (mode) {
314 case StackmapSpecial::SameAsRep:
315 out.print("SameAsRep");
316 return;
317 case StackmapSpecial::ForceLateUseUnlessRecoverable:
318 out.print("ForceLateUseUnlessRecoverable");
319 return;
320 case StackmapSpecial::ForceLateUse:
321 out.print("ForceLateUse");
322 return;
323 }
324 RELEASE_ASSERT_NOT_REACHED();
325}
326
327} // namespace WTF
328
329#endif // ENABLE(B3_JIT)
330