Add assembler for riscv64, part 4.

Implement fences, LR/SC and atomic operations.

Test: m  test-art-host-gtest
Bug: 283082089
Signed-off-by: Lifang Xia <lifang_xia@linux.alibaba.com>
Signed-off-by: Wendong Wang <wangwd@xcvmbyte.com>
Signed-off-by: Cao Xia <caoxia@eswincomputing.com>
Change-Id: If10463d33071db577c4cc775ad3700c1746e093d
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index fb950f8..47ce8e6 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -174,7 +174,7 @@
                 "--disassemble",
                 "--no-print-imm-hex",
                 "--no-show-raw-insn",
-                "--mattr=+F,+D",  // Disassemble "F" and "D" Standard Extensions.
+                "--mattr=+F,+D,+A",  // Disassemble "F", "D" and "A" Standard Extensions.
                 "-M",
                 "no-aliases"};
       default:
diff --git a/compiler/utils/riscv64/assembler_riscv64.cc b/compiler/utils/riscv64/assembler_riscv64.cc
index 80b97a3..8787005 100644
--- a/compiler/utils/riscv64/assembler_riscv64.cc
+++ b/compiler/utils/riscv64/assembler_riscv64.cc
@@ -70,7 +70,7 @@
 
 /////////////////////////////// RV64 VARIANTS extension ///////////////////////////////
 
-/////////////////////////////// RV64 "IM" Instructions ///////////////////////////////
+//////////////////////////////// RV64 "I" Instructions ////////////////////////////////
 
 // LUI/AUIPC (RV32I, with sign-extension on RV64I), opcode = 0x17, 0x37
 
@@ -295,6 +295,24 @@
   EmitR(0x20, rs2, rs1, 0x5, rd, 0x3b);
 }
 
+// Fence instruction (RV32I): opcode = 0xf, funct3 = 0
+void Riscv64Assembler::Fence(uint32_t pred, uint32_t succ) {
+  DCHECK(IsUint<4>(pred));
+  DCHECK(IsUint<4>(succ));
+  EmitI(/* normal fence */ 0x0 << 8 | pred << 4 | succ, 0x0, 0x0, 0x0, 0xf);
+}
+
+//////////////////////////////// RV64 "I" Instructions  END ////////////////////////////////
+
+/////////////////////////// RV64 "Zifencei" Instructions  START ////////////////////////////
+
+// "Zifencei" Standard Extension, opcode = 0xf, funct3 = 1
+void Riscv64Assembler::FenceI() { EmitI(0x0, 0x0, 0x1, 0x0, 0xf); }
+
+//////////////////////////// RV64 "Zifencei" Instructions  END /////////////////////////////
+
+/////////////////////////////// RV64 "M" Instructions  START ///////////////////////////////
+
 // RV32M Standard Extension: opcode = 0x33, funct3 from 0x0 ~ 0x7
 
 void Riscv64Assembler::Mul(XRegister rd, XRegister rs1, XRegister rs2) {
@@ -351,7 +369,99 @@
   EmitR(0x1, rs2, rs1, 0x7, rd, 0x3b);
 }
 
-/////////////////////////////// RV64 "IM" Instructions  END ///////////////////////////////
+//////////////////////////////// RV64 "M" Instructions  END ////////////////////////////////
+
+/////////////////////////////// RV64 "A" Instructions  START ///////////////////////////////
+
+void Riscv64Assembler::LrW(XRegister rd, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x2, aqrl, 0x0, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::LrD(XRegister rd, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x2, aqrl, 0x0, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::ScW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x3, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::ScD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x3, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoSwapW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x1, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoSwapD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x1, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoAddW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x0, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoAddD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x0, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoXorW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x4, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoXorD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x4, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoAndW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0xc, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoAndD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0xc, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoOrW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x8, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoOrD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x8, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoMinW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x10, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoMinD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x10, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoMaxW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x14, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoMaxD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x14, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoMinuW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x18, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoMinuD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x18, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoMaxuW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x1c, aqrl, rs2, rs1, 0x2, rd, 0x2f);
+}
+
+void Riscv64Assembler::AmoMaxuD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl) {
+  EmitR4(0x1c, aqrl, rs2, rs1, 0x3, rd, 0x2f);
+}
+
+/////////////////////////////// RV64 "A" Instructions  END ///////////////////////////////
 
 /////////////////////////////// RV64 "FD" Instructions  START ///////////////////////////////
 
diff --git a/compiler/utils/riscv64/assembler_riscv64.h b/compiler/utils/riscv64/assembler_riscv64.h
index 1071add..5f7c246 100644
--- a/compiler/utils/riscv64/assembler_riscv64.h
+++ b/compiler/utils/riscv64/assembler_riscv64.h
@@ -52,6 +52,16 @@
 static constexpr size_t kRiscv64WordSize = 4;
 static constexpr size_t kRiscv64DoublewordSize = 8;
 
