2017-08-12 16:48:01 +00:00
|
|
|
/*
|
2018-01-03 05:16:05 +00:00
|
|
|
* Copyright (C) 2009-2017 Apple Inc. All rights reserved.
|
2017-08-12 16:48:01 +00:00
|
|
|
* Copyright (C) 2009 University of Szeged
|
|
|
|
* All rights reserved.
|
|
|
|
* Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#if ENABLE(ASSEMBLER) && CPU(MIPS)
|
|
|
|
|
|
|
|
#include "AssemblerBuffer.h"
|
|
|
|
#include "JITCompilationEffort.h"
|
2020-08-29 13:27:11 +00:00
|
|
|
#include "MIPSRegisters.h"
|
2018-01-03 05:16:05 +00:00
|
|
|
#include <limits.h>
|
2017-08-12 16:48:01 +00:00
|
|
|
#include <wtf/Assertions.h>
|
|
|
|
#include <wtf/SegmentedVector.h>
|
|
|
|
|
|
|
|
namespace JSC {
|
|
|
|
|
|
|
|
typedef uint32_t MIPSWord;
|
|
|
|
|
2020-08-29 13:27:11 +00:00
|
|
|
namespace RegisterNames {
|
|
|
|
typedef enum : int8_t {
|
|
|
|
#define REGISTER_ID(id, name, r, cs) id,
|
|
|
|
FOR_EACH_GP_REGISTER(REGISTER_ID)
|
|
|
|
#undef REGISTER_ID
|
|
|
|
#define REGISTER_ALIAS(id, alias) id = alias,
|
|
|
|
FOR_EACH_REGISTER_ALIAS(REGISTER_ALIAS)
|
|
|
|
#undef REGISTER_ALIAS
|
|
|
|
InvalidGPRReg = -1,
|
2017-08-12 16:48:01 +00:00
|
|
|
} RegisterID;
|
|
|
|
|
2020-08-29 13:27:11 +00:00
|
|
|
typedef enum : int8_t {
|
|
|
|
#define REGISTER_ID(id, name, idx) id = idx,
|
|
|
|
FOR_EACH_SP_REGISTER(REGISTER_ID)
|
|
|
|
#undef REGISTER_ID
|
|
|
|
} SPRegisterID;
|
|
|
|
|
|
|
|
typedef enum : int8_t {
|
|
|
|
#define REGISTER_ID(id, name, r, cs) id,
|
|
|
|
FOR_EACH_FP_REGISTER(REGISTER_ID)
|
|
|
|
#undef REGISTER_ID
|
|
|
|
InvalidFPRReg = -1,
|
2017-08-12 16:48:01 +00:00
|
|
|
} FPRegisterID;
|
|
|
|
|
|
|
|
} // namespace MIPSRegisters
|
|
|
|
|
|
|
|
class MIPSAssembler {
|
|
|
|
public:
|
|
|
|
typedef MIPSRegisters::RegisterID RegisterID;
|
2020-08-29 13:27:11 +00:00
|
|
|
typedef MIPSRegisters::SPRegisterID SPRegisterID;
|
2017-08-12 16:48:01 +00:00
|
|
|
typedef MIPSRegisters::FPRegisterID FPRegisterID;
|
|
|
|
typedef SegmentedVector<AssemblerLabel, 64> Jumps;
|
|
|
|
|
|
|
|
static constexpr RegisterID firstRegister() { return MIPSRegisters::r0; }
|
|
|
|
static constexpr RegisterID lastRegister() { return MIPSRegisters::r31; }
|
2020-08-29 13:27:11 +00:00
|
|
|
static constexpr unsigned numberOfRegisters() { return lastRegister() - firstRegister() + 1; }
|
|
|
|
|
|
|
|
static constexpr SPRegisterID firstSPRegister() { return MIPSRegisters::fir; }
|
|
|
|
static constexpr SPRegisterID lastSPRegister() { return MIPSRegisters::pc; }
|
|
|
|
static constexpr unsigned numberOfSPRegisters() { return lastSPRegister() - firstSPRegister() + 1; }
|
2017-08-12 16:48:01 +00:00
|
|
|
|
|
|
|
static constexpr FPRegisterID firstFPRegister() { return MIPSRegisters::f0; }
|
|
|
|
static constexpr FPRegisterID lastFPRegister() { return MIPSRegisters::f31; }
|
2020-08-29 13:27:11 +00:00
|
|
|
static constexpr unsigned numberOfFPRegisters() { return lastFPRegister() - firstFPRegister() + 1; }
|
|
|
|
|
|
|
|
static const char* gprName(RegisterID id)
|
|
|
|
{
|
|
|
|
ASSERT(id >= firstRegister() && id <= lastRegister());
|
|
|
|
static const char* const nameForRegister[numberOfRegisters()] = {
|
|
|
|
#define REGISTER_NAME(id, name, r, c) name,
|
|
|
|
FOR_EACH_GP_REGISTER(REGISTER_NAME)
|
|
|
|
#undef REGISTER_NAME
|
|
|
|
};
|
|
|
|
return nameForRegister[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char* sprName(SPRegisterID id)
|
|
|
|
{
|
|
|
|
ASSERT(id >= firstSPRegister() && id <= lastSPRegister());
|
|
|
|
static const char* const nameForRegister[numberOfSPRegisters()] = {
|
|
|
|
#define REGISTER_NAME(id, name, idx) name,
|
|
|
|
FOR_EACH_SP_REGISTER(REGISTER_NAME)
|
|
|
|
#undef REGISTER_NAME
|
|
|
|
};
|
|
|
|
return nameForRegister[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char* fprName(FPRegisterID id)
|
|
|
|
{
|
|
|
|
ASSERT(id >= firstFPRegister() && id <= lastFPRegister());
|
|
|
|
static const char* const nameForRegister[numberOfFPRegisters()] = {
|
|
|
|
#define REGISTER_NAME(id, name, r, cs) name,
|
|
|
|
FOR_EACH_FP_REGISTER(REGISTER_NAME)
|
|
|
|
#undef REGISTER_NAME
|
|
|
|
};
|
|
|
|
return nameForRegister[id];
|
|
|
|
}
|
2017-08-12 16:48:01 +00:00
|
|
|
|
|
|
|
MIPSAssembler()
|
|
|
|
: m_indexOfLastWatchpoint(INT_MIN)
|
|
|
|
, m_indexOfTailOfLastWatchpoint(INT_MIN)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AssemblerBuffer& buffer() { return m_buffer; }
|
|
|
|
|
|
|
|
// MIPS instruction opcode field position
|
|
|
|
enum {
|
|
|
|
OP_SH_RD = 11,
|
|
|
|
OP_SH_RT = 16,
|
|
|
|
OP_SH_RS = 21,
|
|
|
|
OP_SH_SHAMT = 6,
|
|
|
|
OP_SH_CODE = 16,
|
|
|
|
OP_SH_FD = 6,
|
|
|
|
OP_SH_FS = 11,
|
2020-08-29 13:27:11 +00:00
|
|
|
OP_SH_FT = 16,
|
|
|
|
OP_SH_MSB = 11,
|
|
|
|
OP_SH_LSB = 6
|
|
|
|
};
|
|
|
|
|
|
|
|
// FCSR Bits
|
|
|
|
enum {
|
|
|
|
FP_CAUSE_INVALID_OPERATION = 1 << 16
|
2017-08-12 16:48:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void emitInst(MIPSWord op)
|
|
|
|
{
|
|
|
|
void* oldBase = m_buffer.data();
|
|
|
|
|
|
|
|
m_buffer.putInt(op);
|
|
|
|
|
|
|
|
void* newBase = m_buffer.data();
|
|
|
|
if (oldBase != newBase)
|
|
|
|
relocateJumps(oldBase, newBase);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nop()
|
|
|
|
{
|
|
|
|
emitInst(0x00000000);
|
|
|
|
}
|
2018-01-03 05:16:05 +00:00
|
|
|
|
2022-10-23 02:55:20 +00:00
|
|
|
using CopyFunction = void*(&)(void*, const void*, size_t);
|
|
|
|
|
|
|
|
template <CopyFunction copy>
|
|
|
|
ALWAYS_INLINE static void fillNops(void* base, size_t size)
|
2018-01-03 05:16:05 +00:00
|
|
|
{
|
2020-08-29 13:27:11 +00:00
|
|
|
UNUSED_PARAM(copy);
|
2018-01-03 05:16:05 +00:00
|
|
|
RELEASE_ASSERT(!(size % sizeof(int32_t)));
|
2017-08-12 16:48:01 +00:00
|
|
|
|
2018-01-03 05:16:05 +00:00
|
|
|
int32_t* ptr = static_cast<int32_t*>(base);
|
|
|
|
const size_t num32s = size / sizeof(int32_t);
|
|
|
|
const int32_t insn = 0x00000000;
|
|
|
|
for (size_t i = 0; i < num32s; i++)
|
|
|
|
*ptr++ = insn;
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
void sync()
|
|
|
|
{
|
2018-01-03 05:16:05 +00:00
|
|
|
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=169984
|
|
|
|
// We might get a performance improvements by using SYNC_MB in some or
|
|
|
|
// all cases.
|
2017-08-12 16:48:01 +00:00
|
|
|
emitInst(0x0000000f);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Need to insert one load data delay nop for mips1. */
|
|
|
|
void loadDelayNop()
|
|
|
|
{
|
|
|
|
#if WTF_MIPS_ISA(1)
|
|
|
|
nop();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Need to insert one coprocessor access delay nop for mips1. */
|
|
|
|
void copDelayNop()
|
|
|
|
{
|
|
|
|
#if WTF_MIPS_ISA(1)
|
|
|
|
nop();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void move(RegisterID rd, RegisterID rs)
|
|
|
|
{
|
|
|
|
/* addu */
|
|
|
|
emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set an immediate value to a register. This may generate 1 or 2
|
|
|
|
instructions. */
|
|
|
|
void li(RegisterID dest, int imm)
|
|
|
|
{
|
|
|
|
if (imm >= -32768 && imm <= 32767)
|
|
|
|
addiu(dest, MIPSRegisters::zero, imm);
|
|
|
|
else if (imm >= 0 && imm < 65536)
|
|
|
|
ori(dest, MIPSRegisters::zero, imm);
|
|
|
|
else {
|
|
|
|
lui(dest, imm >> 16);
|
|
|
|
if (imm & 0xffff)
|
|
|
|
ori(dest, dest, imm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-29 13:27:11 +00:00
|
|
|
void ext(RegisterID rt, RegisterID rs, int pos, int size)
|
|
|
|
{
|
|
|
|
int msb = size - 1;
|
|
|
|
emitInst(0x7c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (pos << OP_SH_LSB) | (msb << OP_SH_MSB));
|
|
|
|
}
|
|
|
|
|
|
|
|
void mfhc1(RegisterID rt, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x4460000 | (rt << OP_SH_RT) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
void lui(RegisterID rt, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x3c000000 | (rt << OP_SH_RT) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void clz(RegisterID rd, RegisterID rs)
|
|
|
|
{
|
|
|
|
emitInst(0x70000020 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rd << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void addiu(RegisterID rt, RegisterID rs, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void addu(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void subu(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void mult(RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000018 | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void div(RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x0000001a | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void mfhi(RegisterID rd)
|
|
|
|
{
|
|
|
|
emitInst(0x00000010 | (rd << OP_SH_RD));
|
|
|
|
}
|
|
|
|
|
|
|
|
void mflo(RegisterID rd)
|
|
|
|
{
|
|
|
|
emitInst(0x00000012 | (rd << OP_SH_RD));
|
|
|
|
}
|
|
|
|
|
|
|
|
void mul(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
#if WTF_MIPS_ISA_AT_LEAST(32)
|
|
|
|
emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
#else
|
|
|
|
mult(rs, rt);
|
|
|
|
mflo(rd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void andInsn(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void andi(RegisterID rt, RegisterID rs, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void nor(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void orInsn(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ori(RegisterID rt, RegisterID rs, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void xori(RegisterID rt, RegisterID rs, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void slt(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void sltu(RegisterID rd, RegisterID rs, RegisterID rt)
|
|
|
|
{
|
|
|
|
emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT));
|
|
|
|
}
|
|
|
|
|
2020-08-29 13:27:11 +00:00
|
|
|
void slti(RegisterID rt, RegisterID rs, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x28000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
void sltiu(RegisterID rt, RegisterID rs, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void sll(RegisterID rd, RegisterID rt, int shamt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void sllv(RegisterID rd, RegisterID rt, RegisterID rs)
|
|
|
|
{
|
|
|
|
emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void sra(RegisterID rd, RegisterID rt, int shamt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void srav(RegisterID rd, RegisterID rt, RegisterID rs)
|
|
|
|
{
|
|
|
|
emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void srl(RegisterID rd, RegisterID rt, int shamt)
|
|
|
|
{
|
|
|
|
emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void srlv(RegisterID rd, RegisterID rt, RegisterID rs)
|
|
|
|
{
|
|
|
|
emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void lb(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0x80000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
loadDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void lbu(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
loadDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void lw(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
loadDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void lwl(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
loadDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void lwr(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
loadDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void lh(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0x84000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
loadDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void lhu(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
loadDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void sb(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0xa0000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void sh(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0xa4000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void sw(RegisterID rt, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void jr(RegisterID rs)
|
|
|
|
{
|
|
|
|
emitInst(0x00000008 | (rs << OP_SH_RS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void jalr(RegisterID rs)
|
|
|
|
{
|
|
|
|
emitInst(0x0000f809 | (rs << OP_SH_RS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void jal()
|
|
|
|
{
|
|
|
|
emitInst(0x0c000000);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bkpt()
|
|
|
|
{
|
|
|
|
int value = 512; /* BRK_BUG */
|
|
|
|
emitInst(0x0000000d | ((value & 0x3ff) << OP_SH_CODE));
|
|
|
|
}
|
|
|
|
|
2018-01-03 05:16:05 +00:00
|
|
|
static bool isBkpt(void* address)
|
|
|
|
{
|
|
|
|
int value = 512; /* BRK_BUG */
|
|
|
|
MIPSWord expected = (0x0000000d | ((value & 0x3ff) << OP_SH_CODE));
|
|
|
|
MIPSWord candidateInstruction = *reinterpret_cast<MIPSWord*>(address);
|
|
|
|
return candidateInstruction == expected;
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
void bgez(RegisterID rs, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x04010000 | (rs << OP_SH_RS) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void bltz(RegisterID rs, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x04000000 | (rs << OP_SH_RS) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void beq(RegisterID rs, RegisterID rt, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x10000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void bne(RegisterID rs, RegisterID rt, int imm)
|
|
|
|
{
|
|
|
|
emitInst(0x14000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void bc1t()
|
|
|
|
{
|
|
|
|
emitInst(0x45010000);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bc1f()
|
|
|
|
{
|
|
|
|
emitInst(0x45000000);
|
|
|
|
}
|
|
|
|
|
|
|
|
void appendJump()
|
|
|
|
{
|
|
|
|
m_jumps.append(m_buffer.label());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
}
|
|
|
|
|
|
|
|
void lwc1(FPRegisterID ft, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ldc1(FPRegisterID ft, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void swc1(FPRegisterID ft, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void sdc1(FPRegisterID ft, RegisterID rs, int offset)
|
|
|
|
{
|
|
|
|
emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff));
|
|
|
|
}
|
|
|
|
|
|
|
|
void mtc1(RegisterID rt, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x44800000 | (fs << OP_SH_FS) | (rt << OP_SH_RT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void mthc1(RegisterID rt, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x44e00000 | (fs << OP_SH_FS) | (rt << OP_SH_RT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void mfc1(RegisterID rt, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x44000000 | (fs << OP_SH_FS) | (rt << OP_SH_RT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void sqrtd(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x46200004 | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void absd(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x46200005 | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void movd(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x46200006 | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void negd(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x46200007 | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void truncwd(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x4620000d | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void cvtdw(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void cvtds(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x46000021 | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void cvtwd(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void cvtsd(FPRegisterID fd, FPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x46200020 | (fd << OP_SH_FD) | (fs << OP_SH_FS));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ceqd(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cngtd(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x4620003f | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cnged(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x4620003d | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cltd(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x4620003c | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cled(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x4620003e | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cueqd(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200033 | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void coled(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200036 | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void coltd(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200034 | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void culed(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200037 | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cultd(FPRegisterID fs, FPRegisterID ft)
|
|
|
|
{
|
|
|
|
emitInst(0x46200035 | (fs << OP_SH_FS) | (ft << OP_SH_FT));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
2020-08-29 13:27:11 +00:00
|
|
|
void cfc1(RegisterID rt, SPRegisterID fs)
|
|
|
|
{
|
|
|
|
emitInst(0x44400000 | (rt << OP_SH_RT) | (fs << OP_SH_FS));
|
|
|
|
copDelayNop();
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
// General helpers
|
|
|
|
|
|
|
|
AssemblerLabel labelIgnoringWatchpoints()
|
|
|
|
{
|
|
|
|
return m_buffer.label();
|
|
|
|
}
|
|
|
|
|
|
|
|
AssemblerLabel labelForWatchpoint()
|
|
|
|
{
|
|
|
|
AssemblerLabel result = m_buffer.label();
|
2022-10-23 02:55:20 +00:00
|
|
|
if (static_cast<int>(result.offset()) != m_indexOfLastWatchpoint)
|
2017-08-12 16:48:01 +00:00
|
|
|
result = label();
|
2022-10-23 02:55:20 +00:00
|
|
|
m_indexOfLastWatchpoint = result.offset();
|
|
|
|
m_indexOfTailOfLastWatchpoint = result.offset() + maxJumpReplacementSize();
|
2017-08-12 16:48:01 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
AssemblerLabel label()
|
|
|
|
{
|
|
|
|
AssemblerLabel result = m_buffer.label();
|
2022-10-23 02:55:20 +00:00
|
|
|
while (UNLIKELY(static_cast<int>(result.offset()) < m_indexOfTailOfLastWatchpoint)) {
|
2017-08-12 16:48:01 +00:00
|
|
|
nop();
|
|
|
|
result = m_buffer.label();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
AssemblerLabel align(int alignment)
|
|
|
|
{
|
|
|
|
while (!m_buffer.isAligned(alignment))
|
|
|
|
bkpt();
|
|
|
|
|
|
|
|
return label();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void* getRelocatedAddress(void* code, AssemblerLabel label)
|
|
|
|
{
|
2022-10-23 02:55:20 +00:00
|
|
|
return reinterpret_cast<void*>(reinterpret_cast<char*>(code) + label.offset());
|
2017-08-12 16:48:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b)
|
|
|
|
{
|
2022-10-23 02:55:20 +00:00
|
|
|
return b.offset() - a.offset();
|
2017-08-12 16:48:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Assembler admin methods:
|
|
|
|
|
|
|
|
size_t codeSize() const
|
|
|
|
{
|
|
|
|
return m_buffer.codeSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned debugOffset() { return m_buffer.debugOffset(); }
|
|
|
|
|
|
|
|
// Assembly helpers for moving data between fp and registers.
|
|
|
|
void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn)
|
|
|
|
{
|
|
|
|
#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64
|
|
|
|
mfc1(rd1, rn);
|
|
|
|
mfhc1(rd2, rn);
|
|
|
|
#else
|
|
|
|
mfc1(rd1, rn);
|
|
|
|
mfc1(rd2, FPRegisterID(rn + 1));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2)
|
|
|
|
{
|
|
|
|
#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64
|
|
|
|
mtc1(rn1, rd);
|
|
|
|
mthc1(rn2, rd);
|
|
|
|
#else
|
|
|
|
mtc1(rn1, rd);
|
|
|
|
mtc1(rn2, FPRegisterID(rd + 1));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned getCallReturnOffset(AssemblerLabel call)
|
|
|
|
{
|
|
|
|
// The return address is after a call and a delay slot instruction
|
2022-10-23 02:55:20 +00:00
|
|
|
return call.offset();
|
2017-08-12 16:48:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Linking & patching:
|
|
|
|
//
|
|
|
|
// 'link' and 'patch' methods are for use on unprotected code - such as the code
|
|
|
|
// within the AssemblerBuffer, and code being patched by the patch buffer. Once
|
|
|
|
// code has been finalized it is (platform support permitting) within a non-
|
|
|
|
// writable region of memory; to modify the code in an execute-only execuable
|
|
|
|
// pool the 'repatch' and 'relink' methods should be used.
|
|
|
|
|
|
|
|
static size_t linkDirectJump(void* code, void* to)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(code));
|
|
|
|
size_t ops = 0;
|
|
|
|
int32_t slotAddr = reinterpret_cast<int>(insn) + 4;
|
|
|
|
int32_t toAddr = reinterpret_cast<int>(to);
|
|
|
|
|
|
|
|
if ((slotAddr & 0xf0000000) != (toAddr & 0xf0000000)) {
|
|
|
|
// lui
|
|
|
|
*insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((toAddr >> 16) & 0xffff);
|
|
|
|
++insn;
|
|
|
|
// ori
|
|
|
|
*insn = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (toAddr & 0xffff);
|
|
|
|
++insn;
|
|
|
|
// jr
|
|
|
|
*insn = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS);
|
|
|
|
++insn;
|
|
|
|
ops = 4 * sizeof(MIPSWord);
|
|
|
|
} else {
|
|
|
|
// j
|
|
|
|
*insn = 0x08000000 | ((toAddr & 0x0fffffff) >> 2);
|
|
|
|
++insn;
|
|
|
|
ops = 2 * sizeof(MIPSWord);
|
|
|
|
}
|
|
|
|
// nop
|
|
|
|
*insn = 0x00000000;
|
|
|
|
return ops;
|
|
|
|
}
|
|
|
|
|
|
|
|
void linkJump(AssemblerLabel from, AssemblerLabel to)
|
|
|
|
{
|
|
|
|
ASSERT(to.isSet());
|
|
|
|
ASSERT(from.isSet());
|
2022-10-23 02:55:20 +00:00
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(m_buffer.data()) + from.offset());
|
|
|
|
MIPSWord* toPos = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(m_buffer.data()) + to.offset());
|
2017-08-12 16:48:01 +00:00
|
|
|
|
|
|
|
ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5)));
|
|
|
|
insn = insn - 6;
|
|
|
|
linkWithOffset(insn, toPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void linkJump(void* code, AssemblerLabel from, void* to)
|
|
|
|
{
|
|
|
|
ASSERT(from.isSet());
|
2022-10-23 02:55:20 +00:00
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(code) + from.offset());
|
2017-08-12 16:48:01 +00:00
|
|
|
|
|
|
|
ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5)));
|
|
|
|
insn = insn - 6;
|
|
|
|
linkWithOffset(insn, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void linkCall(void* code, AssemblerLabel from, void* to)
|
|
|
|
{
|
2022-10-23 02:55:20 +00:00
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(code) + from.offset());
|
2017-08-12 16:48:01 +00:00
|
|
|
linkCallInternal(insn, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void linkPointer(void* code, AssemblerLabel from, void* to)
|
|
|
|
{
|
2022-10-23 02:55:20 +00:00
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(code) + from.offset());
|
2017-08-12 16:48:01 +00:00
|
|
|
ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui
|
|
|
|
*insn = (*insn & 0xffff0000) | ((reinterpret_cast<intptr_t>(to) >> 16) & 0xffff);
|
|
|
|
insn++;
|
|
|
|
ASSERT((*insn & 0xfc000000) == 0x34000000); // ori
|
|
|
|
*insn = (*insn & 0xffff0000) | (reinterpret_cast<intptr_t>(to) & 0xffff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void relinkJump(void* from, void* to)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(from);
|
|
|
|
|
|
|
|
ASSERT(!(*(insn - 1)) && !(*(insn - 5)));
|
|
|
|
insn = insn - 6;
|
|
|
|
int flushSize = linkWithOffset(insn, to);
|
|
|
|
|
|
|
|
cacheFlush(insn, flushSize);
|
|
|
|
}
|
|
|
|
|
2018-01-03 05:16:05 +00:00
|
|
|
static void relinkJumpToNop(void* from)
|
|
|
|
{
|
|
|
|
relinkJump(from, from);
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
static void relinkCall(void* from, void* to)
|
|
|
|
{
|
|
|
|
void* start;
|
|
|
|
int size = linkCallInternal(from, to);
|
|
|
|
if (size == sizeof(MIPSWord))
|
|
|
|
start = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(from) - 2 * sizeof(MIPSWord));
|
|
|
|
else
|
|
|
|
start = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(from) - 4 * sizeof(MIPSWord));
|
|
|
|
|
|
|
|
cacheFlush(start, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void repatchInt32(void* from, int32_t to)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(from);
|
|
|
|
ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui
|
|
|
|
*insn = (*insn & 0xffff0000) | ((to >> 16) & 0xffff);
|
|
|
|
insn++;
|
|
|
|
ASSERT((*insn & 0xfc000000) == 0x34000000); // ori
|
|
|
|
*insn = (*insn & 0xffff0000) | (to & 0xffff);
|
2020-08-29 13:27:11 +00:00
|
|
|
cacheFlush(from, 2 * sizeof(MIPSWord));
|
2017-08-12 16:48:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t readInt32(void* from)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(from);
|
|
|
|
ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui
|
|
|
|
int32_t result = (*insn & 0x0000ffff) << 16;
|
|
|
|
insn++;
|
|
|
|
ASSERT((*insn & 0xfc000000) == 0x34000000); // ori
|
|
|
|
result |= *insn & 0x0000ffff;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void repatchCompact(void* where, int32_t value)
|
|
|
|
{
|
|
|
|
repatchInt32(where, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void repatchPointer(void* from, void* to)
|
|
|
|
{
|
|
|
|
repatchInt32(from, reinterpret_cast<int32_t>(to));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void* readPointer(void* from)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<void*>(readInt32(from));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void* readCallTarget(void* from)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(from);
|
|
|
|
insn -= 4;
|
|
|
|
ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui
|
|
|
|
int32_t result = (*insn & 0x0000ffff) << 16;
|
|
|
|
insn++;
|
|
|
|
ASSERT((*insn & 0xfc000000) == 0x34000000); // ori
|
|
|
|
result |= *insn & 0x0000ffff;
|
|
|
|
return reinterpret_cast<void*>(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cacheFlush(void* code, size_t size)
|
|
|
|
{
|
|
|
|
intptr_t end = reinterpret_cast<intptr_t>(code) + size;
|
|
|
|
__builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ptrdiff_t maxJumpReplacementSize()
|
|
|
|
{
|
|
|
|
return sizeof(MIPSWord) * 4;
|
|
|
|
}
|
|
|
|
|
2018-01-03 05:16:05 +00:00
|
|
|
static constexpr ptrdiff_t patchableJumpSize()
|
|
|
|
{
|
|
|
|
return sizeof(MIPSWord) * 8;
|
|
|
|
}
|
|
|
|
|
2017-08-12 16:48:01 +00:00
|
|
|
static void revertJumpToMove(void* instructionStart, RegisterID rt, int imm)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = static_cast<MIPSWord*>(instructionStart);
|
|
|
|
size_t codeSize = 2 * sizeof(MIPSWord);
|
|
|
|
|
|
|
|
// lui
|
|
|
|
*insn = 0x3c000000 | (rt << OP_SH_RT) | ((imm >> 16) & 0xffff);
|
|
|
|
++insn;
|
|
|
|
// ori
|
|
|
|
*insn = 0x34000000 | (rt << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff);
|
|
|
|
++insn;
|
|
|
|
// if jr $t9
|
|
|
|
if (*insn == 0x03200008) {
|
|
|
|
*insn = 0x00000000;
|
|
|
|
codeSize += sizeof(MIPSWord);
|
|
|
|
}
|
2020-08-29 13:27:11 +00:00
|
|
|
cacheFlush(instructionStart, codeSize);
|
2017-08-12 16:48:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void replaceWithJump(void* instructionStart, void* to)
|
|
|
|
{
|
|
|
|
ASSERT(!(bitwise_cast<uintptr_t>(instructionStart) & 3));
|
|
|
|
ASSERT(!(bitwise_cast<uintptr_t>(to) & 3));
|
|
|
|
size_t ops = linkDirectJump(instructionStart, to);
|
|
|
|
cacheFlush(instructionStart, ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void replaceWithLoad(void* instructionStart)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(instructionStart);
|
|
|
|
ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui
|
|
|
|
insn++;
|
|
|
|
ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu
|
|
|
|
insn++;
|
|
|
|
*insn = 0x8c000000 | ((*insn) & 0x3ffffff); // lw
|
|
|
|
cacheFlush(insn, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void replaceWithAddressComputation(void* instructionStart)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(instructionStart);
|
|
|
|
ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui
|
|
|
|
insn++;
|
|
|
|
ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu
|
|
|
|
insn++;
|
|
|
|
*insn = 0x24000000 | ((*insn) & 0x3ffffff); // addiu
|
|
|
|
cacheFlush(insn, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update each jump in the buffer of newBase. */
|
|
|
|
void relocateJumps(void* oldBase, void* newBase)
|
|
|
|
{
|
|
|
|
// Check each jump
|
|
|
|
for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) {
|
2022-10-23 02:55:20 +00:00
|
|
|
int pos = iter->offset();
|
2017-08-12 16:48:01 +00:00
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(newBase) + pos);
|
|
|
|
insn = insn + 2;
|
|
|
|
// Need to make sure we have 5 valid instructions after pos
|
|
|
|
if ((unsigned)pos >= m_buffer.codeSize() - 5 * sizeof(MIPSWord))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((*insn & 0xfc000000) == 0x08000000) { // j
|
|
|
|
int offset = *insn & 0x03ffffff;
|
|
|
|
int oldInsnAddress = (int)insn - (int)newBase + (int)oldBase;
|
|
|
|
int topFourBits = (oldInsnAddress + 4) >> 28;
|
|
|
|
int oldTargetAddress = (topFourBits << 28) | (offset << 2);
|
|
|
|
int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase;
|
|
|
|
int newInsnAddress = (int)insn;
|
|
|
|
if (((newInsnAddress + 4) >> 28) == (newTargetAddress >> 28))
|
|
|
|
*insn = 0x08000000 | ((newTargetAddress >> 2) & 0x3ffffff);
|
|
|
|
else {
|
|
|
|
/* lui */
|
|
|
|
*insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff);
|
|
|
|
/* ori */
|
|
|
|
*(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff);
|
|
|
|
/* jr */
|
|
|
|
*(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS);
|
|
|
|
}
|
|
|
|
} else if ((*insn & 0xffe00000) == 0x3c000000) { // lui
|
|
|
|
int high = (*insn & 0xffff) << 16;
|
|
|
|
int low = *(insn + 1) & 0xffff;
|
|
|
|
int oldTargetAddress = high | low;
|
|
|
|
int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase;
|
|
|
|
/* lui */
|
|
|
|
*insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff);
|
|
|
|
/* ori */
|
|
|
|
*(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static int linkWithOffset(MIPSWord* insn, void* to)
|
|
|
|
{
|
|
|
|
ASSERT((*insn & 0xfc000000) == 0x10000000 // beq
|
|
|
|
|| (*insn & 0xfc000000) == 0x14000000 // bne
|
|
|
|
|| (*insn & 0xffff0000) == 0x45010000 // bc1t
|
|
|
|
|| (*insn & 0xffff0000) == 0x45000000); // bc1f
|
|
|
|
intptr_t diff = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(insn) - 4) >> 2;
|
|
|
|
|
|
|
|
if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) {
|
|
|
|
/*
|
|
|
|
Convert the sequence:
|
|
|
|
beq $2, $3, target
|
|
|
|
nop
|
|
|
|
b 1f
|
|
|
|
nop
|
|
|
|
nop
|
|
|
|
nop
|
|
|
|
1:
|
|
|
|
|
|
|
|
to the new sequence if possible:
|
|
|
|
bne $2, $3, 1f
|
|
|
|
nop
|
|
|
|
j target
|
|
|
|
nop
|
|
|
|
nop
|
|
|
|
nop
|
|
|
|
1:
|
|
|
|
|
|
|
|
OR to the new sequence:
|
|
|
|
bne $2, $3, 1f
|
|
|
|
nop
|
|
|
|
lui $25, target >> 16
|
|
|
|
ori $25, $25, target & 0xffff
|
|
|
|
jr $25
|
|
|
|
nop
|
|
|
|
1:
|
|
|
|
|
|
|
|
Note: beq/bne/bc1t/bc1f are converted to bne/beq/bc1f/bc1t.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (*(insn + 2) == 0x10000003) {
|
|
|
|
if ((*insn & 0xfc000000) == 0x10000000) // beq
|
|
|
|
*insn = (*insn & 0x03ff0000) | 0x14000005; // bne
|
|
|
|
else if ((*insn & 0xfc000000) == 0x14000000) // bne
|
|
|
|
*insn = (*insn & 0x03ff0000) | 0x10000005; // beq
|
|
|
|
else if ((*insn & 0xffff0000) == 0x45010000) // bc1t
|
|
|
|
*insn = 0x45000005; // bc1f
|
|
|
|
else if ((*insn & 0xffff0000) == 0x45000000) // bc1f
|
|
|
|
*insn = 0x45010005; // bc1t
|
|
|
|
else
|
|
|
|
ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
insn = insn + 2;
|
|
|
|
if ((reinterpret_cast<intptr_t>(insn) + 4) >> 28
|
|
|
|
== reinterpret_cast<intptr_t>(to) >> 28) {
|
|
|
|
*insn = 0x08000000 | ((reinterpret_cast<intptr_t>(to) >> 2) & 0x3ffffff);
|
|
|
|
*(insn + 1) = 0;
|
|
|
|
return 4 * sizeof(MIPSWord);
|
|
|
|
}
|
|
|
|
|
|
|
|
intptr_t newTargetAddress = reinterpret_cast<intptr_t>(to);
|
|
|
|
/* lui */
|
|
|
|
*insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff);
|
|
|
|
/* ori */
|
|
|
|
*(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff);
|
|
|
|
/* jr */
|
|
|
|
*(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS);
|
|
|
|
return 5 * sizeof(MIPSWord);
|
|
|
|
}
|
|
|
|
|
|
|
|
*insn = (*insn & 0xffff0000) | (diff & 0xffff);
|
|
|
|
return sizeof(MIPSWord);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int linkCallInternal(void* from, void* to)
|
|
|
|
{
|
|
|
|
MIPSWord* insn = reinterpret_cast<MIPSWord*>(from);
|
|
|
|
insn = insn - 4;
|
|
|
|
|
|
|
|
if ((*(insn + 2) & 0xfc000000) == 0x0c000000) { // jal
|
|
|
|
if ((reinterpret_cast<intptr_t>(from) - 4) >> 28
|
|
|
|
== reinterpret_cast<intptr_t>(to) >> 28) {
|
|
|
|
*(insn + 2) = 0x0c000000 | ((reinterpret_cast<intptr_t>(to) >> 2) & 0x3ffffff);
|
|
|
|
return sizeof(MIPSWord);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lui $25, (to >> 16) & 0xffff */
|
|
|
|
*insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((reinterpret_cast<intptr_t>(to) >> 16) & 0xffff);
|
|
|
|
/* ori $25, $25, to & 0xffff */
|
|
|
|
*(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (reinterpret_cast<intptr_t>(to) & 0xffff);
|
|
|
|
/* jalr $25 */
|
|
|
|
*(insn + 2) = 0x0000f809 | (MIPSRegisters::t9 << OP_SH_RS);
|
|
|
|
return 3 * sizeof(MIPSWord);
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui
|
|
|
|
ASSERT((*(insn + 1) & 0xfc000000) == 0x34000000); // ori
|
|
|
|
|
|
|
|
/* lui */
|
|
|
|
*insn = (*insn & 0xffff0000) | ((reinterpret_cast<intptr_t>(to) >> 16) & 0xffff);
|
|
|
|
/* ori */
|
|
|
|
*(insn + 1) = (*(insn + 1) & 0xffff0000) | (reinterpret_cast<intptr_t>(to) & 0xffff);
|
|
|
|
return 2 * sizeof(MIPSWord);
|
|
|
|
}
|
|
|
|
|
|
|
|
AssemblerBuffer m_buffer;
|
|
|
|
Jumps m_jumps;
|
|
|
|
int m_indexOfLastWatchpoint;
|
|
|
|
int m_indexOfTailOfLastWatchpoint;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace JSC
|
|
|
|
|
|
|
|
#endif // ENABLE(ASSEMBLER) && CPU(MIPS)
|