Bug 836486: Part 1 - Add in ARMv6 support for IonMonkey (r=jbramley)

* * *
No Bug: Fix android bustage on armv6 push (r=red, CLOSED TREE)
* * *
aaand b2g
This commit is contained in:
Marty Rosenberg 2013-03-28 13:51:23 -04:00
parent 541c7cafe0
commit 8150d9a40c
7 changed files with 248 additions and 55 deletions

View File

@ -325,29 +325,53 @@ MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
{
JS_ASSERT(input != ScratchFloatReg);
#ifdef JS_CPU_ARM
Label notSplit;
ma_vimm(0.5, ScratchFloatReg);
ma_vadd(input, ScratchFloatReg, ScratchFloatReg);
// Convert the double into an unsigned fixed point value with 24 bits of
// precision. The resulting number will look like 0xII.DDDDDD
as_vcvtFixed(ScratchFloatReg, false, 24, true);
// Move the fixed point value into an integer register
as_vxfer(output, InvalidReg, ScratchFloatReg, FloatToCore);
// See if this value *might* have been an exact integer after adding 0.5
// This tests the 1/2 through 1/16,777,216th places, but 0.5 needs to be tested out to
// the 1/140,737,488,355,328th place.
ma_tst(output, Imm32(0x00ffffff));
// convert to a uint8 by shifting out all of the fraction bits
ma_lsr(Imm32(24), output, output);
// If any of the bottom 24 bits were non-zero, then we're good, since this number
// can't be exactly XX.0
ma_b(&notSplit, NonZero);
as_vxfer(ScratchRegister, InvalidReg, input, FloatToCore);
ma_cmp(ScratchRegister, Imm32(0));
// If the lower 32 bits of the double were 0, then this was an exact number,
// and it should be even.
ma_bic(Imm32(1), output, NoSetCond, Zero);
bind(&notSplit);
if (hasVFPv3()) {
Label notSplit;
ma_vadd(input, ScratchFloatReg, ScratchFloatReg);
// Convert the double into an unsigned fixed point value with 24 bits of
// precision. The resulting number will look like 0xII.DDDDDD
as_vcvtFixed(ScratchFloatReg, false, 24, true);
// Move the fixed point value into an integer register
as_vxfer(output, InvalidReg, ScratchFloatReg, FloatToCore);
// see if this value *might* have been an exact integer after adding 0.5
// This tests the 1/2 through 1/16,777,216th places, but 0.5 needs to be tested out to
// the 1/140,737,488,355,328th place.
ma_tst(output, Imm32(0x00ffffff));
// convert to a uint8 by shifting out all of the fraction bits
ma_lsr(Imm32(24), output, output);
// If any of the bottom 24 bits were non-zero, then we're good, since this number
// can't be exactly XX.0
ma_b(&notSplit, NonZero);
as_vxfer(ScratchRegister, InvalidReg, input, FloatToCore);
ma_cmp(ScratchRegister, Imm32(0));
// If the lower 32 bits of the double were 0, then this was an exact number,
// and it should be even.
ma_bic(Imm32(1), output, NoSetCond, Zero);
bind(&notSplit);
} else {
Label outOfRange;
ma_vcmpz(input);
// do the add, in place so we can reference it later
ma_vadd(input, ScratchFloatReg, input);
// do the conversion to an integer.
as_vcvt(VFPRegister(ScratchFloatReg).uintOverlay(), VFPRegister(input));
// copy the converted value out
as_vxfer(output, InvalidReg, ScratchFloatReg, FloatToCore);
as_vmrs(pc);
ma_b(&outOfRange, Overflow);
ma_cmp(output, Imm32(0xff));
ma_mov(Imm32(0xff), output, NoSetCond, Above);
ma_b(&outOfRange, Above);
// convert it back to see if we got the same value back
as_vcvt(ScratchFloatReg, VFPRegister(ScratchFloatReg).uintOverlay());
// do the check
as_vcmp(ScratchFloatReg, input);
as_vmrs(pc);
ma_bic(Imm32(1), output, NoSetCond, Zero);
bind(&outOfRange);
}
#else
Label positive, done;

View File

@ -4,21 +4,124 @@
* 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/. */
#define HWCAP_ARMv7 (1 << 31)
#include <mozilla/StandardInteger.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
// lame check for kernel version
// see bug 586550
#if !(defined(ANDROID) || defined(MOZ_B2G))
#include <asm/hwcap.h>
#else
#define HWCAP_VFP (1<<0)
#define HWCAP_VFPv3 (1<<1)
#define HWCAP_VFPv3D16 (1<<2)
#define HWCAP_VFPv4 (1<<3)
#define HWCAP_IDIVA (1<<4)
#define HWCAP_IDIVT (1<<5)
#define HWCAP_NEON (1<<6)
#define HWCAP_ARMv7 (1<<7)
#endif
#include "ion/arm/Architecture-arm.h"
#include "ion/arm/Assembler-arm.h"
namespace js {
namespace ion {
uint32_t getFlags()
{
static bool isSet = false;
static uint32_t flags = 0;
if (isSet)
return flags;
#if WTF_OS_LINUX
int fd = open("/proc/self/auxv", O_RDONLY);
if (fd > 0) {
Elf32_auxv_t aux;
while (read(fd, &aux, sizeof(Elf32_auxv_t))) {
if (aux.a_type == AT_HWCAP) {
close(fd);
flags = aux.a_un.a_val;
isSet = true;
#ifdef __ARM_ARCH_7__
// this should really be detected at runtime, but
// /proc/*/auxv doesn't seem to carry the ISA
// I could look in /proc/cpuinfo as well, but
// the chances that it will be different from this
// are low.
flags |= HWCAP_ARMv7;
#endif
return flags;
}
}
close(fd);
}
#elif defined(WTF_OS_ANDROID) || defined(MOZ_B2G)
FILE *fp = fopen("/proc/cpuinfo", "r");
if (!fp)
return false;
char buf[1024];
fread(buf, sizeof(char), sizeof(buf), fp);
fclose(fp);
if (strstr(buf, "vfp"))
flags |= HWCAP_VFP;
if (strstr(buf, "vfpv3"))
flags |= HWCAP_VFPv3;
if (strstr(buf, "vfpv3d16"))
flags |= HWCAP_VFPv3D16;
if (strstr(buf, "vfpv4"))
flags |= HWCAP_VFPv4;
if (strstr(buf, "idiva"))
flags |= HWCAP_IDIVA;
if (strstr(buf, "idivt"))
flags |= HWCAP_IDIVT;
if (strstr(buf, "neon"))
flags |= HWCAP_NEON;
// not part of the HWCAP flag, but I need to know this, and we're not using
// that bit, so... I'm using it
if (strstr(buf, "ARMv7"))
flags |= HWCAP_ARMv7;
isSet = true;
return flags;
#endif
return false;
}
bool hasMOVWT()
{
return true;
return js::ion::getFlags() & HWCAP_ARMv7;
}
bool hasVFPv3()
{
return true;
return js::ion::getFlags() & HWCAP_VFPv3;
}
bool has32DR()
bool hasVFP()
{
return true;
return js::ion::getFlags() & HWCAP_VFP;
}
bool has32DP()
{
return !(js::ion::getFlags() & HWCAP_VFPv3D16 && !(js::ion::getFlags() & HWCAP_NEON));
}
bool useConvReg()
{
return has32DP();
}
} // namespace ion

View File

@ -16,7 +16,7 @@
namespace js {
namespace ion {
static const ptrdiff_t STACK_SLOT_SIZE = 4;
static const uint32_t STACK_SLOT_SIZE = 4;
static const uint32_t DOUBLE_STACK_ALIGNMENT = 2;
// In bytes: slots needed for potential memory->memory move spills.
@ -209,6 +209,7 @@ class FloatRegisters
bool hasMOVWT();
bool hasVFPv3();
bool hasVFP();
bool has16DP();
} // namespace ion

View File

@ -160,7 +160,7 @@ InstDTR::isTHIS(const Instruction &i)
}
InstDTR *
InstDTR::asTHIS(Instruction &i)
InstDTR::asTHIS(const Instruction &i)
{
if (isTHIS(i))
return (InstDTR*)&i;
@ -174,7 +174,7 @@ InstLDR::isTHIS(const Instruction &i)
}
InstLDR *
InstLDR::asTHIS(Instruction &i)
InstLDR::asTHIS(const Instruction &i)
{
if (isTHIS(i))
return (InstLDR*)&i;
@ -605,9 +605,20 @@ Assembler::getCF32Target(Iter *iter)
uint32_t *dest = (uint32_t*) (targ_bot.decode() | (targ_top.decode() << 16));
return dest;
}
if (inst1->is<InstLDR>()) {
JS_NOT_REACHED("ldr-based relocs NYI");
InstLDR *load = inst1->as<InstLDR>();
uint32_t inst = load->encode();
// get the address of the instruction as a raw pointer
char *dataInst = reinterpret_cast<char*>(load);
IsUp_ iu = IsUp_(inst & IsUp);
int32_t offset = inst & 0xfff;
if (iu != IsUp) {
offset = - offset;
}
uint32_t **ptr = (uint32_t **)&dataInst[offset + 8];
return *ptr;
}
JS_NOT_REACHED("unsupported branch relocation");
@ -658,7 +669,22 @@ Assembler::getPtr32Target(Iter *start, Register *dest, RelocStyle *style)
uint32_t *value = (uint32_t*) (targ_bot.decode() | (targ_top.decode() << 16));
return value;
}
if (load1->is<InstLDR>()) {
InstLDR *load = load1->as<InstLDR>();
uint32_t inst = load->encode();
// get the address of the instruction as a raw pointer
char *dataInst = reinterpret_cast<char*>(load);
IsUp_ iu = IsUp_(inst & IsUp);
int32_t offset = inst & 0xfff;
if (iu == IsDown)
offset = - offset;
if (dest)
*dest = toRD(*load);
if (style)
*style = L_LDR;
uint32_t **ptr = (uint32_t **)&dataInst[offset + 8];
return *ptr;
}
JS_NOT_REACHED("unsupported relocation");
return NULL;
}
@ -1539,6 +1565,20 @@ Assembler::as_Imm32Pool(Register dest, uint32_t value, ARMBuffer::PoolEntry *pe,
php.phd.init(0, c, PoolHintData::poolDTR, dest);
return m_buffer.insertEntry(4, (uint8_t*)&php.raw, int32Pool, (uint8_t*)&value, pe);
}
void
Assembler::as_WritePoolEntry(Instruction *addr, Condition c, uint32_t data)
{
JS_ASSERT(addr->is<InstLDR>());
int32_t offset = addr->encode() & 0xfff;
if ((addr->encode() & IsUp) != IsUp)
offset = -offset;
char * rawAddr = reinterpret_cast<char*>(addr);
uint32_t * dest = reinterpret_cast<uint32_t*>(&rawAddr[offset + 8]);
*dest = data;
Condition orig_cond;
addr->extractCond(&orig_cond);
JS_ASSERT(orig_cond == c);
}
BufferOffset
Assembler::as_BranchPool(uint32_t value, RepatchLabel *label, ARMBuffer::PoolEntry *pe, Condition c)
@ -2310,9 +2350,11 @@ Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmWord newValue, Im
const uint32_t *val = getPtr32Target(&iter, &dest, &rs);
JS_ASSERT((uint32_t)val == expectedValue.value);
reinterpret_cast<MacroAssemblerARM*>(dummy)->ma_movPatchable(Imm32(newValue.value), dest, Always, rs, ptr);
AutoFlushCache::updateTop(uintptr_t(ptr), 4);
AutoFlushCache::updateTop(uintptr_t(ptr->next()), 4);
// L_LDR won't cause any instructions to be updated.
if (rs != L_LDR) {
AutoFlushCache::updateTop(uintptr_t(ptr), 4);
AutoFlushCache::updateTop(uintptr_t(ptr->next()), 4);
}
}
// This just stomps over memory with 32 bits of raw data. Its purpose is to