+// the type for fence
+enum FenceType {
+  kFenceNone = 0,
+  kFenceWrite = 1,
+  kFenceRead = 2,
+  kFenceOutput = 4,
+  kFenceInput = 8,
+  kFenceDefault = 0xf,
+};
+
 class Riscv64Label : public Label {
  public:
   Riscv64Label() : prev_branch_id_(kNoPrevBranchId) {}
@@ -220,6 +230,12 @@
   void Srlw(XRegister rd, XRegister rs1, XRegister rs2);
   void Sraw(XRegister rd, XRegister rs1, XRegister rs2);
 
+  // Fence instruction (RV32I): opcode = 0xf, funct3 = 0
+  void Fence(uint32_t pred = kFenceDefault, uint32_t succ = kFenceDefault);
+
+  // "Zifencei" Standard Extension, opcode = 0xf, funct3 = 1
+  void FenceI();
+
   // RV32M Standard Extension: opcode = 0x33, funct3 from 0x0 ~ 0x7
   void Mul(XRegister rd, XRegister rs1, XRegister rs2);
   void Mulh(XRegister rd, XRegister rs1, XRegister rs2);
@@ -237,6 +253,30 @@
   void Remw(XRegister rd, XRegister rs1, XRegister rs2);
   void Remuw(XRegister rd, XRegister rs1, XRegister rs2);
 
+  // RV32A/RV64A Standard Extension
+  void LrW(XRegister rd, XRegister rs1, uint32_t aqrl);
+  void LrD(XRegister rd, XRegister rs1, uint32_t aqrl);
+  void ScW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void ScD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoSwapW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoSwapD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoAddW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoAddD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoXorW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoXorD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoAndW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoAndD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoOrW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoOrD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoMinW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoMinD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoMaxW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoMaxD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoMinuW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoMinuD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoMaxuW(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+  void AmoMaxuD(XRegister rd, XRegister rs2, XRegister rs1, uint32_t aqrl);
+
   // FP load/store instructions (RV32F+RV32D): opcode = 0x07, 0x27
   void FLw(FRegister rd, XRegister rs1, int32_t offset);
   void FLd(FRegister rd, XRegister rs1, int32_t offset);
diff --git a/compiler/utils/riscv64/assembler_riscv64_test.cc b/compiler/utils/riscv64/assembler_riscv64_test.cc
index be20faa..f936cb6 100644
--- a/compiler/utils/riscv64/assembler_riscv64_test.cc
+++ b/compiler/utils/riscv64/assembler_riscv64_test.cc
@@ -531,7 +531,83 @@
     DriverStr(expected, test_name);
   }
 
+  std::string RepeatRRAqRl(void (Riscv64Assembler::*f)(XRegister, XRegister, uint32_t),
+                           const std::string& fmt) {
+    CHECK(f != nullptr);
+    std::vector<int64_t> imms = CreateImmediateValuesBits(2, /*as_uint=*/ true);
+    std::string str;
+    for (XRegister* reg1 : GetRegisters()) {
+      for (XRegister* reg2 : GetRegisters()) {
+        for (int64_t imm : imms) {
+          (GetAssembler()->*f)(*reg1, *reg2, dchecked_integral_cast<uint32_t>(imm));
+          std::string base = fmt;
+
+          ReplaceReg(REG1_TOKEN, GetRegisterName(*reg1), &base);
+          ReplaceReg(REG2_TOKEN, GetRegisterName(*reg2), &base);
+          ReplaceAqRl(imm, &base);
+
+          str += base;
+          str += "\n";
+        }
+      }
+    }
+    return str;
+  }
+
+  std::string RepeatRRRAqRl(void (Riscv64Assembler::*f)(XRegister, XRegister, XRegister, uint32_t),
+                            const std::string& fmt) {
+    CHECK(f != nullptr);
+    std::vector<int64_t> imms = CreateImmediateValuesBits(2, /*as_uint=*/ true);
+    std::string str;
+    for (XRegister* reg1 : GetRegisters()) {
+      for (XRegister* reg2 : GetRegisters()) {
+        for (XRegister* reg3 : GetRegisters()) {
+          for (int64_t imm : imms) {
+            (GetAssembler()->*f)(*reg1, *reg2, *reg3, dchecked_integral_cast<uint32_t>(imm));
+            std::string base = fmt;
+
+            ReplaceReg(REG1_TOKEN, GetRegisterName(*reg1), &base);
+            ReplaceReg(REG2_TOKEN, GetRegisterName(*reg2), &base);
+            ReplaceReg(REG3_TOKEN, GetRegisterName(*reg3), &base);
+            ReplaceAqRl(imm, &base);
+
+            str += base;
+            str += "\n";
+          }
+        }
+      }
+    }
+    return str;
+  }
+
  private:
+  static constexpr const char* AQRL_TOKEN = "{aqrl}";
+
+  void ReplaceAqRl(int64_t aqrl, /*inout*/ std::string* str) {
+    const char* replacement;
+    switch (aqrl) {
+      case 0:
+        replacement = "";
+        break;
+      case 1:
+        replacement = ".rl";
+        break;
+      case 2:
+        replacement = ".aq";
+        break;
+      case 3:
+        replacement = ".aqrl";
+        break;
+      default:
+        LOG(FATAL) << "Unexpected value for `aqrl`: " << aqrl;
+        UNREACHABLE();
+    }
+    size_t aqrl_index = str->find(AQRL_TOKEN);
+    if (aqrl_index != std::string::npos) {
+      str->replace(aqrl_index, ConstexprStrLen(AQRL_TOKEN), replacement);
+    }
+  }
+
   std::vector<riscv64::XRegister*> registers_;
   std::map<riscv64::XRegister, std::string, RISCV64CpuRegisterCompare> secondary_register_names_;
 
@@ -762,6 +838,46 @@
   DriverStr(RepeatRRR(&riscv64::Riscv64Assembler::Sraw, "sraw {reg1}, {reg2}, {reg3}"), "Sraw");
 }
 
