1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Brendan Eich <brendan@mozilla.org>
22 : *
23 : * Contributor(s):
24 : * David Anderson <danderson@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #ifndef js_typedarray_ic_h___
41 : #define js_typedarray_ic_h___
42 :
43 : #include "jscntxt.h"
44 : #include "jstypedarray.h"
45 : #include "jstypedarrayinlines.h"
46 :
47 : #include "jsnuminlines.h"
48 : #include "jstypedarrayinlines.h"
49 :
50 : namespace js {
51 : namespace mjit {
52 :
53 : #ifdef JS_METHODJIT_TYPED_ARRAY
54 :
55 : typedef JSC::MacroAssembler::RegisterID RegisterID;
56 : typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
57 : typedef JSC::MacroAssembler::Jump Jump;
58 : typedef JSC::MacroAssembler::Imm32 Imm32;
59 : typedef JSC::MacroAssembler::ImmDouble ImmDouble;
60 :
61 : static inline bool
62 122 : ConstantFoldForFloatArray(JSContext *cx, ValueRemat *vr)
63 : {
64 122 : if (!vr->isTypeKnown())
65 43 : return true;
66 :
67 : // Objects and undefined coerce to NaN, which coerces to 0.
68 : // Null converts to 0.
69 147 : if (vr->knownType() == JSVAL_TYPE_OBJECT ||
70 68 : vr->knownType() == JSVAL_TYPE_UNDEFINED) {
71 11 : *vr = ValueRemat::FromConstant(DoubleValue(js_NaN));
72 11 : return true;
73 : }
74 68 : if (vr->knownType() == JSVAL_TYPE_NULL) {
75 0 : *vr = ValueRemat::FromConstant(DoubleValue(0));
76 0 : return true;
77 : }
78 :
79 68 : if (!vr->isConstant())
80 16 : return true;
81 :
82 52 : if (vr->knownType() == JSVAL_TYPE_DOUBLE)
83 14 : return true;
84 :
85 38 : double d = 0;
86 38 : Value v = vr->value();
87 38 : if (v.isString()) {
88 3 : if (!StringToNumberType<double>(cx, v.toString(), &d))
89 0 : return false;
90 35 : } else if (v.isBoolean()) {
91 8 : d = v.toBoolean() ? 1 : 0;
92 27 : } else if (v.isInt32()) {
93 27 : d = v.toInt32();
94 : } else {
95 0 : JS_NOT_REACHED("unknown constant type");
96 : }
97 38 : *vr = ValueRemat::FromConstant(DoubleValue(d));
98 38 : return true;
99 : }
100 :
101 : static inline bool
102 518 : ConstantFoldForIntArray(JSContext *cx, JSObject *tarray, ValueRemat *vr)
103 : {
104 518 : if (!vr->isTypeKnown())
105 165 : return true;
106 :
107 : // Objects and undefined coerce to NaN, which coerces to 0.
108 : // Null converts to 0.
109 997 : if (vr->knownType() == JSVAL_TYPE_OBJECT ||
110 322 : vr->knownType() == JSVAL_TYPE_UNDEFINED ||
111 322 : vr->knownType() == JSVAL_TYPE_NULL) {
112 31 : *vr = ValueRemat::FromConstant(Int32Value(0));
113 31 : return true;
114 : }
115 :
116 322 : if (!vr->isConstant())
117 8 : return true;
118 :
119 : // Convert from string to double first (see bug 624483).
120 314 : Value v = vr->value();
121 314 : if (v.isString()) {
122 : double d;
123 35 : if (!StringToNumberType<double>(cx, v.toString(), &d))
124 0 : return false;
125 35 : v.setNumber(d);
126 : }
127 :
128 314 : int32_t i32 = 0;
129 314 : if (v.isDouble()) {
130 38 : i32 = (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
131 8 : ? js_TypedArray_uint8_clamp_double(v.toDouble())
132 46 : : js_DoubleToECMAInt32(v.toDouble());
133 276 : } else if (v.isInt32()) {
134 224 : i32 = v.toInt32();
135 224 : if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
136 36 : i32 = ClampIntForUint8Array(i32);
137 52 : } else if (v.isBoolean()) {
138 52 : i32 = v.toBoolean() ? 1 : 0;
139 : } else {
140 0 : JS_NOT_REACHED("unknown constant type");
141 : }
142 :
143 314 : *vr = ValueRemat::FromConstant(Int32Value(i32));
144 :
145 314 : return true;
146 : }
147 :
148 : // Generate code that will ensure a dynamically typed value, pinned in |vr|,
149 : // can be stored in an integer typed array. If any sort of conversion is
150 : // required, |dataReg| will be clobbered by a new value. |saveMask| is
151 : // used to ensure that |dataReg| (and volatile registers) are preserved
152 : // across any conversion process.
153 : static void
154 518 : GenConversionForIntArray(Assembler &masm, JSObject *tarray, const ValueRemat &vr,
155 : uint32_t saveMask)
156 : {
157 518 : if (vr.isConstant()) {
158 : // Constants are always folded to ints up-front.
159 345 : JS_ASSERT(vr.knownType() == JSVAL_TYPE_INT32);
160 345 : return;
161 : }
162 :
163 173 : if (!vr.isTypeKnown() || vr.knownType() != JSVAL_TYPE_INT32) {
164 : // If a conversion is necessary, save registers now.
165 165 : MaybeJump checkInt32;
166 165 : if (!vr.isTypeKnown())
167 165 : checkInt32 = masm.testInt32(Assembler::Equal, vr.typeReg());
168 :
169 : // Store the value to convert.
170 165 : StackMarker vp = masm.allocStack(sizeof(Value), sizeof(double));
171 165 : masm.storeValue(vr, masm.addressOfExtra(vp));
172 :
173 : // Preserve volatile registers.
174 330 : PreserveRegisters saveForCall(masm);
175 165 : saveForCall.preserve(saveMask & Registers::TempRegs);
176 :
177 165 : masm.setupABICall(Registers::FastCall, 2);
178 165 : masm.storeArg(0, masm.vmFrameOffset(offsetof(VMFrame, cx)));
179 165 : masm.storeArgAddr(1, masm.addressOfExtra(vp));
180 :
181 : typedef int32_t (JS_FASTCALL *Int32CxVp)(JSContext *, Value *);
182 : Int32CxVp stub;
183 165 : if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
184 23 : stub = stubs::ConvertToTypedInt<true>;
185 : else
186 142 : stub = stubs::ConvertToTypedInt<false>;
187 165 : masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, stub), false);
188 165 : if (vr.dataReg() != Registers::ReturnReg)
189 119 : masm.move(Registers::ReturnReg, vr.dataReg());
190 :
191 165 : saveForCall.restore();
192 165 : masm.freeStack(vp);
193 :
194 165 : if (checkInt32.isSet())
195 165 : checkInt32.get().linkTo(masm.label(), &masm);
196 : }
197 :
198 : // Performing clamping, if needed.
199 173 : if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
200 27 : masm.clampInt32ToUint8(vr.dataReg());
201 : }
202 :
203 : // Generate code that will ensure a dynamically typed value, pinned in |vr|,
204 : // can be stored in an integer typed array. saveMask| is used to ensure that
205 : // |dataReg| (and volatile registers) are preserved across any conversion
206 : // process.
207 : //
208 : // Constants are left untouched. Any other value is placed into destReg.
209 : static void
210 122 : GenConversionForFloatArray(Assembler &masm, JSObject *tarray, const ValueRemat &vr,
211 : FPRegisterID destReg, uint32_t saveMask)
212 : {
213 122 : if (vr.isConstant()) {
214 : // Constants are always folded to doubles up-front.
215 63 : JS_ASSERT(vr.knownType() == JSVAL_TYPE_DOUBLE);
216 63 : return;
217 : }
218 :
219 : // Fast-path, if the value is a double, skip converting.
220 59 : MaybeJump isDouble;
221 59 : if (!vr.isTypeKnown())
222 43 : isDouble = masm.testDouble(Assembler::Equal, vr.typeReg());
223 :
224 : // If the value is an integer, inline the conversion.
225 59 : MaybeJump skip1, skip2;
226 59 : if (!vr.isTypeKnown() || vr.knownType() == JSVAL_TYPE_INT32) {
227 51 : MaybeJump isNotInt32;
228 51 : if (!vr.isTypeKnown())
229 43 : isNotInt32 = masm.testInt32(Assembler::NotEqual, vr.typeReg());
230 51 : masm.convertInt32ToDouble(vr.dataReg(), destReg);
231 51 : if (isNotInt32.isSet()) {
232 43 : skip1 = masm.jump();
233 43 : isNotInt32.get().linkTo(masm.label(), &masm);
234 : }
235 : }
236 :
237 : // Generate a generic conversion call, if not known to be int32_t or double.
238 83 : if (!vr.isTypeKnown() ||
239 16 : (vr.knownType() != JSVAL_TYPE_INT32 &&
240 8 : vr.knownType() != JSVAL_TYPE_DOUBLE)) {
241 : // Store this value, which is also an outparam.
242 51 : StackMarker vp = masm.allocStack(sizeof(Value), sizeof(double));
243 51 : masm.storeValue(vr, masm.addressOfExtra(vp));
244 :
245 : // Preserve volatile registers, and make the call.
246 102 : PreserveRegisters saveForCall(masm);
247 51 : saveForCall.preserve(saveMask & Registers::TempRegs);
248 51 : masm.setupABICall(Registers::FastCall, 2);
249 51 : masm.storeArg(0, masm.vmFrameOffset(offsetof(VMFrame, cx)));
250 51 : masm.storeArgAddr(1, masm.addressOfExtra(vp));
251 51 : masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, stubs::ConvertToTypedFloat), false);
252 51 : saveForCall.restore();
253 :
254 : // Load the value from the outparam, then pop the stack.
255 51 : masm.loadDouble(masm.addressOfExtra(vp), destReg);
256 51 : masm.freeStack(vp);
257 51 : skip2 = masm.jump();
258 : }
259 :
260 59 : if (isDouble.isSet())
261 43 : isDouble.get().linkTo(masm.label(), &masm);
262 :
263 : // If it's possible the value was already a double, load it directly
264 : // from registers (the known type is distinct from typeReg, which has
265 : // 32-bits of the 64-bit double).
266 59 : if (!vr.isTypeKnown() || vr.knownType() == JSVAL_TYPE_DOUBLE)
267 43 : masm.fastLoadDouble(vr.dataReg(), vr.typeReg(), destReg);
268 :
269 : // At this point, all loads into xmm1 are complete.
270 59 : if (skip1.isSet())
271 43 : skip1.get().linkTo(masm.label(), &masm);
272 59 : if (skip2.isSet())
273 51 : skip2.get().linkTo(masm.label(), &masm);
274 :
275 59 : if (TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT32)
276 40 : masm.convertDoubleToFloat(destReg, destReg);
277 : }
278 :
279 : template <typename T>
280 : static bool
281 640 : StoreToTypedArray(JSContext *cx, Assembler &masm, JSObject *tarray, T address,
282 : const ValueRemat &vrIn, uint32_t saveMask)
283 : {
284 640 : ValueRemat vr = vrIn;
285 :
286 640 : uint32_t type = TypedArray::getType(tarray);
287 640 : switch (type) {
288 : case js::TypedArray::TYPE_INT8:
289 : case js::TypedArray::TYPE_UINT8:
290 : case js::TypedArray::TYPE_UINT8_CLAMPED:
291 : case js::TypedArray::TYPE_INT16:
292 : case js::TypedArray::TYPE_UINT16:
293 : case js::TypedArray::TYPE_INT32:
294 : case js::TypedArray::TYPE_UINT32:
295 : {
296 518 : if (!ConstantFoldForIntArray(cx, tarray, &vr))
297 0 : return false;
298 :
299 1036 : PreserveRegisters saveRHS(masm);
300 1036 : PreserveRegisters saveLHS(masm);
301 :
302 : // There are three tricky situations to handle:
303 : // (1) The RHS needs conversion. saveMask will be stomped, and
304 : // the RHS may need to be stomped.
305 : // (2) The RHS may need to be clamped, which clobbers it.
306 : // (3) The RHS may need to be in a single-byte register.
307 : //
308 : // In all of these cases, we try to find a free register that can be
309 : // used to mutate the RHS. Failing that, we evict an existing volatile
310 : // register.
311 : //
312 : // Note that we are careful to preserve the RHS before saving registers
313 : // for the conversion call. This is because the object and key may be
314 : // in temporary registers, and we want to restore those without killing
315 : // the mutated RHS.
316 : bool singleByte = (type == js::TypedArray::TYPE_INT8 ||
317 : type == js::TypedArray::TYPE_UINT8 ||
318 518 : type == js::TypedArray::TYPE_UINT8_CLAMPED);
319 518 : bool mayNeedConversion = (!vr.isTypeKnown() || vr.knownType() != JSVAL_TYPE_INT32);
320 518 : bool mayNeedClamping = !vr.isConstant() && (type == js::TypedArray::TYPE_UINT8_CLAMPED);
321 : bool needsSingleByteReg = singleByte &&
322 : !vr.isConstant() &&
323 518 : !(Registers::SingleByteRegs & Registers::maskReg(vr.dataReg()));
324 518 : bool rhsIsMutable = !vr.isConstant() && !(saveMask & Registers::maskReg(vr.dataReg()));
325 :
326 518 : if (((mayNeedConversion || mayNeedClamping) && !rhsIsMutable) || needsSingleByteReg) {
327 : // First attempt to find a free temporary register that:
328 : // - is compatible with the RHS constraints
329 : // - won't clobber the key, object, or RHS type regs
330 : // - is temporary, but
331 : // - is not in saveMask, which contains live volatile registers.
332 63 : uint32_t allowMask = Registers::AvailRegs;
333 63 : if (singleByte)
334 29 : allowMask &= Registers::SingleByteRegs;
335 :
336 : // Create a mask of registers we absolutely cannot clobber.
337 63 : uint32_t pinned = Assembler::maskAddress(address);
338 63 : if (!vr.isTypeKnown())
339 59 : pinned |= Registers::maskReg(vr.typeReg());
340 :
341 63 : Registers avail = allowMask & ~(pinned | saveMask);
342 :
343 : RegisterID newReg;
344 63 : if (!avail.empty()) {
345 7 : newReg = avail.takeAnyReg().reg();
346 : } else {
347 : // If no registers meet the ideal set, relax a constraint and spill.
348 56 : avail = allowMask & ~pinned;
349 :
350 56 : if (!avail.empty()) {
351 56 : newReg = avail.takeAnyReg().reg();
352 56 : saveRHS.preserve(Registers::maskReg(newReg));
353 : } else {
354 : // Oh no! *All* single byte registers are pinned. This
355 : // sucks. We'll swap the type and data registers in |vr|
356 : // and unswap them later.
357 :
358 : // If |vr|'s registers are part of the address, swapping is
359 : // going to cause problems during the store.
360 0 : uint32_t vrRegs = Registers::mask2Regs(vr.dataReg(), vr.typeReg());
361 0 : uint32_t lhsMask = vrRegs & Assembler::maskAddress(address);
362 :
363 : // We'll also need to save any of the registers which won't
364 : // be restored via |lhsMask| above.
365 0 : uint32_t rhsMask = vrRegs & ~lhsMask;
366 :
367 : // Push them, but get the order right. We'll pop LHS first.
368 0 : saveRHS.preserve(rhsMask);
369 0 : saveLHS.preserve(lhsMask);
370 :
371 : // Don't store/restore registers if we dont have to.
372 0 : saveMask &= ~lhsMask;
373 :
374 : // Actually perform the swap.
375 0 : masm.swap(vr.typeReg(), vr.dataReg());
376 0 : vr = ValueRemat::FromRegisters(vr.dataReg(), vr.typeReg());
377 0 : newReg = vr.dataReg();
378 : }
379 :
380 : // Now, make sure the new register is not in the saveMask,
381 : // so it won't get restored right after the call.
382 56 : saveMask &= ~Registers::maskReg(newReg);
383 : }
384 :
385 63 : if (vr.dataReg() != newReg)
386 39 : masm.move(vr.dataReg(), newReg);
387 :
388 : // Update |vr|.
389 63 : if (vr.isTypeKnown())
390 4 : vr = ValueRemat::FromKnownType(vr.knownType(), newReg);
391 : else
392 59 : vr = ValueRemat::FromRegisters(vr.typeReg(), newReg);
393 : }
394 :
395 518 : GenConversionForIntArray(masm, tarray, vr, saveMask);
396 :
397 : // Restore the registers in |address|. |GenConversionForIntArray| won't
398 : // restore them because we told it not to by fiddling with |saveMask|.
399 518 : saveLHS.restore();
400 :
401 518 : if (vr.isConstant())
402 345 : masm.storeToTypedIntArray(type, Imm32(vr.value().toInt32()), address);
403 : else
404 173 : masm.storeToTypedIntArray(type, vr.dataReg(), address);
405 :
406 : // Note that this will finish restoring the damage from the
407 : // earlier register swap.
408 518 : saveRHS.restore();
409 : break;
410 : }
411 :
412 : case js::TypedArray::TYPE_FLOAT32:
413 : case js::TypedArray::TYPE_FLOAT64: {
414 : /*
415 : * Use a temporary for conversion. Inference is disabled, so no FP
416 : * registers are live.
417 : */
418 122 : Registers regs(Registers::TempFPRegs);
419 122 : FPRegisterID temp = regs.takeAnyReg().fpreg();
420 :
421 122 : if (!ConstantFoldForFloatArray(cx, &vr))
422 0 : return false;
423 122 : GenConversionForFloatArray(masm, tarray, vr, temp, saveMask);
424 122 : if (vr.isConstant())
425 63 : masm.storeToTypedFloatArray(type, ImmDouble(vr.value().toDouble()), address);
426 : else
427 59 : masm.storeToTypedFloatArray(type, temp, address);
428 122 : break;
429 : }
430 : }
431 :
432 640 : return true;
433 : }
434 :
435 : #endif /* JS_METHODJIT_TYPED_ARRAY */
436 :
437 : } /* namespace mjit */
438 : } /* namespace js */
439 :
440 : #endif /* js_typedarray_ic_h___ */
441 :
|