Fix incorrect encoding of single and double precision registers for some VFP instructions. Also fix incorrect disassembling of vldr/vstr. This is a commit of http://codereview.chromium.org/3107027 for Rodolph Perfetta.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5352 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
erik.corry@gmail.com 2010-08-26 08:53:00 +00:00
parent 6d5451d685
commit 66d13be5f9
7 changed files with 221 additions and 86 deletions

View File

@ -1809,6 +1809,7 @@ void Assembler::stc2(Coprocessor coproc,
// Support for VFP.
void Assembler::vldr(const DwVfpRegister dst,
const Register base,
int offset,
@ -1838,7 +1839,9 @@ void Assembler::vldr(const SwVfpRegister dst,
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
ASSERT(offset >= 0);
emit(cond | 0xD9*B20 | base.code()*B16 | dst.code()*B12 |
int sd, d;
dst.split_code(&sd, &d);
emit(cond | d*B22 | 0xD9*B20 | base.code()*B16 | sd*B12 |
0xA*B8 | ((offset / 4) & 255));
}
@ -1872,7 +1875,9 @@ void Assembler::vstr(const SwVfpRegister src,
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
ASSERT(offset >= 0);
emit(cond | 0xD8*B20 | base.code()*B16 | src.code()*B12 |
int sd, d;
src.split_code(&sd, &d);
emit(cond | d*B22 | 0xD8*B20 | base.code()*B16 | sd*B12 |
0xA*B8 | ((offset / 4) & 255));
}
@ -1979,8 +1984,10 @@ void Assembler::vmov(const SwVfpRegister dst,
// Sd = Sm
// Instruction details available in ARM DDI 0406B, A8-642.
ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | 0xB*B20 |
dst.code()*B12 | 0x5*B9 | B6 | src.code());
int sd, d, sm, m;
dst.split_code(&sd, &d);
src.split_code(&sm, &m);
emit(cond | 0xE*B24 | d*B22 | 0xB*B20 | sd*B12 | 0xA*B8 | B6 | m*B5 | sm);
}
@ -2034,8 +2041,9 @@ void Assembler::vmov(const SwVfpRegister dst,
// Rt(15-12) | 1010(11-8) | N(7)=0 | 00(6-5) | 1(4) | 0000(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(!src.is(pc));
emit(cond | 0xE*B24 | (dst.code() >> 1)*B16 |
src.code()*B12 | 0xA*B8 | (0x1 & dst.code())*B7 | B4);
int sn, n;
dst.split_code(&sn, &n);
emit(cond | 0xE*B24 | sn*B16 | src.code()*B12 | 0xA*B8 | n*B7 | B4);
}
@ -2048,8 +2056,9 @@ void Assembler::vmov(const Register dst,
// Rt(15-12) | 1010(11-8) | N(7)=0 | 00(6-5) | 1(4) | 0000(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(!dst.is(pc));
emit(cond | 0xE*B24 | B20 | (src.code() >> 1)*B16 |
dst.code()*B12 | 0xA*B8 | (0x1 & src.code())*B7 | B4);
int sn, n;
src.split_code(&sn, &n);
emit(cond | 0xE*B24 | B20 | sn*B16 | dst.code()*B12 | 0xA*B8 | n*B7 | B4);
}
@ -2099,16 +2108,21 @@ static bool IsDoubleVFPType(VFPType type) {
}
// Depending on split_last_bit split binary representation of reg_code into Vm:M
// or M:Vm form (where M is single bit).
static void SplitRegCode(bool split_last_bit,
// Split five bit reg_code based on size of reg_type.
// 32-bit register codes are Vm:M
// 64-bit register codes are M:Vm
// where Vm is four bits, and M is a single bit.
static void SplitRegCode(VFPType reg_type,
int reg_code,
int* vm,
int* m) {
if (split_last_bit) {
ASSERT((reg_code >= 0) && (reg_code <= 31));
if (IsIntegerVFPType(reg_type) || !IsDoubleVFPType(reg_type)) {
// 32 bit type.
*m = reg_code & 0x1;
*vm = reg_code >> 1;
} else {
// 64 bit type.
*m = (reg_code & 0x10) >> 4;
*vm = reg_code & 0x0F;
}
@ -2121,6 +2135,11 @@ static Instr EncodeVCVT(const VFPType dst_type,
const VFPType src_type,
const int src_code,
const Condition cond) {
ASSERT(src_type != dst_type);
int D, Vd, M, Vm;
SplitRegCode(src_type, src_code, &Vm, &M);
SplitRegCode(dst_type, dst_code, &Vd, &D);
if (IsIntegerVFPType(dst_type) || IsIntegerVFPType(src_type)) {
// Conversion between IEEE floating point and 32-bit integer.
// Instruction details available in ARM DDI 0406B, A8.6.295.
@ -2128,22 +2147,17 @@ static Instr EncodeVCVT(const VFPType dst_type,
// Vd(15-12) | 101(11-9) | sz(8) | op(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
ASSERT(!IsIntegerVFPType(dst_type) || !IsIntegerVFPType(src_type));
int sz, opc2, D, Vd, M, Vm, op;
int sz, opc2, op;
if (IsIntegerVFPType(dst_type)) {
opc2 = IsSignedVFPType(dst_type) ? 0x5 : 0x4;
sz = IsDoubleVFPType(src_type) ? 0x1 : 0x0;
op = 1; // round towards zero
SplitRegCode(!IsDoubleVFPType(src_type), src_code, &Vm, &M);
SplitRegCode(true, dst_code, &Vd, &D);
} else {
ASSERT(IsIntegerVFPType(src_type));
opc2 = 0x0;
sz = IsDoubleVFPType(dst_type) ? 0x1 : 0x0;
op = IsSignedVFPType(src_type) ? 0x1 : 0x0;
SplitRegCode(true, src_code, &Vm, &M);
SplitRegCode(!IsDoubleVFPType(dst_type), dst_code, &Vd, &D);
}
return (cond | 0xE*B24 | B23 | D*B22 | 0x3*B20 | B19 | opc2*B16 |
@ -2153,13 +2167,7 @@ static Instr EncodeVCVT(const VFPType dst_type,
// Instruction details available in ARM DDI 0406B, A8.6.298.
// cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 0111(19-16) |
// Vd(15-12) | 101(11-9) | sz(8) | 1(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
int sz, D, Vd, M, Vm;
ASSERT(IsDoubleVFPType(dst_type) != IsDoubleVFPType(src_type));
sz = IsDoubleVFPType(src_type) ? 0x1 : 0x0;
SplitRegCode(IsDoubleVFPType(src_type), dst_code, &Vd, &D);
SplitRegCode(!IsDoubleVFPType(src_type), src_code, &Vm, &M);
int sz = IsDoubleVFPType(src_type) ? 0x1 : 0x0;
return (cond | 0xE*B24 | B23 | D*B22 | 0x3*B20 | 0x7*B16 |
Vd*B12 | 0x5*B9 | sz*B8 | B7 | B6 | M*B5 | Vm);
}

View File

@ -120,6 +120,11 @@ struct SwVfpRegister {
ASSERT(is_valid());
return 1 << code_;
}
void split_code(int* vm, int* m) const {
ASSERT(is_valid());
*m = code_ & 0x1;
*vm = code_ >> 1;
}
int code_;
};
@ -152,6 +157,11 @@ struct DwVfpRegister {
ASSERT(is_valid());
return 1 << code_;
}
void split_code(int* vm, int* m) const {
ASSERT(is_valid());
*m = (code_ & 0x10) >> 4;
*vm = code_ & 0x0F;
}
int code_;
};

View File

@ -194,6 +194,13 @@ enum SoftwareInterruptCodes {
};
// Type of VFP register. Determines register encoding.
enum VFPRegPrecision {
kSinglePrecision = 0,
kDoublePrecision = 1
};
typedef int32_t instr_t;
@ -269,6 +276,15 @@ class Instr {
inline int VCField() const { return Bit(8); }
inline int VAField() const { return Bits(23, 21); }
inline int VBField() const { return Bits(6, 5); }
inline int VFPNRegCode(VFPRegPrecision pre) {
return VFPGlueRegCode(pre, 16, 7);
}
inline int VFPMRegCode(VFPRegPrecision pre) {
return VFPGlueRegCode(pre, 0, 5);
}
inline int VFPDRegCode(VFPRegPrecision pre) {
return VFPGlueRegCode(pre, 12, 22);
}
// Fields used in Data processing instructions
inline Opcode OpcodeField() const {
@ -343,6 +359,17 @@ class Instr {
static Instr* At(byte* pc) { return reinterpret_cast<Instr*>(pc); }
private:
// Join split register codes, depending on single or double precision.
// four_bit is the position of the least-significant bit of the four
// bit specifier. one_bit is the position of the additional single bit
// specifier.
inline int VFPGlueRegCode(VFPRegPrecision pre, int four_bit, int one_bit) {
if (pre == kSinglePrecision) {
return (Bits(four_bit + 3, four_bit) << 1) | Bit(one_bit);
}
return (Bit(one_bit) << 4) | Bits(four_bit + 3, four_bit);
}
// We need to prevent the creation of instances of class Instr.
DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
};

View File

@ -463,7 +463,7 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
ASSERT((width + lsb) <= 32);
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
"#%d",
"%d",
instr->Bits(width + lsb - 1, lsb));
return 8;
}
@ -931,7 +931,7 @@ void Decoder::DecodeType3(Instr* instr) {
if (instr->HasW()) {
ASSERT(instr->Bits(5, 4) == 0x1);
if (instr->Bit(22) == 0x1) {
Format(instr, "usat 'rd, 'imm05@16, 'rm'shift_sat");
Format(instr, "usat 'rd, #'imm05@16, 'rm'shift_sat");
} else {
UNREACHABLE(); // SSAT.
}
@ -1269,17 +1269,19 @@ void Decoder::DecodeType6CoprocessorIns(Instr* instr) {
if (instr->CoprocessorField() == 0xA) {
switch (instr->OpcodeField()) {
case 0x8:
case 0xA:
if (instr->HasL()) {
Format(instr, "vldr'cond 'Sd, ['rn - 4*'off8]");
Format(instr, "vldr'cond 'Sd, ['rn - 4*'imm08@00]");
} else {
Format(instr, "vstr'cond 'Sd, ['rn - 4*'off8]");
Format(instr, "vstr'cond 'Sd, ['rn - 4*'imm08@00]");
}
break;
case 0xC:
case 0xE:
if (instr->HasL()) {
Format(instr, "vldr'cond 'Sd, ['rn + 4*'off8]");
Format(instr, "vldr'cond 'Sd, ['rn + 4*'imm08@00]");
} else {
Format(instr, "vstr'cond 'Sd, ['rn + 4*'off8]");
Format(instr, "vstr'cond 'Sd, ['rn + 4*'imm08@00]");
}
break;
default:
@ -1300,16 +1302,16 @@ void Decoder::DecodeType6CoprocessorIns(Instr* instr) {
break;
case 0x8:
if (instr->HasL()) {
Format(instr, "vldr'cond 'Dd, ['rn - 4*'off8]");
Format(instr, "vldr'cond 'Dd, ['rn - 4*'imm08@00]");
} else {
Format(instr, "vstr'cond 'Dd, ['rn - 4*'off8]");
Format(instr, "vstr'cond 'Dd, ['rn - 4*'imm08@00]");
}
break;
case 0xC:
if (instr->HasL()) {
Format(instr, "vldr'cond 'Dd, ['rn + 4*'off8]");
Format(instr, "vldr'cond 'Dd, ['rn + 4*'imm08@00]");
} else {
Format(instr, "vstr'cond 'Dd, ['rn + 4*'off8]");
Format(instr, "vstr'cond 'Dd, ['rn + 4*'imm08@00]");
}
break;
default:

View File

@ -2281,13 +2281,6 @@ void Simulator::DecodeUnconditional(Instr* instr) {
}
// Depending on value of last_bit flag glue register code from vm and m values
// (where m is expected to be a single bit).
static int GlueRegCode(bool last_bit, int vm, int m) {
return last_bit ? ((vm << 1) | m) : ((m << 4) | vm);
}
// void Simulator::DecodeTypeVFP(Instr* instr)
// The Following ARMv7 VFPv instructions are currently supported.
// vmov :Sn = Rt
@ -2305,9 +2298,10 @@ void Simulator::DecodeTypeVFP(Instr* instr) {
ASSERT((instr->TypeField() == 7) && (instr->Bit(24) == 0x0) );
ASSERT(instr->Bits(11, 9) == 0x5);
int vm = instr->VmField();
int vd = instr->VdField();
int vn = instr->VnField();
// Obtain double precision register codes.
int vm = instr->VFPMRegCode(kDoublePrecision);
int vd = instr->VFPDRegCode(kDoublePrecision);
int vn = instr->VFPNRegCode(kDoublePrecision);
if (instr->Bit(4) == 0) {
if (instr->Opc1Field() == 0x7) {
@ -2315,9 +2309,13 @@ void Simulator::DecodeTypeVFP(Instr* instr) {
if ((instr->Opc2Field() == 0x0) && (instr->Opc3Field() == 0x1)) {
// vmov register to register.
if (instr->SzField() == 0x1) {
set_d_register_from_double(vd, get_double_from_d_register(vm));
int m = instr->VFPMRegCode(kDoublePrecision);
int d = instr->VFPDRegCode(kDoublePrecision);
set_d_register_from_double(d, get_double_from_d_register(m));
} else {
set_s_register_from_float(vd, get_float_from_s_register(vm));
int m = instr->VFPMRegCode(kSinglePrecision);
int d = instr->VFPDRegCode(kSinglePrecision);
set_s_register_from_float(d, get_float_from_s_register(m));
}
} else if ((instr->Opc2Field() == 0x7) && (instr->Opc3Field() == 0x3)) {
DecodeVCVTBetweenDoubleAndSingle(instr);
@ -2410,7 +2408,7 @@ void Simulator::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(Instr* instr) {
(instr->VAField() == 0x0));
int t = instr->RtField();
int n = GlueRegCode(true, instr->VnField(), instr->NField());
int n = instr->VFPNRegCode(kSinglePrecision);
bool to_arm_register = (instr->VLField() == 0x1);
if (to_arm_register) {
@ -2427,22 +2425,25 @@ void Simulator::DecodeVCMP(Instr* instr) {
ASSERT((instr->Bit(4) == 0) && (instr->Opc1Field() == 0x7));
ASSERT(((instr->Opc2Field() == 0x4) || (instr->Opc2Field() == 0x5)) &&
(instr->Opc3Field() & 0x1));
// Comparison.
bool dp_operation = (instr->SzField() == 1);
VFPRegPrecision precision = kSinglePrecision;
if (instr->SzField() == 1) {
precision = kDoublePrecision;
}
if (instr->Bit(7) != 0) {
// Raising exceptions for quiet NaNs are not supported.
UNIMPLEMENTED(); // Not used by V8.
}
int d = GlueRegCode(!dp_operation, instr->VdField(), instr->DField());
int d = instr->VFPDRegCode(precision);
int m = 0;
if (instr->Opc2Field() == 0x4) {
m = GlueRegCode(!dp_operation, instr->VmField(), instr->MField());
m = instr->VFPMRegCode(precision);
}
if (dp_operation) {
if (precision == kDoublePrecision) {
double dd_value = get_double_from_d_register(d);
double dm_value = 0.0;
if (instr->Opc2Field() == 0x4) {
@ -2460,11 +2461,17 @@ void Simulator::DecodeVCVTBetweenDoubleAndSingle(Instr* instr) {
ASSERT((instr->Bit(4) == 0) && (instr->Opc1Field() == 0x7));
ASSERT((instr->Opc2Field() == 0x7) && (instr->Opc3Field() == 0x3));
bool double_to_single = (instr->SzField() == 1);
int dst = GlueRegCode(double_to_single, instr->VdField(), instr->DField());
int src = GlueRegCode(!double_to_single, instr->VmField(), instr->MField());
VFPRegPrecision dst_precision = kDoublePrecision;
VFPRegPrecision src_precision = kSinglePrecision;
if (instr->SzField() == 1) {
dst_precision = kSinglePrecision;
src_precision = kDoublePrecision;
}
if (double_to_single) {
int dst = instr->VFPDRegCode(dst_precision);
int src = instr->VFPMRegCode(src_precision);
if (dst_precision == kSinglePrecision) {
double val = get_double_from_d_register(src);
set_s_register_from_float(dst, static_cast<float>(val));
} else {
@ -2480,13 +2487,13 @@ void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instr* instr) {
(((instr->Opc2Field() >> 1) == 0x6) && (instr->Opc3Field() & 0x1)));
// Conversion between floating-point and integer.
int vd = instr->VdField();
int d = instr->DField();
int vm = instr->VmField();
int m = instr->MField();
bool to_integer = (instr->Bit(18) == 1);
bool dp_operation = (instr->SzField() == 1);
VFPRegPrecision src_precision = kSinglePrecision;
if (instr->SzField() == 1) {
src_precision = kDoublePrecision;
}
if (to_integer) {
bool unsigned_integer = (instr->Bit(16) == 0);
if (instr->Bit(7) != 1) {
@ -2494,10 +2501,10 @@ void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instr* instr) {
UNIMPLEMENTED(); // Not used by V8.
}
int dst = GlueRegCode(true, vd, d);
int src = GlueRegCode(!dp_operation, vm, m);
int dst = instr->VFPDRegCode(kSinglePrecision);
int src = instr->VFPMRegCode(src_precision);
if (dp_operation) {
if (src_precision == kDoublePrecision) {
double val = get_double_from_d_register(src);
int sint = unsigned_integer ? static_cast<uint32_t>(val) :
@ -2515,12 +2522,12 @@ void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instr* instr) {
} else {
bool unsigned_integer = (instr->Bit(7) == 0);
int dst = GlueRegCode(!dp_operation, vd, d);
int src = GlueRegCode(true, vm, m);
int dst = instr->VFPDRegCode(src_precision);
int src = instr->VFPMRegCode(kSinglePrecision);
int val = get_sinteger_from_s_register(src);
if (dp_operation) {
if (src_precision == kDoublePrecision) {
if (unsigned_integer) {
set_d_register_from_double(dst,
static_cast<double>((uint32_t)val));
@ -2551,9 +2558,11 @@ void Simulator::DecodeType6CoprocessorIns(Instr* instr) {
if (instr->CoprocessorField() == 0xA) {
switch (instr->OpcodeField()) {
case 0x8:
case 0xC: { // Load and store float to memory.
case 0xA:
case 0xC:
case 0xE: { // Load and store single precision float to memory.
int rn = instr->RnField();
int vd = instr->VdField();
int vd = instr->VFPDRegCode(kSinglePrecision);
int offset = instr->Immed8Field();
if (!instr->HasU()) {
offset = -offset;

View File

@ -226,13 +226,17 @@ TEST(4) {
double a;
double b;
double c;
float d;
float e;
double d;
double e;
double f;
int i;
float x;
float y;
} T;
T t;
// Create a function that accepts &t, and loads, manipulates, and stores
// the doubles t.a, t.b, and t.c, and floats t.d, t.e.
// the doubles and floats.
Assembler assm(NULL, 0);
Label L, C;
@ -254,15 +258,34 @@ TEST(4) {
__ vmov(d4, r2, r3);
__ vstr(d4, r4, OFFSET_OF(T, b));
// Load t.d and t.e, switch values, and store back to the struct.
__ vldr(s0, r4, OFFSET_OF(T, d));
__ vldr(s1, r4, OFFSET_OF(T, e));
__ vmov(s2, s0);
__ vmov(s0, s1);
__ vmov(s1, s2);
__ vstr(s0, r4, OFFSET_OF(T, d));
__ vstr(s1, r4, OFFSET_OF(T, e));
// Load t.x and t.y, switch values, and store back to the struct.
__ vldr(s0, r4, OFFSET_OF(T, x));
__ vldr(s31, r4, OFFSET_OF(T, y));
__ vmov(s16, s0);
__ vmov(s0, s31);
__ vmov(s31, s16);
__ vstr(s0, r4, OFFSET_OF(T, x));
__ vstr(s31, r4, OFFSET_OF(T, y));
// Move a literal into a register that can be encoded in the instruction.
__ vmov(d4, 1.0);
__ vstr(d4, r4, OFFSET_OF(T, e));
// Move a literal into a register that requires 64 bits to encode.
// 0x3ff0000010000000 = 1.000000059604644775390625
__ vmov(d4, 1.000000059604644775390625);
__ vstr(d4, r4, OFFSET_OF(T, d));
// Convert from floating point to integer.
__ vmov(d4, 2.0);
__ vcvt_s32_f64(s31, d4);
__ vstr(s31, r4, OFFSET_OF(T, i));
// Convert from integer to floating point.
__ mov(lr, Operand(42));
__ vmov(s31, lr);
__ vcvt_f64_s32(d4, s31);
__ vstr(d4, r4, OFFSET_OF(T, f));
__ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
CodeDesc desc;
@ -278,12 +301,20 @@ TEST(4) {
t.a = 1.5;
t.b = 2.75;
t.c = 17.17;
t.d = 4.5;
t.e = 9.0;
t.d = 0.0;
t.e = 0.0;
t.f = 0.0;
t.i = 0;
t.x = 4.5;
t.y = 9.0;
Object* dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0);
USE(dummy);
CHECK_EQ(4.5, t.e);
CHECK_EQ(9.0, t.d);
CHECK_EQ(4.5, t.y);
CHECK_EQ(9.0, t.x);
CHECK_EQ(2, t.i);
CHECK_EQ(42.0, t.f);
CHECK_EQ(1.0, t.e);
CHECK_EQ(1.000000059604644775390625, t.d);
CHECK_EQ(4.25, t.c);
CHECK_EQ(4.25, t.b);
CHECK_EQ(1.5, t.a);

View File

@ -422,6 +422,19 @@ TEST(Vfp) {
COMPARE(vmov(d3, d3, eq),
"0eb03b43 vmov.f64eq d3, d3");
COMPARE(vmov(s0, s31),
"eeb00a6f vmov.f32 s0, s31");
COMPARE(vmov(s31, s0),
"eef0fa40 vmov.f32 s31, s0");
COMPARE(vmov(r0, s0),
"ee100a10 vmov r0, s0");
COMPARE(vmov(r10, s31),
"ee1faa90 vmov r10, s31");
COMPARE(vmov(s0, r0),
"ee000a10 vmov s0, r0");
COMPARE(vmov(s31, r10),
"ee0faa90 vmov s31, r10");
COMPARE(vadd(d0, d1, d2),
"ee310b02 vadd.f64 d0, d1, d2");
COMPARE(vadd(d3, d4, d5, mi),
@ -451,6 +464,41 @@ TEST(Vfp) {
"eeb70b00 vmov.f64 d0, #1");
COMPARE(vmov(d2, -13.0),
"eeba2b0a vmov.f64 d2, #-13");
COMPARE(vldr(s0, r0, 0),
"ed900a00 vldr s0, [r0 + 4*0]");
COMPARE(vldr(s1, r1, 4),
"edd10a01 vldr s1, [r1 + 4*1]");
COMPARE(vldr(s15, r4, 16),
"edd47a04 vldr s15, [r4 + 4*4]");
COMPARE(vldr(s16, r5, 20),
"ed958a05 vldr s16, [r5 + 4*5]");
COMPARE(vldr(s31, r10, 1020),
"eddafaff vldr s31, [r10 + 4*255]");
COMPARE(vstr(s0, r0, 0),
"ed800a00 vstr s0, [r0 + 4*0]");
COMPARE(vstr(s1, r1, 4),
"edc10a01 vstr s1, [r1 + 4*1]");
COMPARE(vstr(s15, r8, 8),
"edc87a02 vstr s15, [r8 + 4*2]");
COMPARE(vstr(s16, r9, 12),
"ed898a03 vstr s16, [r9 + 4*3]");
COMPARE(vstr(s31, r10, 1020),
"edcafaff vstr s31, [r10 + 4*255]");
COMPARE(vldr(d0, r0, 0),
"ed900b00 vldr d0, [r0 + 4*0]");
COMPARE(vldr(d1, r1, 4),
"ed911b01 vldr d1, [r1 + 4*1]");
COMPARE(vldr(d15, r10, 1020),
"ed9afbff vldr d15, [r10 + 4*255]");
COMPARE(vstr(d0, r0, 0),
"ed800b00 vstr d0, [r0 + 4*0]");
COMPARE(vstr(d1, r1, 4),
"ed811b01 vstr d1, [r1 + 4*1]");
COMPARE(vstr(d15, r10, 1020),
"ed8afbff vstr d15, [r10 + 4*255]");
}
VERIFY_RUN();