+TEST_F(AssemblerRISCV64Test, Fence) {
+  auto get_fence_type_string = [](uint32_t fence_type) {
+    CHECK_LE(fence_type, 0xfu);
+    std::string result;
+    if ((fence_type & kFenceInput) != 0u) {
+      result += "i";
+    }
+    if ((fence_type & kFenceOutput) != 0u) {
+      result += "o";
+    }
+    if ((fence_type & kFenceRead) != 0u) {
+      result += "r";
+    }
+    if ((fence_type & kFenceWrite) != 0u) {
+      result += "w";
+    }
+    if (result.empty()) {
+      result += "0";
+    }
+    return result;
+  };
+
+  std::string expected;
+  // Note: The `pred` and `succ` are 4 bits each.
+  // Some combinations are not really useful but the assembler can emit them all.
+  for (uint32_t pred = 0u; pred != 0x10; ++pred) {
+    for (uint32_t succ = 0u; succ != 0x10; ++succ) {
+      __ Fence(pred, succ);
+      expected +=
+          "fence " + get_fence_type_string(pred) + ", " + get_fence_type_string(succ) + "\n";
+    }
+  }
+  DriverStr(expected, "Fence");
+}
+
+TEST_F(AssemblerRISCV64Test, FenceI) {
+  __ FenceI();
+  DriverStr("fence.i", "FenceI");
+}
+
 TEST_F(AssemblerRISCV64Test, Mul) {
   DriverStr(RepeatRRR(&riscv64::Riscv64Assembler::Mul, "mul {reg1}, {reg2}, {reg3}"), "Mul");
 }
@@ -814,6 +930,132 @@
   DriverStr(RepeatRRR(&riscv64::Riscv64Assembler::Remuw, "remuw {reg1}, {reg2}, {reg3}"), "Remuw");
 }
 
+TEST_F(AssemblerRISCV64Test, LrW) {
+  DriverStr(RepeatRRAqRl(&riscv64::Riscv64Assembler::LrW, "lr.w{aqrl} {reg1}, ({reg2})"), "LrW");
+}
+
+TEST_F(AssemblerRISCV64Test, LrD) {
+  DriverStr(RepeatRRAqRl(&riscv64::Riscv64Assembler::LrD, "lr.d{aqrl} {reg1}, ({reg2})"), "LrD");
+}
+
+TEST_F(AssemblerRISCV64Test, ScW) {
+  DriverStr(RepeatRRRAqRl(&riscv64::Riscv64Assembler::ScW, "sc.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "ScW");
+}
+
+TEST_F(AssemblerRISCV64Test, ScD) {
+  DriverStr(RepeatRRRAqRl(&riscv64::Riscv64Assembler::ScD, "sc.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "ScD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoSwapW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoSwapW, "amoswap.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoSwapW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoSwapD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoSwapD, "amoswap.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoSwapD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoAddW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoAddW, "amoadd.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoAddW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoAddD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoAddD, "amoadd.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoAddD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoXorW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoXorW, "amoxor.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoXorW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoXorD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoXorD, "amoxor.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoXorD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoAndW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoAndW, "amoand.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoAndW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoAndD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoAndD, "amoand.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoAndD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoOrW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoOrW, "amoor.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoOrW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoOrD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoOrD, "amoor.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoOrD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoMinW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoMinW, "amomin.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoMinW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoMinD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoMinD, "amomin.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoMinD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoMaxW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoMaxW, "amomax.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoMaxW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoMaxD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoMaxD, "amomax.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoMaxD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoMinuW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoMinuW, "amominu.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoMinuW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoMinuD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoMinuD, "amominu.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoMinuD");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoMaxuW) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoMaxuW, "amomaxu.w{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoMaxuW");
+}
+
+TEST_F(AssemblerRISCV64Test, AmoMaxuD) {
+  DriverStr(RepeatRRRAqRl(
+                &riscv64::Riscv64Assembler::AmoMaxuD, "amomaxu.d{aqrl} {reg1}, {reg2}, ({reg3})"),
+            "AmoMaxuD");
+}
+
 TEST_F(AssemblerRISCV64Test, FLw) {
   DriverStr(RepeatFRIb(&riscv64::Riscv64Assembler::FLw, -12, "flw {reg1}, {imm}({reg2})"), "FLw");
 }