ARM: Make use of d16-d31 when available.

Review URL: https://chromiumcodereview.appspot.com/11428137
Patch from Hans Wennborg <hans@chromium.org>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13484 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
ulan@chromium.org 2013-01-23 16:29:48 +00:00
parent cd21056819
commit bed569b548
32 changed files with 846 additions and 278 deletions

View File

@ -51,6 +51,13 @@
'v8_can_use_vfp2_instructions%': 'false', 'v8_can_use_vfp2_instructions%': 'false',
'v8_can_use_vfp3_instructions%': 'false', 'v8_can_use_vfp3_instructions%': 'false',
# Setting 'v8_can_use_vfp32dregs' to 'true' will cause V8 to use the VFP
# registers d16-d31 in the generated code, both in the snapshot and for the
# ARM target. Leaving the default value of 'false' will avoid the use of
# these registers in the snapshot and use CPU feature probing when running
# on the target.
'v8_can_use_vfp32dregs%': 'false',
# Similar to vfp but on MIPS. # Similar to vfp but on MIPS.
'v8_can_use_fpu_instructions%': 'true', 'v8_can_use_fpu_instructions%': 'true',
@ -178,6 +185,11 @@
'USE_EABI_HARDFLOAT=0', 'USE_EABI_HARDFLOAT=0',
], ],
}], }],
[ 'v8_can_use_vfp32dregs=="true"', {
'defines': [
'CAN_USE_VFP32DREGS',
],
}],
], ],
}], # v8_target_arch=="arm" }], # v8_target_arch=="arm"
['v8_target_arch=="ia32"', { ['v8_target_arch=="ia32"', {

View File

@ -58,7 +58,7 @@ int Register::NumAllocatableRegisters() {
int DwVfpRegister::NumRegisters() { int DwVfpRegister::NumRegisters() {
if (CpuFeatures::IsSupported(VFP2)) { if (CpuFeatures::IsSupported(VFP2)) {
return DwVfpRegister::kNumRegisters; return CpuFeatures::IsSupported(VFP32DREGS) ? 32 : 16;
} else { } else {
return 1; return 1;
} }
@ -67,7 +67,7 @@ int DwVfpRegister::NumRegisters() {
int DwVfpRegister::NumAllocatableRegisters() { int DwVfpRegister::NumAllocatableRegisters() {
if (CpuFeatures::IsSupported(VFP2)) { if (CpuFeatures::IsSupported(VFP2)) {
return DwVfpRegister::kMaxNumAllocatableRegisters; return NumRegisters() - kNumReservedRegisters;
} else { } else {
return 1; return 1;
} }
@ -77,10 +77,24 @@ int DwVfpRegister::NumAllocatableRegisters() {
int DwVfpRegister::ToAllocationIndex(DwVfpRegister reg) { int DwVfpRegister::ToAllocationIndex(DwVfpRegister reg) {
ASSERT(!reg.is(kDoubleRegZero)); ASSERT(!reg.is(kDoubleRegZero));
ASSERT(!reg.is(kScratchDoubleReg)); ASSERT(!reg.is(kScratchDoubleReg));
if (reg.code() > kDoubleRegZero.code()) {
return reg.code() - kNumReservedRegisters;
}
return reg.code(); return reg.code();
} }
DwVfpRegister DwVfpRegister::FromAllocationIndex(int index) {
ASSERT(index >= 0 && index < NumAllocatableRegisters());
ASSERT(kScratchDoubleReg.code() - kDoubleRegZero.code() ==
kNumReservedRegisters - 1);
if (index >= kDoubleRegZero.code()) {
return from_code(index + kNumReservedRegisters);
}
return from_code(index);
}
void RelocInfo::apply(intptr_t delta) { void RelocInfo::apply(intptr_t delta) {
if (RelocInfo::IsInternalReference(rmode_)) { if (RelocInfo::IsInternalReference(rmode_)) {
// absolute code pointer inside code object moves with the code object. // absolute code pointer inside code object moves with the code object.

View File

@ -51,6 +51,11 @@ unsigned CpuFeatures::supported_ = 0;
unsigned CpuFeatures::found_by_runtime_probing_ = 0; unsigned CpuFeatures::found_by_runtime_probing_ = 0;
ExternalReference ExternalReference::cpu_features() {
ASSERT(CpuFeatures::initialized_);
return ExternalReference(&CpuFeatures::supported_);
}
// Get the CPU features enabled by the build. For cross compilation the // Get the CPU features enabled by the build. For cross compilation the
// preprocessor symbols CAN_USE_ARMV7_INSTRUCTIONS and CAN_USE_VFP3_INSTRUCTIONS // preprocessor symbols CAN_USE_ARMV7_INSTRUCTIONS and CAN_USE_VFP3_INSTRUCTIONS
// can be defined to enable ARMv7 and VFPv3 instructions when building the // can be defined to enable ARMv7 and VFPv3 instructions when building the
@ -66,6 +71,9 @@ static unsigned CpuFeaturesImpliedByCompiler() {
#ifdef CAN_USE_VFP2_INSTRUCTIONS #ifdef CAN_USE_VFP2_INSTRUCTIONS
answer |= 1u << VFP2; answer |= 1u << VFP2;
#endif // CAN_USE_VFP2_INSTRUCTIONS #endif // CAN_USE_VFP2_INSTRUCTIONS
#ifdef CAN_USE_VFP32DREGS
answer |= 1u << VFP32DREGS;
#endif // CAN_USE_VFP32DREGS
#ifdef __arm__ #ifdef __arm__
// If the compiler is allowed to use VFP then we can use VFP too in our code // If the compiler is allowed to use VFP then we can use VFP too in our code
@ -87,7 +95,13 @@ static unsigned CpuFeaturesImpliedByCompiler() {
const char* DwVfpRegister::AllocationIndexToString(int index) { const char* DwVfpRegister::AllocationIndexToString(int index) {
if (CpuFeatures::IsSupported(VFP2)) { if (CpuFeatures::IsSupported(VFP2)) {
ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters); ASSERT(index >= 0 && index < NumAllocatableRegisters());
ASSERT(kScratchDoubleReg.code() - kDoubleRegZero.code() ==
kNumReservedRegisters - 1);
if (index >= kDoubleRegZero.code())
index += kNumReservedRegisters;
// TODO(hans): Maybe this could just use VFPRegisters::Name()?
const char* const names[] = { const char* const names[] = {
"d0", "d0",
"d1", "d1",
@ -103,6 +117,24 @@ const char* DwVfpRegister::AllocationIndexToString(int index) {
"d11", "d11",
"d12", "d12",
"d13" "d13"
"d14",
"d15",
"d16",
"d17",
"d18",
"d19",
"d20",
"d21",
"d22",
"d23",
"d24",
"d25",
"d26",
"d27",
"d28",
"d29",
"d30",
"d31"
}; };
return names[index]; return names[index];
} else { } else {
@ -148,6 +180,11 @@ void CpuFeatures::Probe() {
if (FLAG_enable_movw_movt) { if (FLAG_enable_movw_movt) {
supported_ |= 1u << MOVW_MOVT_IMMEDIATE_LOADS; supported_ |= 1u << MOVW_MOVT_IMMEDIATE_LOADS;
} }
if (FLAG_enable_32dregs) {
supported_ |= 1u << VFP32DREGS;
}
#else // __arm__ #else // __arm__
// Probe for additional features not already known to be available. // Probe for additional features not already known to be available.
if (!IsSupported(VFP3) && OS::ArmCpuHasFeature(VFP3)) { if (!IsSupported(VFP3) && OS::ArmCpuHasFeature(VFP3)) {
@ -176,6 +213,10 @@ void CpuFeatures::Probe() {
found_by_runtime_probing_ |= 1u << MOVW_MOVT_IMMEDIATE_LOADS; found_by_runtime_probing_ |= 1u << MOVW_MOVT_IMMEDIATE_LOADS;
} }
if (!IsSupported(VFP32DREGS) && OS::ArmCpuHasFeature(VFP32DREGS)) {
found_by_runtime_probing_ |= 1u << VFP32DREGS;
}
supported_ |= found_by_runtime_probing_; supported_ |= found_by_runtime_probing_;
#endif #endif
@ -1746,19 +1787,21 @@ void Assembler::vldr(const DwVfpRegister dst,
int offset, int offset,
const Condition cond) { const Condition cond) {
// Ddst = MEM(Rbase + offset). // Ddst = MEM(Rbase + offset).
// Instruction details available in ARM DDI 0406A, A8-628. // Instruction details available in ARM DDI 0406C.b, A8-924.
// cond(31-28) | 1101(27-24)| U001(23-20) | Rbase(19-16) | // cond(31-28) | 1101(27-24)| U(23) | D(22) | 01(21-20) | Rbase(19-16) |
// Vdst(15-12) | 1011(11-8) | offset // Vd(15-12) | 1011(11-8) | offset
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
int u = 1; int u = 1;
if (offset < 0) { if (offset < 0) {
offset = -offset; offset = -offset;
u = 0; u = 0;
} }
int vd, d;
dst.split_code(&vd, &d);
ASSERT(offset >= 0); ASSERT(offset >= 0);
if ((offset % 4) == 0 && (offset / 4) < 256) { if ((offset % 4) == 0 && (offset / 4) < 256) {
emit(cond | u*B23 | 0xD1*B20 | base.code()*B16 | dst.code()*B12 | emit(cond | 0xD*B24 | u*B23 | d*B22 | B20 | base.code()*B16 | vd*B12 |
0xB*B8 | ((offset / 4) & 255)); 0xB*B8 | ((offset / 4) & 255));
} else { } else {
// Larger offsets must be handled by computing the correct address // Larger offsets must be handled by computing the correct address
@ -1769,7 +1812,7 @@ void Assembler::vldr(const DwVfpRegister dst,
} else { } else {
sub(ip, base, Operand(offset)); sub(ip, base, Operand(offset));
} }
emit(cond | 0xD1*B20 | ip.code()*B16 | dst.code()*B12 | 0xB*B8); emit(cond | 0xD*B24 | d*B22 | B20 | ip.code()*B16 | vd*B12 | 0xB*B8);
} }
} }
@ -1832,9 +1875,9 @@ void Assembler::vstr(const DwVfpRegister src,
int offset, int offset,
const Condition cond) { const Condition cond) {
// MEM(Rbase + offset) = Dsrc. // MEM(Rbase + offset) = Dsrc.
// Instruction details available in ARM DDI 0406A, A8-786. // Instruction details available in ARM DDI 0406C.b, A8-1082.
// cond(31-28) | 1101(27-24)| U000(23-20) | | Rbase(19-16) | // cond(31-28) | 1101(27-24)| U(23) | D(22) | 00(21-20) | Rbase(19-16) |
// Vsrc(15-12) | 1011(11-8) | (offset/4) // Vd(15-12) | 1011(11-8) | (offset/4)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
int u = 1; int u = 1;
if (offset < 0) { if (offset < 0) {
@ -1842,9 +1885,12 @@ void Assembler::vstr(const DwVfpRegister src,
u = 0; u = 0;
} }
ASSERT(offset >= 0); ASSERT(offset >= 0);
int vd, d;
src.split_code(&vd, &d);
if ((offset % 4) == 0 && (offset / 4) < 256) { if ((offset % 4) == 0 && (offset / 4) < 256) {
emit(cond | u*B23 | 0xD0*B20 | base.code()*B16 | src.code()*B12 | emit(cond | 0xD*B24 | u*B23 | d*B22 | base.code()*B16 | vd*B12 | 0xB*B8 |
0xB*B8 | ((offset / 4) & 255)); ((offset / 4) & 255));
} else { } else {
// Larger offsets must be handled by computing the correct address // Larger offsets must be handled by computing the correct address
// in the ip register. // in the ip register.
@ -1854,7 +1900,7 @@ void Assembler::vstr(const DwVfpRegister src,
} else { } else {
sub(ip, base, Operand(offset)); sub(ip, base, Operand(offset));
} }
emit(cond | 0xD0*B20 | ip.code()*B16 | src.code()*B12 | 0xB*B8); emit(cond | 0xD*B24 | d*B22 | ip.code()*B16 | vd*B12 | 0xB*B8);
} }
} }
@ -1916,9 +1962,9 @@ void Assembler::vldm(BlockAddrMode am,
DwVfpRegister first, DwVfpRegister first,
DwVfpRegister last, DwVfpRegister last,
Condition cond) { Condition cond) {
// Instruction details available in ARM DDI 0406A, A8-626. // Instruction details available in ARM DDI 0406C.b, A8-922.
// cond(31-28) | 110(27-25)| PUDW1(24-20) | Rbase(19-16) | // cond(31-28) | 110(27-25)| PUDW1(24-20) | Rbase(19-16) |
// first(15-12) | 1010(11-8) | (count * 2) // first(15-12) | 1011(11-8) | (count * 2)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
ASSERT_LE(first.code(), last.code()); ASSERT_LE(first.code(), last.code());
ASSERT(am == ia || am == ia_w || am == db_w); ASSERT(am == ia || am == ia_w || am == db_w);
@ -1938,7 +1984,7 @@ void Assembler::vstm(BlockAddrMode am,
DwVfpRegister first, DwVfpRegister first,
DwVfpRegister last, DwVfpRegister last,
Condition cond) { Condition cond) {
// Instruction details available in ARM DDI 0406A, A8-784. // Instruction details available in ARM DDI 0406C.b, A8-1080.
// cond(31-28) | 110(27-25)| PUDW0(24-20) | Rbase(19-16) | // cond(31-28) | 110(27-25)| PUDW0(24-20) | Rbase(19-16) |
// first(15-12) | 1011(11-8) | (count * 2) // first(15-12) | 1011(11-8) | (count * 2)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
@ -2058,14 +2104,19 @@ void Assembler::vmov(const DwVfpRegister dst,
double imm, double imm,
const Register scratch, const Register scratch,
const Condition cond) { const Condition cond) {
// Dd = immediate
// Instruction details available in ARM DDI 0406B, A8-640.
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
uint32_t enc; uint32_t enc;
if (CpuFeatures::IsSupported(VFP3) && FitsVMOVDoubleImmediate(imm, &enc)) { if (CpuFeatures::IsSupported(VFP3) && FitsVMOVDoubleImmediate(imm, &enc)) {
// The double can be encoded in the instruction. // The double can be encoded in the instruction.
emit(cond | 0xE*B24 | 0xB*B20 | dst.code()*B12 | 0xB*B8 | enc); //
// Dd = immediate
// Instruction details available in ARM DDI 0406C.b, A8-936.
// cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | imm4H(19-16) |
// Vd(15-12) | 101(11-9) | sz=1(8) | imm4L(3-0)
int vd, d;
dst.split_code(&vd, &d);
emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | vd*B12 | 0x5*B9 | B8 | enc);
} else if (FLAG_enable_vldr_imm) { } else if (FLAG_enable_vldr_imm) {
// TODO(jfb) Temporarily turned off until we have constant blinding or // TODO(jfb) Temporarily turned off until we have constant blinding or
// some equivalent mitigation: an attacker can otherwise control // some equivalent mitigation: an attacker can otherwise control
@ -2088,20 +2139,31 @@ void Assembler::vmov(const DwVfpRegister dst,
// Synthesise the double from ARM immediates. // Synthesise the double from ARM immediates.
uint32_t lo, hi; uint32_t lo, hi;
DoubleAsTwoUInt32(imm, &lo, &hi); DoubleAsTwoUInt32(imm, &lo, &hi);
mov(ip, Operand(lo));
if (scratch.is(no_reg)) { if (scratch.is(no_reg)) {
// Move the low part of the double into the lower of the corresponsing S if (dst.code() < 16) {
// registers of D register dst. // Move the low part of the double into the lower of the corresponsing S
vmov(dst.low(), ip, cond); // registers of D register dst.
mov(ip, Operand(lo));
vmov(dst.low(), ip, cond);
// Move the high part of the double into the higher of the corresponsing S // Move the high part of the double into the higher of the
// registers of D register dst. // corresponsing S registers of D register dst.
mov(ip, Operand(hi)); mov(ip, Operand(hi));
vmov(dst.high(), ip, cond); vmov(dst.high(), ip, cond);
} else {
// D16-D31 does not have S registers, so move the low and high parts
// directly to the D register using vmov.32.
// Note: This may be slower, so we only do this when we have to.
mov(ip, Operand(lo));
vmov(dst, 0, ip, cond);
mov(ip, Operand(hi));
vmov(dst, 1, ip, cond);
}
} else { } else {
// Move the low and high parts of the double to a D register in one // Move the low and high parts of the double to a D register in one
// instruction. // instruction.
mov(ip, Operand(lo));
mov(scratch, Operand(hi)); mov(scratch, Operand(hi));
vmov(dst, ip, scratch, cond); vmov(dst, ip, scratch, cond);
} }
@ -2126,10 +2188,33 @@ void Assembler::vmov(const DwVfpRegister dst,
const DwVfpRegister src, const DwVfpRegister src,
const Condition cond) { const Condition cond) {
// Dd = Dm // Dd = Dm
// Instruction details available in ARM DDI 0406B, A8-642. // Instruction details available in ARM DDI 0406C.b, A8-938.
// cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | 0000(19-16) | Vd(15-12) |
// 101(11-9) | sz=1(8) | 0(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 | 0xB*B20 | int vd, d;
dst.code()*B12 | 0x5*B9 | B8 | B6 | src.code()); dst.split_code(&vd, &d);
int vm, m;
src.split_code(&vm, &m);
emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | vd*B12 | 0x5*B9 | B8 | B6 | m*B5 |
vm);
}
void Assembler::vmov(const DwVfpRegister dst,
int index,
const Register src,
const Condition cond) {
// Dd[index] = Rt
// Instruction details available in ARM DDI 0406C.b, A8-940.
// cond(31-28) | 1110(27-24) | 0(23) | opc1=0index(22-21) | 0(20) |
// Vd(19-16) | Rt(15-12) | 1011(11-8) | D(7) | opc2=00(6-5) | 1(4) | 0000(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2));
ASSERT(index == 0 || index == 1);
int vd, d;
dst.split_code(&vd, &d);
emit(cond | 0xE*B24 | index*B21 | vd*B16 | src.code()*B12 | 0xB*B8 | d*B7 |
B4);
} }
@ -2138,13 +2223,15 @@ void Assembler::vmov(const DwVfpRegister dst,
const Register src2, const Register src2,
const Condition cond) { const Condition cond) {
// Dm = <Rt,Rt2>. // Dm = <Rt,Rt2>.
// Instruction details available in ARM DDI 0406A, A8-646. // Instruction details available in ARM DDI 0406C.b, A8-948.
// cond(31-28) | 1100(27-24)| 010(23-21) | op=0(20) | Rt2(19-16) | // cond(31-28) | 1100(27-24)| 010(23-21) | op=0(20) | Rt2(19-16) |
// Rt(15-12) | 1011(11-8) | 00(7-6) | M(5) | 1(4) | Vm // Rt(15-12) | 1011(11-8) | 00(7-6) | M(5) | 1(4) | Vm
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
ASSERT(!src1.is(pc) && !src2.is(pc)); ASSERT(!src1.is(pc) && !src2.is(pc));
int vm, m;
dst.split_code(&vm, &m);
emit(cond | 0xC*B24 | B22 | src2.code()*B16 | emit(cond | 0xC*B24 | B22 | src2.code()*B16 |
src1.code()*B12 | 0xB*B8 | B4 | dst.code()); src1.code()*B12 | 0xB*B8 | m*B5 | B4 | vm);
} }
@ -2153,13 +2240,15 @@ void Assembler::vmov(const Register dst1,
const DwVfpRegister src, const DwVfpRegister src,
const Condition cond) { const Condition cond) {
// <Rt,Rt2> = Dm. // <Rt,Rt2> = Dm.
// Instruction details available in ARM DDI 0406A, A8-646. // Instruction details available in ARM DDI 0406C.b, A8-948.
// cond(31-28) | 1100(27-24)| 010(23-21) | op=1(20) | Rt2(19-16) | // cond(31-28) | 1100(27-24)| 010(23-21) | op=1(20) | Rt2(19-16) |
// Rt(15-12) | 1011(11-8) | 00(7-6) | M(5) | 1(4) | Vm // Rt(15-12) | 1011(11-8) | 00(7-6) | M(5) | 1(4) | Vm
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
ASSERT(!dst1.is(pc) && !dst2.is(pc)); ASSERT(!dst1.is(pc) && !dst2.is(pc));
int vm, m;
src.split_code(&vm, &m);
emit(cond | 0xC*B24 | B22 | B20 | dst2.code()*B16 | emit(cond | 0xC*B24 | B22 | B20 | dst2.code()*B16 |
dst1.code()*B12 | 0xB*B8 | B4 | src.code()); dst1.code()*B12 | 0xB*B8 | m*B5 | B4 | vm);
} }
@ -2372,18 +2461,33 @@ void Assembler::vcvt_f32_f64(const SwVfpRegister dst,
void Assembler::vneg(const DwVfpRegister dst, void Assembler::vneg(const DwVfpRegister dst,
const DwVfpRegister src, const DwVfpRegister src,
const Condition cond) { const Condition cond) {
// Instruction details available in ARM DDI 0406C.b, A8-968.
// cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | 0001(19-16) | Vd(15-12) |
// 101(11-9) | sz=1(8) | 0(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 | 0xB*B20 | B16 | dst.code()*B12 | int vd, d;
0x5*B9 | B8 | B6 | src.code()); dst.split_code(&vd, &d);
int vm, m;
src.split_code(&vm, &m);
emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | B16 | vd*B12 | 0x5*B9 | B8 | B6 |
m*B5 | vm);
} }
void Assembler::vabs(const DwVfpRegister dst, void Assembler::vabs(const DwVfpRegister dst,
const DwVfpRegister src, const DwVfpRegister src,
const Condition cond) { const Condition cond) {
// Instruction details available in ARM DDI 0406C.b, A8-524.
// cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | 0000(19-16) | Vd(15-12) |
// 101(11-9) | sz=1(8) | 1(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 | 0xB*B20 | dst.code()*B12 | int vd, d;
0x5*B9 | B8 | 0x3*B6 | src.code()); dst.split_code(&vd, &d);
int vm, m;
src.split_code(&vm, &m);
emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | vd*B12 | 0x5*B9 | B8 | B7 | B6 |
m*B5 | vm);
} }
@ -2393,12 +2497,18 @@ void Assembler::vadd(const DwVfpRegister dst,
const Condition cond) { const Condition cond) {
// Dd = vadd(Dn, Dm) double precision floating point addition. // Dd = vadd(Dn, Dm) double precision floating point addition.
// Dd = D:Vd; Dm=M:Vm; Dn=N:Vm. // Dd = D:Vd; Dm=M:Vm; Dn=N:Vm.
// Instruction details available in ARM DDI 0406A, A8-536. // Instruction details available in ARM DDI 0406C.b, A8-830.
// cond(31-28) | 11100(27-23)| D=?(22) | 11(21-20) | Vn(19-16) | // cond(31-28) | 11100(27-23)| D(22) | 11(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N(7)=0 | 0(6) | M=?(5) | 0(4) | Vm(3-0) // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 0(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 | 0x3*B20 | src1.code()*B16 | int vd, d;
dst.code()*B12 | 0x5*B9 | B8 | src2.code()); dst.split_code(&vd, &d);
int vn, n;
src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(cond | 0x1C*B23 | d*B22 | 0x3*B20 | vn*B16 | vd*B12 | 0x5*B9 | B8 |
n*B7 | m*B5 | vm);
} }
@ -2408,12 +2518,18 @@ void Assembler::vsub(const DwVfpRegister dst,
const Condition cond) { const Condition cond) {
// Dd = vsub(Dn, Dm) double precision floating point subtraction. // Dd = vsub(Dn, Dm) double precision floating point subtraction.
// Dd = D:Vd; Dm=M:Vm; Dn=N:Vm. // Dd = D:Vd; Dm=M:Vm; Dn=N:Vm.
// Instruction details available in ARM DDI 0406A, A8-784. // Instruction details available in ARM DDI 0406C.b, A8-1086.
// cond(31-28) | 11100(27-23)| D=?(22) | 11(21-20) | Vn(19-16) | // cond(31-28) | 11100(27-23)| D(22) | 11(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N(7)=0 | 1(6) | M=?(5) | 0(4) | Vm(3-0) // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 | 0x3*B20 | src1.code()*B16 | int vd, d;
dst.code()*B12 | 0x5*B9 | B8 | B6 | src2.code()); dst.split_code(&vd, &d);
int vn, n;
src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(cond | 0x1C*B23 | d*B22 | 0x3*B20 | vn*B16 | vd*B12 | 0x5*B9 | B8 |
n*B7 | B6 | m*B5 | vm);
} }
@ -2423,12 +2539,18 @@ void Assembler::vmul(const DwVfpRegister dst,
const Condition cond) { const Condition cond) {
// Dd = vmul(Dn, Dm) double precision floating point multiplication. // Dd = vmul(Dn, Dm) double precision floating point multiplication.
// Dd = D:Vd; Dm=M:Vm; Dn=N:Vm. // Dd = D:Vd; Dm=M:Vm; Dn=N:Vm.
// Instruction details available in ARM DDI 0406A, A8-784. // Instruction details available in ARM DDI 0406C.b, A8-960.
// cond(31-28) | 11100(27-23)| D=?(22) | 10(21-20) | Vn(19-16) | // cond(31-28) | 11100(27-23)| D(22) | 10(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N(7)=0 | 0(6) | M=?(5) | 0(4) | Vm(3-0) // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 0(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 | 0x2*B20 | src1.code()*B16 | int vd, d;
dst.code()*B12 | 0x5*B9 | B8 | src2.code()); dst.split_code(&vd, &d);
int vn, n;
src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(cond | 0x1C*B23 | d*B22 | 0x2*B20 | vn*B16 | vd*B12 | 0x5*B9 | B8 |
n*B7 | m*B5 | vm);
} }
@ -2436,13 +2558,17 @@ void Assembler::vmla(const DwVfpRegister dst,
const DwVfpRegister src1, const DwVfpRegister src1,
const DwVfpRegister src2, const DwVfpRegister src2,
const Condition cond) { const Condition cond) {
// Instruction details available in ARM DDI 0406C.b, A8-892. // Instruction details available in ARM DDI 0406C.b, A8-932.
// cond(31-28) | 11100(27-23) | D=?(22) | 00(21-20) | Vn(19-16) | // cond(31-28) | 11100(27-23) | D(22) | 00(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N=?(7) | op(6)=0 | M=?(5) | 0(4) | // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | op=0(6) | M(5) | 0(4) | Vm(3-0)
// Vm(3-0) int vd, d;
unsigned x = (cond | 0x1C*B23 | src1.code()*B16 | dst.split_code(&vd, &d);
dst.code()*B12 | 0x5*B9 | B8 | src2.code()); int vn, n;
emit(x); src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(cond | 0x1C*B23 | d*B22 | vn*B16 | vd*B12 | 0x5*B9 | B8 | n*B7 | m*B5 |
vm);
} }
@ -2452,12 +2578,18 @@ void Assembler::vdiv(const DwVfpRegister dst,
const Condition cond) { const Condition cond) {
// Dd = vdiv(Dn, Dm) double precision floating point division. // Dd = vdiv(Dn, Dm) double precision floating point division.
// Dd = D:Vd; Dm=M:Vm; Dn=N:Vm. // Dd = D:Vd; Dm=M:Vm; Dn=N:Vm.
// Instruction details available in ARM DDI 0406A, A8-584. // Instruction details available in ARM DDI 0406C.b, A8-882.
// cond(31-28) | 11101(27-23)| D=?(22) | 00(21-20) | Vn(19-16) | // cond(31-28) | 11101(27-23)| D(22) | 00(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N(7)=? | 0(6) | M=?(5) | 0(4) | Vm(3-0) // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 0(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 | B23 | src1.code()*B16 | int vd, d;
dst.code()*B12 | 0x5*B9 | B8 | src2.code()); dst.split_code(&vd, &d);
int vn, n;
src1.split_code(&vn, &n);
int vm, m;
src2.split_code(&vm, &m);
emit(cond | 0x1D*B23 | d*B22 | vn*B16 | vd*B12 | 0x5*B9 | B8 | n*B7 | m*B5 |
vm);
} }
@ -2465,26 +2597,31 @@ void Assembler::vcmp(const DwVfpRegister src1,
const DwVfpRegister src2, const DwVfpRegister src2,
const Condition cond) { const Condition cond) {
// vcmp(Dd, Dm) double precision floating point comparison. // vcmp(Dd, Dm) double precision floating point comparison.
// Instruction details available in ARM DDI 0406A, A8-570. // Instruction details available in ARM DDI 0406C.b, A8-864.
// cond(31-28) | 11101 (27-23)| D=?(22) | 11 (21-20) | 0100 (19-16) | // cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 0100(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | E(7)=0 | 1(6) | M(5)=? | 0(4) | Vm(3-0) // Vd(15-12) | 101(11-9) | sz=1(8) | E=0(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 |B23 | 0x3*B20 | B18 | int vd, d;
src1.code()*B12 | 0x5*B9 | B8 | B6 | src2.code()); src1.split_code(&vd, &d);
int vm, m;
src2.split_code(&vm, &m);
emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | 0x4*B16 | vd*B12 | 0x5*B9 | B8 | B6 |
m*B5 | vm);
} }
void Assembler::vcmp(const DwVfpRegister src1, void Assembler::vcmp(const DwVfpRegister src1,
const double src2, const double src2,
const Condition cond) { const Condition cond) {
// vcmp(Dd, Dm) double precision floating point comparison. // vcmp(Dd, #0.0) double precision floating point comparison.
// Instruction details available in ARM DDI 0406A, A8-570. // Instruction details available in ARM DDI 0406C.b, A8-864.
// cond(31-28) | 11101 (27-23)| D=?(22) | 11 (21-20) | 0101 (19-16) | // cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 0101(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | E(7)=0 | 1(6) | M(5)=? | 0(4) | 0000(3-0) // Vd(15-12) | 101(11-9) | sz=1(8) | E=0(7) | 1(6) | 0(5) | 0(4) | 0000(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
ASSERT(src2 == 0.0); ASSERT(src2 == 0.0);
emit(cond | 0xE*B24 |B23 | 0x3*B20 | B18 | B16 | int vd, d;
src1.code()*B12 | 0x5*B9 | B8 | B6); src1.split_code(&vd, &d);
emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | 0x5*B16 | vd*B12 | 0x5*B9 | B8 | B6);
} }
@ -2511,11 +2648,16 @@ void Assembler::vmrs(Register dst, Condition cond) {
void Assembler::vsqrt(const DwVfpRegister dst, void Assembler::vsqrt(const DwVfpRegister dst,
const DwVfpRegister src, const DwVfpRegister src,
const Condition cond) { const Condition cond) {
// cond(31-28) | 11101 (27-23)| D=?(22) | 11 (21-20) | 0001 (19-16) | // Instruction details available in ARM DDI 0406C.b, A8-1058.
// Vd(15-12) | 101(11-9) | sz(8)=1 | 11 (7-6) | M(5)=? | 0(4) | Vm(3-0) // cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 0001(19-16) |
// Vd(15-12) | 101(11-9) | sz=1(8) | 11(7-6) | M(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP2)); ASSERT(CpuFeatures::IsEnabled(VFP2));
emit(cond | 0xE*B24 | B23 | 0x3*B20 | B16 | int vd, d;
dst.code()*B12 | 0x5*B9 | B8 | 3*B6 | src.code()); dst.split_code(&vd, &d);
int vm, m;
src.split_code(&vm, &m);
emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | B16 | vd*B12 | 0x5*B9 | B8 | 0x3*B6 |
m*B5 | vm);
} }

View File

@ -47,6 +47,116 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
// CpuFeatures keeps track of which features are supported by the target CPU.
// Supported features must be enabled by a Scope before use.
class CpuFeatures : public AllStatic {
public:
// Detect features of the target CPU. Set safe defaults if the serializer
// is enabled (snapshots must be portable).
static void Probe();
// Check whether a feature is supported by the target CPU.
static bool IsSupported(CpuFeature f) {
ASSERT(initialized_);
if (f == VFP3 && !FLAG_enable_vfp3) return false;
if (f == VFP2 && !FLAG_enable_vfp2) return false;
if (f == SUDIV && !FLAG_enable_sudiv) return false;
if (f == UNALIGNED_ACCESSES && !FLAG_enable_unaligned_accesses) {
return false;
}
if (f == VFP32DREGS && !FLAG_enable_32dregs) return false;
return (supported_ & (1u << f)) != 0;
}
#ifdef DEBUG
// Check whether a feature is currently enabled.
static bool IsEnabled(CpuFeature f) {
ASSERT(initialized_);
Isolate* isolate = Isolate::UncheckedCurrent();
if (isolate == NULL) {
// When no isolate is available, work as if we're running in
// release mode.
return IsSupported(f);
}
unsigned enabled = static_cast<unsigned>(isolate->enabled_cpu_features());
return (enabled & (1u << f)) != 0;
}
#endif
// Enable a specified feature within a scope.
class Scope BASE_EMBEDDED {
#ifdef DEBUG
public:
explicit Scope(CpuFeature f) {
unsigned mask = 1u << f;
// VFP2 and ARMv7 are implied by VFP3.
if (f == VFP3) mask |= 1u << VFP2 | 1u << ARMv7;
ASSERT(CpuFeatures::IsSupported(f));
ASSERT(!Serializer::enabled() ||
(CpuFeatures::found_by_runtime_probing_ & mask) == 0);
isolate_ = Isolate::UncheckedCurrent();
old_enabled_ = 0;
if (isolate_ != NULL) {
old_enabled_ = static_cast<unsigned>(isolate_->enabled_cpu_features());
isolate_->set_enabled_cpu_features(old_enabled_ | mask);
}
}
~Scope() {
ASSERT_EQ(Isolate::UncheckedCurrent(), isolate_);
if (isolate_ != NULL) {
isolate_->set_enabled_cpu_features(old_enabled_);
}
}
private:
Isolate* isolate_;
unsigned old_enabled_;
#else
public:
explicit Scope(CpuFeature f) {}
#endif
};
class TryForceFeatureScope BASE_EMBEDDED {
public:
explicit TryForceFeatureScope(CpuFeature f)
: old_supported_(CpuFeatures::supported_) {
if (CanForce()) {
CpuFeatures::supported_ |= (1u << f);
}
}
~TryForceFeatureScope() {
if (CanForce()) {
CpuFeatures::supported_ = old_supported_;
}
}
private:
static bool CanForce() {
// It's only safe to temporarily force support of CPU features
// when there's only a single isolate, which is guaranteed when
// the serializer is enabled.
return Serializer::enabled();
}
const unsigned old_supported_;
};
private:
#ifdef DEBUG
static bool initialized_;
#endif
static unsigned supported_;
static unsigned found_by_runtime_probing_;
friend class ExternalReference;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
};
// CPU Registers. // CPU Registers.
// //
// 1) We would prefer to use an enum, but enum values are assignment- // 1) We would prefer to use an enum, but enum values are assignment-
@ -192,7 +302,7 @@ struct SwVfpRegister {
// Double word VFP register. // Double word VFP register.
struct DwVfpRegister { struct DwVfpRegister {
static const int kNumRegisters = 16; static const int kNumRegisters = 32;
// A few double registers are reserved: one as a scratch register and one to // A few double registers are reserved: one as a scratch register and one to
// hold 0.0, that does not fit in the immediate field of vmov instructions. // hold 0.0, that does not fit in the immediate field of vmov instructions.
// d14: 0.0 // d14: 0.0
@ -201,25 +311,27 @@ struct DwVfpRegister {
static const int kMaxNumAllocatableRegisters = kNumRegisters - static const int kMaxNumAllocatableRegisters = kNumRegisters -
kNumReservedRegisters; kNumReservedRegisters;
// Note: the number of registers can be different at snapshot and run-time.
// Any code included in the snapshot must be able to run both with 16 or 32
// registers.
inline static int NumRegisters(); inline static int NumRegisters();
inline static int NumAllocatableRegisters(); inline static int NumAllocatableRegisters();
inline static int ToAllocationIndex(DwVfpRegister reg); inline static int ToAllocationIndex(DwVfpRegister reg);
static const char* AllocationIndexToString(int index); static const char* AllocationIndexToString(int index);
inline static DwVfpRegister FromAllocationIndex(int index);
static DwVfpRegister FromAllocationIndex(int index) {
ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
return from_code(index);
}
static DwVfpRegister from_code(int code) { static DwVfpRegister from_code(int code) {
DwVfpRegister r = { code }; DwVfpRegister r = { code };
return r; return r;
} }
// Supporting d0 to d15, can be later extended to d31. bool is_valid() const {
bool is_valid() const { return 0 <= code_ && code_ < 16; } return 0 <= code_ && code_ < kNumRegisters;
}
bool is(DwVfpRegister reg) const { return code_ == reg.code_; } bool is(DwVfpRegister reg) const { return code_ == reg.code_; }
SwVfpRegister low() const { SwVfpRegister low() const {
ASSERT(code_ < 16);
SwVfpRegister reg; SwVfpRegister reg;
reg.code_ = code_ * 2; reg.code_ = code_ * 2;
@ -227,6 +339,7 @@ struct DwVfpRegister {
return reg; return reg;
} }
SwVfpRegister high() const { SwVfpRegister high() const {
ASSERT(code_ < 16);
SwVfpRegister reg; SwVfpRegister reg;
reg.code_ = (code_ * 2) + 1; reg.code_ = (code_ * 2) + 1;
@ -306,6 +419,22 @@ const DwVfpRegister d12 = { 12 };
const DwVfpRegister d13 = { 13 }; const DwVfpRegister d13 = { 13 };
const DwVfpRegister d14 = { 14 }; const DwVfpRegister d14 = { 14 };
const DwVfpRegister d15 = { 15 }; const DwVfpRegister d15 = { 15 };
const DwVfpRegister d16 = { 16 };
const DwVfpRegister d17 = { 17 };
const DwVfpRegister d18 = { 18 };
const DwVfpRegister d19 = { 19 };
const DwVfpRegister d20 = { 20 };
const DwVfpRegister d21 = { 21 };
const DwVfpRegister d22 = { 22 };
const DwVfpRegister d23 = { 23 };
const DwVfpRegister d24 = { 24 };
const DwVfpRegister d25 = { 25 };
const DwVfpRegister d26 = { 26 };
const DwVfpRegister d27 = { 27 };
const DwVfpRegister d28 = { 28 };
const DwVfpRegister d29 = { 29 };
const DwVfpRegister d30 = { 30 };
const DwVfpRegister d31 = { 31 };
const Register sfpd_lo = { kRegister_r6_Code }; const Register sfpd_lo = { kRegister_r6_Code };
const Register sfpd_hi = { kRegister_r7_Code }; const Register sfpd_hi = { kRegister_r7_Code };
@ -485,114 +614,6 @@ class MemOperand BASE_EMBEDDED {
friend class Assembler; friend class Assembler;
}; };
// CpuFeatures keeps track of which features are supported by the target CPU.
// Supported features must be enabled by a Scope before use.
class CpuFeatures : public AllStatic {
public:
// Detect features of the target CPU. Set safe defaults if the serializer
// is enabled (snapshots must be portable).
static void Probe();
// Check whether a feature is supported by the target CPU.
static bool IsSupported(CpuFeature f) {
ASSERT(initialized_);
if (f == VFP3 && !FLAG_enable_vfp3) return false;
if (f == VFP2 && !FLAG_enable_vfp2) return false;
if (f == SUDIV && !FLAG_enable_sudiv) return false;
if (f == UNALIGNED_ACCESSES && !FLAG_enable_unaligned_accesses) {
return false;
}
return (supported_ & (1u << f)) != 0;
}
#ifdef DEBUG
// Check whether a feature is currently enabled.
static bool IsEnabled(CpuFeature f) {
ASSERT(initialized_);
Isolate* isolate = Isolate::UncheckedCurrent();
if (isolate == NULL) {
// When no isolate is available, work as if we're running in
// release mode.
return IsSupported(f);
}
unsigned enabled = static_cast<unsigned>(isolate->enabled_cpu_features());
return (enabled & (1u << f)) != 0;
}
#endif
// Enable a specified feature within a scope.
class Scope BASE_EMBEDDED {
#ifdef DEBUG
public:
explicit Scope(CpuFeature f) {
unsigned mask = 1u << f;
// VFP2 and ARMv7 are implied by VFP3.
if (f == VFP3) mask |= 1u << VFP2 | 1u << ARMv7;
ASSERT(CpuFeatures::IsSupported(f));
ASSERT(!Serializer::enabled() ||
(CpuFeatures::found_by_runtime_probing_ & mask) == 0);
isolate_ = Isolate::UncheckedCurrent();
old_enabled_ = 0;
if (isolate_ != NULL) {
old_enabled_ = static_cast<unsigned>(isolate_->enabled_cpu_features());
isolate_->set_enabled_cpu_features(old_enabled_ | mask);
}
}
~Scope() {
ASSERT_EQ(Isolate::UncheckedCurrent(), isolate_);
if (isolate_ != NULL) {
isolate_->set_enabled_cpu_features(old_enabled_);
}
}
private:
Isolate* isolate_;
unsigned old_enabled_;
#else
public:
explicit Scope(CpuFeature f) {}
#endif
};
class TryForceFeatureScope BASE_EMBEDDED {
public:
explicit TryForceFeatureScope(CpuFeature f)
: old_supported_(CpuFeatures::supported_) {
if (CanForce()) {
CpuFeatures::supported_ |= (1u << f);
}
}
~TryForceFeatureScope() {
if (CanForce()) {
CpuFeatures::supported_ = old_supported_;
}
}
private:
static bool CanForce() {
// It's only safe to temporarily force support of CPU features
// when there's only a single isolate, which is guaranteed when
// the serializer is enabled.
return Serializer::enabled();
}
const unsigned old_supported_;
};
private:
#ifdef DEBUG
static bool initialized_;
#endif
static unsigned supported_;
static unsigned found_by_runtime_probing_;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
};
extern const Instr kMovLrPc; extern const Instr kMovLrPc;
extern const Instr kLdrPCMask; extern const Instr kLdrPCMask;
extern const Instr kLdrPCPattern; extern const Instr kLdrPCPattern;
@ -981,10 +1002,7 @@ class Assembler : public AssemblerBase {
LFlag l = Short); // v5 and above LFlag l = Short); // v5 and above
// Support for VFP. // Support for VFP.
// All these APIs support S0 to S31 and D0 to D15. // All these APIs support S0 to S31 and D0 to D31.
// Currently these APIs do not support extended D registers, i.e, D16 to D31.
// However, some simple modifications can allow
// these APIs to support D16 to D31.
void vldr(const DwVfpRegister dst, void vldr(const DwVfpRegister dst,
const Register base, const Register base,
@ -1052,6 +1070,10 @@ class Assembler : public AssemblerBase {
void vmov(const DwVfpRegister dst, void vmov(const DwVfpRegister dst,
const DwVfpRegister src, const DwVfpRegister src,
const Condition cond = al); const Condition cond = al);
void vmov(const DwVfpRegister dst,
int index,
const Register src,
const Condition cond = al);
void vmov(const DwVfpRegister dst, void vmov(const DwVfpRegister dst,
const Register src1, const Register src1,
const Register src2, const Register src2,

View File

@ -2067,17 +2067,22 @@ void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
// store the registers in any particular way, but we do have to store and // store the registers in any particular way, but we do have to store and
// restore them. // restore them.
__ stm(db_w, sp, kCallerSaved | lr.bit()); __ stm(db_w, sp, kCallerSaved | lr.bit());
const Register scratch = r1;
if (save_doubles_ == kSaveFPRegs) { if (save_doubles_ == kSaveFPRegs) {
CpuFeatures::Scope scope(VFP2); CpuFeatures::Scope scope(VFP2);
// Check CPU flags for number of registers, setting the Z condition flag.
__ CheckFor32DRegs(scratch);
__ sub(sp, sp, Operand(kDoubleSize * DwVfpRegister::kNumRegisters)); __ sub(sp, sp, Operand(kDoubleSize * DwVfpRegister::kNumRegisters));
for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) { for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) {
DwVfpRegister reg = DwVfpRegister::from_code(i); DwVfpRegister reg = DwVfpRegister::from_code(i);
__ vstr(reg, MemOperand(sp, i * kDoubleSize)); __ vstr(reg, MemOperand(sp, i * kDoubleSize), i < 16 ? al : ne);
} }
} }
const int argument_count = 1; const int argument_count = 1;
const int fp_argument_count = 0; const int fp_argument_count = 0;
const Register scratch = r1;
AllowExternalCallThatCantCauseGC scope(masm); AllowExternalCallThatCantCauseGC scope(masm);
__ PrepareCallCFunction(argument_count, fp_argument_count, scratch); __ PrepareCallCFunction(argument_count, fp_argument_count, scratch);
@ -2087,9 +2092,13 @@ void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
argument_count); argument_count);
if (save_doubles_ == kSaveFPRegs) { if (save_doubles_ == kSaveFPRegs) {
CpuFeatures::Scope scope(VFP2); CpuFeatures::Scope scope(VFP2);
// Check CPU flags for number of registers, setting the Z condition flag.
__ CheckFor32DRegs(scratch);
for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) { for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) {
DwVfpRegister reg = DwVfpRegister::from_code(i); DwVfpRegister reg = DwVfpRegister::from_code(i);
__ vldr(reg, MemOperand(sp, i * kDoubleSize)); __ vldr(reg, MemOperand(sp, i * kDoubleSize), i < 16 ? al : ne);
} }
__ add(sp, sp, Operand(kDoubleSize * DwVfpRegister::kNumRegisters)); __ add(sp, sp, Operand(kDoubleSize * DwVfpRegister::kNumRegisters));
} }

View File

@ -469,12 +469,15 @@ class RecordWriteStub: public PlatformCodeStub {
void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
masm->stm(db_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit()); masm->stm(db_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit());
if (mode == kSaveFPRegs) { if (mode == kSaveFPRegs) {
// Number of d-regs not known at snapshot time.
ASSERT(!Serializer::enabled());
CpuFeatures::Scope scope(VFP2); CpuFeatures::Scope scope(VFP2);
masm->sub(sp, masm->sub(sp,
sp, sp,
Operand(kDoubleSize * (DwVfpRegister::kNumRegisters - 1))); Operand(kDoubleSize * (DwVfpRegister::NumRegisters() - 1)));
// Save all VFP registers except d0. // Save all VFP registers except d0.
for (int i = DwVfpRegister::kNumRegisters - 1; i > 0; i--) { // TODO(hans): We should probably save d0 too. And maybe use vstm.
for (int i = DwVfpRegister::NumRegisters() - 1; i > 0; i--) {
DwVfpRegister reg = DwVfpRegister::from_code(i); DwVfpRegister reg = DwVfpRegister::from_code(i);
masm->vstr(reg, MemOperand(sp, (i - 1) * kDoubleSize)); masm->vstr(reg, MemOperand(sp, (i - 1) * kDoubleSize));
} }
@ -484,15 +487,18 @@ class RecordWriteStub: public PlatformCodeStub {
inline void RestoreCallerSaveRegisters(MacroAssembler*masm, inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
SaveFPRegsMode mode) { SaveFPRegsMode mode) {
if (mode == kSaveFPRegs) { if (mode == kSaveFPRegs) {
// Number of d-regs not known at snapshot time.
ASSERT(!Serializer::enabled());
CpuFeatures::Scope scope(VFP2); CpuFeatures::Scope scope(VFP2);
// Restore all VFP registers except d0. // Restore all VFP registers except d0.
for (int i = DwVfpRegister::kNumRegisters - 1; i > 0; i--) { // TODO(hans): We should probably restore d0 too. And maybe use vldm.
for (int i = DwVfpRegister::NumRegisters() - 1; i > 0; i--) {
DwVfpRegister reg = DwVfpRegister::from_code(i); DwVfpRegister reg = DwVfpRegister::from_code(i);
masm->vldr(reg, MemOperand(sp, (i - 1) * kDoubleSize)); masm->vldr(reg, MemOperand(sp, (i - 1) * kDoubleSize));
} }
masm->add(sp, masm->add(sp,
sp, sp,
Operand(kDoubleSize * (DwVfpRegister::kNumRegisters - 1))); Operand(kDoubleSize * (DwVfpRegister::NumRegisters() - 1)));
} }
masm->ldm(ia_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit()); masm->ldm(ia_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit());
} }

View File

@ -87,8 +87,8 @@ const char* Registers::Name(int reg) {
} }
// Support for VFP registers s0 to s31 (d0 to d15). // Support for VFP registers s0 to s31 (d0 to d15) and d16-d31.
// Note that "sN:sM" is the same as "dN/2" // Note that "sN:sM" is the same as "dN/2" up to d15.
// These register names are defined in a way to match the native disassembler // These register names are defined in a way to match the native disassembler
// formatting. See for example the command "objdump -d <binary file>". // formatting. See for example the command "objdump -d <binary file>".
const char* VFPRegisters::names_[kNumVFPRegisters] = { const char* VFPRegisters::names_[kNumVFPRegisters] = {
@ -97,7 +97,9 @@ const char* VFPRegisters::names_[kNumVFPRegisters] = {
"s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
"s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
"d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15" "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
"d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
}; };

View File

@ -102,7 +102,7 @@ const int kNumRegisters = 16;
// VFP support. // VFP support.
const int kNumVFPSingleRegisters = 32; const int kNumVFPSingleRegisters = 32;
const int kNumVFPDoubleRegisters = 16; const int kNumVFPDoubleRegisters = 32;
const int kNumVFPRegisters = kNumVFPSingleRegisters + kNumVFPDoubleRegisters; const int kNumVFPRegisters = kNumVFPSingleRegisters + kNumVFPDoubleRegisters;
// PC is register 15. // PC is register 15.

View File

@ -981,22 +981,17 @@ void Deoptimizer::EntryGenerator::Generate() {
if (CpuFeatures::IsSupported(VFP2)) { if (CpuFeatures::IsSupported(VFP2)) {
CpuFeatures::Scope scope(VFP2); CpuFeatures::Scope scope(VFP2);
// Save all VFP registers before messing with them. // Save all allocatable VFP registers before messing with them.
DwVfpRegister first = DwVfpRegister::FromAllocationIndex(0); ASSERT(kDoubleRegZero.code() == 14);
DwVfpRegister last = ASSERT(kScratchDoubleReg.code() == 15);
DwVfpRegister::FromAllocationIndex(
DwVfpRegister::kMaxNumAllocatableRegisters - 1); // Check CPU flags for number of registers, setting the Z condition flag.
ASSERT(last.code() > first.code()); __ CheckFor32DRegs(ip);
ASSERT((last.code() - first.code()) ==
(DwVfpRegister::kMaxNumAllocatableRegisters - 1)); // Push registers d0-d13, and possibly d16-d31, on the stack.
#ifdef DEBUG __ vstm(db_w, sp, d16, d31, ne);
int max = DwVfpRegister::kMaxNumAllocatableRegisters - 1; __ sub(sp, sp, Operand(16 * kDoubleSize), LeaveCC, eq);
for (int i = 0; i <= max; i++) { __ vstm(db_w, sp, d0, d13);
ASSERT((DwVfpRegister::FromAllocationIndex(i).code() <= last.code()) &&
(DwVfpRegister::FromAllocationIndex(i).code() >= first.code()));
}
#endif
__ vstm(db_w, sp, first, last);
} else { } else {
__ sub(sp, sp, Operand(kDoubleRegsSize)); __ sub(sp, sp, Operand(kDoubleRegsSize));
} }
@ -1063,7 +1058,7 @@ void Deoptimizer::EntryGenerator::Generate() {
// Copy VFP registers to // Copy VFP registers to
// double_registers_[DoubleRegister::kMaxNumAllocatableRegisters] // double_registers_[DoubleRegister::kMaxNumAllocatableRegisters]
int double_regs_offset = FrameDescription::double_registers_offset(); int double_regs_offset = FrameDescription::double_registers_offset();
for (int i = 0; i < DwVfpRegister::NumAllocatableRegisters(); ++i) { for (int i = 0; i < DwVfpRegister::kMaxNumAllocatableRegisters; ++i) {
int dst_offset = i * kDoubleSize + double_regs_offset; int dst_offset = i * kDoubleSize + double_regs_offset;
int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize; int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
__ vldr(d0, sp, src_offset); __ vldr(d0, sp, src_offset);
@ -1111,6 +1106,11 @@ void Deoptimizer::EntryGenerator::Generate() {
} }
__ pop(r0); // Restore deoptimizer object (class Deoptimizer). __ pop(r0); // Restore deoptimizer object (class Deoptimizer).
// TODO(hans): Change the code below to not clobber r0, so that it can be
// used in the "restore the d registers" code further down, making this mov
// redundant.
__ mov(r4, r0);
// Replace the current (input) frame with the output frames. // Replace the current (input) frame with the output frames.
Label outer_push_loop, inner_push_loop, Label outer_push_loop, inner_push_loop,
outer_loop_header, inner_loop_header; outer_loop_header, inner_loop_header;
@ -1138,6 +1138,26 @@ void Deoptimizer::EntryGenerator::Generate() {
__ cmp(r0, r1); __ cmp(r0, r1);
__ b(lt, &outer_push_loop); __ b(lt, &outer_push_loop);
if (CpuFeatures::IsSupported(VFP2)) {
CpuFeatures::Scope scope(VFP2);
// In case of OSR, we have to restore the d registers.
if (type() == OSR) {
// Check CPU flags for number of registers, setting the Z condition flag.
__ CheckFor32DRegs(ip);
__ ldr(r1, MemOperand(r4, Deoptimizer::input_offset()));
int src_offset = FrameDescription::double_registers_offset();
for (int i = 0; i < DwVfpRegister::kNumRegisters; ++i) {
if (i == kDoubleRegZero.code()) continue;
if (i == kScratchDoubleReg.code()) continue;
const DwVfpRegister reg = DwVfpRegister::from_code(i);
__ vldr(reg, r1, src_offset, i < 16 ? al : ne);
src_offset += kDoubleSize;
}
}
}
// Push state, pc, and continuation from the last output frame. // Push state, pc, and continuation from the last output frame.
if (type() != OSR) { if (type() != OSR) {
__ ldr(r6, MemOperand(r2, FrameDescription::state_offset())); __ ldr(r6, MemOperand(r2, FrameDescription::state_offset()));

View File

@ -192,7 +192,7 @@ void Decoder::PrintSRegister(int reg) {
Print(VFPRegisters::Name(reg, false)); Print(VFPRegisters::Name(reg, false));
} }
// Print the VFP D register name according to the active name converter. // Print the VFP D register name according to the active name converter.
void Decoder::PrintDRegister(int reg) { void Decoder::PrintDRegister(int reg) {
Print(VFPRegisters::Name(reg, true)); Print(VFPRegisters::Name(reg, true));
} }
@ -381,7 +381,16 @@ int Decoder::FormatVFPRegister(Instruction* instr, const char* format) {
} else if (format[1] == 'm') { } else if (format[1] == 'm') {
reg = instr->VFPMRegValue(precision); reg = instr->VFPMRegValue(precision);
} else if (format[1] == 'd') { } else if (format[1] == 'd') {
reg = instr->VFPDRegValue(precision); if ((instr->TypeValue() == 7) &&
(instr->Bit(24) == 0x0) &&
(instr->Bits(11, 9) == 0x5) &&
(instr->Bit(4) == 0x1)) {
// vmov.32 has Vd in a different place.
reg = instr->Bits(19, 16) | (instr->Bit(7) << 4);
} else {
reg = instr->VFPDRegValue(precision);
}
if (format[2] == '+') { if (format[2] == '+') {
int immed8 = instr->Immed8Value(); int immed8 = instr->Immed8Value();
if (format[0] == 'S') reg += immed8 - 1; if (format[0] == 'S') reg += immed8 - 1;
@ -1180,6 +1189,14 @@ void Decoder::DecodeTypeVFP(Instruction* instr) {
if ((instr->VCValue() == 0x0) && if ((instr->VCValue() == 0x0) &&
(instr->VAValue() == 0x0)) { (instr->VAValue() == 0x0)) {
DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr);
} else if ((instr->VLValue() == 0x0) &&
(instr->VCValue() == 0x1) &&
(instr->Bit(23) == 0x0)) {
if (instr->Bit(21) == 0x0) {
Format(instr, "vmov.32'cond 'Dd[0], 'rt");
} else {
Format(instr, "vmov.32'cond 'Dd[1], 'rt");
}
} else if ((instr->VCValue() == 0x0) && } else if ((instr->VCValue() == 0x0) &&
(instr->VAValue() == 0x7) && (instr->VAValue() == 0x7) &&
(instr->Bits(19, 16) == 0x1)) { (instr->Bits(19, 16) == 0x1)) {
@ -1343,7 +1360,7 @@ void Decoder::DecodeType6CoprocessorIns(Instruction* instr) {
switch (instr->OpcodeValue()) { switch (instr->OpcodeValue()) {
case 0x2: case 0x2:
// Load and store double to two GP registers // Load and store double to two GP registers
if (instr->Bits(7, 4) != 0x1) { if (instr->Bits(7, 6) != 0 || instr->Bit(4) != 1) {
Unknown(instr); // Not used by V8. Unknown(instr); // Not used by V8.
} else if (instr->HasL()) { } else if (instr->HasL()) {
Format(instr, "vmov'cond 'rt, 'rn, 'Dm"); Format(instr, "vmov'cond 'rt, 'rn, 'Dm");
@ -1352,6 +1369,7 @@ void Decoder::DecodeType6CoprocessorIns(Instruction* instr) {
} }
break; break;
case 0x8: case 0x8:
case 0xA:
if (instr->HasL()) { if (instr->HasL()) {
Format(instr, "vldr'cond 'Dd, ['rn - 4*'imm08@00]"); Format(instr, "vldr'cond 'Dd, ['rn - 4*'imm08@00]");
} else { } else {
@ -1359,6 +1377,7 @@ void Decoder::DecodeType6CoprocessorIns(Instruction* instr) {
} }
break; break;
case 0xC: case 0xC:
case 0xE:
if (instr->HasL()) { if (instr->HasL()) {
Format(instr, "vldr'cond 'Dd, ['rn + 4*'imm08@00]"); Format(instr, "vldr'cond 'Dd, ['rn + 4*'imm08@00]");
} else { } else {
@ -1367,7 +1386,10 @@ void Decoder::DecodeType6CoprocessorIns(Instruction* instr) {
break; break;
case 0x4: case 0x4:
case 0x5: case 0x5:
case 0x9: { case 0x6:
case 0x7:
case 0x9:
case 0xB: {
bool to_vfp_register = (instr->VLValue() == 0x1); bool to_vfp_register = (instr->VLValue() == 0x1);
if (to_vfp_register) { if (to_vfp_register) {
Format(instr, "vldm'cond'pu 'rn'w, {'Dd-'Dd+}"); Format(instr, "vldm'cond'pu 'rn'w, {'Dd-'Dd+}");

View File

@ -3178,8 +3178,8 @@ void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) {
if (CpuFeatures::IsSupported(VFP2)) { if (CpuFeatures::IsSupported(VFP2)) {
CpuFeatures::Scope scope(VFP2); CpuFeatures::Scope scope(VFP2);
if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
__ vldr(result.low(), scratch0(), additional_offset); __ vldr(kScratchDoubleReg.low(), scratch0(), additional_offset);
__ vcvt_f64_f32(result, result.low()); __ vcvt_f64_f32(result, kScratchDoubleReg.low());
} else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS
__ vldr(result, scratch0(), additional_offset); __ vldr(result, scratch0(), additional_offset);
} }

View File

@ -642,6 +642,8 @@ void MacroAssembler::PopSafepointRegisters() {
void MacroAssembler::PushSafepointRegistersAndDoubles() { void MacroAssembler::PushSafepointRegistersAndDoubles() {
// Number of d-regs not known at snapshot time.
ASSERT(!Serializer::enabled());
PushSafepointRegisters(); PushSafepointRegisters();
sub(sp, sp, Operand(DwVfpRegister::NumAllocatableRegisters() * sub(sp, sp, Operand(DwVfpRegister::NumAllocatableRegisters() *
kDoubleSize)); kDoubleSize));
@ -652,6 +654,8 @@ void MacroAssembler::PushSafepointRegistersAndDoubles() {
void MacroAssembler::PopSafepointRegistersAndDoubles() { void MacroAssembler::PopSafepointRegistersAndDoubles() {
// Number of d-regs not known at snapshot time.
ASSERT(!Serializer::enabled());
for (int i = 0; i < DwVfpRegister::NumAllocatableRegisters(); i++) { for (int i = 0; i < DwVfpRegister::NumAllocatableRegisters(); i++) {
vldr(DwVfpRegister::FromAllocationIndex(i), sp, i * kDoubleSize); vldr(DwVfpRegister::FromAllocationIndex(i), sp, i * kDoubleSize);
} }
@ -690,6 +694,8 @@ MemOperand MacroAssembler::SafepointRegisterSlot(Register reg) {
MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) { MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) {
// Number of d-regs not known at snapshot time.
ASSERT(!Serializer::enabled());
// General purpose registers are pushed last on the stack. // General purpose registers are pushed last on the stack.
int doubles_size = DwVfpRegister::NumAllocatableRegisters() * kDoubleSize; int doubles_size = DwVfpRegister::NumAllocatableRegisters() * kDoubleSize;
int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize; int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize;
@ -878,10 +884,12 @@ void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space) {
// Optionally save all double registers. // Optionally save all double registers.
if (save_doubles) { if (save_doubles) {
DwVfpRegister first = d0; // Check CPU flags for number of registers, setting the Z condition flag.
DwVfpRegister last = CheckFor32DRegs(ip);
DwVfpRegister::from_code(DwVfpRegister::kNumRegisters - 1);
vstm(db_w, sp, first, last); vstm(db_w, sp, d16, d31, ne);
sub(sp, sp, Operand(16 * kDoubleSize), LeaveCC, eq);
vstm(db_w, sp, d0, d15);
// Note that d0 will be accessible at // Note that d0 will be accessible at
// fp - 2 * kPointerSize - DwVfpRegister::kNumRegisters * kDoubleSize, // fp - 2 * kPointerSize - DwVfpRegister::kNumRegisters * kDoubleSize,
// since the sp slot and code slot were pushed after the fp. // since the sp slot and code slot were pushed after the fp.
@ -941,10 +949,13 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles,
// Calculate the stack location of the saved doubles and restore them. // Calculate the stack location of the saved doubles and restore them.
const int offset = 2 * kPointerSize; const int offset = 2 * kPointerSize;
sub(r3, fp, Operand(offset + DwVfpRegister::kNumRegisters * kDoubleSize)); sub(r3, fp, Operand(offset + DwVfpRegister::kNumRegisters * kDoubleSize));
DwVfpRegister first = d0;
DwVfpRegister last = // Check CPU flags for number of registers, setting the Z condition flag.
DwVfpRegister::from_code(DwVfpRegister::kNumRegisters - 1); CheckFor32DRegs(ip);
vldm(ia, r3, first, last);
vldm(ia_w, r3, d0, d15);
vldm(ia_w, r3, d16, d31, ne);
add(r3, r3, Operand(16 * kDoubleSize), LeaveCC, eq);
} }
// Clear top frame. // Clear top frame.
@ -3392,6 +3403,13 @@ void MacroAssembler::CountLeadingZeros(Register zeros, // Answer.
} }
void MacroAssembler::CheckFor32DRegs(Register scratch) {
mov(scratch, Operand(ExternalReference::cpu_features()));
ldr(scratch, MemOperand(scratch));
tst(scratch, Operand(1u << VFP32DREGS));
}
void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii( void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
Register first, Register first,
Register second, Register second,

View File

@ -997,6 +997,11 @@ class MacroAssembler: public Assembler {
Register source, Register source,
Register scratch); Register scratch);
// Check whether d16-d31 are available on the CPU. The result is given by the
// Z condition flag: Z==0 if d16-d31 available, Z==1 otherwise.
void CheckFor32DRegs(Register scratch);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Runtime calls // Runtime calls

View File

@ -34,6 +34,7 @@
#include "disasm.h" #include "disasm.h"
#include "assembler.h" #include "assembler.h"
#include "codegen.h"
#include "arm/constants-arm.h" #include "arm/constants-arm.h"
#include "arm/simulator-arm.h" #include "arm/simulator-arm.h"
@ -764,7 +765,7 @@ Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
// All registers are initialized to zero to start with // All registers are initialized to zero to start with
// even though s_registers_ & d_registers_ share the same // even though s_registers_ & d_registers_ share the same
// physical registers in the target. // physical registers in the target.
for (int i = 0; i < num_s_registers; i++) { for (int i = 0; i < num_d_registers * 2; i++) {
vfp_register[i] = 0; vfp_register[i] = 0;
} }
n_flag_FPSCR_ = false; n_flag_FPSCR_ = false;
@ -949,7 +950,7 @@ template<class InputType, int register_size>
void Simulator::SetVFPRegister(int reg_index, const InputType& value) { void Simulator::SetVFPRegister(int reg_index, const InputType& value) {
ASSERT(reg_index >= 0); ASSERT(reg_index >= 0);
if (register_size == 1) ASSERT(reg_index < num_s_registers); if (register_size == 1) ASSERT(reg_index < num_s_registers);
if (register_size == 2) ASSERT(reg_index < num_d_registers); if (register_size == 2) ASSERT(reg_index < DwVfpRegister::NumRegisters());
char buffer[register_size * sizeof(vfp_register[0])]; char buffer[register_size * sizeof(vfp_register[0])];
memcpy(buffer, &value, register_size * sizeof(vfp_register[0])); memcpy(buffer, &value, register_size * sizeof(vfp_register[0]));
@ -962,7 +963,7 @@ template<class ReturnType, int register_size>
ReturnType Simulator::GetFromVFPRegister(int reg_index) { ReturnType Simulator::GetFromVFPRegister(int reg_index) {
ASSERT(reg_index >= 0); ASSERT(reg_index >= 0);
if (register_size == 1) ASSERT(reg_index < num_s_registers); if (register_size == 1) ASSERT(reg_index < num_s_registers);
if (register_size == 2) ASSERT(reg_index < num_d_registers); if (register_size == 2) ASSERT(reg_index < DwVfpRegister::NumRegisters());
ReturnType value = 0; ReturnType value = 0;
char buffer[register_size * sizeof(vfp_register[0])]; char buffer[register_size * sizeof(vfp_register[0])];
@ -1613,15 +1614,19 @@ void Simulator::HandleVList(Instruction* instr) {
address += 1; address += 1;
} else { } else {
if (load) { if (load) {
set_s_register_from_sinteger( int32_t data[] = {
2 * reg, ReadW(reinterpret_cast<int32_t>(address), instr)); ReadW(reinterpret_cast<int32_t>(address), instr),
set_s_register_from_sinteger( ReadW(reinterpret_cast<int32_t>(address + 1), instr)
2 * reg + 1, ReadW(reinterpret_cast<int32_t>(address + 1), instr)); };
double d;
memcpy(&d, data, 8);
set_d_register_from_double(reg, d);
} else { } else {
WriteW(reinterpret_cast<int32_t>(address), int32_t data[2];
get_sinteger_from_s_register(2 * reg), instr); double d = get_double_from_d_register(reg);
WriteW(reinterpret_cast<int32_t>(address + 1), memcpy(data, &d, 8);
get_sinteger_from_s_register(2 * reg + 1), instr); WriteW(reinterpret_cast<int32_t>(address), data[0], instr);
WriteW(reinterpret_cast<int32_t>(address + 1), data[1], instr);
} }
address += 2; address += 2;
} }
@ -2810,6 +2815,17 @@ void Simulator::DecodeTypeVFP(Instruction* instr) {
if ((instr->VCValue() == 0x0) && if ((instr->VCValue() == 0x0) &&
(instr->VAValue() == 0x0)) { (instr->VAValue() == 0x0)) {
DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr);
} else if ((instr->VLValue() == 0x0) &&
(instr->VCValue() == 0x1) &&
(instr->Bit(23) == 0x0)) {
// vmov (ARM core register to scalar)
int vd = instr->Bits(19, 16) | (instr->Bit(7) << 4);
double dd_value = get_double_from_d_register(vd);
int32_t data[2];
memcpy(data, &dd_value, 8);
data[instr->Bit(21)] = get_register(instr->RtValue());
memcpy(&dd_value, data, 8);
set_d_register_from_double(vd, dd_value);
} else if ((instr->VLValue() == 0x1) && } else if ((instr->VLValue() == 0x1) &&
(instr->VCValue() == 0x0) && (instr->VCValue() == 0x0) &&
(instr->VAValue() == 0x7) && (instr->VAValue() == 0x7) &&
@ -3148,31 +3164,32 @@ void Simulator::DecodeType6CoprocessorIns(Instruction* instr) {
switch (instr->OpcodeValue()) { switch (instr->OpcodeValue()) {
case 0x2: case 0x2:
// Load and store double to two GP registers // Load and store double to two GP registers
if (instr->Bits(7, 4) != 0x1) { if (instr->Bits(7, 6) != 0 || instr->Bit(4) != 1) {
UNIMPLEMENTED(); // Not used by V8. UNIMPLEMENTED(); // Not used by V8.
} else { } else {
int rt = instr->RtValue(); int rt = instr->RtValue();
int rn = instr->RnValue(); int rn = instr->RnValue();
int vm = instr->VmValue(); int vm = instr->VFPMRegValue(kDoublePrecision);
if (instr->HasL()) { if (instr->HasL()) {
int32_t rt_int_value = get_sinteger_from_s_register(2*vm); int32_t data[2];
int32_t rn_int_value = get_sinteger_from_s_register(2*vm+1); double d = get_double_from_d_register(vm);
memcpy(data, &d, 8);
set_register(rt, rt_int_value); set_register(rt, data[0]);
set_register(rn, rn_int_value); set_register(rn, data[1]);
} else { } else {
int32_t rs_val = get_register(rt); int32_t data[] = { get_register(rt), get_register(rn) };
int32_t rn_val = get_register(rn); double d;
memcpy(&d, data, 8);
set_s_register_from_sinteger(2*vm, rs_val); set_d_register_from_double(vm, d);
set_s_register_from_sinteger((2*vm+1), rn_val);
} }
} }
break; break;
case 0x8: case 0x8:
case 0xC: { // Load and store double to memory. case 0xA:
case 0xC:
case 0xE: { // Load and store double to memory.
int rn = instr->RnValue(); int rn = instr->RnValue();
int vd = instr->VdValue(); int vd = instr->VFPDRegValue(kDoublePrecision);
int offset = instr->Immed8Value(); int offset = instr->Immed8Value();
if (!instr->HasU()) { if (!instr->HasU()) {
offset = -offset; offset = -offset;
@ -3180,18 +3197,29 @@ void Simulator::DecodeType6CoprocessorIns(Instruction* instr) {
int32_t address = get_register(rn) + 4 * offset; int32_t address = get_register(rn) + 4 * offset;
if (instr->HasL()) { if (instr->HasL()) {
// Load double from memory: vldr. // Load double from memory: vldr.
set_s_register_from_sinteger(2*vd, ReadW(address, instr)); int32_t data[] = {
set_s_register_from_sinteger(2*vd + 1, ReadW(address + 4, instr)); ReadW(address, instr),
ReadW(address + 4, instr)
};
double val;
memcpy(&val, data, 8);
set_d_register_from_double(vd, val);
} else { } else {
// Store double to memory: vstr. // Store double to memory: vstr.
WriteW(address, get_sinteger_from_s_register(2*vd), instr); int32_t data[2];
WriteW(address + 4, get_sinteger_from_s_register(2*vd + 1), instr); double val = get_double_from_d_register(vd);
memcpy(data, &val, 8);
WriteW(address, data[0], instr);
WriteW(address + 4, data[1], instr);
} }
break; break;
} }
case 0x4: case 0x4:
case 0x5: case 0x5:
case 0x6:
case 0x7:
case 0x9: case 0x9:
case 0xB:
// Load/store multiple double from memory: vldm/vstm. // Load/store multiple double from memory: vldm/vstm.
HandleVList(instr); HandleVList(instr);
break; break;

View File

@ -142,7 +142,9 @@ class Simulator {
num_s_registers = 32, num_s_registers = 32,
d0 = 0, d1, d2, d3, d4, d5, d6, d7, d0 = 0, d1, d2, d3, d4, d5, d6, d7,
d8, d9, d10, d11, d12, d13, d14, d15, d8, d9, d10, d11, d12, d13, d14, d15,
num_d_registers = 16 d16, d17, d18, d19, d20, d21, d22, d23,
d24, d25, d26, d27, d28, d29, d30, d31,
num_d_registers = 32
}; };
explicit Simulator(Isolate* isolate); explicit Simulator(Isolate* isolate);
@ -371,7 +373,8 @@ class Simulator {
bool v_flag_; bool v_flag_;
// VFP architecture state. // VFP architecture state.
unsigned int vfp_register[num_s_registers]; // TODO(hans): Rename vfp_register to vfp_registers_.
unsigned int vfp_register[num_d_registers * 2];
bool n_flag_FPSCR_; bool n_flag_FPSCR_;
bool z_flag_FPSCR_; bool z_flag_FPSCR_;
bool c_flag_FPSCR_; bool c_flag_FPSCR_;

View File

@ -749,6 +749,8 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference ForDeoptEntry(Address entry); static ExternalReference ForDeoptEntry(Address entry);
static ExternalReference cpu_features();
Address address() const {return reinterpret_cast<Address>(address_);} Address address() const {return reinterpret_cast<Address>(address_);}
#ifdef ENABLE_DEBUGGER_SUPPORT #ifdef ENABLE_DEBUGGER_SUPPORT

View File

@ -303,6 +303,8 @@ DEFINE_bool(enable_movw_movt, false,
"instruction pairs (ARM only)") "instruction pairs (ARM only)")
DEFINE_bool(enable_unaligned_accesses, true, DEFINE_bool(enable_unaligned_accesses, true,
"enable unaligned accesses for ARMv7 (ARM only)") "enable unaligned accesses for ARMv7 (ARM only)")
DEFINE_bool(enable_32dregs, true,
"enable use of d16-d31 registers on ARM - this requires VFP3")
DEFINE_bool(enable_fpu, true, DEFINE_bool(enable_fpu, true,
"enable use of MIPS FPU instructions if available (MIPS only)") "enable use of MIPS FPU instructions if available (MIPS only)")
DEFINE_bool(enable_vldr_imm, false, DEFINE_bool(enable_vldr_imm, false,

View File

@ -643,6 +643,8 @@ void StandardFrame::IterateCompiledFrame(ObjectVisitor* v) const {
// Skip saved double registers. // Skip saved double registers.
if (safepoint_entry.has_doubles()) { if (safepoint_entry.has_doubles()) {
// Number of doubles not known at snapshot time.
ASSERT(!Serializer::enabled());
parameters_base += DoubleRegister::NumAllocatableRegisters() * parameters_base += DoubleRegister::NumAllocatableRegisters() *
kDoubleSize / kPointerSize; kDoubleSize / kPointerSize;
} }

View File

@ -55,6 +55,12 @@ uint64_t CpuFeatures::supported_ = 0;
uint64_t CpuFeatures::found_by_runtime_probing_ = 0; uint64_t CpuFeatures::found_by_runtime_probing_ = 0;
ExternalReference ExternalReference::cpu_features() {
ASSERT(CpuFeatures::initialized_);
return ExternalReference(&CpuFeatures::supported_);
}
int IntelDoubleRegister::NumAllocatableRegisters() { int IntelDoubleRegister::NumAllocatableRegisters() {
if (CpuFeatures::IsSupported(SSE2)) { if (CpuFeatures::IsSupported(SSE2)) {
return XMMRegister::kNumAllocatableRegisters; return XMMRegister::kNumAllocatableRegisters;

View File

@ -611,6 +611,7 @@ class CpuFeatures : public AllStatic {
static uint64_t supported_; static uint64_t supported_;
static uint64_t found_by_runtime_probing_; static uint64_t found_by_runtime_probing_;
friend class ExternalReference;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures); DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
}; };

View File

@ -1509,7 +1509,7 @@ void LAllocator::AllocateRegisters() {
ASSERT(inactive_live_ranges_.is_empty()); ASSERT(inactive_live_ranges_.is_empty());
if (mode_ == DOUBLE_REGISTERS) { if (mode_ == DOUBLE_REGISTERS) {
for (int i = 0; i < fixed_double_live_ranges_.length(); ++i) { for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); ++i) {
LiveRange* current = fixed_double_live_ranges_.at(i); LiveRange* current = fixed_double_live_ranges_.at(i);
if (current != NULL) { if (current != NULL) {
AddToInactive(current); AddToInactive(current);

View File

@ -50,6 +50,12 @@ unsigned CpuFeatures::supported_ = 0;
unsigned CpuFeatures::found_by_runtime_probing_ = 0; unsigned CpuFeatures::found_by_runtime_probing_ = 0;
ExternalReference ExternalReference::cpu_features() {
ASSERT(CpuFeatures::initialized_);
return ExternalReference(&CpuFeatures::supported_);
}
// Get the CPU features enabled by the build. For cross compilation the // Get the CPU features enabled by the build. For cross compilation the
// preprocessor symbols CAN_USE_FPU_INSTRUCTIONS // preprocessor symbols CAN_USE_FPU_INSTRUCTIONS
// can be defined to enable FPU instructions when building the // can be defined to enable FPU instructions when building the

View File

@ -490,6 +490,7 @@ class CpuFeatures : public AllStatic {
static unsigned supported_; static unsigned supported_;
static unsigned found_by_runtime_probing_; static unsigned found_by_runtime_probing_;
friend class ExternalReference;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures); DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
}; };

View File

@ -156,10 +156,17 @@ bool OS::ArmCpuHasFeature(CpuFeature feature) {
case SUDIV: case SUDIV:
search_string = "idiva"; search_string = "idiva";
break; break;
case VFP32DREGS:
// This case is handled specially below.
break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
if (feature == VFP32DREGS) {
return ArmCpuHasFeature(VFP3) && !CPUInfoContainsString("d16");
}
if (CPUInfoContainsString(search_string)) { if (CPUInfoContainsString(search_string)) {
return true; return true;
} }

View File

@ -528,6 +528,10 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) {
UNCLASSIFIED, UNCLASSIFIED,
51, 51,
"Code::MakeCodeYoung"); "Code::MakeCodeYoung");
Add(ExternalReference::cpu_features().address(),
UNCLASSIFIED,
52,
"cpu_features");
// Add a small set of deopt entry addresses to encoder without generating the // Add a small set of deopt entry addresses to encoder without generating the
// deopt table code, which isn't possible at deserialization time. // deopt table code, which isn't possible at deserialization time.
@ -537,7 +541,7 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) {
entry, entry,
Deoptimizer::LAZY, Deoptimizer::LAZY,
Deoptimizer::CALCULATE_ENTRY_ADDRESS); Deoptimizer::CALCULATE_ENTRY_ADDRESS);
Add(address, LAZY_DEOPTIMIZATION, 52 + entry, "lazy_deopt"); Add(address, LAZY_DEOPTIMIZATION, 53 + entry, "lazy_deopt");
} }
} }

View File

@ -431,6 +431,7 @@ enum CpuFeature { SSE4_1 = 32 + 19, // x86
SUDIV = 4, // ARM SUDIV = 4, // ARM
UNALIGNED_ACCESSES = 5, // ARM UNALIGNED_ACCESSES = 5, // ARM
MOVW_MOVT_IMMEDIATE_LOADS = 6, // ARM MOVW_MOVT_IMMEDIATE_LOADS = 6, // ARM
VFP32DREGS = 7, // ARM
SAHF = 0, // x86 SAHF = 0, // x86
FPU = 1}; // MIPS FPU = 1}; // MIPS

View File

@ -46,6 +46,12 @@ uint64_t CpuFeatures::supported_ = CpuFeatures::kDefaultCpuFeatures;
uint64_t CpuFeatures::found_by_runtime_probing_ = 0; uint64_t CpuFeatures::found_by_runtime_probing_ = 0;
ExternalReference ExternalReference::cpu_features() {
ASSERT(CpuFeatures::initialized_);
return ExternalReference(&CpuFeatures::supported_);
}
void CpuFeatures::Probe() { void CpuFeatures::Probe() {
ASSERT(supported_ == CpuFeatures::kDefaultCpuFeatures); ASSERT(supported_ == CpuFeatures::kDefaultCpuFeatures);
#ifdef DEBUG #ifdef DEBUG

View File

@ -530,6 +530,7 @@ class CpuFeatures : public AllStatic {
static uint64_t supported_; static uint64_t supported_;
static uint64_t found_by_runtime_probing_; static uint64_t found_by_runtime_probing_;
friend class ExternalReference;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures); DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
}; };

View File

@ -1026,4 +1026,122 @@ TEST(12) {
__ nop(); __ nop();
} }
TEST(13) {
// Test VFP instructions using registers d16-d31.
InitializeVM();
v8::HandleScope scope;
if (!CpuFeatures::IsSupported(VFP32DREGS)) {
return;
}
typedef struct {
double a;
double b;
double c;
double x;
double y;
double z;
double i;
double j;
double k;
} T;
T t;
// Create a function that accepts &t, and loads, manipulates, and stores
// the doubles and floats.
Assembler assm(Isolate::Current(), NULL, 0);
Label L, C;
if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ stm(db_w, sp, r4.bit() | lr.bit());
// Load a, b, c into d16, d17, d18.
__ mov(r4, Operand(r0));
__ vldr(d16, r4, OFFSET_OF(T, a));
__ vldr(d17, r4, OFFSET_OF(T, b));
__ vldr(d18, r4, OFFSET_OF(T, c));
__ vneg(d25, d16);
__ vadd(d25, d25, d17);
__ vsub(d25, d25, d18);
__ vmul(d25, d25, d25);
__ vdiv(d25, d25, d18);
__ vmov(d16, d25);
__ vsqrt(d17, d25);
__ vneg(d17, d17);
__ vabs(d17, d17);
__ vmla(d18, d16, d17);
// Store d16, d17, d18 into a, b, c.
__ mov(r4, Operand(r0));
__ vstr(d16, r4, OFFSET_OF(T, a));
__ vstr(d17, r4, OFFSET_OF(T, b));
__ vstr(d18, r4, OFFSET_OF(T, c));
// Load x, y, z into d29-d31.
__ add(r4, r0, Operand(OFFSET_OF(T, x)));
__ vldm(ia_w, r4, d29, d31);
// Swap d29 and d30 via r registers.
__ vmov(r1, r2, d29);
__ vmov(d29, d30);
__ vmov(d30, r1, r2);
// Convert to and from integer.
__ vcvt_s32_f64(s1, d31);
__ vcvt_f64_u32(d31, s1);
// Store d29-d31 into x, y, z.
__ add(r4, r0, Operand(OFFSET_OF(T, x)));
__ vstm(ia_w, r4, d29, d31);
// Move constants into d20, d21, d22 and store into i, j, k.
__ vmov(d20, 14.7610017472335499);
__ vmov(d21, 16.0);
__ mov(r1, Operand(372106121));
__ mov(r2, Operand(1079146608));
__ vmov(d22, 0, r1);
__ vmov(d22, 1, r2);
__ add(r4, r0, Operand(OFFSET_OF(T, i)));
__ vstm(ia_w, r4, d20, d22);
__ ldm(ia_w, sp, r4.bit() | pc.bit());
CodeDesc desc;
assm.GetCode(&desc);
Object* code = HEAP->CreateCode(
desc,
Code::ComputeFlags(Code::STUB),
Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
CHECK(code->IsCode());
#ifdef DEBUG
Code::cast(code)->Print();
#endif
F3 f = FUNCTION_CAST<F3>(Code::cast(code)->entry());
t.a = 1.5;
t.b = 2.75;
t.c = 17.17;
t.x = 1.5;
t.y = 2.75;
t.z = 17.17;
Object* dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0);
USE(dummy);
CHECK_EQ(14.7610017472335499, t.a);
CHECK_EQ(3.84200491244266251, t.b);
CHECK_EQ(73.8818412254460241, t.c);
CHECK_EQ(2.75, t.x);
CHECK_EQ(1.5, t.y);
CHECK_EQ(17.0, t.z);
CHECK_EQ(14.7610017472335499, t.i);
CHECK_EQ(16.0, t.j);
CHECK_EQ(73.8818412254460241, t.k);
}
}
#undef __ #undef __

View File

@ -425,6 +425,10 @@ TEST(Vfp) {
if (CpuFeatures::IsSupported(VFP3)) { if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3); CpuFeatures::Scope scope(VFP3);
COMPARE(vmov(d0, r2, r3),
"ec432b10 vmov d0, r2, r3");
COMPARE(vmov(r2, r3, d0),
"ec532b10 vmov r2, r3, d0");
COMPARE(vmov(d0, d1), COMPARE(vmov(d0, d1),
"eeb00b41 vmov.f64 d0, d1"); "eeb00b41 vmov.f64 d0, d1");
COMPARE(vmov(d3, d3, eq), COMPARE(vmov(d3, d3, eq),
@ -473,6 +477,11 @@ TEST(Vfp) {
COMPARE(vdiv(d6, d7, d7, hi), COMPARE(vdiv(d6, d7, d7, hi),
"8e876b07 vdiv.f64hi d6, d7, d7"); "8e876b07 vdiv.f64hi d6, d7, d7");
COMPARE(vcmp(d0, d1),
"eeb40b41 vcmp.f64 d0, d1");
COMPARE(vcmp(d0, 0.0),
"eeb50b40 vcmp.f64 d0, #0.0");
COMPARE(vsqrt(d0, d0), COMPARE(vsqrt(d0, d0),
"eeb10bc0 vsqrt.f64 d0, d0"); "eeb10bc0 vsqrt.f64 d0, d0");
COMPARE(vsqrt(d2, d3, ne), COMPARE(vsqrt(d2, d3, ne),
@ -483,6 +492,11 @@ TEST(Vfp) {
COMPARE(vmov(d2, -13.0), COMPARE(vmov(d2, -13.0),
"eeba2b0a vmov.f64 d2, #-13"); "eeba2b0a vmov.f64 d2, #-13");
COMPARE(vmov(d0, 0, r0),
"ee000b10 vmov.32 d0[0], r0");
COMPARE(vmov(d0, 1, r0),
"ee200b10 vmov.32 d0[1], r0");
COMPARE(vldr(s0, r0, 0), COMPARE(vldr(s0, r0, 0),
"ed900a00 vldr s0, [r0 + 4*0]"); "ed900a00 vldr s0, [r0 + 4*0]");
COMPARE(vldr(s1, r1, 4), COMPARE(vldr(s1, r1, 4),
@ -552,6 +566,97 @@ TEST(Vfp) {
"ee012b00 vmla.f64 d2, d1, d0"); "ee012b00 vmla.f64 d2, d1, d0");
COMPARE(vmla(d6, d4, d5, cc), COMPARE(vmla(d6, d4, d5, cc),
"3e046b05 vmla.f64cc d6, d4, d5"); "3e046b05 vmla.f64cc d6, d4, d5");
COMPARE(vcvt_u32_f64(s0, d0),
"eebc0bc0 vcvt.u32.f64 s0, d0");
COMPARE(vcvt_s32_f64(s0, d0),
"eebd0bc0 vcvt.s32.f64 s0, d0");
COMPARE(vcvt_f64_u32(d0, s1),
"eeb80b60 vcvt.f64.u32 d0, s1");
COMPARE(vcvt_f64_s32(d0, s1),
"eeb80be0 vcvt.f64.s32 d0, s1");
COMPARE(vcvt_f32_s32(s0, s2),
"eeb80ac1 vcvt.f32.s32 s0, s2");
if (CpuFeatures::IsSupported(VFP32DREGS)) {
COMPARE(vmov(d3, d27),
"eeb03b6b vmov.f64 d3, d27");
COMPARE(vmov(d18, d7),
"eef02b47 vmov.f64 d18, d7");
COMPARE(vmov(d18, r2, r3),
"ec432b32 vmov d18, r2, r3");
COMPARE(vmov(r2, r3, d18),
"ec532b32 vmov r2, r3, d18");
COMPARE(vmov(d20, d31),
"eef04b6f vmov.f64 d20, d31");
COMPARE(vabs(d16, d31),
"eef00bef vabs.f64 d16, d31");
COMPARE(vneg(d16, d31),
"eef10b6f vneg.f64 d16, d31");
COMPARE(vadd(d16, d17, d18),
"ee710ba2 vadd.f64 d16, d17, d18");
COMPARE(vsub(d16, d17, d18),
"ee710be2 vsub.f64 d16, d17, d18");
COMPARE(vmul(d16, d17, d18),
"ee610ba2 vmul.f64 d16, d17, d18");
COMPARE(vdiv(d16, d17, d18),
"eec10ba2 vdiv.f64 d16, d17, d18");
COMPARE(vcmp(d16, d17),
"eef40b61 vcmp.f64 d16, d17");
COMPARE(vcmp(d16, 0.0),
"eef50b40 vcmp.f64 d16, #0.0");
COMPARE(vsqrt(d16, d17),
"eef10be1 vsqrt.f64 d16, d17");
COMPARE(vmov(d30, 16.0),
"eef3eb00 vmov.f64 d30, #16");
COMPARE(vmov(d31, 0, r7),
"ee0f7b90 vmov.32 d31[0], r7");
COMPARE(vmov(d31, 1, r7),
"ee2f7b90 vmov.32 d31[1], r7");
COMPARE(vldr(d25, r0, 0),
"edd09b00 vldr d25, [r0 + 4*0]");
COMPARE(vldr(d26, r1, 4),
"edd1ab01 vldr d26, [r1 + 4*1]");
COMPARE(vldr(d31, r10, 1020),
"eddafbff vldr d31, [r10 + 4*255]");
COMPARE(vstr(d16, r0, 0),
"edc00b00 vstr d16, [r0 + 4*0]");
COMPARE(vstr(d17, r1, 4),
"edc11b01 vstr d17, [r1 + 4*1]");
COMPARE(vstr(d31, r10, 1020),
"edcafbff vstr d31, [r10 + 4*255]");
COMPARE(vstm(ia, r0, d16, d31),
"ecc00b20 vstmia r0, {d16-d31}");
COMPARE(vldm(ia, r3, d16, d31),
"ecd30b20 vldmia r3, {d16-d31}");
COMPARE(vstm(ia, r0, d23, d27),
"ecc07b0a vstmia r0, {d23-d27}");
COMPARE(vldm(ia, r3, d23, d27),
"ecd37b0a vldmia r3, {d23-d27}");
COMPARE(vmla(d16, d17, d18),
"ee410ba2 vmla.f64 d16, d17, d18");
COMPARE(vcvt_u32_f64(s0, d16),
"eebc0be0 vcvt.u32.f64 s0, d16");
COMPARE(vcvt_s32_f64(s0, d16),
"eebd0be0 vcvt.s32.f64 s0, d16");
COMPARE(vcvt_f64_u32(d16, s1),
"eef80b60 vcvt.f64.u32 d16, s1");
}
} }
VERIFY_RUN(); VERIFY_RUN();

View File

@ -132,6 +132,8 @@ TEST(ExternalReferenceEncoder) {
CHECK_EQ(make_code(UNCLASSIFIED, 3), CHECK_EQ(make_code(UNCLASSIFIED, 3),
encoder.Encode( encoder.Encode(
ExternalReference::roots_array_start(isolate).address())); ExternalReference::roots_array_start(isolate).address()));
CHECK_EQ(make_code(UNCLASSIFIED, 52),
encoder.Encode(ExternalReference::cpu_features().address()));
} }

View File

@ -158,8 +158,9 @@
['armv7==1', { ['armv7==1', {
# The ARM Architecture Manual mandates VFPv3 if NEON is # The ARM Architecture Manual mandates VFPv3 if NEON is
# available. # available.
# The current V8 doesn't use d16-d31, so for vfpv3-d16, we can # V8 does not use d16-d31 unless explicitly enabled
# also enable vfp3 for the better performance. # (--enable_32dregs) or detected at run-time, so for vfpv3-d16,
# we can also enable vfp3 for the better performance.
'conditions': [ 'conditions': [
['arm_neon!=1 and arm_fpu!="vfpv3" and arm_fpu!="vfpv3-d16"', { ['arm_neon!=1 and arm_fpu!="vfpv3" and arm_fpu!="vfpv3-d16"', {
'variables': { 'variables': {