View File

@ -1409,6 +1409,8 @@ class Assembler
BufferOffset as_dtm(LoadStore ls, Register rn, uint32_t mask,
DTMMode mode, DTMWriteBack wb, Condition c = Always);
//overwrite a pool entry with new data.
void as_WritePoolEntry(Instruction *addr, Condition c, uint32_t data);
// load a 32 bit immediate from a pool into a register
BufferOffset as_Imm32Pool(Register dest, uint32_t value, ARMBuffer::PoolEntry *pe = NULL, Condition c = Always);
// make a patchable jump that can target the entire 32 bit address space.
@ -1789,7 +1791,7 @@ class InstDTR : public Instruction
{ }
static bool isTHIS(const Instruction &i);
static InstDTR *asTHIS(Instruction &i);
static InstDTR *asTHIS(const Instruction &i);
};
JS_STATIC_ASSERT(sizeof(InstDTR) == sizeof(Instruction));
@ -1801,7 +1803,7 @@ class InstLDR : public InstDTR
: InstDTR(IsLoad, IsWord, mode, rt, addr, c)
{ }
static bool isTHIS(const Instruction &i);
static InstLDR *asTHIS(Instruction &i);
static InstLDR *asTHIS(const Instruction &i);
};
JS_STATIC_ASSERT(sizeof(InstDTR) == sizeof(InstLDR));

