blob: 0c7aa136459c60bd4f216f9ada5982a56e4c2e42 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/SharedIC.h"
#include "jit/SharedICHelpers.h"
#ifdef JS_SIMULATOR_ARM64
#include "jit/arm64/Assembler-arm64.h"
#include "jit/arm64/BaselineCompiler-arm64.h"
#include "jit/arm64/vixl/Debugger-vixl.h"
#endif
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
// ICBinaryArith_Int32
bool
ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
{
// Guard that R0 is an integer and R1 is an integer.
Label failure;
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
// Add R0 and R1. Don't need to explicitly unbox, just use R2.
Register Rscratch = R2_;
ARMRegister Wscratch = ARMRegister(Rscratch, 32);
#ifdef MERGE
// DIV and MOD need an extra non-volatile ValueOperand to hold R0.
AllocatableGeneralRegisterSet savedRegs(availableGeneralRegs(2));
savedRegs.set() = GeneralRegisterSet::Intersect(GeneralRegisterSet::NonVolatile(), savedRegs);
#endif
// get some more ARM-y names for the registers
ARMRegister W0(R0_, 32);
ARMRegister X0(R0_, 64);
ARMRegister W1(R1_, 32);
ARMRegister X1(R1_, 64);
ARMRegister WTemp(ExtractTemp0, 32);
ARMRegister XTemp(ExtractTemp0, 64);
Label maybeNegZero, revertRegister;
switch(op_) {
case JSOP_ADD:
masm.Adds(WTemp, W0, Operand(W1));
// Just jump to failure on overflow. R0 and R1 are preserved, so we can
// just jump to the next stub.
masm.j(Assembler::Overflow, &failure);
// Box the result and return. We know R0 already contains the
// integer tag, so we just need to move the payload into place.
masm.movePayload(ExtractTemp0, R0_);
break;
case JSOP_SUB:
masm.Subs(WTemp, W0, Operand(W1));
masm.j(Assembler::Overflow, &failure);
masm.movePayload(ExtractTemp0, R0_);
break;
case JSOP_MUL:
masm.mul32(R0.valueReg(), R1.valueReg(), Rscratch, &failure, &maybeNegZero);
masm.movePayload(Rscratch, R0_);
break;
case JSOP_DIV:
case JSOP_MOD: {
// Check for INT_MIN / -1, it results in a double.
Label check2;
masm.Cmp(W0, Operand(INT_MIN));
masm.B(&check2, Assembler::NotEqual);
masm.Cmp(W1, Operand(-1));
masm.j(Assembler::Equal, &failure);
masm.bind(&check2);
Label no_fail;
// Check for both division by zero and 0 / X with X < 0 (results in -0).
masm.Cmp(W1, Operand(0));
// If x > 0, then it can't be bad.
masm.B(&no_fail, Assembler::GreaterThan);
// if x == 0, then ignore any comparison, and force
// it to fail, if x < 0 (the only other case)
// then do the comparison, and fail if y == 0
masm.Ccmp(W0, Operand(0), vixl::ZFlag, Assembler::NotEqual);
masm.B(&failure, Assembler::Equal);
masm.bind(&no_fail);
masm.Sdiv(Wscratch, W0, W1);
// Start calculating the remainder, x - (x / y) * y.
masm.mul(WTemp, W1, Wscratch);
if (op_ == JSOP_DIV) {
// Result is a double if the remainder != 0, which happens
// when (x/y)*y != x.
masm.branch32(Assembler::NotEqual, R0.valueReg(), ExtractTemp0, &revertRegister);
masm.movePayload(Rscratch, R0_);
} else {
// Calculate the actual mod. Set the condition code, so we can see if it is non-zero.
masm.Subs(WTemp, W0, WTemp);
// If X % Y == 0 and X < 0, the result is -0.
masm.Ccmp(W0, Operand(0), vixl::NoFlag, Assembler::Equal);
masm.branch(Assembler::LessThan, &revertRegister);
masm.movePayload(ExtractTemp0, R0_);
}
break;
}
// ORR, EOR, AND can trivially be coerced int
// working without affecting the tag of the dest..
case JSOP_BITOR:
masm.Orr(X0, X0, Operand(X1));
break;
case JSOP_BITXOR:
masm.Eor(X0, X0, Operand(W1, vixl::UXTW));
break;
case JSOP_BITAND:
masm.And(X0, X0, Operand(X1));
break;
// LSH, RSH and URSH can not.
case JSOP_LSH:
// ARM will happily try to shift by more than 0x1f.
masm.Lsl(Wscratch, W0, W1);
masm.movePayload(Rscratch, R0.valueReg());
break;
case JSOP_RSH:
masm.Asr(Wscratch, W0, W1);
masm.movePayload(Rscratch, R0.valueReg());
break;
case JSOP_URSH:
masm.Lsr(Wscratch, W0, W1);
if (allowDouble_) {
Label toUint;
// Testing for negative is equivalent to testing bit 31
masm.Tbnz(Wscratch, 31, &toUint);
// Move result and box for return.
masm.movePayload(Rscratch, R0_);
EmitReturnFromIC(masm);
masm.bind(&toUint);
masm.convertUInt32ToDouble(Rscratch, ScratchDoubleReg);
masm.boxDouble(ScratchDoubleReg, R0);
} else {
// Testing for negative is equivalent to testing bit 31
masm.Tbnz(Wscratch, 31, &failure);
// Move result for return.
masm.movePayload(Rscratch, R0_);
}
break;
default:
MOZ_CRASH("Unhandled op for BinaryArith_Int32.");
}
EmitReturnFromIC(masm);
switch (op_) {
case JSOP_MUL:
masm.bind(&maybeNegZero);
// Result is -0 if exactly one of lhs or rhs is negative.
masm.Cmn(W0, W1);
masm.j(Assembler::Signed, &failure);
// Result is +0, so use the zero register.
masm.movePayload(rzr, R0_);
EmitReturnFromIC(masm);
break;
case JSOP_DIV:
case JSOP_MOD:
masm.bind(&revertRegister);
break;
default:
break;
}
// Failure case - jump to next stub.
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure;
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
switch (op) {
case JSOP_BITNOT:
masm.Mvn(ARMRegister(R1.valueReg(), 32), ARMRegister(R0.valueReg(), 32));
masm.movePayload(R1.valueReg(), R0.valueReg());
break;
case JSOP_NEG:
// Guard against 0 and MIN_INT, both result in a double.
masm.branchTest32(Assembler::Zero, R0.valueReg(), Imm32(0x7fffffff), &failure);
// Compile -x as 0 - x.
masm.Sub(ARMRegister(R1.valueReg(), 32), wzr, ARMRegister(R0.valueReg(), 32));
masm.movePayload(R1.valueReg(), R0.valueReg());
break;
default:
MOZ_CRASH("Unexpected op");
}
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
} // namespace jit
} // namespace js