1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/shared/MacroAssembler-x86-shared.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,704 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef jit_shared_MacroAssembler_x86_shared_h 1.11 +#define jit_shared_MacroAssembler_x86_shared_h 1.12 + 1.13 +#include "mozilla/Casting.h" 1.14 +#include "mozilla/DebugOnly.h" 1.15 + 1.16 +#if defined(JS_CODEGEN_X86) 1.17 +# include "jit/x86/Assembler-x86.h" 1.18 +#elif defined(JS_CODEGEN_X64) 1.19 +# include "jit/x64/Assembler-x64.h" 1.20 +#endif 1.21 + 1.22 +namespace js { 1.23 +namespace jit { 1.24 + 1.25 +class MacroAssemblerX86Shared : public Assembler 1.26 +{ 1.27 + protected: 1.28 + // Bytes pushed onto the frame by the callee; includes frameDepth_. This is 1.29 + // needed to compute offsets to stack slots while temporary space has been 1.30 + // reserved for unexpected spills or C++ function calls. It is maintained 1.31 + // by functions which track stack alignment, which for clear distinction 1.32 + // use StudlyCaps (for example, Push, Pop). 1.33 + uint32_t framePushed_; 1.34 + 1.35 + public: 1.36 + using Assembler::call; 1.37 + 1.38 + MacroAssemblerX86Shared() 1.39 + : framePushed_(0) 1.40 + { } 1.41 + 1.42 + void compareDouble(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs) { 1.43 + if (cond & DoubleConditionBitInvert) 1.44 + ucomisd(rhs, lhs); 1.45 + else 1.46 + ucomisd(lhs, rhs); 1.47 + } 1.48 + void branchDouble(DoubleCondition cond, const FloatRegister &lhs, 1.49 + const FloatRegister &rhs, Label *label) 1.50 + { 1.51 + compareDouble(cond, lhs, rhs); 1.52 + 1.53 + if (cond == DoubleEqual) { 1.54 + Label unordered; 1.55 + j(Parity, &unordered); 1.56 + j(Equal, label); 1.57 + bind(&unordered); 1.58 + return; 1.59 + } 1.60 + if (cond == DoubleNotEqualOrUnordered) { 1.61 + j(NotEqual, label); 1.62 + j(Parity, label); 1.63 + return; 1.64 + } 1.65 + 1.66 + JS_ASSERT(!(cond & DoubleConditionBitSpecial)); 1.67 + j(ConditionFromDoubleCondition(cond), label); 1.68 + } 1.69 + 1.70 + void compareFloat(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs) { 1.71 + if (cond & DoubleConditionBitInvert) 1.72 + ucomiss(rhs, lhs); 1.73 + else 1.74 + ucomiss(lhs, rhs); 1.75 + } 1.76 + void branchFloat(DoubleCondition cond, const FloatRegister &lhs, 1.77 + const FloatRegister &rhs, Label *label) 1.78 + { 1.79 + compareFloat(cond, lhs, rhs); 1.80 + 1.81 + if (cond == DoubleEqual) { 1.82 + Label unordered; 1.83 + j(Parity, &unordered); 1.84 + j(Equal, label); 1.85 + bind(&unordered); 1.86 + return; 1.87 + } 1.88 + if (cond == DoubleNotEqualOrUnordered) { 1.89 + j(NotEqual, label); 1.90 + j(Parity, label); 1.91 + return; 1.92 + } 1.93 + 1.94 + JS_ASSERT(!(cond & DoubleConditionBitSpecial)); 1.95 + j(ConditionFromDoubleCondition(cond), label); 1.96 + } 1.97 + 1.98 + void branchNegativeZero(const FloatRegister ®, const Register &scratch, Label *label); 1.99 + void branchNegativeZeroFloat32(const FloatRegister ®, const Register &scratch, Label *label); 1.100 + 1.101 + void move32(const Imm32 &imm, const Register &dest) { 1.102 + // Use the ImmWord version of mov to register, which has special 1.103 + // optimizations. Casting to uint32_t here ensures that the value 1.104 + // is zero-extended. 1.105 + mov(ImmWord(uint32_t(imm.value)), dest); 1.106 + } 1.107 + void move32(const Imm32 &imm, const Operand &dest) { 1.108 + movl(imm, dest); 1.109 + } 1.110 + void move32(const Register &src, const Register &dest) { 1.111 + movl(src, dest); 1.112 + } 1.113 + void move32(const Register &src, const Operand &dest) { 1.114 + movl(src, dest); 1.115 + } 1.116 + void and32(const Imm32 &imm, const Register &dest) { 1.117 + andl(imm, dest); 1.118 + } 1.119 + void and32(const Imm32 &imm, const Address &dest) { 1.120 + andl(imm, Operand(dest)); 1.121 + } 1.122 + void or32(const Register &src, const Register &dest) { 1.123 + orl(src, dest); 1.124 + } 1.125 + void or32(const Imm32 &imm, const Register &dest) { 1.126 + orl(imm, dest); 1.127 + } 1.128 + void or32(const Imm32 &imm, const Address &dest) { 1.129 + orl(imm, Operand(dest)); 1.130 + } 1.131 + void neg32(const Register ®) { 1.132 + negl(reg); 1.133 + } 1.134 + void test32(const Register &lhs, const Register &rhs) { 1.135 + testl(lhs, rhs); 1.136 + } 1.137 + void test32(const Address &addr, Imm32 imm) { 1.138 + testl(Operand(addr), imm); 1.139 + } 1.140 + void test32(const Register &lhs, const Imm32 &rhs) { 1.141 + testl(lhs, rhs); 1.142 + } 1.143 + void cmp32(const Register &lhs, const Imm32 &rhs) { 1.144 + cmpl(lhs, rhs); 1.145 + } 1.146 + void cmp32(Register a, Register b) { 1.147 + cmpl(a, b); 1.148 + } 1.149 + void cmp32(const Operand &lhs, const Imm32 &rhs) { 1.150 + cmpl(lhs, rhs); 1.151 + } 1.152 + void cmp32(const Operand &lhs, const Register &rhs) { 1.153 + cmpl(lhs, rhs); 1.154 + } 1.155 + void add32(Register src, Register dest) { 1.156 + addl(src, dest); 1.157 + } 1.158 + void add32(Imm32 imm, Register dest) { 1.159 + addl(imm, dest); 1.160 + } 1.161 + void add32(Imm32 imm, const Address &dest) { 1.162 + addl(imm, Operand(dest)); 1.163 + } 1.164 + void sub32(Imm32 imm, Register dest) { 1.165 + subl(imm, dest); 1.166 + } 1.167 + void sub32(Register src, Register dest) { 1.168 + subl(src, dest); 1.169 + } 1.170 + template <typename T> 1.171 + void branchAdd32(Condition cond, T src, Register dest, Label *label) { 1.172 + add32(src, dest); 1.173 + j(cond, label); 1.174 + } 1.175 + template <typename T> 1.176 + void branchSub32(Condition cond, T src, Register dest, Label *label) { 1.177 + sub32(src, dest); 1.178 + j(cond, label); 1.179 + } 1.180 + void xor32(Imm32 imm, Register dest) { 1.181 + xorl(imm, dest); 1.182 + } 1.183 + void xor32(Register src, Register dest) { 1.184 + xorl(src, dest); 1.185 + } 1.186 + void not32(Register reg) { 1.187 + notl(reg); 1.188 + } 1.189 + void inc32(const Operand &addr) { 1.190 + incl(addr); 1.191 + } 1.192 + void atomic_inc32(const Operand &addr) { 1.193 + lock_incl(addr); 1.194 + } 1.195 + void dec32(const Operand &addr) { 1.196 + decl(addr); 1.197 + } 1.198 + void atomic_dec32(const Operand &addr) { 1.199 + lock_decl(addr); 1.200 + } 1.201 + void atomic_cmpxchg32(const Register &src, const Operand &addr, const Register &dest) { 1.202 + // %eax must be explicitly provided for calling clarity. 1.203 + MOZ_ASSERT(dest.code() == JSC::X86Registers::eax); 1.204 + lock_cmpxchg32(src, addr); 1.205 + } 1.206 + 1.207 + void branch16(Condition cond, const Register &lhs, const Register &rhs, Label *label) { 1.208 + cmpw(lhs, rhs); 1.209 + j(cond, label); 1.210 + } 1.211 + void branch32(Condition cond, const Operand &lhs, const Register &rhs, Label *label) { 1.212 + cmpl(lhs, rhs); 1.213 + j(cond, label); 1.214 + } 1.215 + void branch32(Condition cond, const Operand &lhs, Imm32 rhs, Label *label) { 1.216 + cmpl(lhs, rhs); 1.217 + j(cond, label); 1.218 + } 1.219 + void branch32(Condition cond, const Address &lhs, const Register &rhs, Label *label) { 1.220 + cmpl(Operand(lhs), rhs); 1.221 + j(cond, label); 1.222 + } 1.223 + void branch32(Condition cond, const Address &lhs, Imm32 imm, Label *label) { 1.224 + cmpl(Operand(lhs), imm); 1.225 + j(cond, label); 1.226 + } 1.227 + void branch32(Condition cond, const Register &lhs, Imm32 imm, Label *label) { 1.228 + cmpl(lhs, imm); 1.229 + j(cond, label); 1.230 + } 1.231 + void branch32(Condition cond, const Register &lhs, const Register &rhs, Label *label) { 1.232 + cmpl(lhs, rhs); 1.233 + j(cond, label); 1.234 + } 1.235 + void branchTest16(Condition cond, const Register &lhs, const Register &rhs, Label *label) { 1.236 + testw(lhs, rhs); 1.237 + j(cond, label); 1.238 + } 1.239 + void branchTest32(Condition cond, const Register &lhs, const Register &rhs, Label *label) { 1.240 + testl(lhs, rhs); 1.241 + j(cond, label); 1.242 + } 1.243 + void branchTest32(Condition cond, const Register &lhs, Imm32 imm, Label *label) { 1.244 + testl(lhs, imm); 1.245 + j(cond, label); 1.246 + } 1.247 + void branchTest32(Condition cond, const Address &address, Imm32 imm, Label *label) { 1.248 + testl(Operand(address), imm); 1.249 + j(cond, label); 1.250 + } 1.251 + 1.252 + // The following functions are exposed for use in platform-shared code. 1.253 + template <typename T> 1.254 + void Push(const T &t) { 1.255 + push(t); 1.256 + framePushed_ += sizeof(intptr_t); 1.257 + } 1.258 + void Push(const FloatRegister &t) { 1.259 + push(t); 1.260 + framePushed_ += sizeof(double); 1.261 + } 1.262 + CodeOffsetLabel PushWithPatch(const ImmWord &word) { 1.263 + framePushed_ += sizeof(word.value); 1.264 + return pushWithPatch(word); 1.265 + } 1.266 + CodeOffsetLabel PushWithPatch(const ImmPtr &imm) { 1.267 + return PushWithPatch(ImmWord(uintptr_t(imm.value))); 1.268 + } 1.269 + 1.270 + template <typename T> 1.271 + void Pop(const T &t) { 1.272 + pop(t); 1.273 + framePushed_ -= sizeof(intptr_t); 1.274 + } 1.275 + void Pop(const FloatRegister &t) { 1.276 + pop(t); 1.277 + framePushed_ -= sizeof(double); 1.278 + } 1.279 + void implicitPop(uint32_t args) { 1.280 + JS_ASSERT(args % sizeof(intptr_t) == 0); 1.281 + framePushed_ -= args; 1.282 + } 1.283 + uint32_t framePushed() const { 1.284 + return framePushed_; 1.285 + } 1.286 + void setFramePushed(uint32_t framePushed) { 1.287 + framePushed_ = framePushed; 1.288 + } 1.289 + 1.290 + void jump(Label *label) { 1.291 + jmp(label); 1.292 + } 1.293 + void jump(RepatchLabel *label) { 1.294 + jmp(label); 1.295 + } 1.296 + void jump(Register reg) { 1.297 + jmp(Operand(reg)); 1.298 + } 1.299 + void jump(const Address &addr) { 1.300 + jmp(Operand(addr)); 1.301 + } 1.302 + 1.303 + void convertInt32ToDouble(const Register &src, const FloatRegister &dest) { 1.304 + // cvtsi2sd and friends write only part of their output register, which 1.305 + // causes slowdowns on out-of-order processors. Explicitly break 1.306 + // dependencies with xorpd (and xorps elsewhere), which are handled 1.307 + // specially in modern CPUs, for this purpose. See sections 8.14, 9.8, 1.308 + // 10.8, 12.9, 13.16, 14.14, and 15.8 of Agner's Microarchitecture 1.309 + // document. 1.310 + zeroDouble(dest); 1.311 + cvtsi2sd(src, dest); 1.312 + } 1.313 + void convertInt32ToDouble(const Address &src, FloatRegister dest) { 1.314 + convertInt32ToDouble(Operand(src), dest); 1.315 + } 1.316 + void convertInt32ToDouble(const Operand &src, FloatRegister dest) { 1.317 + // Clear the output register first to break dependencies; see above; 1.318 + zeroDouble(dest); 1.319 + cvtsi2sd(Operand(src), dest); 1.320 + } 1.321 + void convertInt32ToFloat32(const Register &src, const FloatRegister &dest) { 1.322 + // Clear the output register first to break dependencies; see above; 1.323 + zeroFloat32(dest); 1.324 + cvtsi2ss(src, dest); 1.325 + } 1.326 + void convertInt32ToFloat32(const Address &src, FloatRegister dest) { 1.327 + convertInt32ToFloat32(Operand(src), dest); 1.328 + } 1.329 + void convertInt32ToFloat32(const Operand &src, FloatRegister dest) { 1.330 + // Clear the output register first to break dependencies; see above; 1.331 + zeroFloat32(dest); 1.332 + cvtsi2ss(src, dest); 1.333 + } 1.334 + Condition testDoubleTruthy(bool truthy, const FloatRegister ®) { 1.335 + zeroDouble(ScratchFloatReg); 1.336 + ucomisd(ScratchFloatReg, reg); 1.337 + return truthy ? NonZero : Zero; 1.338 + } 1.339 + void branchTestDoubleTruthy(bool truthy, const FloatRegister ®, Label *label) { 1.340 + Condition cond = testDoubleTruthy(truthy, reg); 1.341 + j(cond, label); 1.342 + } 1.343 + void load8ZeroExtend(const Address &src, const Register &dest) { 1.344 + movzbl(Operand(src), dest); 1.345 + } 1.346 + void load8ZeroExtend(const BaseIndex &src, const Register &dest) { 1.347 + movzbl(Operand(src), dest); 1.348 + } 1.349 + void load8SignExtend(const Address &src, const Register &dest) { 1.350 + movsbl(Operand(src), dest); 1.351 + } 1.352 + void load8SignExtend(const BaseIndex &src, const Register &dest) { 1.353 + movsbl(Operand(src), dest); 1.354 + } 1.355 + template <typename S, typename T> 1.356 + void store8(const S &src, const T &dest) { 1.357 + movb(src, Operand(dest)); 1.358 + } 1.359 + void load16ZeroExtend(const Address &src, const Register &dest) { 1.360 + movzwl(Operand(src), dest); 1.361 + } 1.362 + void load16ZeroExtend(const BaseIndex &src, const Register &dest) { 1.363 + movzwl(Operand(src), dest); 1.364 + } 1.365 + template <typename S, typename T> 1.366 + void store16(const S &src, const T &dest) { 1.367 + movw(src, Operand(dest)); 1.368 + } 1.369 + void load16SignExtend(const Address &src, const Register &dest) { 1.370 + movswl(Operand(src), dest); 1.371 + } 1.372 + void load16SignExtend(const BaseIndex &src, const Register &dest) { 1.373 + movswl(Operand(src), dest); 1.374 + } 1.375 + void load32(const Address &address, Register dest) { 1.376 + movl(Operand(address), dest); 1.377 + } 1.378 + void load32(const BaseIndex &src, Register dest) { 1.379 + movl(Operand(src), dest); 1.380 + } 1.381 + void load32(const Operand &src, Register dest) { 1.382 + movl(src, dest); 1.383 + } 1.384 + template <typename S, typename T> 1.385 + void store32(const S &src, const T &dest) { 1.386 + movl(src, Operand(dest)); 1.387 + } 1.388 + void loadDouble(const Address &src, FloatRegister dest) { 1.389 + movsd(src, dest); 1.390 + } 1.391 + void loadDouble(const BaseIndex &src, FloatRegister dest) { 1.392 + movsd(src, dest); 1.393 + } 1.394 + void loadDouble(const Operand &src, FloatRegister dest) { 1.395 + switch (src.kind()) { 1.396 + case Operand::MEM_REG_DISP: 1.397 + loadDouble(src.toAddress(), dest); 1.398 + break; 1.399 + case Operand::MEM_SCALE: 1.400 + loadDouble(src.toBaseIndex(), dest); 1.401 + break; 1.402 + default: 1.403 + MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); 1.404 + } 1.405 + } 1.406 + void storeDouble(FloatRegister src, const Address &dest) { 1.407 + movsd(src, dest); 1.408 + } 1.409 + void storeDouble(FloatRegister src, const BaseIndex &dest) { 1.410 + movsd(src, dest); 1.411 + } 1.412 + void storeDouble(FloatRegister src, const Operand &dest) { 1.413 + switch (dest.kind()) { 1.414 + case Operand::MEM_REG_DISP: 1.415 + storeDouble(src, dest.toAddress()); 1.416 + break; 1.417 + case Operand::MEM_SCALE: 1.418 + storeDouble(src, dest.toBaseIndex()); 1.419 + break; 1.420 + default: 1.421 + MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); 1.422 + } 1.423 + } 1.424 + void moveDouble(FloatRegister src, FloatRegister dest) { 1.425 + // Use movapd instead of movsd to avoid dependencies. 1.426 + movapd(src, dest); 1.427 + } 1.428 + void zeroDouble(FloatRegister reg) { 1.429 + xorpd(reg, reg); 1.430 + } 1.431 + void zeroFloat32(FloatRegister reg) { 1.432 + xorps(reg, reg); 1.433 + } 1.434 + void negateDouble(FloatRegister reg) { 1.435 + // From MacroAssemblerX86Shared::maybeInlineDouble 1.436 + pcmpeqw(ScratchFloatReg, ScratchFloatReg); 1.437 + psllq(Imm32(63), ScratchFloatReg); 1.438 + 1.439 + // XOR the float in a float register with -0.0. 1.440 + xorpd(ScratchFloatReg, reg); // s ^ 0x80000000000000 1.441 + } 1.442 + void negateFloat(FloatRegister reg) { 1.443 + pcmpeqw(ScratchFloatReg, ScratchFloatReg); 1.444 + psllq(Imm32(31), ScratchFloatReg); 1.445 + 1.446 + // XOR the float in a float register with -0.0. 1.447 + xorps(ScratchFloatReg, reg); // s ^ 0x80000000 1.448 + } 1.449 + void addDouble(FloatRegister src, FloatRegister dest) { 1.450 + addsd(src, dest); 1.451 + } 1.452 + void subDouble(FloatRegister src, FloatRegister dest) { 1.453 + subsd(src, dest); 1.454 + } 1.455 + void mulDouble(FloatRegister src, FloatRegister dest) { 1.456 + mulsd(src, dest); 1.457 + } 1.458 + void divDouble(FloatRegister src, FloatRegister dest) { 1.459 + divsd(src, dest); 1.460 + } 1.461 + void convertFloat32ToDouble(const FloatRegister &src, const FloatRegister &dest) { 1.462 + cvtss2sd(src, dest); 1.463 + } 1.464 + void convertDoubleToFloat32(const FloatRegister &src, const FloatRegister &dest) { 1.465 + cvtsd2ss(src, dest); 1.466 + } 1.467 + void moveFloatAsDouble(const Register &src, FloatRegister dest) { 1.468 + movd(src, dest); 1.469 + cvtss2sd(dest, dest); 1.470 + } 1.471 + void loadFloatAsDouble(const Address &src, FloatRegister dest) { 1.472 + movss(src, dest); 1.473 + cvtss2sd(dest, dest); 1.474 + } 1.475 + void loadFloatAsDouble(const BaseIndex &src, FloatRegister dest) { 1.476 + movss(src, dest); 1.477 + cvtss2sd(dest, dest); 1.478 + } 1.479 + void loadFloatAsDouble(const Operand &src, FloatRegister dest) { 1.480 + loadFloat32(src, dest); 1.481 + cvtss2sd(dest, dest); 1.482 + } 1.483 + void loadFloat32(const Address &src, FloatRegister dest) { 1.484 + movss(src, dest); 1.485 + } 1.486 + void loadFloat32(const BaseIndex &src, FloatRegister dest) { 1.487 + movss(src, dest); 1.488 + } 1.489 + void loadFloat32(const Operand &src, FloatRegister dest) { 1.490 + switch (src.kind()) { 1.491 + case Operand::MEM_REG_DISP: 1.492 + loadFloat32(src.toAddress(), dest); 1.493 + break; 1.494 + case Operand::MEM_SCALE: 1.495 + loadFloat32(src.toBaseIndex(), dest); 1.496 + break; 1.497 + default: 1.498 + MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); 1.499 + } 1.500 + } 1.501 + void storeFloat32(FloatRegister src, const Address &dest) { 1.502 + movss(src, dest); 1.503 + } 1.504 + void storeFloat32(FloatRegister src, const BaseIndex &dest) { 1.505 + movss(src, dest); 1.506 + } 1.507 + void storeFloat32(FloatRegister src, const Operand &dest) { 1.508 + switch (dest.kind()) { 1.509 + case Operand::MEM_REG_DISP: 1.510 + storeFloat32(src, dest.toAddress()); 1.511 + break; 1.512 + case Operand::MEM_SCALE: 1.513 + storeFloat32(src, dest.toBaseIndex()); 1.514 + break; 1.515 + default: 1.516 + MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); 1.517 + } 1.518 + } 1.519 + void moveFloat32(FloatRegister src, FloatRegister dest) { 1.520 + // Use movaps instead of movss to avoid dependencies. 1.521 + movaps(src, dest); 1.522 + } 1.523 + 1.524 + // Checks whether a double is representable as a 32-bit integer. If so, the 1.525 + // integer is written to the output register. Otherwise, a bailout is taken to 1.526 + // the given snapshot. This function overwrites the scratch float register. 1.527 + void convertDoubleToInt32(FloatRegister src, Register dest, Label *fail, 1.528 + bool negativeZeroCheck = true) 1.529 + { 1.530 + // Check for -0.0 1.531 + if (negativeZeroCheck) 1.532 + branchNegativeZero(src, dest, fail); 1.533 + 1.534 + cvttsd2si(src, dest); 1.535 + cvtsi2sd(dest, ScratchFloatReg); 1.536 + ucomisd(src, ScratchFloatReg); 1.537 + j(Assembler::Parity, fail); 1.538 + j(Assembler::NotEqual, fail); 1.539 + 1.540 + } 1.541 + 1.542 + // Checks whether a float32 is representable as a 32-bit integer. If so, the 1.543 + // integer is written to the output register. Otherwise, a bailout is taken to 1.544 + // the given snapshot. This function overwrites the scratch float register. 1.545 + void convertFloat32ToInt32(FloatRegister src, Register dest, Label *fail, 1.546 + bool negativeZeroCheck = true) 1.547 + { 1.548 + // Check for -0.0 1.549 + if (negativeZeroCheck) 1.550 + branchNegativeZeroFloat32(src, dest, fail); 1.551 + 1.552 + cvttss2si(src, dest); 1.553 + convertInt32ToFloat32(dest, ScratchFloatReg); 1.554 + ucomiss(src, ScratchFloatReg); 1.555 + j(Assembler::Parity, fail); 1.556 + j(Assembler::NotEqual, fail); 1.557 + } 1.558 + 1.559 + void clampIntToUint8(Register reg) { 1.560 + Label inRange; 1.561 + branchTest32(Assembler::Zero, reg, Imm32(0xffffff00), &inRange); 1.562 + { 1.563 + sarl(Imm32(31), reg); 1.564 + notl(reg); 1.565 + andl(Imm32(255), reg); 1.566 + } 1.567 + bind(&inRange); 1.568 + } 1.569 + 1.570 + bool maybeInlineDouble(double d, const FloatRegister &dest) { 1.571 + uint64_t u = mozilla::BitwiseCast<uint64_t>(d); 1.572 + 1.573 + // Loading zero with xor is specially optimized in hardware. 1.574 + if (u == 0) { 1.575 + xorpd(dest, dest); 1.576 + return true; 1.577 + } 1.578 + 1.579 + // It is also possible to load several common constants using pcmpeqw 1.580 + // to get all ones and then psllq and psrlq to get zeros at the ends, 1.581 + // as described in "13.4 Generating constants" of 1.582 + // "2. Optimizing subroutines in assembly language" by Agner Fog, and as 1.583 + // previously implemented here. However, with x86 and x64 both using 1.584 + // constant pool loads for double constants, this is probably only 1.585 + // worthwhile in cases where a load is likely to be delayed. 1.586 + 1.587 + return false; 1.588 + } 1.589 + 1.590 + bool maybeInlineFloat(float f, const FloatRegister &dest) { 1.591 + uint32_t u = mozilla::BitwiseCast<uint32_t>(f); 1.592 + 1.593 + // See comment above 1.594 + if (u == 0) { 1.595 + xorps(dest, dest); 1.596 + return true; 1.597 + } 1.598 + return false; 1.599 + } 1.600 + 1.601 + void convertBoolToInt32(Register source, Register dest) { 1.602 + // Note that C++ bool is only 1 byte, so zero extend it to clear the 1.603 + // higher-order bits. 1.604 + movzbl(source, dest); 1.605 + } 1.606 + 1.607 + void emitSet(Assembler::Condition cond, const Register &dest, 1.608 + Assembler::NaNCond ifNaN = Assembler::NaN_HandledByCond) { 1.609 + if (GeneralRegisterSet(Registers::SingleByteRegs).has(dest)) { 1.610 + // If the register we're defining is a single byte register, 1.611 + // take advantage of the setCC instruction 1.612 + setCC(cond, dest); 1.613 + movzbl(dest, dest); 1.614 + 1.615 + if (ifNaN != Assembler::NaN_HandledByCond) { 1.616 + Label noNaN; 1.617 + j(Assembler::NoParity, &noNaN); 1.618 + mov(ImmWord(ifNaN == Assembler::NaN_IsTrue), dest); 1.619 + bind(&noNaN); 1.620 + } 1.621 + } else { 1.622 + Label end; 1.623 + Label ifFalse; 1.624 + 1.625 + if (ifNaN == Assembler::NaN_IsFalse) 1.626 + j(Assembler::Parity, &ifFalse); 1.627 + // Note a subtlety here: FLAGS is live at this point, and the 1.628 + // mov interface doesn't guarantee to preserve FLAGS. Use 1.629 + // movl instead of mov, because the movl instruction 1.630 + // preserves FLAGS. 1.631 + movl(Imm32(1), dest); 1.632 + j(cond, &end); 1.633 + if (ifNaN == Assembler::NaN_IsTrue) 1.634 + j(Assembler::Parity, &end); 1.635 + bind(&ifFalse); 1.636 + mov(ImmWord(0), dest); 1.637 + 1.638 + bind(&end); 1.639 + } 1.640 + } 1.641 + 1.642 + template <typename T1, typename T2> 1.643 + void cmp32Set(Assembler::Condition cond, T1 lhs, T2 rhs, const Register &dest) 1.644 + { 1.645 + cmp32(lhs, rhs); 1.646 + emitSet(cond, dest); 1.647 + } 1.648 + 1.649 + // Emit a JMP that can be toggled to a CMP. See ToggleToJmp(), ToggleToCmp(). 1.650 + CodeOffsetLabel toggledJump(Label *label) { 1.651 + CodeOffsetLabel offset(size()); 1.652 + jump(label); 1.653 + return offset; 1.654 + } 1.655 + 1.656 + template <typename T> 1.657 + void computeEffectiveAddress(const T &address, Register dest) { 1.658 + lea(Operand(address), dest); 1.659 + } 1.660 + 1.661 + // Builds an exit frame on the stack, with a return address to an internal 1.662 + // non-function. Returns offset to be passed to markSafepointAt(). 1.663 + bool buildFakeExitFrame(const Register &scratch, uint32_t *offset); 1.664 + void callWithExitFrame(JitCode *target); 1.665 + 1.666 + void callIon(const Register &callee) { 1.667 + call(callee); 1.668 + } 1.669 + 1.670 + void appendCallSite(const CallSiteDesc &desc) { 1.671 + // Add an extra sizeof(void*) to include the return address that was 1.672 + // pushed by the call instruction (see CallSite::stackDepth). 1.673 + enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_ + sizeof(void*))); 1.674 + } 1.675 + 1.676 + void call(const CallSiteDesc &desc, Label *label) { 1.677 + call(label); 1.678 + appendCallSite(desc); 1.679 + } 1.680 + void call(const CallSiteDesc &desc, const Register ®) { 1.681 + call(reg); 1.682 + appendCallSite(desc); 1.683 + } 1.684 + void callIonFromAsmJS(const Register ®) { 1.685 + call(CallSiteDesc::Exit(), reg); 1.686 + } 1.687 + 1.688 + void checkStackAlignment() { 1.689 + // Exists for ARM compatibility. 1.690 + } 1.691 + 1.692 + CodeOffsetLabel labelForPatch() { 1.693 + return CodeOffsetLabel(size()); 1.694 + } 1.695 + 1.696 + void abiret() { 1.697 + ret(); 1.698 + } 1.699 + 1.700 + protected: 1.701 + bool buildOOLFakeExitFrame(void *fakeReturnAddr); 1.702 +}; 1.703 + 1.704 +} // namespace jit 1.705 +} // namespace js 1.706 + 1.707 +#endif /* jit_shared_MacroAssembler_x86_shared_h */