Adds ldp and stp instructions to ARM64.

These should be used instead of single
register load and store on frame entry
and exit. I'll update these in another
change.

R=regis@google.com

Review URL: https://codereview.chromium.org//630033003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@40960 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
zra@google.com
2014-10-07 16:25:56 +00:00
parent 932218aea1
commit fb29f5e342
5 changed files with 308 additions and 27 deletions
+76 -4
View File
@@ -105,6 +105,9 @@ class Address : public ValueObject {
Offset,
PreIndex,
PostIndex,
PairOffset,
PairPreIndex,
PairPostIndex,
Reg,
PCOffset,
Unknown,
@@ -132,14 +135,29 @@ class Address : public ValueObject {
encoding_ =
((offset & 0x1ff) << kImm9Shift) |
(static_cast<int32_t>(crn) << kRnShift);
} else {
} else if ((at == PreIndex) || (at == PostIndex)) {
ASSERT(Utils::IsInt(9, offset));
ASSERT((at == PreIndex) || (at == PostIndex));
int32_t idx = (at == PostIndex) ? B10 : (B11 | B10);
encoding_ =
idx |
((offset & 0x1ff) << kImm9Shift) |
(static_cast<int32_t>(crn) << kRnShift);
} else {
ASSERT((at == PairOffset) || (at == PairPreIndex) ||
(at == PairPostIndex));
ASSERT(Utils::IsInt(7 + scale, offset) &&
(offset == ((offset >> scale) << scale)));
int32_t idx = 0;
switch (at) {
case PairPostIndex: idx = B23; break;
case PairPreIndex: idx = B24 | B23; break;
case PairOffset: idx = B24; break;
default: UNREACHABLE(); break;
}
encoding_ =
idx |
(((offset >> scale) << kImm7Shift) & kImm7Mask) |
(static_cast<int32_t>(crn) << kRnShift);
}
type_ = at;
base_ = crn;
@@ -161,9 +179,14 @@ class Address : public ValueObject {
} else if (at == PCOffset) {
return Utils::IsInt(21, offset) &&
(offset == ((offset >> 2) << 2));
} else {
ASSERT((at == PreIndex) || (at == PostIndex));
} else if ((at == PreIndex) || (at == PostIndex)) {
return Utils::IsInt(9, offset);
} else {
ASSERT((at == PairOffset) || (at == PairPreIndex) ||
(at == PairPostIndex));
const int32_t scale = Log2OperandSizeBytes(sz);
return (Utils::IsInt(7 + scale, offset) &&
(offset == ((offset >> scale) << scale)));
}
}
@@ -177,6 +200,13 @@ class Address : public ValueObject {
return addr;
}
static Address Pair(Register rn,
int32_t offset = 0,
AddressType at = PairOffset,
OperandSize sz = kDoubleWord) {
return Address(rn, offset, at, sz);
}
// This addressing mode does not exist.
static Address PC(Register r);
@@ -630,6 +660,9 @@ class Assembler : public ValueObject {
// Loads and Stores.
void ldr(Register rt, Address a, OperandSize sz = kDoubleWord) {
ASSERT((a.type() != Address::PairOffset) &&
(a.type() != Address::PairPostIndex) &&
(a.type() != Address::PairPreIndex));
if (a.type() == Address::PCOffset) {
ASSERT(sz == kDoubleWord);
EmitLoadRegLiteral(LDRpc, rt, a, sz);
@@ -648,9 +681,25 @@ class Assembler : public ValueObject {
}
}
void str(Register rt, Address a, OperandSize sz = kDoubleWord) {
ASSERT((a.type() != Address::PairOffset) &&
(a.type() != Address::PairPostIndex) &&
(a.type() != Address::PairPreIndex));
EmitLoadStoreReg(STR, rt, a, sz);
}
void ldp(Register rt, Register rt2, Address a, OperandSize sz = kDoubleWord) {
ASSERT((a.type() == Address::PairOffset) ||
(a.type() == Address::PairPostIndex) ||
(a.type() == Address::PairPreIndex));
EmitLoadStoreRegPair(LDP, rt, rt2, a, sz);
}
void stp(Register rt, Register rt2, Address a, OperandSize sz = kDoubleWord) {
ASSERT((a.type() == Address::PairOffset) ||
(a.type() == Address::PairPostIndex) ||
(a.type() == Address::PairPreIndex));
EmitLoadStoreRegPair(STP, rt, rt2, a, sz);
}
// Conditional select.
void csel(Register rd, Register rn, Register rm, Condition cond) {
EmitConditionalSelect(CSEL, rd, rn, rm, cond, kDoubleWord);
@@ -1601,6 +1650,29 @@ class Assembler : public ValueObject {
Emit(encoding);
}
void EmitLoadStoreRegPair(LoadStoreRegPairOp op,
Register rt, Register rt2, Address a,
OperandSize sz) {
ASSERT((sz == kDoubleWord) || (sz == kWord) || (sz == kUnsignedWord));
ASSERT((rt != CSP) && (rt != R31));
ASSERT((rt2 != CSP) && (rt2 != R31));
const Register crt = ConcreteRegister(rt);
const Register crt2 = ConcreteRegister(rt2);
int32_t opc;
switch (sz) {
case kDoubleWord: opc = B31; break;
case kWord: opc = B30; break;
case kUnsignedWord: opc = 0; break;
default: UNREACHABLE(); break;
}
const int32_t encoding =
opc | op |
(static_cast<int32_t>(crt) << kRtShift) |
(static_cast<int32_t>(crt2) << kRt2Shift) |
a.encoding();
Emit(encoding);
}
void EmitPCRelOp(PCRelOp op, Register rd, const Immediate& imm) {
ASSERT(Utils::IsInt(21, imm.value()));
ASSERT((rd != R31) && (rd != CSP));
+38
View File
@@ -423,6 +423,44 @@ ASSEMBLER_TEST_RUN(LoadSigned32Bit, test) {
}
ASSEMBLER_TEST_GENERATE(SimpleLoadStorePair, assembler) {
__ SetupDartSP(kTestStackSpace);
__ LoadImmediate(R2, 43, kNoPP);
__ LoadImmediate(R3, 42, kNoPP);
__ stp(R2, R3, Address(SP, -2*kWordSize, Address::PairPreIndex));
__ ldp(R0, R1, Address(SP, 2*kWordSize, Address::PairPostIndex));
__ sub(R0, R0, Operand(R1));
__ mov(CSP, SP);
__ ret();
}
ASSEMBLER_TEST_RUN(SimpleLoadStorePair, test) {
typedef int64_t (*Int64Return)() DART_UNUSED;
EXPECT_EQ(1, EXECUTE_TEST_CODE_INT64(Int64Return, test->entry()));
}
ASSEMBLER_TEST_GENERATE(LoadStorePairOffset, assembler) {
__ SetupDartSP(kTestStackSpace);
__ LoadImmediate(R2, 43, kNoPP);
__ LoadImmediate(R3, 42, kNoPP);
__ sub(SP, SP, Operand(4 * kWordSize));
__ stp(R2, R3, Address::Pair(SP, 2 * kWordSize));
__ ldp(R0, R1, Address::Pair(SP, 2 * kWordSize));
__ add(SP, SP, Operand(4 * kWordSize));
__ sub(R0, R0, Operand(R1));
__ mov(CSP, SP);
__ ret();
}
ASSEMBLER_TEST_RUN(LoadStorePairOffset, test) {
typedef int64_t (*Int64Return)() DART_UNUSED;
EXPECT_EQ(1, EXECUTE_TEST_CODE_INT64(Int64Return, test->entry()));
}
// Logical register operations.
ASSEMBLER_TEST_GENERATE(AndRegs, assembler) {
__ movz(R1, Immediate(43), 0);
+20
View File
@@ -369,6 +369,14 @@ enum LoadStoreRegOp {
FLDRQ = LDR | B26 | B23,
};
// C3.3.14-16
enum LoadStoreRegPairOp {
LoadStoreRegPairMask = 0x3a000000,
LoadStoreRegPairFixed = LoadStoreFixed | B29,
STP = LoadStoreRegPairFixed,
LDP = LoadStoreRegPairFixed | B22,
};
// C3.4.1
enum AddSubImmOp {
AddSubImmMask = 0x1f000000,
@@ -581,6 +589,7 @@ _V(TestAndBranch) \
_V(UnconditionalBranch) \
_V(UnconditionalBranchReg) \
_V(LoadStoreReg) \
_V(LoadStoreRegPair) \
_V(LoadRegLiteral) \
_V(AddSubImm) \
_V(LogicalImm) \
@@ -654,6 +663,8 @@ enum InstructionFields {
kRmBits = 5,
kRtShift = 0,
kRtBits = 5,
kRt2Shift = 10,
kRt2Bits = 5,
// V Registers.
kVdShift = 0,
@@ -674,6 +685,9 @@ enum InstructionFields {
kImm5Bits = 5,
kImm6Shift = 10,
kImm6Bits = 6,
kImm7Shift = 15,
kImm7Bits = 7,
kImm7Mask = 0x7f << kImm7Shift,
kImm8Shift = 13,
kImm8Bits = 8,
kImm9Shift = 12,
@@ -839,6 +853,8 @@ class Instr {
Bits(kRmShift, kRmBits)); }
inline Register RtField() const { return static_cast<Register>(
Bits(kRtShift, kRtBits)); }
inline Register Rt2Field() const { return static_cast<Register>(
Bits(kRt2Shift, kRt2Bits)); }
inline VRegister VdField() const { return static_cast<VRegister>(
Bits(kVdShift, kVdBits)); }
@@ -852,6 +868,10 @@ class Instr {
// Immediates
inline int Imm3Field() const { return Bits(kImm3Shift, kImm3Bits); }
inline int Imm6Field() const { return Bits(kImm6Shift, kImm6Bits); }
inline int Imm7Field() const { return Bits(kImm7Shift, kImm7Bits); }
// Sign-extended Imm7Field()
inline int64_t SImm7Field() const {
return (static_cast<int32_t>(Imm7Field()) << 25) >> 25; }
inline int Imm8Field() const { return Bits(kImm8Shift, kImm8Bits); }
inline int Imm9Field() const { return Bits(kImm9Shift, kImm9Bits); }
// Sign-extended Imm9Field()
+78 -23
View File
@@ -34,6 +34,7 @@ class ARM64Decoder : public ValueObject {
void PrintVRegister(int reg);
void PrintShiftExtendRm(Instr* instr);
void PrintMemOperand(Instr* instr);
void PrintPairMemOperand(Instr* instr);
void PrintS(Instr* instr);
void PrintCondition(Instr* instr);
@@ -216,9 +217,9 @@ void ARM64Decoder::PrintMemOperand(Instr* instr) {
Print("[");
PrintRegister(rn, R31IsSP);
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
remaining_size_in_buffer(),
", #%d",
imm9);
remaining_size_in_buffer(),
", #%d",
imm9);
Print("]");
break;
}
@@ -272,6 +273,41 @@ void ARM64Decoder::PrintMemOperand(Instr* instr) {
}
void ARM64Decoder::PrintPairMemOperand(Instr* instr) {
const Register rn = instr->RnField();
const int32_t simm7 = instr->SImm7Field();
const int32_t offset = simm7 << (2 + instr->Bit(31));
Print("[");
PrintRegister(rn, R31IsSP);
switch (instr->Bits(23, 3)) {
case 1:
// rn + (imm7 << (2 + B31)), post-index, writeback.
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
remaining_size_in_buffer(),
"], #%d !",
offset);
break;
case 2:
// rn + (imm7 << (2 + B31)), pre-index, no writeback.
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
remaining_size_in_buffer(),
", #%d ]",
offset);
break;
case 3:
// rn + (imm7 << (2 + B31)), pre-index, writeback.
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
remaining_size_in_buffer(),
", #%d ]!",
offset);
break;
default:
Print(", ???]");
break;
}
}
// Handle all register based formatting in these functions to reduce the
// complexity of FormatOption.
int ARM64Decoder::FormatRegister(Instr* instr, const char* format) {
@@ -511,28 +547,34 @@ int ARM64Decoder::FormatOption(Instr* instr, const char* format) {
return 5;
}
case 'p': {
if (format[2] == 'a') {
ASSERT(STRING_STARTS_WITH(format, "pcadr"));
const int64_t immhi = instr->SImm19Field();
const int64_t immlo = instr->Bits(29, 2);
const int64_t off = (immhi << 2) | immlo;
const int64_t pc = reinterpret_cast<int64_t>(instr);
const int64_t dest = pc + off;
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
remaining_size_in_buffer(),
"0x%"Px64,
dest);
if (format[1] == 'c') {
if (format[2] == 'a') {
ASSERT(STRING_STARTS_WITH(format, "pcadr"));
const int64_t immhi = instr->SImm19Field();
const int64_t immlo = instr->Bits(29, 2);
const int64_t off = (immhi << 2) | immlo;
const int64_t pc = reinterpret_cast<int64_t>(instr);
const int64_t dest = pc + off;
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
remaining_size_in_buffer(),
"0x%"Px64,
dest);
} else {
ASSERT(STRING_STARTS_WITH(format, "pcldr"));
const int64_t off = instr->SImm19Field() << 2;
const int64_t pc = reinterpret_cast<int64_t>(instr);
const int64_t dest = pc + off;
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
remaining_size_in_buffer(),
"0x%"Px64,
dest);
}
return 5;
} else {
ASSERT(STRING_STARTS_WITH(format, "pcldr"));
const int64_t off = instr->SImm19Field() << 2;
const int64_t pc = reinterpret_cast<int64_t>(instr);
const int64_t dest = pc + off;
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
remaining_size_in_buffer(),
"0x%"Px64,
dest);
ASSERT(STRING_STARTS_WITH(format, "pmemop"));
PrintPairMemOperand(instr);
return 6;
}
return 5;
}
case 'r': {
return FormatRegister(instr, format);
@@ -674,6 +716,17 @@ void ARM64Decoder::DecodeLoadStoreReg(Instr* instr) {
}
void ARM64Decoder::DecodeLoadStoreRegPair(Instr* instr) {
if (instr->Bit(22) == 1) {
// Load.
Format(instr, "ldp'sf 'rt, 'ra, 'pmemop");
} else {
// Store.
Format(instr, "stp'sf 'rt, 'ra, 'pmemop");
}
}
void ARM64Decoder::DecodeLoadRegLiteral(Instr* instr) {
if ((instr->Bit(31) != 0) || (instr->Bit(29) != 0) ||
(instr->Bits(24, 3) != 0)) {
@@ -881,6 +934,8 @@ void ARM64Decoder::DecodeCompareBranch(Instr* instr) {
void ARM64Decoder::DecodeLoadStore(Instr* instr) {
if (instr->IsLoadStoreRegOp()) {
DecodeLoadStoreReg(instr);
} else if (instr->IsLoadStoreRegPairOp()) {
DecodeLoadStoreRegPair(instr);
} else if (instr->IsLoadRegLiteralOp()) {
DecodeLoadRegLiteral(instr);
} else {
+96
View File
@@ -1724,6 +1724,100 @@ void Simulator::DecodeLoadStoreReg(Instr* instr) {
}
void Simulator::DecodeLoadStoreRegPair(Instr* instr) {
const int32_t opc = instr->Bits(23, 3);
const Register rn = instr->RnField();
const Register rt = instr->RtField();
const Register rt2 = instr->Rt2Field();
const int64_t rn_val = get_register(rn, R31IsSP);
const intptr_t shift = 2 + instr->SFField();
const intptr_t size = 1 << shift;
const int32_t offset = (instr->SImm7Field() << shift);
uword address = 0;
uword wb_address = 0;
bool wb = false;
if ((instr->Bits(30, 2) == 3) || (instr->Bit(26) != 0)) {
UnimplementedInstruction(instr);
return;
}
// Calculate address.
switch (opc) {
case 1:
address = rn_val;
wb_address = rn_val + offset;
wb = true;
break;
case 2:
address = rn_val + offset;
break;
case 3:
address = rn_val + offset;
wb_address = address;
wb = true;
break;
default:
UnimplementedInstruction(instr);
return;
}
// Check the address.
if (IsIllegalAddress(address)) {
HandleIllegalAccess(address, instr);
return;
}
// Do access.
if (instr->Bit(22)) {
// Format(instr, "ldp'sf 'rt, 'ra, 'memop");
const bool signd = instr->Bit(30) == 1;
int64_t val1 = 0; // Sign extend into an int64_t.
int64_t val2 = 0;
if (instr->Bit(31) == 1) {
// 64-bit read.
val1 = ReadX(address, instr);
val2 = ReadX(address + size, instr);
} else {
if (signd) {
val1 = static_cast<int64_t>(ReadW(address, instr));
val2 = static_cast<int64_t>(ReadW(address + size, instr));
} else {
val1 = static_cast<int64_t>(ReadWU(address, instr));
val2 = static_cast<int64_t>(ReadWU(address + size, instr));
}
}
// Write to register.
if (instr->Bit(31) == 1) {
set_register(instr, rt, val1, R31IsZR);
set_register(instr, rt2, val2, R31IsZR);
} else {
set_wregister(rt, static_cast<int32_t>(val1), R31IsZR);
set_wregister(rt2, static_cast<int32_t>(val2), R31IsZR);
}
} else {
// Format(instr, "stp'sf 'rt, 'ra, 'memop");
if (instr->Bit(31) == 1) {
const int64_t val1 = get_register(rt, R31IsZR);
const int64_t val2 = get_register(rt2, R31IsZR);
WriteX(address, val1, instr);
WriteX(address + size, val2, instr);
} else {
const int32_t val1 = get_wregister(rt, R31IsZR);
const int32_t val2 = get_wregister(rt2, R31IsZR);
WriteW(address, val1, instr);
WriteW(address + size, val2, instr);
}
}
// Do writeback.
if (wb) {
set_register(instr, rn, wb_address, R31IsSP);
}
}
void Simulator::DecodeLoadRegLiteral(Instr* instr) {
if ((instr->Bit(31) != 0) || (instr->Bit(29) != 0) ||
(instr->Bits(24, 3) != 0)) {
@@ -1748,6 +1842,8 @@ void Simulator::DecodeLoadRegLiteral(Instr* instr) {
void Simulator::DecodeLoadStore(Instr* instr) {
if (instr->IsLoadStoreRegOp()) {
DecodeLoadStoreReg(instr);
} else if (instr->IsLoadStoreRegPairOp()) {
DecodeLoadStoreRegPair(instr);
} else if (instr->IsLoadRegLiteralOp()) {
DecodeLoadRegLiteral(instr);
} else {