1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef jit_MacroAssembler_inl_h
8 #define jit_MacroAssembler_inl_h
9
10 #include "jit/MacroAssembler.h"
11
12 #include "mozilla/FloatingPoint.h"
13 #include "mozilla/MathAlgorithms.h"
14
15 #include "gc/Zone.h"
16 #include "jit/CalleeToken.h"
17 #include "jit/CompileWrappers.h"
18 #include "jit/JitFrames.h"
19 #include "jit/JSJitFrameIter.h"
20 #include "util/DifferentialTesting.h"
21 #include "vm/JSObject.h"
22 #include "vm/ProxyObject.h"
23 #include "vm/Runtime.h"
24 #include "vm/StringType.h"
25
26 #include "jit/ABIFunctionList-inl.h"
27
28 #if defined(JS_CODEGEN_X86)
29 # include "jit/x86/MacroAssembler-x86-inl.h"
30 #elif defined(JS_CODEGEN_X64)
31 # include "jit/x64/MacroAssembler-x64-inl.h"
32 #elif defined(JS_CODEGEN_ARM)
33 # include "jit/arm/MacroAssembler-arm-inl.h"
34 #elif defined(JS_CODEGEN_ARM64)
35 # include "jit/arm64/MacroAssembler-arm64-inl.h"
36 #elif defined(JS_CODEGEN_MIPS32)
37 # include "jit/mips32/MacroAssembler-mips32-inl.h"
38 #elif defined(JS_CODEGEN_MIPS64)
39 # include "jit/mips64/MacroAssembler-mips64-inl.h"
40 #elif defined(JS_CODEGEN_LOONG64)
41 # include "jit/loong64/MacroAssembler-loong64-inl.h"
42 #elif !defined(JS_CODEGEN_NONE)
43 # error "Unknown architecture!"
44 #endif
45
46 #include "wasm/WasmBuiltins.h"
47
48 namespace js {
49 namespace jit {
50
51 template <typename Sig>
DynamicFunction(Sig fun)52 DynFn DynamicFunction(Sig fun) {
53 ABIFunctionSignature<Sig> sig;
54 return DynFn{sig.address(fun)};
55 }
56
57 // Helper for generatePreBarrier.
JitPreWriteBarrier(MIRType type)58 inline DynFn JitPreWriteBarrier(MIRType type) {
59 switch (type) {
60 case MIRType::Value: {
61 using Fn = void (*)(JSRuntime * rt, Value * vp);
62 return DynamicFunction<Fn>(JitValuePreWriteBarrier);
63 }
64 case MIRType::String: {
65 using Fn = void (*)(JSRuntime * rt, JSString * *stringp);
66 return DynamicFunction<Fn>(JitStringPreWriteBarrier);
67 }
68 case MIRType::Object: {
69 using Fn = void (*)(JSRuntime * rt, JSObject * *objp);
70 return DynamicFunction<Fn>(JitObjectPreWriteBarrier);
71 }
72 case MIRType::Shape: {
73 using Fn = void (*)(JSRuntime * rt, Shape * *shapep);
74 return DynamicFunction<Fn>(JitShapePreWriteBarrier);
75 }
76 default:
77 MOZ_CRASH();
78 }
79 }
80
81 //{{{ check_macroassembler_style
82 // ===============================================================
83 // Stack manipulation functions.
84
PushWithPatch(ImmWord word)85 CodeOffset MacroAssembler::PushWithPatch(ImmWord word) {
86 framePushed_ += sizeof(word.value);
87 return pushWithPatch(word);
88 }
89
PushWithPatch(ImmPtr imm)90 CodeOffset MacroAssembler::PushWithPatch(ImmPtr imm) {
91 return PushWithPatch(ImmWord(uintptr_t(imm.value)));
92 }
93
94 // ===============================================================
95 // Simple call functions.
96
call(TrampolinePtr code)97 void MacroAssembler::call(TrampolinePtr code) { call(ImmPtr(code.value)); }
98
call(const wasm::CallSiteDesc & desc,const Register reg)99 CodeOffset MacroAssembler::call(const wasm::CallSiteDesc& desc,
100 const Register reg) {
101 CodeOffset l = call(reg);
102 append(desc, l);
103 return l;
104 }
105
call(const wasm::CallSiteDesc & desc,uint32_t funcIndex)106 CodeOffset MacroAssembler::call(const wasm::CallSiteDesc& desc,
107 uint32_t funcIndex) {
108 CodeOffset l = callWithPatch();
109 append(desc, l, funcIndex);
110 return l;
111 }
112
call(const wasm::CallSiteDesc & desc,wasm::Trap trap)113 void MacroAssembler::call(const wasm::CallSiteDesc& desc, wasm::Trap trap) {
114 CodeOffset l = callWithPatch();
115 append(desc, l, trap);
116 }
117
call(const wasm::CallSiteDesc & desc,wasm::SymbolicAddress imm)118 CodeOffset MacroAssembler::call(const wasm::CallSiteDesc& desc,
119 wasm::SymbolicAddress imm) {
120 MOZ_ASSERT(wasm::NeedsBuiltinThunk(imm),
121 "only for functions which may appear in profiler");
122 CodeOffset raOffset = call(imm);
123 append(desc, raOffset);
124 return raOffset;
125 }
126
127 // ===============================================================
128 // ABI function calls.
129
passABIArg(Register reg)130 void MacroAssembler::passABIArg(Register reg) {
131 passABIArg(MoveOperand(reg), MoveOp::GENERAL);
132 }
133
passABIArg(FloatRegister reg,MoveOp::Type type)134 void MacroAssembler::passABIArg(FloatRegister reg, MoveOp::Type type) {
135 passABIArg(MoveOperand(reg), type);
136 }
137
callWithABI(DynFn fun,MoveOp::Type result,CheckUnsafeCallWithABI check)138 void MacroAssembler::callWithABI(DynFn fun, MoveOp::Type result,
139 CheckUnsafeCallWithABI check) {
140 AutoProfilerCallInstrumentation profiler(*this);
141 callWithABINoProfiler(fun.address, result, check);
142 }
143
144 template <typename Sig, Sig fun>
callWithABI(MoveOp::Type result,CheckUnsafeCallWithABI check)145 void MacroAssembler::callWithABI(MoveOp::Type result,
146 CheckUnsafeCallWithABI check) {
147 ABIFunction<Sig, fun> abiFun;
148 AutoProfilerCallInstrumentation profiler(*this);
149 callWithABINoProfiler(abiFun.address(), result, check);
150 }
151
callWithABI(Register fun,MoveOp::Type result)152 void MacroAssembler::callWithABI(Register fun, MoveOp::Type result) {
153 AutoProfilerCallInstrumentation profiler(*this);
154 callWithABINoProfiler(fun, result);
155 }
156
callWithABI(const Address & fun,MoveOp::Type result)157 void MacroAssembler::callWithABI(const Address& fun, MoveOp::Type result) {
158 AutoProfilerCallInstrumentation profiler(*this);
159 callWithABINoProfiler(fun, result);
160 }
161
appendSignatureType(MoveOp::Type type)162 void MacroAssembler::appendSignatureType(MoveOp::Type type) {
163 #ifdef JS_SIMULATOR
164 signature_ <<= ArgType_Shift;
165 switch (type) {
166 case MoveOp::GENERAL:
167 signature_ |= ArgType_General;
168 break;
169 case MoveOp::DOUBLE:
170 signature_ |= ArgType_Float64;
171 break;
172 case MoveOp::FLOAT32:
173 signature_ |= ArgType_Float32;
174 break;
175 default:
176 MOZ_CRASH("Invalid argument type");
177 }
178 #endif
179 }
180
signature()181 ABIFunctionType MacroAssembler::signature() const {
182 #ifdef JS_SIMULATOR
183 # ifdef DEBUG
184 switch (signature_) {
185 case Args_General0:
186 case Args_General1:
187 case Args_General2:
188 case Args_General3:
189 case Args_General4:
190 case Args_General5:
191 case Args_General6:
192 case Args_General7:
193 case Args_General8:
194 case Args_Double_None:
195 case Args_Int_Double:
196 case Args_Float32_Float32:
197 case Args_Int_Float32:
198 case Args_Double_Double:
199 case Args_Double_Int:
200 case Args_Double_DoubleInt:
201 case Args_Double_DoubleDouble:
202 case Args_Double_IntDouble:
203 case Args_Int_IntDouble:
204 case Args_Int_DoubleInt:
205 case Args_Int_DoubleIntInt:
206 case Args_Int_IntDoubleIntInt:
207 case Args_Double_DoubleDoubleDouble:
208 case Args_Double_DoubleDoubleDoubleDouble:
209 break;
210 default:
211 MOZ_CRASH("Unexpected type");
212 }
213 # endif // DEBUG
214
215 return ABIFunctionType(signature_);
216 #else
217 // No simulator enabled.
218 MOZ_CRASH("Only available for making calls within a simulator.");
219 #endif
220 }
221
222 // ===============================================================
223 // Jit Frames.
224
callJitNoProfiler(Register callee)225 uint32_t MacroAssembler::callJitNoProfiler(Register callee) {
226 #ifdef JS_USE_LINK_REGISTER
227 // The return address is pushed by the callee.
228 call(callee);
229 #else
230 callAndPushReturnAddress(callee);
231 #endif
232 return currentOffset();
233 }
234
callJit(Register callee)235 uint32_t MacroAssembler::callJit(Register callee) {
236 AutoProfilerCallInstrumentation profiler(*this);
237 uint32_t ret = callJitNoProfiler(callee);
238 return ret;
239 }
240
callJit(JitCode * callee)241 uint32_t MacroAssembler::callJit(JitCode* callee) {
242 AutoProfilerCallInstrumentation profiler(*this);
243 call(callee);
244 return currentOffset();
245 }
246
callJit(TrampolinePtr code)247 uint32_t MacroAssembler::callJit(TrampolinePtr code) {
248 AutoProfilerCallInstrumentation profiler(*this);
249 call(code);
250 return currentOffset();
251 }
252
callJit(ImmPtr callee)253 uint32_t MacroAssembler::callJit(ImmPtr callee) {
254 AutoProfilerCallInstrumentation profiler(*this);
255 call(callee);
256 return currentOffset();
257 }
258
makeFrameDescriptor(Register frameSizeReg,FrameType type,uint32_t headerSize)259 void MacroAssembler::makeFrameDescriptor(Register frameSizeReg, FrameType type,
260 uint32_t headerSize) {
261 // See JitFrames.h for a description of the frame descriptor format.
262 // The saved-frame bit is zero for new frames. See js::SavedStacks.
263
264 lshiftPtr(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
265
266 headerSize = EncodeFrameHeaderSize(headerSize);
267 orPtr(Imm32((headerSize << FRAME_HEADER_SIZE_SHIFT) | uint32_t(type)),
268 frameSizeReg);
269 }
270
pushStaticFrameDescriptor(FrameType type,uint32_t headerSize)271 void MacroAssembler::pushStaticFrameDescriptor(FrameType type,
272 uint32_t headerSize) {
273 uint32_t descriptor = MakeFrameDescriptor(framePushed(), type, headerSize);
274 Push(Imm32(descriptor));
275 }
276
PushCalleeToken(Register callee,bool constructing)277 void MacroAssembler::PushCalleeToken(Register callee, bool constructing) {
278 if (constructing) {
279 orPtr(Imm32(CalleeToken_FunctionConstructing), callee);
280 Push(callee);
281 andPtr(Imm32(uint32_t(CalleeTokenMask)), callee);
282 } else {
283 static_assert(CalleeToken_Function == 0,
284 "Non-constructing call requires no tagging");
285 Push(callee);
286 }
287 }
288
loadFunctionFromCalleeToken(Address token,Register dest)289 void MacroAssembler::loadFunctionFromCalleeToken(Address token, Register dest) {
290 #ifdef DEBUG
291 Label ok;
292 loadPtr(token, dest);
293 andPtr(Imm32(uint32_t(~CalleeTokenMask)), dest);
294 branchPtr(Assembler::Equal, dest, Imm32(CalleeToken_Function), &ok);
295 branchPtr(Assembler::Equal, dest, Imm32(CalleeToken_FunctionConstructing),
296 &ok);
297 assumeUnreachable("Unexpected CalleeToken tag");
298 bind(&ok);
299 #endif
300 loadPtr(token, dest);
301 andPtr(Imm32(uint32_t(CalleeTokenMask)), dest);
302 }
303
buildFakeExitFrame(Register scratch)304 uint32_t MacroAssembler::buildFakeExitFrame(Register scratch) {
305 mozilla::DebugOnly<uint32_t> initialDepth = framePushed();
306
307 pushStaticFrameDescriptor(FrameType::IonJS, ExitFrameLayout::Size());
308 uint32_t retAddr = pushFakeReturnAddress(scratch);
309
310 MOZ_ASSERT(framePushed() == initialDepth + ExitFrameLayout::Size());
311 return retAddr;
312 }
313
314 // ===============================================================
315 // Exit frame footer.
316
enterExitFrame(Register cxreg,Register scratch,const VMFunctionData * f)317 void MacroAssembler::enterExitFrame(Register cxreg, Register scratch,
318 const VMFunctionData* f) {
319 MOZ_ASSERT(f);
320 linkExitFrame(cxreg, scratch);
321 // Push VMFunction pointer, to mark arguments.
322 Push(ImmPtr(f));
323 }
324
enterFakeExitFrame(Register cxreg,Register scratch,ExitFrameType type)325 void MacroAssembler::enterFakeExitFrame(Register cxreg, Register scratch,
326 ExitFrameType type) {
327 linkExitFrame(cxreg, scratch);
328 Push(Imm32(int32_t(type)));
329 }
330
enterFakeExitFrameForNative(Register cxreg,Register scratch,bool isConstructing)331 void MacroAssembler::enterFakeExitFrameForNative(Register cxreg,
332 Register scratch,
333 bool isConstructing) {
334 enterFakeExitFrame(cxreg, scratch,
335 isConstructing ? ExitFrameType::ConstructNative
336 : ExitFrameType::CallNative);
337 }
338
leaveExitFrame(size_t extraFrame)339 void MacroAssembler::leaveExitFrame(size_t extraFrame) {
340 freeStack(ExitFooterFrame::Size() + extraFrame);
341 }
342
343 // ===============================================================
344 // Move instructions
345
moveValue(const ConstantOrRegister & src,const ValueOperand & dest)346 void MacroAssembler::moveValue(const ConstantOrRegister& src,
347 const ValueOperand& dest) {
348 if (src.constant()) {
349 moveValue(src.value(), dest);
350 return;
351 }
352
353 moveValue(src.reg(), dest);
354 }
355
356 // ===============================================================
357 // Arithmetic functions
358
addPtr(ImmPtr imm,Register dest)359 void MacroAssembler::addPtr(ImmPtr imm, Register dest) {
360 addPtr(ImmWord(uintptr_t(imm.value)), dest);
361 }
362
363 // ===============================================================
364 // Branch functions
365
366 template <class L>
branchIfFalseBool(Register reg,L label)367 void MacroAssembler::branchIfFalseBool(Register reg, L label) {
368 // Note that C++ bool is only 1 byte, so ignore the higher-order bits.
369 branchTest32(Assembler::Zero, reg, Imm32(0xFF), label);
370 }
371
branchIfTrueBool(Register reg,Label * label)372 void MacroAssembler::branchIfTrueBool(Register reg, Label* label) {
373 // Note that C++ bool is only 1 byte, so ignore the higher-order bits.
374 branchTest32(Assembler::NonZero, reg, Imm32(0xFF), label);
375 }
376
branchIfRope(Register str,Label * label)377 void MacroAssembler::branchIfRope(Register str, Label* label) {
378 Address flags(str, JSString::offsetOfFlags());
379 branchTest32(Assembler::Zero, flags, Imm32(JSString::LINEAR_BIT), label);
380 }
381
branchIfNotRope(Register str,Label * label)382 void MacroAssembler::branchIfNotRope(Register str, Label* label) {
383 Address flags(str, JSString::offsetOfFlags());
384 branchTest32(Assembler::NonZero, flags, Imm32(JSString::LINEAR_BIT), label);
385 }
386
branchLatin1String(Register string,Label * label)387 void MacroAssembler::branchLatin1String(Register string, Label* label) {
388 branchTest32(Assembler::NonZero, Address(string, JSString::offsetOfFlags()),
389 Imm32(JSString::LATIN1_CHARS_BIT), label);
390 }
391
branchTwoByteString(Register string,Label * label)392 void MacroAssembler::branchTwoByteString(Register string, Label* label) {
393 branchTest32(Assembler::Zero, Address(string, JSString::offsetOfFlags()),
394 Imm32(JSString::LATIN1_CHARS_BIT), label);
395 }
396
branchIfBigIntIsNegative(Register bigInt,Label * label)397 void MacroAssembler::branchIfBigIntIsNegative(Register bigInt, Label* label) {
398 branchTest32(Assembler::NonZero, Address(bigInt, BigInt::offsetOfFlags()),
399 Imm32(BigInt::signBitMask()), label);
400 }
401
branchIfBigIntIsNonNegative(Register bigInt,Label * label)402 void MacroAssembler::branchIfBigIntIsNonNegative(Register bigInt,
403 Label* label) {
404 branchTest32(Assembler::Zero, Address(bigInt, BigInt::offsetOfFlags()),
405 Imm32(BigInt::signBitMask()), label);
406 }
407
branchIfBigIntIsZero(Register bigInt,Label * label)408 void MacroAssembler::branchIfBigIntIsZero(Register bigInt, Label* label) {
409 branch32(Assembler::Equal, Address(bigInt, BigInt::offsetOfLength()),
410 Imm32(0), label);
411 }
412
branchIfBigIntIsNonZero(Register bigInt,Label * label)413 void MacroAssembler::branchIfBigIntIsNonZero(Register bigInt, Label* label) {
414 branch32(Assembler::NotEqual, Address(bigInt, BigInt::offsetOfLength()),
415 Imm32(0), label);
416 }
417
branchTestFunctionFlags(Register fun,uint32_t flags,Condition cond,Label * label)418 void MacroAssembler::branchTestFunctionFlags(Register fun, uint32_t flags,
419 Condition cond, Label* label) {
420 Address address(fun, JSFunction::offsetOfFlagsAndArgCount());
421 branchTest32(cond, address, Imm32(flags), label);
422 }
423
branchIfNotFunctionIsNonBuiltinCtor(Register fun,Register scratch,Label * label)424 void MacroAssembler::branchIfNotFunctionIsNonBuiltinCtor(Register fun,
425 Register scratch,
426 Label* label) {
427 // Guard the function has the BASESCRIPT and CONSTRUCTOR flags and does NOT
428 // have the SELF_HOSTED flag.
429 // This is equivalent to JSFunction::isNonBuiltinConstructor.
430 constexpr int32_t mask =
431 Imm32_16Adj(FunctionFlags::BASESCRIPT | FunctionFlags::SELF_HOSTED |
432 FunctionFlags::CONSTRUCTOR);
433 constexpr int32_t expected =
434 Imm32_16Adj(FunctionFlags::BASESCRIPT | FunctionFlags::CONSTRUCTOR);
435
436 load32(Address(fun, JSFunction::offsetOfFlagsAndArgCount()), scratch);
437 and32(Imm32(mask), scratch);
438 branch32(Assembler::NotEqual, scratch, Imm32(expected), label);
439 }
440
branchIfFunctionHasNoJitEntry(Register fun,bool isConstructing,Label * label)441 void MacroAssembler::branchIfFunctionHasNoJitEntry(Register fun,
442 bool isConstructing,
443 Label* label) {
444 uint16_t flags = FunctionFlags::HasJitEntryFlags(isConstructing);
445 branchTestFunctionFlags(fun, flags, Assembler::Zero, label);
446 }
447
branchIfFunctionHasJitEntry(Register fun,bool isConstructing,Label * label)448 void MacroAssembler::branchIfFunctionHasJitEntry(Register fun,
449 bool isConstructing,
450 Label* label) {
451 uint16_t flags = FunctionFlags::HasJitEntryFlags(isConstructing);
452 branchTestFunctionFlags(fun, flags, Assembler::NonZero, label);
453 }
454
branchIfScriptHasJitScript(Register script,Label * label)455 void MacroAssembler::branchIfScriptHasJitScript(Register script, Label* label) {
456 static_assert(ScriptWarmUpData::JitScriptTag == 0,
457 "Code below depends on tag value");
458 branchTestPtr(Assembler::Zero,
459 Address(script, JSScript::offsetOfWarmUpData()),
460 Imm32(ScriptWarmUpData::TagMask), label);
461 }
462
branchIfScriptHasNoJitScript(Register script,Label * label)463 void MacroAssembler::branchIfScriptHasNoJitScript(Register script,
464 Label* label) {
465 static_assert(ScriptWarmUpData::JitScriptTag == 0,
466 "Code below depends on tag value");
467 static_assert(BaseScript::offsetOfWarmUpData() ==
468 SelfHostedLazyScript::offsetOfWarmUpData(),
469 "SelfHostedLazyScript and BaseScript must use same layout for "
470 "warmUpData_");
471 branchTestPtr(Assembler::NonZero,
472 Address(script, JSScript::offsetOfWarmUpData()),
473 Imm32(ScriptWarmUpData::TagMask), label);
474 }
475
loadJitScript(Register script,Register dest)476 void MacroAssembler::loadJitScript(Register script, Register dest) {
477 #ifdef DEBUG
478 Label ok;
479 branchIfScriptHasJitScript(script, &ok);
480 assumeUnreachable("Script has no JitScript!");
481 bind(&ok);
482 #endif
483
484 static_assert(ScriptWarmUpData::JitScriptTag == 0,
485 "Code below depends on tag value");
486 loadPtr(Address(script, JSScript::offsetOfWarmUpData()), dest);
487 }
488
branchIfObjectEmulatesUndefined(Register objReg,Register scratch,Label * slowCheck,Label * label)489 void MacroAssembler::branchIfObjectEmulatesUndefined(Register objReg,
490 Register scratch,
491 Label* slowCheck,
492 Label* label) {
493 // The branches to out-of-line code here implement a conservative version
494 // of the JSObject::isWrapper test performed in EmulatesUndefined.
495 loadObjClassUnsafe(objReg, scratch);
496
497 branchTestClassIsProxy(true, scratch, slowCheck);
498
499 Address flags(scratch, JSClass::offsetOfFlags());
500 branchTest32(Assembler::NonZero, flags, Imm32(JSCLASS_EMULATES_UNDEFINED),
501 label);
502 }
503
branchFunctionKind(Condition cond,FunctionFlags::FunctionKind kind,Register fun,Register scratch,Label * label)504 void MacroAssembler::branchFunctionKind(Condition cond,
505 FunctionFlags::FunctionKind kind,
506 Register fun, Register scratch,
507 Label* label) {
508 Address address(fun, JSFunction::offsetOfFlagsAndArgCount());
509 load32(address, scratch);
510 and32(Imm32(FunctionFlags::FUNCTION_KIND_MASK), scratch);
511 branch32(cond, scratch, Imm32(kind), label);
512 }
513
branchTestObjClass(Condition cond,Register obj,const JSClass * clasp,Register scratch,Register spectreRegToZero,Label * label)514 void MacroAssembler::branchTestObjClass(Condition cond, Register obj,
515 const JSClass* clasp, Register scratch,
516 Register spectreRegToZero,
517 Label* label) {
518 MOZ_ASSERT(obj != scratch);
519 MOZ_ASSERT(scratch != spectreRegToZero);
520
521 loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
522 loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
523 branchPtr(cond, Address(scratch, BaseShape::offsetOfClasp()), ImmPtr(clasp),
524 label);
525
526 if (JitOptions.spectreObjectMitigations) {
527 spectreZeroRegister(cond, scratch, spectreRegToZero);
528 }
529 }
530
branchTestObjClassNoSpectreMitigations(Condition cond,Register obj,const JSClass * clasp,Register scratch,Label * label)531 void MacroAssembler::branchTestObjClassNoSpectreMitigations(
532 Condition cond, Register obj, const JSClass* clasp, Register scratch,
533 Label* label) {
534 loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
535 loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
536 branchPtr(cond, Address(scratch, BaseShape::offsetOfClasp()), ImmPtr(clasp),
537 label);
538 }
539
branchTestObjClass(Condition cond,Register obj,const Address & clasp,Register scratch,Register spectreRegToZero,Label * label)540 void MacroAssembler::branchTestObjClass(Condition cond, Register obj,
541 const Address& clasp, Register scratch,
542 Register spectreRegToZero,
543 Label* label) {
544 MOZ_ASSERT(obj != scratch);
545 MOZ_ASSERT(scratch != spectreRegToZero);
546
547 loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
548 loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
549 loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch);
550 branchPtr(cond, clasp, scratch, label);
551
552 if (JitOptions.spectreObjectMitigations) {
553 spectreZeroRegister(cond, scratch, spectreRegToZero);
554 }
555 }
556
branchTestObjClassNoSpectreMitigations(Condition cond,Register obj,const Address & clasp,Register scratch,Label * label)557 void MacroAssembler::branchTestObjClassNoSpectreMitigations(
558 Condition cond, Register obj, const Address& clasp, Register scratch,
559 Label* label) {
560 MOZ_ASSERT(obj != scratch);
561 loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
562 loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
563 loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch);
564 branchPtr(cond, clasp, scratch, label);
565 }
566
branchTestObjClass(Condition cond,Register obj,Register clasp,Register scratch,Register spectreRegToZero,Label * label)567 void MacroAssembler::branchTestObjClass(Condition cond, Register obj,
568 Register clasp, Register scratch,
569 Register spectreRegToZero,
570 Label* label) {
571 MOZ_ASSERT(obj != scratch);
572 MOZ_ASSERT(scratch != spectreRegToZero);
573
574 loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
575 loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
576 loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch);
577 branchPtr(cond, clasp, scratch, label);
578
579 if (JitOptions.spectreObjectMitigations) {
580 spectreZeroRegister(cond, scratch, spectreRegToZero);
581 }
582 }
583
branchTestClassIsFunction(Condition cond,Register clasp,Label * label)584 void MacroAssembler::branchTestClassIsFunction(Condition cond, Register clasp,
585 Label* label) {
586 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
587
588 if (cond == Assembler::Equal) {
589 branchPtr(Assembler::Equal, clasp, ImmPtr(&FunctionClass), label);
590 branchPtr(Assembler::Equal, clasp, ImmPtr(&ExtendedFunctionClass), label);
591 return;
592 }
593
594 Label isFunction;
595 branchPtr(Assembler::Equal, clasp, ImmPtr(&FunctionClass), &isFunction);
596 branchPtr(Assembler::NotEqual, clasp, ImmPtr(&ExtendedFunctionClass), label);
597 bind(&isFunction);
598 }
599
branchTestObjIsFunction(Condition cond,Register obj,Register scratch,Register spectreRegToZero,Label * label)600 void MacroAssembler::branchTestObjIsFunction(Condition cond, Register obj,
601 Register scratch,
602 Register spectreRegToZero,
603 Label* label) {
604 MOZ_ASSERT(scratch != spectreRegToZero);
605
606 branchTestObjIsFunctionNoSpectreMitigations(cond, obj, scratch, label);
607
608 if (JitOptions.spectreObjectMitigations) {
609 spectreZeroRegister(cond, scratch, spectreRegToZero);
610 }
611 }
612
branchTestObjIsFunctionNoSpectreMitigations(Condition cond,Register obj,Register scratch,Label * label)613 void MacroAssembler::branchTestObjIsFunctionNoSpectreMitigations(
614 Condition cond, Register obj, Register scratch, Label* label) {
615 MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
616 MOZ_ASSERT(obj != scratch);
617
618 loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
619 loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
620 loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch);
621 branchTestClassIsFunction(cond, scratch, label);
622 }
623
branchTestObjShape(Condition cond,Register obj,const Shape * shape,Register scratch,Register spectreRegToZero,Label * label)624 void MacroAssembler::branchTestObjShape(Condition cond, Register obj,
625 const Shape* shape, Register scratch,
626 Register spectreRegToZero,
627 Label* label) {
628 MOZ_ASSERT(obj != scratch);
629 MOZ_ASSERT(spectreRegToZero != scratch);
630
631 if (JitOptions.spectreObjectMitigations) {
632 move32(Imm32(0), scratch);
633 }
634
635 branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape),
636 label);
637
638 if (JitOptions.spectreObjectMitigations) {
639 spectreMovePtr(cond, scratch, spectreRegToZero);
640 }
641 }
642
branchTestObjShapeNoSpectreMitigations(Condition cond,Register obj,const Shape * shape,Label * label)643 void MacroAssembler::branchTestObjShapeNoSpectreMitigations(Condition cond,
644 Register obj,
645 const Shape* shape,
646 Label* label) {
647 branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape),
648 label);
649 }
650
branchTestObjShape(Condition cond,Register obj,Register shape,Register scratch,Register spectreRegToZero,Label * label)651 void MacroAssembler::branchTestObjShape(Condition cond, Register obj,
652 Register shape, Register scratch,
653 Register spectreRegToZero,
654 Label* label) {
655 MOZ_ASSERT(obj != scratch);
656 MOZ_ASSERT(obj != shape);
657 MOZ_ASSERT(spectreRegToZero != scratch);
658
659 if (JitOptions.spectreObjectMitigations) {
660 move32(Imm32(0), scratch);
661 }
662
663 branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
664
665 if (JitOptions.spectreObjectMitigations) {
666 spectreMovePtr(cond, scratch, spectreRegToZero);
667 }
668 }
669
branchTestObjShapeNoSpectreMitigations(Condition cond,Register obj,Register shape,Label * label)670 void MacroAssembler::branchTestObjShapeNoSpectreMitigations(Condition cond,
671 Register obj,
672 Register shape,
673 Label* label) {
674 branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
675 }
676
branchTestObjShapeUnsafe(Condition cond,Register obj,Register shape,Label * label)677 void MacroAssembler::branchTestObjShapeUnsafe(Condition cond, Register obj,
678 Register shape, Label* label) {
679 branchTestObjShapeNoSpectreMitigations(cond, obj, shape, label);
680 }
681
branchTestClassIsProxy(bool proxy,Register clasp,Label * label)682 void MacroAssembler::branchTestClassIsProxy(bool proxy, Register clasp,
683 Label* label) {
684 branchTest32(proxy ? Assembler::NonZero : Assembler::Zero,
685 Address(clasp, JSClass::offsetOfFlags()),
686 Imm32(JSCLASS_IS_PROXY), label);
687 }
688
branchTestObjectIsProxy(bool proxy,Register object,Register scratch,Label * label)689 void MacroAssembler::branchTestObjectIsProxy(bool proxy, Register object,
690 Register scratch, Label* label) {
691 loadObjClassUnsafe(object, scratch);
692 branchTestClassIsProxy(proxy, scratch, label);
693 }
694
branchTestProxyHandlerFamily(Condition cond,Register proxy,Register scratch,const void * handlerp,Label * label)695 void MacroAssembler::branchTestProxyHandlerFamily(Condition cond,
696 Register proxy,
697 Register scratch,
698 const void* handlerp,
699 Label* label) {
700 #ifdef DEBUG
701 Label ok;
702 loadObjClassUnsafe(proxy, scratch);
703 branchTestClassIsProxy(true, scratch, &ok);
704 assumeUnreachable("Expected ProxyObject in branchTestProxyHandlerFamily");
705 bind(&ok);
706 #endif
707
708 Address handlerAddr(proxy, ProxyObject::offsetOfHandler());
709 loadPtr(handlerAddr, scratch);
710 Address familyAddr(scratch, BaseProxyHandler::offsetOfFamily());
711 branchPtr(cond, familyAddr, ImmPtr(handlerp), label);
712 }
713
branchTestNeedsIncrementalBarrier(Condition cond,Label * label)714 void MacroAssembler::branchTestNeedsIncrementalBarrier(Condition cond,
715 Label* label) {
716 MOZ_ASSERT(cond == Zero || cond == NonZero);
717 CompileZone* zone = GetJitContext()->realm()->zone();
718 const uint32_t* needsBarrierAddr = zone->addressOfNeedsIncrementalBarrier();
719 branchTest32(cond, AbsoluteAddress(needsBarrierAddr), Imm32(0x1), label);
720 }
721
branchTestNeedsIncrementalBarrierAnyZone(Condition cond,Label * label,Register scratch)722 void MacroAssembler::branchTestNeedsIncrementalBarrierAnyZone(
723 Condition cond, Label* label, Register scratch) {
724 MOZ_ASSERT(cond == Zero || cond == NonZero);
725 if (GetJitContext()->maybeRealm()) {
726 branchTestNeedsIncrementalBarrier(cond, label);
727 } else {
728 // We are compiling the interpreter or another runtime-wide trampoline, so
729 // we have to load cx->zone.
730 loadPtr(AbsoluteAddress(GetJitContext()->runtime->addressOfZone()),
731 scratch);
732 Address needsBarrierAddr(scratch, Zone::offsetOfNeedsIncrementalBarrier());
733 branchTest32(cond, needsBarrierAddr, Imm32(0x1), label);
734 }
735 }
736
branchTestMagicValue(Condition cond,const ValueOperand & val,JSWhyMagic why,Label * label)737 void MacroAssembler::branchTestMagicValue(Condition cond,
738 const ValueOperand& val,
739 JSWhyMagic why, Label* label) {
740 MOZ_ASSERT(cond == Equal || cond == NotEqual);
741 branchTestValue(cond, val, MagicValue(why), label);
742 }
743
branchDoubleNotInInt64Range(Address src,Register temp,Label * fail)744 void MacroAssembler::branchDoubleNotInInt64Range(Address src, Register temp,
745 Label* fail) {
746 using mozilla::FloatingPoint;
747
748 // Tests if double is in [INT64_MIN; INT64_MAX] range
749 uint32_t EXPONENT_MASK = 0x7ff00000;
750 uint32_t EXPONENT_SHIFT = FloatingPoint<double>::kExponentShift - 32;
751 uint32_t TOO_BIG_EXPONENT = (FloatingPoint<double>::kExponentBias + 63)
752 << EXPONENT_SHIFT;
753
754 load32(Address(src.base, src.offset + sizeof(int32_t)), temp);
755 and32(Imm32(EXPONENT_MASK), temp);
756 branch32(Assembler::GreaterThanOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
757 }
758
branchDoubleNotInUInt64Range(Address src,Register temp,Label * fail)759 void MacroAssembler::branchDoubleNotInUInt64Range(Address src, Register temp,
760 Label* fail) {
761 using mozilla::FloatingPoint;
762
763 // Note: returns failure on -0.0
764 // Tests if double is in [0; UINT64_MAX] range
765 // Take the sign also in the equation. That way we can compare in one test?
766 uint32_t EXPONENT_MASK = 0xfff00000;
767 uint32_t EXPONENT_SHIFT = FloatingPoint<double>::kExponentShift - 32;
768 uint32_t TOO_BIG_EXPONENT = (FloatingPoint<double>::kExponentBias + 64)
769 << EXPONENT_SHIFT;
770
771 load32(Address(src.base, src.offset + sizeof(int32_t)), temp);
772 and32(Imm32(EXPONENT_MASK), temp);
773 branch32(Assembler::AboveOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
774 }
775
branchFloat32NotInInt64Range(Address src,Register temp,Label * fail)776 void MacroAssembler::branchFloat32NotInInt64Range(Address src, Register temp,
777 Label* fail) {
778 using mozilla::FloatingPoint;
779
780 // Tests if float is in [INT64_MIN; INT64_MAX] range
781 uint32_t EXPONENT_MASK = 0x7f800000;
782 uint32_t EXPONENT_SHIFT = FloatingPoint<float>::kExponentShift;
783 uint32_t TOO_BIG_EXPONENT = (FloatingPoint<float>::kExponentBias + 63)
784 << EXPONENT_SHIFT;
785
786 load32(src, temp);
787 and32(Imm32(EXPONENT_MASK), temp);
788 branch32(Assembler::GreaterThanOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
789 }
790
branchFloat32NotInUInt64Range(Address src,Register temp,Label * fail)791 void MacroAssembler::branchFloat32NotInUInt64Range(Address src, Register temp,
792 Label* fail) {
793 using mozilla::FloatingPoint;
794
795 // Note: returns failure on -0.0
796 // Tests if float is in [0; UINT64_MAX] range
797 // Take the sign also in the equation. That way we can compare in one test?
798 uint32_t EXPONENT_MASK = 0xff800000;
799 uint32_t EXPONENT_SHIFT = FloatingPoint<float>::kExponentShift;
800 uint32_t TOO_BIG_EXPONENT = (FloatingPoint<float>::kExponentBias + 64)
801 << EXPONENT_SHIFT;
802
803 load32(src, temp);
804 and32(Imm32(EXPONENT_MASK), temp);
805 branch32(Assembler::AboveOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
806 }
807
808 // ========================================================================
809 // Canonicalization primitives.
canonicalizeFloat(FloatRegister reg)810 void MacroAssembler::canonicalizeFloat(FloatRegister reg) {
811 Label notNaN;
812 branchFloat(DoubleOrdered, reg, reg, ¬NaN);
813 loadConstantFloat32(float(JS::GenericNaN()), reg);
814 bind(¬NaN);
815 }
816
canonicalizeFloatIfDeterministic(FloatRegister reg)817 void MacroAssembler::canonicalizeFloatIfDeterministic(FloatRegister reg) {
818 // See the comment in TypedArrayObjectTemplate::getElement.
819 if (js::SupportDifferentialTesting()) {
820 canonicalizeFloat(reg);
821 }
822 }
823
canonicalizeDouble(FloatRegister reg)824 void MacroAssembler::canonicalizeDouble(FloatRegister reg) {
825 Label notNaN;
826 branchDouble(DoubleOrdered, reg, reg, ¬NaN);
827 loadConstantDouble(JS::GenericNaN(), reg);
828 bind(¬NaN);
829 }
830
canonicalizeDoubleIfDeterministic(FloatRegister reg)831 void MacroAssembler::canonicalizeDoubleIfDeterministic(FloatRegister reg) {
832 // See the comment in TypedArrayObjectTemplate::getElement.
833 if (js::SupportDifferentialTesting()) {
834 canonicalizeDouble(reg);
835 }
836 }
837
838 // ========================================================================
839 // Memory access primitives.
840 template <class T>
storeDouble(FloatRegister src,const T & dest)841 void MacroAssembler::storeDouble(FloatRegister src, const T& dest) {
842 canonicalizeDoubleIfDeterministic(src);
843 storeUncanonicalizedDouble(src, dest);
844 }
845
846 template void MacroAssembler::storeDouble(FloatRegister src,
847 const Address& dest);
848 template void MacroAssembler::storeDouble(FloatRegister src,
849 const BaseIndex& dest);
850
851 template <class T>
boxDouble(FloatRegister src,const T & dest)852 void MacroAssembler::boxDouble(FloatRegister src, const T& dest) {
853 storeDouble(src, dest);
854 }
855
856 template <class T>
storeFloat32(FloatRegister src,const T & dest)857 void MacroAssembler::storeFloat32(FloatRegister src, const T& dest) {
858 canonicalizeFloatIfDeterministic(src);
859 storeUncanonicalizedFloat32(src, dest);
860 }
861
862 template void MacroAssembler::storeFloat32(FloatRegister src,
863 const Address& dest);
864 template void MacroAssembler::storeFloat32(FloatRegister src,
865 const BaseIndex& dest);
866
867 template <typename T>
fallibleUnboxInt32(const T & src,Register dest,Label * fail)868 void MacroAssembler::fallibleUnboxInt32(const T& src, Register dest,
869 Label* fail) {
870 // Int32Value can be unboxed efficiently with unboxInt32, so use that.
871 branchTestInt32(Assembler::NotEqual, src, fail);
872 unboxInt32(src, dest);
873 }
874
875 template <typename T>
fallibleUnboxBoolean(const T & src,Register dest,Label * fail)876 void MacroAssembler::fallibleUnboxBoolean(const T& src, Register dest,
877 Label* fail) {
878 // BooleanValue can be unboxed efficiently with unboxBoolean, so use that.
879 branchTestBoolean(Assembler::NotEqual, src, fail);
880 unboxBoolean(src, dest);
881 }
882
883 template <typename T>
fallibleUnboxObject(const T & src,Register dest,Label * fail)884 void MacroAssembler::fallibleUnboxObject(const T& src, Register dest,
885 Label* fail) {
886 fallibleUnboxPtr(src, dest, JSVAL_TYPE_OBJECT, fail);
887 }
888
889 template <typename T>
fallibleUnboxString(const T & src,Register dest,Label * fail)890 void MacroAssembler::fallibleUnboxString(const T& src, Register dest,
891 Label* fail) {
892 fallibleUnboxPtr(src, dest, JSVAL_TYPE_STRING, fail);
893 }
894
895 template <typename T>
fallibleUnboxSymbol(const T & src,Register dest,Label * fail)896 void MacroAssembler::fallibleUnboxSymbol(const T& src, Register dest,
897 Label* fail) {
898 fallibleUnboxPtr(src, dest, JSVAL_TYPE_SYMBOL, fail);
899 }
900
901 template <typename T>
fallibleUnboxBigInt(const T & src,Register dest,Label * fail)902 void MacroAssembler::fallibleUnboxBigInt(const T& src, Register dest,
903 Label* fail) {
904 fallibleUnboxPtr(src, dest, JSVAL_TYPE_BIGINT, fail);
905 }
906
907 //}}} check_macroassembler_style
908 // ===============================================================
909
910 #ifndef JS_CODEGEN_ARM64
911
912 template <typename T>
branchTestStackPtr(Condition cond,T t,Label * label)913 void MacroAssembler::branchTestStackPtr(Condition cond, T t, Label* label) {
914 branchTestPtr(cond, getStackPointer(), t, label);
915 }
916
917 template <typename T>
branchStackPtr(Condition cond,T rhs,Label * label)918 void MacroAssembler::branchStackPtr(Condition cond, T rhs, Label* label) {
919 branchPtr(cond, getStackPointer(), rhs, label);
920 }
921
922 template <typename T>
branchStackPtrRhs(Condition cond,T lhs,Label * label)923 void MacroAssembler::branchStackPtrRhs(Condition cond, T lhs, Label* label) {
924 branchPtr(cond, lhs, getStackPointer(), label);
925 }
926
927 template <typename T>
addToStackPtr(T t)928 void MacroAssembler::addToStackPtr(T t) {
929 addPtr(t, getStackPointer());
930 }
931
932 template <typename T>
addStackPtrTo(T t)933 void MacroAssembler::addStackPtrTo(T t) {
934 addPtr(getStackPointer(), t);
935 }
936
reserveStack(uint32_t amount)937 void MacroAssembler::reserveStack(uint32_t amount) {
938 subFromStackPtr(Imm32(amount));
939 adjustFrame(amount);
940 }
941 #endif // !JS_CODEGEN_ARM64
942
loadObjClassUnsafe(Register obj,Register dest)943 void MacroAssembler::loadObjClassUnsafe(Register obj, Register dest) {
944 loadPtr(Address(obj, JSObject::offsetOfShape()), dest);
945 loadPtr(Address(dest, Shape::offsetOfBaseShape()), dest);
946 loadPtr(Address(dest, BaseShape::offsetOfClasp()), dest);
947 }
948
949 template <typename EmitPreBarrier>
storeObjShape(Register shape,Register obj,EmitPreBarrier emitPreBarrier)950 void MacroAssembler::storeObjShape(Register shape, Register obj,
951 EmitPreBarrier emitPreBarrier) {
952 MOZ_ASSERT(shape != obj);
953 Address shapeAddr(obj, JSObject::offsetOfShape());
954 emitPreBarrier(*this, shapeAddr);
955 storePtr(shape, shapeAddr);
956 }
957
958 template <typename EmitPreBarrier>
storeObjShape(Shape * shape,Register obj,EmitPreBarrier emitPreBarrier)959 void MacroAssembler::storeObjShape(Shape* shape, Register obj,
960 EmitPreBarrier emitPreBarrier) {
961 Address shapeAddr(obj, JSObject::offsetOfShape());
962 emitPreBarrier(*this, shapeAddr);
963 storePtr(ImmGCPtr(shape), shapeAddr);
964 }
965
loadObjProto(Register obj,Register dest)966 void MacroAssembler::loadObjProto(Register obj, Register dest) {
967 loadPtr(Address(obj, JSObject::offsetOfShape()), dest);
968 loadPtr(Address(dest, Shape::offsetOfBaseShape()), dest);
969 loadPtr(Address(dest, BaseShape::offsetOfProto()), dest);
970 }
971
loadStringLength(Register str,Register dest)972 void MacroAssembler::loadStringLength(Register str, Register dest) {
973 load32(Address(str, JSString::offsetOfLength()), dest);
974 }
975
assertStackAlignment(uint32_t alignment,int32_t offset)976 void MacroAssembler::assertStackAlignment(uint32_t alignment,
977 int32_t offset /* = 0 */) {
978 #ifdef DEBUG
979 Label ok, bad;
980 MOZ_ASSERT(mozilla::IsPowerOfTwo(alignment));
981
982 // Wrap around the offset to be a non-negative number.
983 offset %= alignment;
984 if (offset < 0) {
985 offset += alignment;
986 }
987
988 // Test if each bit from offset is set.
989 uint32_t off = offset;
990 while (off) {
991 uint32_t lowestBit = 1 << mozilla::CountTrailingZeroes32(off);
992 branchTestStackPtr(Assembler::Zero, Imm32(lowestBit), &bad);
993 off ^= lowestBit;
994 }
995
996 // Check that all remaining bits are zero.
997 branchTestStackPtr(Assembler::Zero, Imm32((alignment - 1) ^ offset), &ok);
998
999 bind(&bad);
1000 breakpoint();
1001 bind(&ok);
1002 #endif
1003 }
1004
storeCallBoolResult(Register reg)1005 void MacroAssembler::storeCallBoolResult(Register reg) {
1006 if (reg != ReturnReg) {
1007 mov(ReturnReg, reg);
1008 }
1009 // C++ compilers like to only use the bottom byte for bools, but we
1010 // need to maintain the entire register.
1011 and32(Imm32(0xFF), reg);
1012 }
1013
storeCallInt32Result(Register reg)1014 void MacroAssembler::storeCallInt32Result(Register reg) {
1015 #if JS_BITS_PER_WORD == 32
1016 storeCallPointerResult(reg);
1017 #else
1018 // Ensure the upper 32 bits are cleared.
1019 move32(ReturnReg, reg);
1020 #endif
1021 }
1022
storeCallResultValue(AnyRegister dest,JSValueType type)1023 void MacroAssembler::storeCallResultValue(AnyRegister dest, JSValueType type) {
1024 unboxValue(JSReturnOperand, dest, type);
1025 }
1026
storeCallResultValue(TypedOrValueRegister dest)1027 void MacroAssembler::storeCallResultValue(TypedOrValueRegister dest) {
1028 if (dest.hasValue()) {
1029 storeCallResultValue(dest.valueReg());
1030 } else {
1031 storeCallResultValue(dest.typedReg(), ValueTypeFromMIRType(dest.type()));
1032 }
1033 }
1034
1035 } // namespace jit
1036 } // namespace js
1037
1038 #endif /* jit_MacroAssembler_inl_h */
1039