From fb29f5e3425247d712fb347e76dfd0412df3df2e Mon Sep 17 00:00:00 2001 From: "zra@google.com" Date: Tue, 7 Oct 2014 16:25:56 +0000 Subject: [PATCH] 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 --- runtime/vm/assembler_arm64.h | 80 +++++++++++++++++++++-- runtime/vm/assembler_arm64_test.cc | 38 +++++++++++ runtime/vm/constants_arm64.h | 20 ++++++ runtime/vm/disassembler_arm64.cc | 101 ++++++++++++++++++++++------- runtime/vm/simulator_arm64.cc | 96 +++++++++++++++++++++++++++ 5 files changed, 308 insertions(+), 27 deletions(-) diff --git a/runtime/vm/assembler_arm64.h b/runtime/vm/assembler_arm64.h index 7ec2eca72d6..2eb1d972dd0 100644 --- a/runtime/vm/assembler_arm64.h +++ b/runtime/vm/assembler_arm64.h @@ -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(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(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(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(crt) << kRtShift) | + (static_cast(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)); diff --git a/runtime/vm/assembler_arm64_test.cc b/runtime/vm/assembler_arm64_test.cc index e5e5e05a0aa..379e43ed32d 100644 --- a/runtime/vm/assembler_arm64_test.cc +++ b/runtime/vm/assembler_arm64_test.cc @@ -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); diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h index ffe23036d98..26a2ddf6cd7 100644 --- a/runtime/vm/constants_arm64.h +++ b/runtime/vm/constants_arm64.h @@ -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( Bits(kRtShift, kRtBits)); } + inline Register Rt2Field() const { return static_cast( + Bits(kRt2Shift, kRt2Bits)); } inline VRegister VdField() const { return static_cast( 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(Imm7Field()) << 25) >> 25; } inline int Imm8Field() const { return Bits(kImm8Shift, kImm8Bits); } inline int Imm9Field() const { return Bits(kImm9Shift, kImm9Bits); } // Sign-extended Imm9Field() diff --git a/runtime/vm/disassembler_arm64.cc b/runtime/vm/disassembler_arm64.cc index 6b38b78d2ae..423ed634cd8 100644 --- a/runtime/vm/disassembler_arm64.cc +++ b/runtime/vm/disassembler_arm64.cc @@ -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(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(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(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(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 { diff --git a/runtime/vm/simulator_arm64.cc b/runtime/vm/simulator_arm64.cc index dd4e611818f..b81697bd738 100644 --- a/runtime/vm/simulator_arm64.cc +++ b/runtime/vm/simulator_arm64.cc @@ -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(ReadW(address, instr)); + val2 = static_cast(ReadW(address + size, instr)); + } else { + val1 = static_cast(ReadWU(address, instr)); + val2 = static_cast(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(val1), R31IsZR); + set_wregister(rt2, static_cast(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 {