View File

@ -260,7 +260,15 @@ MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest,
if ((imm.value >> 16) != 0)
as_movt(ScratchRegister, (imm.value >> 16) & 0xffff, c);
} else {
JS_NOT_REACHED("non-ARMv7 loading of immediates NYI.");
// Going to have to use a load. If the operation is a move, then just move it into the
// destination register
if (op == op_mov) {
as_Imm32Pool(dest, imm.value, NULL, c);
return;
} else {
// If this isn't just going into a register, then stick it in a temp, and then proceed.
as_Imm32Pool(ScratchRegister, imm.value, NULL, c);
}
}
as_alu(dest, src1, O2Reg(ScratchRegister), op, sc, c);
}
@ -301,11 +309,17 @@ MacroAssemblerARM::ma_movPatchable(Imm32 imm_, Register dest,
switch(rs) {
case L_MOVWT:
as_movw(dest, Imm16(imm & 0xffff), c, i);
// i can be NULL here. that just means "insert in the next in sequence."
// NextInst is special cased to not do anything when it is passed NULL, so two
// consecutive instructions will be inserted.
i = NextInst(i);
as_movt(dest, Imm16(imm >> 16 & 0xffff), c, i);
break;
case L_LDR:
//as_Imm32Pool(dest, imm, c, i);
if(i == NULL)
as_Imm32Pool(dest, imm, NULL, c);
else
as_WritePoolEntry(i, c, imm);
break;
}
}
@ -331,7 +345,13 @@ MacroAssemblerARM::ma_mov(const ImmGCPtr &ptr, Register dest)
// As opposed to x86/x64 version, the data relocation has to be executed
// before to recover the pointer, and not after.
writeDataRelocation(ptr);
ma_movPatchable(Imm32(ptr.value), dest, Always, L_MOVWT);
RelocStyle rs;
if (hasMOVWT()) {
rs = L_MOVWT;
} else {
rs = L_LDR;
}
ma_movPatchable(Imm32(ptr.value), dest, Always, rs);
}
// Shifts (just a move with a shifting op2)
@ -1232,22 +1252,23 @@ MacroAssemblerARM::ma_vimm(double value, FloatRegister dest, Condition cc)
double d;
} dpun;
dpun.d = value;
if (hasVFPv3()) {
if (dpun.s.lo == 0) {
if (dpun.s.hi == 0) {
// To zero a register, load 1.0, then execute dN <- dN - dN
VFPImm dblEnc(0x3FF00000);
as_vimm(dest, dblEnc, cc);
as_vsub(dest, dest, dest, cc);
return;
}
VFPImm dblEnc(dpun.s.hi);
if (dblEnc.isValid()) {
as_vimm(dest, dblEnc, cc);
return;
}
if ((dpun.s.lo) == 0) {
if (dpun.s.hi == 0) {
// To zero a register, load 1.0, then execute dN <- dN - dN
VFPImm dblEnc(0x3FF00000);
as_vimm(dest, dblEnc, cc);
as_vsub(dest, dest, dest, cc);
return;
}
VFPImm dblEnc(dpun.s.hi);
if (dblEnc.isValid()) {
as_vimm(dest, dblEnc, cc);
return;
}
}
// Fall back to putting the value in a pool.
as_FImm64Pool(dest, value, NULL, cc);

View File

@ -556,7 +556,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
CodeOffsetLabel pushWithPatch(ImmWord imm) {
CodeOffsetLabel label = currentOffset();
ma_movPatchable(Imm32(imm.value), ScratchRegister, Always, L_MOVWT);
ma_movPatchable(Imm32(imm.value), ScratchRegister, Always, hasMOVWT() ? L_MOVWT : L_LDR);
ma_push(ScratchRegister);
return label;
}