diff options
author | Alexander Gilday <alexander.gilday@arm.com> | 2018-04-16 17:42:00 +0100 |
---|---|---|
committer | Alexander Gilday <alexander.gilday@arm.com> | 2018-04-17 08:52:36 +0100 |
commit | 4e5bad9e4915ba673bfe016dbdced31fe3cb7687 (patch) | |
tree | 6eaebdab0d5b98479841386a6ea96309fd28bc30 | |
parent | 4e52d4db0b9629aa57ecd354afb31bc814eef5b7 (diff) |
Add support for compare and swap in memory instructions.
Part of Armv8.1 Large System Extensions. Includes:
- CAS, CASA, CASL, CASAL - Compare and swap word or doubleword in memory.
- CASB, CASAB, CASLB, CASALB - Compare and swap byte in memory.
- CASH, CASAH, CASLH, CASALH - Compare and swap halfword in memory.
- CASP, CASPA, CASPL, CASPAL - Compare and swap pair of words or doublewords in memory.
Change-Id: I1b55b4f53a987c455d1cbc96210856ebeb9f63bf
-rw-r--r-- | src/aarch64/assembler-aarch64.cc | 116 | ||||
-rw-r--r-- | src/aarch64/assembler-aarch64.h | 64 | ||||
-rw-r--r-- | src/aarch64/constants-aarch64.h | 35 | ||||
-rw-r--r-- | src/aarch64/disasm-aarch64.cc | 239 | ||||
-rw-r--r-- | src/aarch64/macro-assembler-aarch64.h | 48 | ||||
-rw-r--r-- | src/aarch64/operands-aarch64.h | 22 | ||||
-rw-r--r-- | src/aarch64/simulator-aarch64.cc | 368 | ||||
-rw-r--r-- | src/aarch64/simulator-aarch64.h | 4 | ||||
-rw-r--r-- | test/aarch64/test-assembler-aarch64.cc | 415 | ||||
-rw-r--r-- | test/aarch64/test-disasm-aarch64.cc | 64 |
10 files changed, 1130 insertions, 245 deletions
diff --git a/src/aarch64/assembler-aarch64.cc b/src/aarch64/assembler-aarch64.cc index 41841e54..147bb86d 100644 --- a/src/aarch64/assembler-aarch64.cc +++ b/src/aarch64/assembler-aarch64.cc @@ -1388,6 +1388,74 @@ void Assembler::ldar(const Register& rt, const MemOperand& src) { } +// clang-format off +#define COMPARE_AND_SWAP_W_X_LIST(V) \ + V(cas, CAS) \ + V(casa, CASA) \ + V(casl, CASL) \ + V(casal, CASAL) +// clang-format on + +#define DEFINE_ASM_FUNC(FN, OP) \ + void Assembler::FN(const Register& rs, \ + const Register& rt, \ + const MemOperand& src) { \ + VIXL_ASSERT(src.IsImmediateOffset() && (src.GetOffset() == 0)); \ + LoadStoreExclusive op = rt.Is64Bits() ? OP##_x : OP##_w; \ + Emit(op | Rs(rs) | Rt(rt) | Rt2_mask | RnSP(src.GetBaseRegister())); \ + } +COMPARE_AND_SWAP_W_X_LIST(DEFINE_ASM_FUNC) +#undef DEFINE_ASM_FUNC + +// clang-format off +#define COMPARE_AND_SWAP_W_LIST(V) \ + V(casb, CASB) \ + V(casab, CASAB) \ + V(caslb, CASLB) \ + V(casalb, CASALB) \ + V(cash, CASH) \ + V(casah, CASAH) \ + V(caslh, CASLH) \ + V(casalh, CASALH) +// clang-format on + +#define DEFINE_ASM_FUNC(FN, OP) \ + void Assembler::FN(const Register& rs, \ + const Register& rt, \ + const MemOperand& src) { \ + VIXL_ASSERT(src.IsImmediateOffset() && (src.GetOffset() == 0)); \ + Emit(OP | Rs(rs) | Rt(rt) | Rt2_mask | RnSP(src.GetBaseRegister())); \ + } +COMPARE_AND_SWAP_W_LIST(DEFINE_ASM_FUNC) +#undef DEFINE_ASM_FUNC + + +// clang-format off +#define COMPARE_AND_SWAP_PAIR_LIST(V) \ + V(casp, CASP) \ + V(caspa, CASPA) \ + V(caspl, CASPL) \ + V(caspal, CASPAL) +// clang-format on + +#define DEFINE_ASM_FUNC(FN, OP) \ + void Assembler::FN(const Register& rs, \ + const Register& rs1, \ + const Register& rt, \ + const Register& rt1, \ + const MemOperand& src) { \ + USE(rs1, rt1); \ + VIXL_ASSERT(src.IsImmediateOffset() && (src.GetOffset() == 0)); \ + VIXL_ASSERT(AreEven(rs, rt)); \ + VIXL_ASSERT(AreConsecutive(rs, rs1)); \ + VIXL_ASSERT(AreConsecutive(rt, rt1)); \ + LoadStoreExclusive op = rt.Is64Bits() ? OP##_x : OP##_w; \ + Emit(op | Rs(rs) | Rt(rt) | Rt2_mask | RnSP(src.GetBaseRegister())); \ + } +COMPARE_AND_SWAP_PAIR_LIST(DEFINE_ASM_FUNC) +#undef DEFINE_ASM_FUNC + + void Assembler::prfm(PrefetchOperation op, const MemOperand& address, LoadStoreScalingOption option) { @@ -4880,6 +4948,54 @@ bool AreSameSizeAndType(const CPURegister& reg1, return match; } +bool AreEven(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3, + const CPURegister& reg4, + const CPURegister& reg5, + const CPURegister& reg6, + const CPURegister& reg7, + const CPURegister& reg8) { + VIXL_ASSERT(reg1.IsValid()); + bool even = (reg1.GetCode() % 2) == 0; + even &= !reg2.IsValid() || ((reg2.GetCode() % 2) == 0); + even &= !reg3.IsValid() || ((reg3.GetCode() % 2) == 0); + even &= !reg4.IsValid() || ((reg4.GetCode() % 2) == 0); + even &= !reg5.IsValid() || ((reg5.GetCode() % 2) == 0); + even &= !reg6.IsValid() || ((reg6.GetCode() % 2) == 0); + even &= !reg7.IsValid() || ((reg7.GetCode() % 2) == 0); + even &= !reg8.IsValid() || ((reg8.GetCode() % 2) == 0); + return even; +} + + +bool AreConsecutive(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3, + const CPURegister& reg4) { + VIXL_ASSERT(reg1.IsValid()); + + if (!reg2.IsValid()) { + return true; + } else if (reg2.GetCode() != ((reg1.GetCode() + 1) % kNumberOfRegisters)) { + return false; + } + + if (!reg3.IsValid()) { + return true; + } else if (reg3.GetCode() != ((reg2.GetCode() + 1) % kNumberOfRegisters)) { + return false; + } + + if (!reg4.IsValid()) { + return true; + } else if (reg4.GetCode() != ((reg3.GetCode() + 1) % kNumberOfRegisters)) { + return false; + } + + return true; +} + bool AreSameFormat(const VRegister& reg1, const VRegister& reg2, diff --git a/src/aarch64/assembler-aarch64.h b/src/aarch64/assembler-aarch64.h index 1c25886b..0c7abcbc 100644 --- a/src/aarch64/assembler-aarch64.h +++ b/src/aarch64/assembler-aarch64.h @@ -1173,6 +1173,70 @@ class Assembler : public vixl::internal::AssemblerBase { // Load-acquire register. void ldar(const Register& rt, const MemOperand& src); + // Compare and Swap word or doubleword in memory [Armv8.1]. + void cas(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap word or doubleword in memory [Armv8.1]. + void casa(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap word or doubleword in memory [Armv8.1]. + void casl(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap word or doubleword in memory [Armv8.1]. + void casal(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap byte in memory [Armv8.1]. + void casb(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap byte in memory [Armv8.1]. + void casab(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap byte in memory [Armv8.1]. + void caslb(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap byte in memory [Armv8.1]. + void casalb(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap halfword in memory [Armv8.1]. + void cash(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap halfword in memory [Armv8.1]. + void casah(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap halfword in memory [Armv8.1]. + void caslh(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap halfword in memory [Armv8.1]. + void casalh(const Register& rs, const Register& rt, const MemOperand& src); + + // Compare and Swap Pair of words or doublewords in memory [Armv8.1]. + void casp(const Register& rs, + const Register& rs2, + const Register& rt, + const Register& rt2, + const MemOperand& src); + + // Compare and Swap Pair of words or doublewords in memory [Armv8.1]. + void caspa(const Register& rs, + const Register& rs2, + const Register& rt, + const Register& rt2, + const MemOperand& src); + + // Compare and Swap Pair of words or doublewords in memory [Armv8.1]. + void caspl(const Register& rs, + const Register& rs2, + const Register& rt, + const Register& rt2, + const MemOperand& src); + + // Compare and Swap Pair of words or doublewords in memory [Armv8.1]. + void caspal(const Register& rs, + const Register& rs2, + const Register& rt, + const Register& rt2, + const MemOperand& src); + // Prefetch memory. void prfm(PrefetchOperation op, const MemOperand& addr, diff --git a/src/aarch64/constants-aarch64.h b/src/aarch64/constants-aarch64.h index b57bdbfe..64a42469 100644 --- a/src/aarch64/constants-aarch64.h +++ b/src/aarch64/constants-aarch64.h @@ -977,7 +977,40 @@ enum LoadStoreExclusive { LDARB_w = LoadStoreExclusiveFixed | 0x00C08000, LDARH_w = LoadStoreExclusiveFixed | 0x40C08000, LDAR_w = LoadStoreExclusiveFixed | 0x80C08000, - LDAR_x = LoadStoreExclusiveFixed | 0xC0C08000 + LDAR_x = LoadStoreExclusiveFixed | 0xC0C08000, + + // v8.1 Load/store exclusive ops + LSEBit_l = 0x00400000, + LSEBit_o0 = 0x00008000, + LSEBit_sz = 0x40000000, + CASFixed = LoadStoreExclusiveFixed | 0x80A00000, + CASBFixed = LoadStoreExclusiveFixed | 0x00A00000, + CASHFixed = LoadStoreExclusiveFixed | 0x40A00000, + CASPFixed = LoadStoreExclusiveFixed | 0x00200000, + CAS_w = CASFixed, + CAS_x = CASFixed | LSEBit_sz, + CASA_w = CASFixed | LSEBit_l, + CASA_x = CASFixed | LSEBit_l | LSEBit_sz, + CASL_w = CASFixed | LSEBit_o0, + CASL_x = CASFixed | LSEBit_o0 | LSEBit_sz, + CASAL_w = CASFixed | LSEBit_l | LSEBit_o0, + CASAL_x = CASFixed | LSEBit_l | LSEBit_o0 | LSEBit_sz, + CASB = CASBFixed, + CASAB = CASBFixed | LSEBit_l, + CASLB = CASBFixed | LSEBit_o0, + CASALB = CASBFixed | LSEBit_l | LSEBit_o0, + CASH = CASHFixed, + CASAH = CASHFixed | LSEBit_l, + CASLH = CASHFixed | LSEBit_o0, + CASALH = CASHFixed | LSEBit_l | LSEBit_o0, + CASP_w = CASPFixed, + CASP_x = CASPFixed | LSEBit_sz, + CASPA_w = CASPFixed | LSEBit_l, + CASPA_x = CASPFixed | LSEBit_l | LSEBit_sz, + CASPL_w = CASPFixed | LSEBit_o0, + CASPL_x = CASPFixed | LSEBit_o0 | LSEBit_sz, + CASPAL_w = CASPFixed | LSEBit_l | LSEBit_o0, + CASPAL_x = CASPFixed | LSEBit_l | LSEBit_o0 | LSEBit_sz }; // Conditional compare. diff --git a/src/aarch64/disasm-aarch64.cc b/src/aarch64/disasm-aarch64.cc index defa07f8..bc52fb87 100644 --- a/src/aarch64/disasm-aarch64.cc +++ b/src/aarch64/disasm-aarch64.cc @@ -1240,143 +1240,99 @@ void Disassembler::VisitLoadStorePairNonTemporal(const Instruction *instr) { Format(instr, mnemonic, form); } +// clang-format off +#define LOAD_STORE_EXCLUSIVE_LIST(V) \ + V(STXRB_w, "stxrb", "'Ws, 'Wt") \ + V(STXRH_w, "stxrh", "'Ws, 'Wt") \ + V(STXR_w, "stxr", "'Ws, 'Wt") \ + V(STXR_x, "stxr", "'Ws, 'Xt") \ + V(LDXRB_w, "ldxrb", "'Wt") \ + V(LDXRH_w, "ldxrh", "'Wt") \ + V(LDXR_w, "ldxr", "'Wt") \ + V(LDXR_x, "ldxr", "'Xt") \ + V(STXP_w, "stxp", "'Ws, 'Wt, 'Wt2") \ + V(STXP_x, "stxp", "'Ws, 'Xt, 'Xt2") \ + V(LDXP_w, "ldxp", "'Wt, 'Wt2") \ + V(LDXP_x, "ldxp", "'Xt, 'Xt2") \ + V(STLXRB_w, "stlxrb", "'Ws, 'Wt") \ + V(STLXRH_w, "stlxrh", "'Ws, 'Wt") \ + V(STLXR_w, "stlxr", "'Ws, 'Wt") \ + V(STLXR_x, "stlxr", "'Ws, 'Xt") \ + V(LDAXRB_w, "ldaxrb", "'Wt") \ + V(LDAXRH_w, "ldaxrh", "'Wt") \ + V(LDAXR_w, "ldaxr", "'Wt") \ + V(LDAXR_x, "ldaxr", "'Xt") \ + V(STLXP_w, "stlxp", "'Ws, 'Wt, 'Wt2") \ + V(STLXP_x, "stlxp", "'Ws, 'Xt, 'Xt2") \ + V(LDAXP_w, "ldaxp", "'Wt, 'Wt2") \ + V(LDAXP_x, "ldaxp", "'Xt, 'Xt2") \ + V(STLRB_w, "stlrb", "'Wt") \ + V(STLRH_w, "stlrh", "'Wt") \ + V(STLR_w, "stlr", "'Wt") \ + V(STLR_x, "stlr", "'Xt") \ + V(LDARB_w, "ldarb", "'Wt") \ + V(LDARH_w, "ldarh", "'Wt") \ + V(LDAR_w, "ldar", "'Wt") \ + V(LDAR_x, "ldar", "'Xt") \ + V(CAS_w, "cas", "'Ws, 'Wt") \ + V(CAS_x, "cas", "'Xs, 'Xt") \ + V(CASA_w, "casa", "'Ws, 'Wt") \ + V(CASA_x, "casa", "'Xs, 'Xt") \ + V(CASL_w, "casl", "'Ws, 'Wt") \ + V(CASL_x, "casl", "'Xs, 'Xt") \ + V(CASAL_w, "casal", "'Ws, 'Wt") \ + V(CASAL_x, "casal", "'Xs, 'Xt") \ + V(CASB, "casb", "'Ws, 'Wt") \ + V(CASAB, "casab", "'Ws, 'Wt") \ + V(CASLB, "caslb", "'Ws, 'Wt") \ + V(CASALB, "casalb", "'Ws, 'Wt") \ + V(CASH, "cash", "'Ws, 'Wt") \ + V(CASAH, "casah", "'Ws, 'Wt") \ + V(CASLH, "caslh", "'Ws, 'Wt") \ + V(CASALH, "casalh", "'Ws, 'Wt") \ + V(CASP_w, "casp", "'Ws, 'W(s+1), 'Wt, 'W(t+1)") \ + V(CASP_x, "casp", "'Xs, 'X(s+1), 'Xt, 'X(t+1)") \ + V(CASPA_w, "caspa", "'Ws, 'W(s+1), 'Wt, 'W(t+1)") \ + V(CASPA_x, "caspa", "'Xs, 'X(s+1), 'Xt, 'X(t+1)") \ + V(CASPL_w, "caspl", "'Ws, 'W(s+1), 'Wt, 'W(t+1)") \ + V(CASPL_x, "caspl", "'Xs, 'X(s+1), 'Xt, 'X(t+1)") \ + V(CASPAL_w, "caspal", "'Ws, 'W(s+1), 'Wt, 'W(t+1)") \ + V(CASPAL_x, "caspal", "'Xs, 'X(s+1), 'Xt, 'X(t+1)") +// clang-format on + void Disassembler::VisitLoadStoreExclusive(const Instruction *instr) { const char *mnemonic = "unimplemented"; const char *form; switch (instr->Mask(LoadStoreExclusiveMask)) { - case STXRB_w: - mnemonic = "stxrb"; - form = "'Ws, 'Wt, ['Xns]"; - break; - case STXRH_w: - mnemonic = "stxrh"; - form = "'Ws, 'Wt, ['Xns]"; - break; - case STXR_w: - mnemonic = "stxr"; - form = "'Ws, 'Wt, ['Xns]"; - break; - case STXR_x: - mnemonic = "stxr"; - form = "'Ws, 'Xt, ['Xns]"; - break; - case LDXRB_w: - mnemonic = "ldxrb"; - form = "'Wt, ['Xns]"; - break; - case LDXRH_w: - mnemonic = "ldxrh"; - form = "'Wt, ['Xns]"; - break; - case LDXR_w: - mnemonic = "ldxr"; - form = "'Wt, ['Xns]"; - break; - case LDXR_x: - mnemonic = "ldxr"; - form = "'Xt, ['Xns]"; - break; - case STXP_w: - mnemonic = "stxp"; - form = "'Ws, 'Wt, 'Wt2, ['Xns]"; - break; - case STXP_x: - mnemonic = "stxp"; - form = "'Ws, 'Xt, 'Xt2, ['Xns]"; - break; - case LDXP_w: - mnemonic = "ldxp"; - form = "'Wt, 'Wt2, ['Xns]"; - break; - case LDXP_x: - mnemonic = "ldxp"; - form = "'Xt, 'Xt2, ['Xns]"; - break; - case STLXRB_w: - mnemonic = "stlxrb"; - form = "'Ws, 'Wt, ['Xns]"; - break; - case STLXRH_w: - mnemonic = "stlxrh"; - form = "'Ws, 'Wt, ['Xns]"; - break; - case STLXR_w: - mnemonic = "stlxr"; - form = "'Ws, 'Wt, ['Xns]"; - break; - case STLXR_x: - mnemonic = "stlxr"; - form = "'Ws, 'Xt, ['Xns]"; - break; - case LDAXRB_w: - mnemonic = "ldaxrb"; - form = "'Wt, ['Xns]"; - break; - case LDAXRH_w: - mnemonic = "ldaxrh"; - form = "'Wt, ['Xns]"; - break; - case LDAXR_w: - mnemonic = "ldaxr"; - form = "'Wt, ['Xns]"; - break; - case LDAXR_x: - mnemonic = "ldaxr"; - form = "'Xt, ['Xns]"; - break; - case STLXP_w: - mnemonic = "stlxp"; - form = "'Ws, 'Wt, 'Wt2, ['Xns]"; - break; - case STLXP_x: - mnemonic = "stlxp"; - form = "'Ws, 'Xt, 'Xt2, ['Xns]"; - break; - case LDAXP_w: - mnemonic = "ldaxp"; - form = "'Wt, 'Wt2, ['Xns]"; - break; - case LDAXP_x: - mnemonic = "ldaxp"; - form = "'Xt, 'Xt2, ['Xns]"; - break; - case STLRB_w: - mnemonic = "stlrb"; - form = "'Wt, ['Xns]"; - break; - case STLRH_w: - mnemonic = "stlrh"; - form = "'Wt, ['Xns]"; - break; - case STLR_w: - mnemonic = "stlr"; - form = "'Wt, ['Xns]"; - break; - case STLR_x: - mnemonic = "stlr"; - form = "'Xt, ['Xns]"; - break; - case LDARB_w: - mnemonic = "ldarb"; - form = "'Wt, ['Xns]"; - break; - case LDARH_w: - mnemonic = "ldarh"; - form = "'Wt, ['Xns]"; - break; - case LDAR_w: - mnemonic = "ldar"; - form = "'Wt, ['Xns]"; - break; - case LDAR_x: - mnemonic = "ldar"; - form = "'Xt, ['Xns]"; - break; +#define LSX(A, B, C) \ + case A: \ + mnemonic = B; \ + form = C ", ['Xns]"; \ + break; + LOAD_STORE_EXCLUSIVE_LIST(LSX) +#undef LSX default: form = "(LoadStoreExclusive)"; } + + switch (instr->Mask(LoadStoreExclusiveMask)) { + case CASP_w: + case CASP_x: + case CASPA_w: + case CASPA_x: + case CASPL_w: + case CASPL_x: + case CASPAL_w: + case CASPAL_x: + if ((instr->GetRs() % 2 == 1) || (instr->GetRt() % 2 == 1)) { + mnemonic = "unallocated"; + form = "(LoadStoreExclusive)"; + } + break; + } + Format(instr, mnemonic, form); } @@ -4367,12 +4323,37 @@ int Disassembler::SubstituteRegisterField(const Instruction *instr, } } break; + case '(': { + switch (format[2]) { + case 's': + reg_num = instr->GetRs(); + break; + case 't': + reg_num = instr->GetRt(); + break; + default: + VIXL_UNREACHABLE(); + } + + VIXL_ASSERT(format[3] == '+'); + int i = 4; + int addition = 0; + while (format[i] != ')') { + VIXL_ASSERT((format[i] >= '0') && (format[i] <= '9')); + addition *= 10; + addition += format[i] - '0'; + ++i; + } + reg_num += addition; + field_len = i + 1; + break; + } default: VIXL_UNREACHABLE(); } // Increase field length for registers tagged as stack. - if (format[2] == 's') { + if (format[1] != '(' && format[2] == 's') { field_len = 3; } diff --git a/src/aarch64/macro-assembler-aarch64.h b/src/aarch64/macro-assembler-aarch64.h index acb65ed9..e0789306 100644 --- a/src/aarch64/macro-assembler-aarch64.h +++ b/src/aarch64/macro-assembler-aarch64.h @@ -1495,6 +1495,54 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface { SingleEmissionCheckScope guard(this); ldaxrh(rt, src); } + +// clang-format off +#define COMPARE_AND_SWAP_SINGLE_MACRO_LIST(V) \ + V(cas, Cas) \ + V(casa, Casa) \ + V(casl, Casl) \ + V(casal, Casal) \ + V(casb, Casb) \ + V(casab, Casab) \ + V(caslb, Caslb) \ + V(casalb, Casalb) \ + V(cash, Cash) \ + V(casah, Casah) \ + V(caslh, Caslh) \ + V(casalh, Casalh) +// clang-format on + +#define DEFINE_MACRO_ASM_FUNC(ASM, MASM) \ + void MASM(const Register& rs, const Register& rt, const MemOperand& src) { \ + VIXL_ASSERT(allow_macro_instructions_); \ + SingleEmissionCheckScope guard(this); \ + ASM(rs, rt, src); \ + } + COMPARE_AND_SWAP_SINGLE_MACRO_LIST(DEFINE_MACRO_ASM_FUNC) +#undef DEFINE_MACRO_ASM_FUNC + + +// clang-format off +#define COMPARE_AND_SWAP_PAIR_MACRO_LIST(V) \ + V(casp, Casp) \ + V(caspa, Caspa) \ + V(caspl, Caspl) \ + V(caspal, Caspal) +// clang-format on + +#define DEFINE_MACRO_ASM_FUNC(ASM, MASM) \ + void MASM(const Register& rs, \ + const Register& rs2, \ + const Register& rt, \ + const Register& rt2, \ + const MemOperand& src) { \ + VIXL_ASSERT(allow_macro_instructions_); \ + SingleEmissionCheckScope guard(this); \ + ASM(rs, rs2, rt, rt2, src); \ + } + COMPARE_AND_SWAP_PAIR_MACRO_LIST(DEFINE_MACRO_ASM_FUNC) +#undef DEFINE_MACRO_ASM_FUNC + void Ldnp(const CPURegister& rt, const CPURegister& rt2, const MemOperand& src) { diff --git a/src/aarch64/operands-aarch64.h b/src/aarch64/operands-aarch64.h index dff73797..2fb1f1d3 100644 --- a/src/aarch64/operands-aarch64.h +++ b/src/aarch64/operands-aarch64.h @@ -494,6 +494,28 @@ bool AreSameSizeAndType(const CPURegister& reg1, const CPURegister& reg7 = NoCPUReg, const CPURegister& reg8 = NoCPUReg); +// AreEven returns true if all of the specified registers have even register +// indices. Arguments set to NoReg are ignored, as are any subsequent +// arguments. At least one argument (reg1) must be valid (not NoCPUReg). +bool AreEven(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoReg, + const CPURegister& reg4 = NoReg, + const CPURegister& reg5 = NoReg, + const CPURegister& reg6 = NoReg, + const CPURegister& reg7 = NoReg, + const CPURegister& reg8 = NoReg); + + +// AreConsecutive returns true if all of the specified registers are +// consecutive in the register file. Arguments set to NoReg are ignored, as are +// any subsequent arguments. At least one argument (reg1) must be valid +// (not NoCPUReg). +bool AreConsecutive(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoCPUReg, + const CPURegister& reg4 = NoCPUReg); + // AreSameFormat returns true if all of the specified VRegisters have the same // vector format. Arguments set to NoReg are ignored, as are any subsequent diff --git a/src/aarch64/simulator-aarch64.cc b/src/aarch64/simulator-aarch64.cc index 268643dd..5432c1f3 100644 --- a/src/aarch64/simulator-aarch64.cc +++ b/src/aarch64/simulator-aarch64.cc @@ -1545,6 +1545,100 @@ void Simulator::PrintExclusiveAccessWarning() { } } +template <typename T> +void Simulator::CompareAndSwapHelper(const Instruction* instr) { + unsigned rs = instr->GetRs(); + unsigned rt = instr->GetRt(); + unsigned rn = instr->GetRn(); + + unsigned element_size = sizeof(T); + uint64_t address = ReadRegister<uint64_t>(rn, Reg31IsStackPointer); + + bool is_acquire = instr->ExtractBit(22) == 1; + bool is_release = instr->ExtractBit(15) == 1; + + T comparevalue = ReadRegister<T>(rs); + T newvalue = ReadRegister<T>(rt); + + // The architecture permits that the data read clears any exclusive monitors + // associated with that location, even if the compare subsequently fails. + local_monitor_.Clear(); + + T data = Memory::Read<T>(address); + if (is_acquire) { + // Approximate load-acquire by issuing a full barrier after the load. + __sync_synchronize(); + } + + if (data == comparevalue) { + if (is_release) { + // Approximate store-release by issuing a full barrier before the store. + __sync_synchronize(); + } + Memory::Write<T>(address, newvalue); + LogWrite(address, rt, GetPrintRegisterFormatForSize(element_size)); + } + WriteRegister<T>(rs, data); + LogRead(address, rs, GetPrintRegisterFormatForSize(element_size)); +} + +template <typename T> +void Simulator::CompareAndSwapPairHelper(const Instruction* instr) { + VIXL_ASSERT((sizeof(T) == 4) || (sizeof(T) == 8)); + unsigned rs = instr->GetRs(); + unsigned rt = instr->GetRt(); + unsigned rn = instr->GetRn(); + + VIXL_ASSERT((rs % 2 == 0) && (rs % 2 == 0)); + + unsigned element_size = sizeof(T); + uint64_t address = ReadRegister<uint64_t>(rn, Reg31IsStackPointer); + uint64_t address2 = address + element_size; + + bool is_acquire = instr->ExtractBit(22) == 1; + bool is_release = instr->ExtractBit(15) == 1; + + T comparevalue_high = ReadRegister<T>(rs + 1); + T comparevalue_low = ReadRegister<T>(rs); + T newvalue_high = ReadRegister<T>(rt + 1); + T newvalue_low = ReadRegister<T>(rt); + + // The architecture permits that the data read clears any exclusive monitors + // associated with that location, even if the compare subsequently fails. + local_monitor_.Clear(); + + T data_high = Memory::Read<T>(address); + T data_low = Memory::Read<T>(address2); + + if (is_acquire) { + // Approximate load-acquire by issuing a full barrier after the load. + __sync_synchronize(); + } + + bool same = + (data_high == comparevalue_high) && (data_low == comparevalue_low); + if (same) { + if (is_release) { + // Approximate store-release by issuing a full barrier before the store. + __sync_synchronize(); + } + + Memory::Write<T>(address, newvalue_high); + Memory::Write<T>(address2, newvalue_low); + } + + WriteRegister<T>(rs + 1, data_high); + WriteRegister<T>(rs, data_low); + + LogRead(address, rs + 1, GetPrintRegisterFormatForSize(element_size)); + LogRead(address2, rs, GetPrintRegisterFormatForSize(element_size)); + + if (same) { + LogWrite(address, rt + 1, GetPrintRegisterFormatForSize(element_size)); + LogWrite(address2, rt, GetPrintRegisterFormatForSize(element_size)); + } +} + void Simulator::VisitLoadStoreExclusive(const Instruction* instr) { PrintExclusiveAccessWarning(); @@ -1579,129 +1673,173 @@ void Simulator::VisitLoadStoreExclusive(const Instruction* instr) { VIXL_ALIGNMENT_EXCEPTION(); } - if (is_load) { - if (is_exclusive) { - local_monitor_.MarkExclusive(address, access_size); - } else { - // Any non-exclusive load can clear the local monitor as a side effect. We - // don't need to do this, but it is useful to stress the simulated code. - local_monitor_.Clear(); - } - // Use NoRegLog to suppress the register trace (LOG_REGS, LOG_FP_REGS). We - // will print a more detailed log. - switch (op) { - case LDXRB_w: - case LDAXRB_w: - case LDARB_w: - WriteWRegister(rt, Memory::Read<uint8_t>(address), NoRegLog); - break; - case LDXRH_w: - case LDAXRH_w: - case LDARH_w: - WriteWRegister(rt, Memory::Read<uint16_t>(address), NoRegLog); - break; - case LDXR_w: - case LDAXR_w: - case LDAR_w: - WriteWRegister(rt, Memory::Read<uint32_t>(address), NoRegLog); - break; - case LDXR_x: - case LDAXR_x: - case LDAR_x: - WriteXRegister(rt, Memory::Read<uint64_t>(address), NoRegLog); - break; - case LDXP_w: - case LDAXP_w: - WriteWRegister(rt, Memory::Read<uint32_t>(address), NoRegLog); - WriteWRegister(rt2, - Memory::Read<uint32_t>(address + element_size), - NoRegLog); - break; - case LDXP_x: - case LDAXP_x: - WriteXRegister(rt, Memory::Read<uint64_t>(address), NoRegLog); - WriteXRegister(rt2, - Memory::Read<uint64_t>(address + element_size), - NoRegLog); - break; - default: - VIXL_UNREACHABLE(); - } + switch (op) { + case CAS_w: + case CASA_w: + case CASL_w: + case CASAL_w: + CompareAndSwapHelper<uint32_t>(instr); + break; + case CAS_x: + case CASA_x: + case CASL_x: + case CASAL_x: + CompareAndSwapHelper<uint64_t>(instr); + break; + case CASB: + case CASAB: + case CASLB: + case CASALB: + CompareAndSwapHelper<uint8_t>(instr); + break; + case CASH: + case CASAH: + case CASLH: + case CASALH: + CompareAndSwapHelper<uint16_t>(instr); + break; + case CASP_w: + case CASPA_w: + case CASPL_w: + case CASPAL_w: + CompareAndSwapPairHelper<uint32_t>(instr); + break; + case CASP_x: + case CASPA_x: + case CASPL_x: + case CASPAL_x: + CompareAndSwapPairHelper<uint64_t>(instr); + break; + default: + if (is_load) { + if (is_exclusive) { + local_monitor_.MarkExclusive(address, access_size); + } else { + // Any non-exclusive load can clear the local monitor as a side + // effect. We don't need to do this, but it is useful to stress the + // simulated code. + local_monitor_.Clear(); + } - if (is_acquire_release) { - // Approximate load-acquire by issuing a full barrier after the load. - __sync_synchronize(); - } + // Use NoRegLog to suppress the register trace (LOG_REGS, LOG_FP_REGS). + // We will print a more detailed log. + switch (op) { + case LDXRB_w: + case LDAXRB_w: + case LDARB_w: + WriteWRegister(rt, Memory::Read<uint8_t>(address), NoRegLog); + break; + case LDXRH_w: + case LDAXRH_w: + case LDARH_w: + WriteWRegister(rt, Memory::Read<uint16_t>(address), NoRegLog); + break; + case LDXR_w: + case LDAXR_w: + case LDAR_w: + WriteWRegister(rt, Memory::Read<uint32_t>(address), NoRegLog); + break; + case LDXR_x: + case LDAXR_x: + case LDAR_x: + WriteXRegister(rt, Memory::Read<uint64_t>(address), NoRegLog); + break; + case LDXP_w: + case LDAXP_w: + WriteWRegister(rt, Memory::Read<uint32_t>(address), NoRegLog); + WriteWRegister(rt2, + Memory::Read<uint32_t>(address + element_size), + NoRegLog); + break; + case LDXP_x: + case LDAXP_x: + WriteXRegister(rt, Memory::Read<uint64_t>(address), NoRegLog); + WriteXRegister(rt2, + Memory::Read<uint64_t>(address + element_size), + NoRegLog); + break; + default: + VIXL_UNREACHABLE(); + } - LogRead(address, rt, GetPrintRegisterFormatForSize(element_size)); - if (is_pair) { - LogRead(address + element_size, - rt2, - GetPrintRegisterFormatForSize(element_size)); - } - } else { - if (is_acquire_release) { - // Approximate store-release by issuing a full barrier before the store. - __sync_synchronize(); - } + if (is_acquire_release) { + // Approximate load-acquire by issuing a full barrier after the load. + __sync_synchronize(); + } - bool do_store = true; - if (is_exclusive) { - do_store = local_monitor_.IsExclusive(address, access_size) && - global_monitor_.IsExclusive(address, access_size); - WriteWRegister(rs, do_store ? 0 : 1); + LogRead(address, rt, GetPrintRegisterFormatForSize(element_size)); + if (is_pair) { + LogRead(address + element_size, + rt2, + GetPrintRegisterFormatForSize(element_size)); + } + } else { + if (is_acquire_release) { + // Approximate store-release by issuing a full barrier before the + // store. + __sync_synchronize(); + } - // - All exclusive stores explicitly clear the local monitor. - local_monitor_.Clear(); - } else { - // - Any other store can clear the local monitor as a side effect. - local_monitor_.MaybeClear(); - } + bool do_store = true; + if (is_exclusive) { + do_store = local_monitor_.IsExclusive(address, access_size) && + global_monitor_.IsExclusive(address, access_size); + WriteWRegister(rs, do_store ? 0 : 1); - if (do_store) { - switch (op) { - case STXRB_w: - case STLXRB_w: - case STLRB_w: - Memory::Write<uint8_t>(address, ReadWRegister(rt)); - break; - case STXRH_w: - case STLXRH_w: - case STLRH_w: - Memory::Write<uint16_t>(address, ReadWRegister(rt)); - break; - case STXR_w: - case STLXR_w: - case STLR_w: - Memory::Write<uint32_t>(address, ReadWRegister(rt)); - break; - case STXR_x: - case STLXR_x: - case STLR_x: - Memory::Write<uint64_t>(address, ReadXRegister(rt)); - break; - case STXP_w: - case STLXP_w: - Memory::Write<uint32_t>(address, ReadWRegister(rt)); - Memory::Write<uint32_t>(address + element_size, ReadWRegister(rt2)); - break; - case STXP_x: - case STLXP_x: - Memory::Write<uint64_t>(address, ReadXRegister(rt)); - Memory::Write<uint64_t>(address + element_size, ReadXRegister(rt2)); - break; - default: - VIXL_UNREACHABLE(); - } + // - All exclusive stores explicitly clear the local monitor. + local_monitor_.Clear(); + } else { + // - Any other store can clear the local monitor as a side effect. + local_monitor_.MaybeClear(); + } - LogWrite(address, rt, GetPrintRegisterFormatForSize(element_size)); - if (is_pair) { - LogWrite(address + element_size, - rt2, - GetPrintRegisterFormatForSize(element_size)); + if (do_store) { + switch (op) { + case STXRB_w: + case STLXRB_w: + case STLRB_w: + Memory::Write<uint8_t>(address, ReadWRegister(rt)); + break; + case STXRH_w: + case STLXRH_w: + case STLRH_w: + Memory::Write<uint16_t>(address, ReadWRegister(rt)); + break; + case STXR_w: + case STLXR_w: + case STLR_w: + Memory::Write<uint32_t>(address, ReadWRegister(rt)); + break; + case STXR_x: + case STLXR_x: + case STLR_x: + Memory::Write<uint64_t>(address, ReadXRegister(rt)); + break; + case STXP_w: + case STLXP_w: + Memory::Write<uint32_t>(address, ReadWRegister(rt)); + Memory::Write<uint32_t>(address + element_size, + ReadWRegister(rt2)); + break; + case STXP_x: + case STLXP_x: + Memory::Write<uint64_t>(address, ReadXRegister(rt)); + Memory::Write<uint64_t>(address + element_size, + ReadXRegister(rt2)); + break; + default: + VIXL_UNREACHABLE(); + } + + LogWrite(address, rt, GetPrintRegisterFormatForSize(element_size)); + if (is_pair) { + LogWrite(address + element_size, + rt2, + GetPrintRegisterFormatForSize(element_size)); + } + } } - } } } diff --git a/src/aarch64/simulator-aarch64.h b/src/aarch64/simulator-aarch64.h index df86517b..7c84cb38 100644 --- a/src/aarch64/simulator-aarch64.h +++ b/src/aarch64/simulator-aarch64.h @@ -1690,6 +1690,10 @@ class Simulator : public DecoderVisitor { int64_t offset, AddrMode addrmode); void LoadStorePairHelper(const Instruction* instr, AddrMode addrmode); + template <typename T> + void CompareAndSwapHelper(const Instruction* instr); + template <typename T> + void CompareAndSwapPairHelper(const Instruction* instr); uintptr_t AddressModeHelper(unsigned addr_reg, int64_t offset, AddrMode addrmode); diff --git a/test/aarch64/test-assembler-aarch64.cc b/test/aarch64/test-assembler-aarch64.cc index 9cbe2f64..a93319a8 100644 --- a/test/aarch64/test-assembler-aarch64.cc +++ b/test/aarch64/test-assembler-aarch64.cc @@ -16362,6 +16362,421 @@ TEST(ldaxr_stlxr_fail) { } #endif +TEST(cas_casa_casl_casal_w) { + uint64_t data1[] = {0x01234567, 0}; + uint64_t data2[] = {0x01234567, 0}; + uint64_t data3[] = {0x01234567, 0}; + uint64_t data4[] = {0x01234567, 0}; + uint64_t data5[] = {0x01234567, 0}; + uint64_t data6[] = {0x01234567, 0}; + uint64_t data7[] = {0x01234567, 0}; + uint64_t data8[] = {0x01234567, 0}; + + uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); + uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); + uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); + uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); + uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); + uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); + uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); + uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); + + SETUP(); + START(); + + __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); + __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); + __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); + __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); + __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); + __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); + __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); + __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); + + __ Mov(x0, 0xffffffff); + + __ Mov(x1, 0x76543210); + __ Mov(x2, 0x01234567); + __ Mov(x3, 0x76543210); + __ Mov(x4, 0x01234567); + __ Mov(x5, 0x76543210); + __ Mov(x6, 0x01234567); + __ Mov(x7, 0x76543210); + __ Mov(x8, 0x01234567); + + __ Cas(w1, w0, MemOperand(x21)); + __ Cas(w2, w0, MemOperand(x22)); + __ Casa(w3, w0, MemOperand(x23)); + __ Casa(w4, w0, MemOperand(x24)); + __ Casl(w5, w0, MemOperand(x25)); + __ Casl(w6, w0, MemOperand(x26)); + __ Casal(w7, w0, MemOperand(x27)); + __ Casal(w8, w0, MemOperand(x28)); + + END(); + +// TODO: test on real hardware when available +#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 + RUN(); + + ASSERT_EQUAL_64(0x01234567, x1); + ASSERT_EQUAL_64(0x01234567, x2); + ASSERT_EQUAL_64(0x01234567, x3); + ASSERT_EQUAL_64(0x01234567, x4); + ASSERT_EQUAL_64(0x01234567, x5); + ASSERT_EQUAL_64(0x01234567, x6); + ASSERT_EQUAL_64(0x01234567, x7); + ASSERT_EQUAL_64(0x01234567, x8); + + ASSERT_EQUAL_64(0x01234567, data1[0]); + ASSERT_EQUAL_64(0xffffffff, data2[0]); + ASSERT_EQUAL_64(0x01234567, data3[0]); + ASSERT_EQUAL_64(0xffffffff, data4[0]); + ASSERT_EQUAL_64(0x01234567, data5[0]); + ASSERT_EQUAL_64(0xffffffff, data6[0]); + ASSERT_EQUAL_64(0x01234567, data7[0]); + ASSERT_EQUAL_64(0xffffffff, data8[0]); +#endif // VIXL_INCLUDE_SIMULATOR_AARCH64 + + TEARDOWN(); +} + +TEST(cas_casa_casl_casal_x) { + uint64_t data1[] = {0x0123456789abcdef, 0}; + uint64_t data2[] = {0x0123456789abcdef, 0}; + uint64_t data3[] = {0x0123456789abcdef, 0}; + uint64_t data4[] = {0x0123456789abcdef, 0}; + uint64_t data5[] = {0x0123456789abcdef, 0}; + uint64_t data6[] = {0x0123456789abcdef, 0}; + uint64_t data7[] = {0x0123456789abcdef, 0}; + uint64_t data8[] = {0x0123456789abcdef, 0}; + + uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); + uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); + uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); + uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); + uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); + uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); + uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); + uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); + + SETUP(); + START(); + + __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); + __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); + __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); + __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); + __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); + __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); + __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); + __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); + + __ Mov(x0, 0xffffffffffffffff); + + __ Mov(x1, 0xfedcba9876543210); + __ Mov(x2, 0x0123456789abcdef); + __ Mov(x3, 0xfedcba9876543210); + __ Mov(x4, 0x0123456789abcdef); + __ Mov(x5, 0xfedcba9876543210); + __ Mov(x6, 0x0123456789abcdef); + __ Mov(x7, 0xfedcba9876543210); + __ Mov(x8, 0x0123456789abcdef); + + __ Cas(x1, x0, MemOperand(x21)); + __ Cas(x2, x0, MemOperand(x22)); + __ Casa(x3, x0, MemOperand(x23)); + __ Casa(x4, x0, MemOperand(x24)); + __ Casl(x5, x0, MemOperand(x25)); + __ Casl(x6, x0, MemOperand(x26)); + __ Casal(x7, x0, MemOperand(x27)); + __ Casal(x8, x0, MemOperand(x28)); + + END(); + +// TODO: test on real hardware when available +#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 + RUN(); + + ASSERT_EQUAL_64(0x0123456789abcdef, x1); + ASSERT_EQUAL_64(0x0123456789abcdef, x2); + ASSERT_EQUAL_64(0x0123456789abcdef, x3); + ASSERT_EQUAL_64(0x0123456789abcdef, x4); + ASSERT_EQUAL_64(0x0123456789abcdef, x5); + ASSERT_EQUAL_64(0x0123456789abcdef, x6); + ASSERT_EQUAL_64(0x0123456789abcdef, x7); + ASSERT_EQUAL_64(0x0123456789abcdef, x8); + + ASSERT_EQUAL_64(0x0123456789abcdef, data1[0]); + ASSERT_EQUAL_64(0xffffffffffffffff, data2[0]); + ASSERT_EQUAL_64(0x0123456789abcdef, data3[0]); + ASSERT_EQUAL_64(0xffffffffffffffff, data4[0]); + ASSERT_EQUAL_64(0x0123456789abcdef, data5[0]); + ASSERT_EQUAL_64(0xffffffffffffffff, data6[0]); + ASSERT_EQUAL_64(0x0123456789abcdef, data7[0]); + ASSERT_EQUAL_64(0xffffffffffffffff, data8[0]); +#endif // VIXL_INCLUDE_SIMULATOR_AARCH64 + + TEARDOWN(); +} + +TEST(casb_casab_caslb_casalb) { + uint64_t data1[] = {0x01234567, 0}; + uint64_t data2[] = {0x01234567, 0}; + uint64_t data3[] = {0x01234567, 0}; + uint64_t data4[] = {0x01234567, 0}; + uint64_t data5[] = {0x01234567, 0}; + uint64_t data6[] = {0x01234567, 0}; + uint64_t data7[] = {0x01234567, 0}; + uint64_t data8[] = {0x01234567, 0}; + + uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); + uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); + uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); + uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); + uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); + uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); + uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); + uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); + + SETUP(); + START(); + + __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); + __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); + __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); + __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); + __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); + __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); + __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); + __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); + + __ Mov(x0, 0xffffffff); + + __ Mov(x1, 0x76543210); + __ Mov(x2, 0x01234567); + __ Mov(x3, 0x76543210); + __ Mov(x4, 0x01234567); + __ Mov(x5, 0x76543210); + __ Mov(x6, 0x01234567); + __ Mov(x7, 0x76543210); + __ Mov(x8, 0x01234567); + + __ Casb(w1, w0, MemOperand(x21)); + __ Casb(w2, w0, MemOperand(x22)); + __ Casab(w3, w0, MemOperand(x23)); + __ Casab(w4, w0, MemOperand(x24)); + __ Caslb(w5, w0, MemOperand(x25)); + __ Caslb(w6, w0, MemOperand(x26)); + __ Casalb(w7, w0, MemOperand(x27)); + __ Casalb(w8, w0, MemOperand(x28)); + + END(); + +// TODO: test on real hardware when available +#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 + RUN(); + + ASSERT_EQUAL_64(0x00000067, x1); + ASSERT_EQUAL_64(0x00000067, x2); + ASSERT_EQUAL_64(0x00000067, x3); + ASSERT_EQUAL_64(0x00000067, x4); + ASSERT_EQUAL_64(0x00000067, x5); + ASSERT_EQUAL_64(0x00000067, x6); + ASSERT_EQUAL_64(0x00000067, x7); + ASSERT_EQUAL_64(0x00000067, x8); + + ASSERT_EQUAL_64(0x01234567, data1[0]); + ASSERT_EQUAL_64(0x012345ff, data2[0]); + ASSERT_EQUAL_64(0x01234567, data3[0]); + ASSERT_EQUAL_64(0x012345ff, data4[0]); + ASSERT_EQUAL_64(0x01234567, data5[0]); + ASSERT_EQUAL_64(0x012345ff, data6[0]); + ASSERT_EQUAL_64(0x01234567, data7[0]); + ASSERT_EQUAL_64(0x012345ff, data8[0]); +#endif // VIXL_INCLUDE_SIMULATOR_AARCH64 + + TEARDOWN(); +} + +TEST(cash_casah_caslh_casalh) { + uint64_t data1[] = {0x01234567, 0}; + uint64_t data2[] = {0x01234567, 0}; + uint64_t data3[] = {0x01234567, 0}; + uint64_t data4[] = {0x01234567, 0}; + uint64_t data5[] = {0x01234567, 0}; + uint64_t data6[] = {0x01234567, 0}; + uint64_t data7[] = {0x01234567, 0}; + uint64_t data8[] = {0x01234567, 0}; + + uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); + uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); + uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); + uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); + uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); + uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); + uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); + uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); + + SETUP(); + START(); + + __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); + __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); + __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); + __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); + __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); + __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); + __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); + __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); + + __ Mov(x0, 0xffffffff); + + __ Mov(x1, 0x76543210); + __ Mov(x2, 0x01234567); + __ Mov(x3, 0x76543210); + __ Mov(x4, 0x01234567); + __ Mov(x5, 0x76543210); + __ Mov(x6, 0x01234567); + __ Mov(x7, 0x76543210); + __ Mov(x8, 0x01234567); + + __ Cash(w1, w0, MemOperand(x21)); + __ Cash(w2, w0, MemOperand(x22)); + __ Casah(w3, w0, MemOperand(x23)); + __ Casah(w4, w0, MemOperand(x24)); + __ Caslh(w5, w0, MemOperand(x25)); + __ Caslh(w6, w0, MemOperand(x26)); + __ Casalh(w7, w0, MemOperand(x27)); + __ Casalh(w8, w0, MemOperand(x28)); + + END(); + +// TODO: test on real hardware when available +#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 + RUN(); + + ASSERT_EQUAL_64(0x00004567, x1); + ASSERT_EQUAL_64(0x00004567, x2); + ASSERT_EQUAL_64(0x00004567, x3); + ASSERT_EQUAL_64(0x00004567, x4); + ASSERT_EQUAL_64(0x00004567, x5); + ASSERT_EQUAL_64(0x00004567, x6); + ASSERT_EQUAL_64(0x00004567, x7); + ASSERT_EQUAL_64(0x00004567, x8); + + ASSERT_EQUAL_64(0x01234567, data1[0]); + ASSERT_EQUAL_64(0x0123ffff, data2[0]); + ASSERT_EQUAL_64(0x01234567, data3[0]); + ASSERT_EQUAL_64(0x0123ffff, data4[0]); + ASSERT_EQUAL_64(0x01234567, data5[0]); + ASSERT_EQUAL_64(0x0123ffff, data6[0]); + ASSERT_EQUAL_64(0x01234567, data7[0]); + ASSERT_EQUAL_64(0x0123ffff, data8[0]); +#endif // VIXL_INCLUDE_SIMULATOR_AARCH64 + + TEARDOWN(); +} + +TEST(casp_caspa_caspl_caspal) { + uint64_t data1[] = {0x89abcdef01234567, 0}; + uint64_t data2[] = {0x89abcdef01234567, 0}; + uint64_t data3[] = {0x89abcdef01234567, 0}; + uint64_t data4[] = {0x89abcdef01234567, 0}; + uint64_t data5[] = {0x89abcdef01234567, 0}; + uint64_t data6[] = {0x89abcdef01234567, 0}; + uint64_t data7[] = {0x89abcdef01234567, 0}; + uint64_t data8[] = {0x89abcdef01234567, 0}; + + uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); + uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); + uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); + uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); + uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); + uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); + uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); + uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); + + SETUP(); + START(); + + __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); + __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); + __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); + __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); + __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); + __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); + __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); + __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); + + __ Mov(x0, 0xffffffff); + __ Mov(x1, 0xffffffff); + + __ Mov(x2, 0x76543210); + __ Mov(x3, 0xfedcba98); + __ Mov(x4, 0x89abcdef); + __ Mov(x5, 0x01234567); + + __ Mov(x6, 0x76543210); + __ Mov(x7, 0xfedcba98); + __ Mov(x8, 0x89abcdef); + __ Mov(x9, 0x01234567); + + __ Mov(x10, 0x76543210); + __ Mov(x11, 0xfedcba98); + __ Mov(x12, 0x89abcdef); + __ Mov(x13, 0x01234567); + + __ Mov(x14, 0x76543210); + __ Mov(x15, 0xfedcba98); + __ Mov(x16, 0x89abcdef); + __ Mov(x17, 0x01234567); + + __ Casp(w2, w3, w0, w1, MemOperand(x21)); + __ Casp(w4, w5, w0, w1, MemOperand(x22)); + __ Caspa(w6, w7, w0, w1, MemOperand(x23)); + __ Caspa(w8, w9, w0, w1, MemOperand(x24)); + __ Caspl(w10, w11, w0, w1, MemOperand(x25)); + __ Caspl(w12, w13, w0, w1, MemOperand(x26)); + __ Caspal(w14, w15, w0, w1, MemOperand(x27)); + __ Caspal(w16, w17, w0, w1, MemOperand(x28)); + + END(); + +// TODO: test on real hardware when available +#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 + RUN(); + + ASSERT_EQUAL_64(0x89abcdef, x2); + ASSERT_EQUAL_64(0x01234567, x3); + ASSERT_EQUAL_64(0x89abcdef, x4); + ASSERT_EQUAL_64(0x01234567, x5); + ASSERT_EQUAL_64(0x89abcdef, x6); + ASSERT_EQUAL_64(0x01234567, x7); + ASSERT_EQUAL_64(0x89abcdef, x8); + ASSERT_EQUAL_64(0x01234567, x9); + ASSERT_EQUAL_64(0x89abcdef, x10); + ASSERT_EQUAL_64(0x01234567, x11); + ASSERT_EQUAL_64(0x89abcdef, x12); + ASSERT_EQUAL_64(0x01234567, x13); + ASSERT_EQUAL_64(0x89abcdef, x14); + ASSERT_EQUAL_64(0x01234567, x15); + ASSERT_EQUAL_64(0x89abcdef, x16); + ASSERT_EQUAL_64(0x01234567, x17); + + ASSERT_EQUAL_64(0x89abcdef01234567, data1[0]); + ASSERT_EQUAL_64(0xffffffffffffffff, data2[0]); + ASSERT_EQUAL_64(0x89abcdef01234567, data3[0]); + ASSERT_EQUAL_64(0xffffffffffffffff, data4[0]); + ASSERT_EQUAL_64(0x89abcdef01234567, data5[0]); + ASSERT_EQUAL_64(0xffffffffffffffff, data6[0]); + ASSERT_EQUAL_64(0x89abcdef01234567, data7[0]); + ASSERT_EQUAL_64(0xffffffffffffffff, data8[0]); +#endif // VIXL_INCLUDE_SIMULATOR_AARCH64 + + TEARDOWN(); +} + TEST(load_store_tagged_immediate_offset) { uint64_t tags[] = {0x00, 0x1, 0x55, 0xff}; diff --git a/test/aarch64/test-disasm-aarch64.cc b/test/aarch64/test-disasm-aarch64.cc index 616e3555..fefaffe9 100644 --- a/test/aarch64/test-disasm-aarch64.cc +++ b/test/aarch64/test-disasm-aarch64.cc @@ -1861,6 +1861,70 @@ TEST(load_store_exclusive) { COMPARE(ldar(x22, MemOperand(x23)), "ldar x22, [x23]"); COMPARE(ldar(x24, MemOperand(sp)), "ldar x24, [sp]"); + COMPARE(cas(w25, w26, MemOperand(x27)), "cas w25, w26, [x27]"); + COMPARE(cas(w28, w29, MemOperand(sp)), "cas w28, w29, [sp]"); + COMPARE(cas(x30, x0, MemOperand(x1)), "cas x30, x0, [x1]"); + COMPARE(cas(x2, x3, MemOperand(sp)), "cas x2, x3, [sp]"); + COMPARE(casa(w4, w5, MemOperand(x6)), "casa w4, w5, [x6]"); + COMPARE(casa(w7, w8, MemOperand(sp)), "casa w7, w8, [sp]"); + COMPARE(casa(x9, x10, MemOperand(x11)), "casa x9, x10, [x11]"); + COMPARE(casa(x12, x13, MemOperand(sp)), "casa x12, x13, [sp]"); + COMPARE(casl(w14, w15, MemOperand(x16)), "casl w14, w15, [x16]"); + COMPARE(casl(w17, w18, MemOperand(sp)), "casl w17, w18, [sp]"); + COMPARE(casl(x19, x20, MemOperand(x21)), "casl x19, x20, [x21]"); + COMPARE(casl(x22, x23, MemOperand(sp)), "casl x22, x23, [sp]"); + COMPARE(casal(w24, w25, MemOperand(x26)), "casal w24, w25, [x26]"); + COMPARE(casal(w27, w28, MemOperand(sp)), "casal w27, w28, [sp]"); + COMPARE(casal(x29, x30, MemOperand(x0)), "casal x29, x30, [x0]"); + COMPARE(casal(x1, x2, MemOperand(sp)), "casal x1, x2, [sp]"); + COMPARE(casb(w3, w4, MemOperand(x5)), "casb w3, w4, [x5]"); + COMPARE(casb(w6, w7, MemOperand(sp)), "casb w6, w7, [sp]"); + COMPARE(casab(w8, w9, MemOperand(x10)), "casab w8, w9, [x10]"); + COMPARE(casab(w11, w12, MemOperand(sp)), "casab w11, w12, [sp]"); + COMPARE(caslb(w13, w14, MemOperand(x15)), "caslb w13, w14, [x15]"); + COMPARE(caslb(w16, w17, MemOperand(sp)), "caslb w16, w17, [sp]"); + COMPARE(casalb(w18, w19, MemOperand(x20)), "casalb w18, w19, [x20]"); + COMPARE(casalb(w21, w22, MemOperand(sp)), "casalb w21, w22, [sp]"); + COMPARE(cash(w23, w24, MemOperand(x25)), "cash w23, w24, [x25]"); + COMPARE(cash(w26, w27, MemOperand(sp)), "cash w26, w27, [sp]"); + COMPARE(casah(w28, w29, MemOperand(x30)), "casah w28, w29, [x30]"); + COMPARE(casah(w0, w1, MemOperand(sp)), "casah w0, w1, [sp]"); + COMPARE(caslh(w2, w3, MemOperand(x4)), "caslh w2, w3, [x4]"); + COMPARE(caslh(w5, w6, MemOperand(sp)), "caslh w5, w6, [sp]"); + COMPARE(casalh(w7, w8, MemOperand(x9)), "casalh w7, w8, [x9]"); + COMPARE(casalh(w10, w11, MemOperand(sp)), "casalh w10, w11, [sp]"); + COMPARE(casp(w12, w13, w14, w15, MemOperand(x16)), + "casp w12, w13, w14, w15, [x16]"); + COMPARE(casp(w18, w19, w20, w21, MemOperand(sp)), + "casp w18, w19, w20, w21, [sp]"); + COMPARE(casp(x22, x23, x24, x25, MemOperand(x26)), + "casp x22, x23, x24, x25, [x26]"); + COMPARE(casp(x28, x29, x0, x1, MemOperand(sp)), + "casp x28, x29, x0, x1, [sp]"); + COMPARE(caspa(w2, w3, w4, w5, MemOperand(x6)), "caspa w2, w3, w4, w5, [x6]"); + COMPARE(caspa(w8, w9, w10, w11, MemOperand(sp)), + "caspa w8, w9, w10, w11, [sp]"); + COMPARE(caspa(x12, x13, x14, x15, MemOperand(x16)), + "caspa x12, x13, x14, x15, [x16]"); + COMPARE(caspa(x18, x19, x20, x21, MemOperand(sp)), + "caspa x18, x19, x20, x21, [sp]"); + COMPARE(caspl(w22, w23, w24, w25, MemOperand(x26)), + "caspl w22, w23, w24, w25, [x26]"); + COMPARE(caspl(w28, w29, w0, w1, MemOperand(sp)), + "caspl w28, w29, w0, w1, [sp]"); + COMPARE(caspl(x2, x3, x4, x5, MemOperand(x6)), "caspl x2, x3, x4, x5, [x6]"); + COMPARE(caspl(x8, x9, x10, x11, MemOperand(sp)), + "caspl x8, x9, x10, x11, [sp]"); + COMPARE(caspal(w12, w13, w14, w15, MemOperand(x16)), + "caspal w12, w13, w14, w15, [x16]"); + COMPARE(caspal(w18, w19, w20, w21, MemOperand(sp)), + "caspal w18, w19, w20, w21, [sp]"); + COMPARE(caspal(x22, x23, x24, x25, MemOperand(x26)), + "caspal x22, x23, x24, x25, [x26]"); + COMPARE(caspal(x28, x29, x0, x1, MemOperand(sp)), + "caspal x28, x29, x0, x1, [sp]"); + + CLEANUP(); } |