aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Gilday <alexander.gilday@arm.com>2018-04-16 17:42:00 +0100
committerAlexander Gilday <alexander.gilday@arm.com>2018-04-17 08:52:36 +0100
commit4e5bad9e4915ba673bfe016dbdced31fe3cb7687 (patch)
tree6eaebdab0d5b98479841386a6ea96309fd28bc30
parent4e52d4db0b9629aa57ecd354afb31bc814eef5b7 (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.cc116
-rw-r--r--src/aarch64/assembler-aarch64.h64
-rw-r--r--src/aarch64/constants-aarch64.h35
-rw-r--r--src/aarch64/disasm-aarch64.cc239
-rw-r--r--src/aarch64/macro-assembler-aarch64.h48
-rw-r--r--src/aarch64/operands-aarch64.h22
-rw-r--r--src/aarch64/simulator-aarch64.cc368
-rw-r--r--src/aarch64/simulator-aarch64.h4
-rw-r--r--test/aarch64/test-assembler-aarch64.cc415
-rw-r--r--test/aarch64/test-disasm-aarch64.cc64
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();
}