Initial version of a RiscV64g assembler.

There will be some issues and differences between this and
the standard RiscV assembler to be detailed later.

PiperOrigin-RevId: 715854881
Change-Id: I1a9bca71106fbc30f94a93a3aa0af3dc71888427
diff --git a/riscv/BUILD b/riscv/BUILD
index d3e2c14..65faa21 100644
--- a/riscv/BUILD
+++ b/riscv/BUILD
@@ -862,6 +862,56 @@
 )
 
 cc_library(
+    name = "riscv64g_encoder",
+    srcs = [
+        "riscv64g_bin_encoder_interface.cc",
+        "riscv_bin_setters.cc",
+    ],
+    hdrs = [
+        "riscv64g_bin_encoder_interface.h",
+        "riscv_bin_setters.h",
+    ],
+    deps = [
+        ":riscv64g_bin_fmt",
+        ":riscv64g_isa",
+        ":riscv_getters",
+        ":riscv_state",
+        "@com_google_absl//absl/base:no_destructor",
+        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/functional:any_invocable",
+        "@com_google_absl//absl/log:check",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/status:statusor",
+        "@com_google_absl//absl/strings",
+        "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+        "@com_google_mpact-sim//mpact/sim/util/asm",
+        "@com_googlesource_code_re2//:re2",
+    ],
+)
+
+cc_binary(
+    name = "riscv64g_as",
+    srcs = ["riscv64g_as_main.cc"],
+    copts = ["-O3"],
+    deps = [
+        ":riscv64g_bin_fmt",
+        ":riscv64g_encoder",
+        ":riscv64g_isa",
+        "@com_github_serge1_elfio//:elfio",
+        "@com_google_absl//absl/flags:flag",
+        "@com_google_absl//absl/flags:parse",
+        "@com_google_absl//absl/log",
+        "@com_google_absl//absl/log:check",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/strings",
+        "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+        "@com_google_mpact-sim//mpact/sim/util/asm",
+        "@com_google_mpact-sim//mpact/sim/util/asm:simple_assembler",
+        "@com_googlesource_code_re2//:re2",
+    ],
+)
+
+cc_library(
     name = "riscv64g_vec_decoder",
     srcs = [
         "riscv64g_vec_decoder.cc",
diff --git a/riscv/riscv64g.isa b/riscv/riscv64g.isa
index 7fcbc7f..467e57c 100644
--- a/riscv/riscv64g.isa
+++ b/riscv/riscv64g.isa
@@ -43,7 +43,7 @@
 slot riscv64g : riscv64i, riscv64c, riscv64m, riscv64_amo_arithmetic, riscv64f, riscv64d, zicsr, zfencei, privileged {
   default size = 4;
   default opcode =
-    disasm: "Illegal instruction at 0x%(@:08x)",
+    disasm: "Illegal instruction at %(@:08x)",
     semfunc: "&RiscVIllegalInstruction";
 }
 
@@ -57,11 +57,11 @@
   resources TwoOp = { next_pc, rs1 : rd[..rd]};
   resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]};
   opcodes {
-    addi{: rs1, I_imm12 : rd},
+    addi{: rs1, %reloc(I_imm12) : rd},
       resources: TwoOp,
       disasm: "addi", "%rd, %rs1, %I_imm12",
       semfunc: "&RV64::RiscVIAdd";
-    addiw{: rs1, I_imm12 : rd},
+    addiw{: rs1, %reloc(I_imm12) : rd},
       resources: TwoOp,
       disasm: "addiw", "%rd, %rs1, %I_imm12",
       semfunc: "&RV64::RiscVIAddw";
@@ -87,42 +87,42 @@
       semfunc: "&RV64::RiscVIXor";
     slli{: rs1, I_uimm6 : rd},
       resources: TwoOp,
-      disasm: "slli", "%rd, %rs1, 0x%(I_uimm6:x)",
+      disasm: "slli", "%rd, %rs1, %(I_uimm6:x)",
       semfunc: "&RV64::RiscVISll";
     srli{: rs1, I_uimm6 : rd},
       resources: TwoOp,
-      disasm: "srli", "%rd  %rs1, 0x%(I_uimm6:x)",
+      disasm: "srli", "%rd  %rs1, %(I_uimm6:x)",
       semfunc: "&RV64::RiscVISrl";
     srai{: rs1, I_uimm6 : rd},
       resources: TwoOp,
-      disasm: "srai", "%rd, %rs1, 0x%(I_uimm6:x)",
+      disasm: "srai", "%rd, %rs1, %(I_uimm6:x)",
       semfunc: "&RV64::RiscVISra";
     slliw{: rs1, I_uimm5 : rd},
       resources: TwoOp,
-      disasm: "slliw", "%rd, %rs1, 0x%(I_uimm5:x)",
+      disasm: "slliw", "%rd, %rs1, %(I_uimm5:x)",
       semfunc: "&RV64::RiscVISllw";
     srliw{: rs1, I_uimm5 : rd},
       resources: TwoOp,
-      disasm: "srliw", "%rd  %rs1, 0x%(I_uimm5:x)",
+      disasm: "srliw", "%rd  %rs1, %(I_uimm5:x)",
       semfunc: "&RV64::RiscVISrlw";
     sraiw{: rs1, I_uimm5 : rd},
       resources: TwoOp,
-      disasm: "sraiw", "%rd, %rs1, 0x%(I_uimm5:x)",
+      disasm: "sraiw", "%rd, %rs1, %(I_uimm5:x)",
       semfunc: "&RV64::RiscVISraw";
-    lui{: U_imm20 : rd},
+    lui{: %reloc(U_imm20) : rd},
       resources: { next_pc : rd[0..]},
-      disasm: "lui", "%rd, 0x%(U_imm20:08x)",
+      disasm: "lui", "%rd, %(U_imm20:08x)",
       semfunc: "&RV64::RiscVILui";
-    auipc{: U_imm20 : rd},
+    auipc{: %reloc(U_imm20) : rd},
       resources: { next_pc : rd[0..]},
-      disasm: "auipc", "%rd, 0x%(U_imm20:08x)",
+      disasm: "auipc", "%rd, %(U_imm20:08x)",
       semfunc: "&RV64::RiscVIAuipc";
     add{: rs1, rs2 : rd},
       resources: ThreeOp,
       disasm: "add", "%rd, %rs1, %rs2",
       semfunc: "&RV64::RiscVIAdd";
     addw{: rs1, rs2 : rd},
-      disasm: "add", "%rd, %rs1, %rs2",
+      disasm: "addw", "%rd, %rs1, %rs2",
       semfunc: "&RV64::RiscVIAddw";
     slt{: rs1, rs2 : rd},
       resources: ThreeOp,
@@ -170,7 +170,7 @@
       semfunc: "&RV64::RiscVISrlw";
     subw{: rs1, rs2 : rd},
       resources: ThreeOp,
-      disasm: "sub", "%rd, %rs1, %rs2",
+      disasm: "subw", "%rd, %rs1, %rs2",
       semfunc: "&RV64::RiscVISubw";
     sraw{: rs1, rs2 : rd},
       resources: ThreeOp,
@@ -182,87 +182,87 @@
     hint{},
       disasm: "hint",
       semfunc: "&RiscVINop";
-    jal{: J_imm20 : next_pc, rd},
+    jal{: %reloc(J_imm20) : next_pc, rd},
       resources: { next_pc : next_pc[0..], rd[0..]},
       disasm: "jal", "%rd, %(@+J_imm20:08x)",
       semfunc: "&RV64::RiscVIJal";
-    jalr{: rs1, J_imm12 : next_pc, rd},
+    jalr{: rs1, %reloc(J_imm12) : next_pc, rd},
       resources: { next_pc, rs1 : next_pc[0..], rd[0..]},
       disasm: "jalr", "%rd, %rs1, %J_imm12",
       semfunc: "&RV64::RiscVIJalr";
-    j{: J_imm20 : next_pc, rd},
+    j{: %reloc(J_imm20) : next_pc, rd},
       resources: { next_pc : next_pc[0..], rd[0..]},
       disasm: "j", "%(@+J_imm20:08x)",
       semfunc: "&RV64::RiscVIJal";
-    jr{: rs1, J_imm12 : next_pc, rd},
+    jr{: rs1, %reloc(J_imm12) : next_pc, rd},
       resources: { next_pc, rs1 : next_pc[0..], rd[0..]},
-      disasm: "jr", "%rs1, %J_imm12",
+      disasm: "jr", "%rs1, %(J_imm12:08x)",
       semfunc: "&RV64::RiscVIJalr";
-    beq{: rs1, rs2, B_imm12 : next_pc},
+    beq{: rs1, rs2, %reloc(B_imm12) : next_pc},
       resources: { next_pc, rs1, rs2 : next_pc[0..]},
       disasm: "beq", "%rs1, %rs2, %(@+B_imm12:08x)",
       semfunc: "&RV64::RiscVIBeq";
-    bne{: rs1, rs2, B_imm12 : next_pc},
+    bne{: rs1, rs2, %reloc(B_imm12) : next_pc},
       resources: { next_pc, rs1, rs2 : next_pc[0..]},
       disasm: "bne", "%rs1, %rs2, %(@+B_imm12:08x)",
       semfunc: "&RV64::RiscVIBne";
-    blt{: rs1, rs2, B_imm12 : next_pc},
+    blt{: rs1, rs2, %reloc(B_imm12) : next_pc},
       resources: { next_pc, rs1, rs2 : next_pc[0..]},
       disasm: "blt", "%rs1, %rs2, %(@+B_imm12:08x)",
       semfunc: "&RV64::RiscVIBlt";
-    bltu{: rs1, rs2, B_imm12 : next_pc},
+    bltu{: rs1, rs2, %reloc(B_imm12) : next_pc},
       resources: { next_pc, rs1, rs2 : next_pc[0..]},
       disasm: "bltu", "%rs1, %rs2, %(@+B_imm12:08x)",
       semfunc: "&RV64::RiscVIBltu";
-    bge{: rs1, rs2, B_imm12 : next_pc},
+    bge{: rs1, rs2, %reloc(B_imm12) : next_pc},
       resources: { next_pc, rs1, rs2 : next_pc[0..]},
       disasm: "bge", "%rs1, %rs2, %(@+B_imm12:08x)",
       semfunc: "&RV64::RiscVIBge";
-    bgeu{: rs1, rs2, B_imm12 : next_pc},
+    bgeu{: rs1, rs2, %reloc(B_imm12) : next_pc},
       resources: { next_pc, rs1, rs2 : next_pc[0..]},
       disasm: "bgeu", "%rs1, %rs2, %(@+B_imm12:08x)",
       semfunc: "&RV64::RiscVIBgeu";
-    ld{(: rs1, I_imm12), (: : rd)},
+    ld{(: rs1, %reloc(I_imm12)), (: : rd)},
       resources: { next_pc, rs1 : rd[0..]},
       disasm: "ld", "%rd, %I_imm12(%rs1)",
       semfunc: "&RV64::RiscVILd", "&RV64::RiscVILdChild";
-    lw{(: rs1, I_imm12), (: : rd)},
+    lw{(: rs1, %reloc(I_imm12)), (: : rd)},
       resources: { next_pc, rs1 : rd[0..]},
       disasm: "lw", "%rd, %I_imm12(%rs1)",
       semfunc: "&RV64::RiscVILw", "&RV64::RiscVILwChild";
-    lwu{(: rs1, I_imm12), (: : rd)},
+    lwu{(: rs1, %reloc(I_imm12)), (: : rd)},
       resources: { next_pc, rs1 : rd[0..]},
       disasm: "lwu", "%rd, %I_imm12(%rs1)",
       semfunc: "&RV64::RiscVILw", "&RV64::RiscVILwuChild";
-    lh{(: rs1, I_imm12 :), (: : rd)},
+    lh{(: rs1, %reloc(I_imm12) :), (: : rd)},
       resources: { next_pc, rs1 : rd[0..]},
       disasm: "lh", "%rd, %I_imm12(%rs1)",
       semfunc: "&RV64::RiscVILh", "&RV64::RiscVILhChild";
-    lhu{(: rs1, I_imm12 :), (: : rd)},
+    lhu{(: rs1, %reloc(I_imm12) :), (: : rd)},
       resources: { next_pc, rs1 : rd[0..]},
       disasm: "lhu", "%rd, %I_imm12(%rs1)",
       semfunc: "&RV64::RiscVILhu", "&RV64::RiscVILhuChild";
-    lb{(: rs1, I_imm12 :), (: : rd)},
+    lb{(: rs1, %reloc(I_imm12) :), (: : rd)},
       resources: { next_pc, rs1 : rd[0..]},
       disasm: "lb", "%rd, %I_imm12(%rs1)",
       semfunc: "&RV64::RiscVILb", "&RV64::RiscVILbChild";
-    lbu{(: rs1, I_imm12 :), (: : rd)},
+    lbu{(: rs1, %reloc(I_imm12) :), (: : rd)},
       resources: { next_pc, rs1 : rd[0..]},
       disasm: "lbu", "%rd, %I_imm12(%rs1)",
       semfunc: "&RV64::RiscVILbu", "&RV64::RiscVILbuChild";
-    sd{: rs1, S_imm12, rs2 : },
+    sd{: rs1, %reloc(S_imm12), rs2 : },
       resources: { next_pc, rs1, rs2 : },
       disasm: "sd", "%rs2, %S_imm12(%rs1)",
       semfunc: "&RV64::RiscVISd";
-    sw{: rs1, S_imm12, rs2 : },
+    sw{: rs1, %reloc(S_imm12), rs2 : },
       resources: { next_pc, rs1, rs2 : },
       disasm: "sw", "%rs2, %S_imm12(%rs1)",
       semfunc: "&RV64::RiscVISw";
-    sh{: rs1, S_imm12, rs2 : },
+    sh{: rs1, %reloc(S_imm12), rs2 : },
       resources: { next_pc, rs1, rs2 : },
       disasm: "sh", "%rs2, %S_imm12(%rs1)",
       semfunc: "&RV64::RiscVISh";
-    sb{: rs1, S_imm12, rs2 : },
+    sb{: rs1, %reloc(S_imm12), rs2 : },
       resources: { next_pc, rs1, rs2 : },
       disasm: "sb", "%rs2, %S_imm12(%rs1)",
       semfunc: "&RV64::RiscVISb";
@@ -442,11 +442,11 @@
   opcodes {
     amoswapw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoswap.w", "%rd, rs2, (%rs1)",
+      disasm: "amoswap.w", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoswapw", "&RV64::RiscVILwChild";
     amoswapd{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoswap.d", "%rd, rs2, (%rs1)",
+      disasm: "amoswap.d", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoswapd", "&RV64::RiscVILdChild";
   }
 }
@@ -460,27 +460,27 @@
   opcodes {
     amoandw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoand.w", "%rd, rs2, (%rs1)",
+      disasm: "amoand.w%A_aq%A_rl", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoandw", "&RV64::RiscVILwChild";
     amoandd{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoand.d", "%rd, rs2, (%rs1)",
+      disasm: "amoand.d%A_aq%A_rl", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoandd", "&RV64::RiscVILdChild";
     amoorw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoor.w", "%rd, rs2, (%rs1)",
+      disasm: "amoor.w", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoorw", "&RV64::RiscVILwChild";
     amoord{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoor.d", "%rd, rs2, (%rs1)",
+      disasm: "amoor.d", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoord", "&RV64::RiscVILdChild";
     amoxorw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoxor.w", "%rd, rs2, (%rs1)",
+      disasm: "amoxor.w", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoxorw", "&RV64::RiscVILwChild";
     amoxord{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoxor.d", "%rd, rs2, (%rs1)",
+      disasm: "amoxor.d", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoxord", "&RV64::RiscVILdChild";
   }
 }
@@ -494,43 +494,43 @@
   opcodes {
     amoaddw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoadd.w", "%rd, rs2, (%rs1)",
+      disasm: "amoadd.w", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoaddw", "&RV64::RiscVILwChild";
     amoaddd{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amoadd.d", "%rd, rs2, (%rs1)",
+      disasm: "amoadd.d", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmoaddd", "&RV64::RiscVILdChild";
     amomaxw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amomax.w", "%rd, rs2, (%rs1)",
+      disasm: "amomax.w", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmomaxw", "&RV64::RiscVILwChild";
     amomaxd{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amomax.d", "%rd, rs2, (%rs1)",
+      disasm: "amomax.d", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmomaxd", "&RV64::RiscVILdChild";
     amomaxuw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amomaxu.w", "%rd, rs2, (%rs1)",
+      disasm: "amomaxu.w", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmomaxuw", "&RV64::RiscVILwChild";
     amomaxud{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amomaxu.d", "%rd, rs2, (%rs1)",
+      disasm: "amomaxu.d", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmomaxud", "&RV64::RiscVILdChild";
     amominw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amomin.w", "%rd, rs2, (%rs1)",
+      disasm: "amomin.w", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmominw", "&RV64::RiscVILwChild";
     amomind{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amomin.d", "%rd, rs2, (%rs1)",
+      disasm: "amomin.d", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmomind", "&RV64::RiscVILdChild";
     amominuw{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amominu.w", "%rd, rs2, (%rs1)",
+      disasm: "amominu.w", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmominuw", "&RV64::RiscVILwChild";
     amominud{(: rs1, rs2, A_aq, A_rl), (: : rd)},
       resources: ThreeOp,
-      disasm: "amominu.d", "%rd, rs2, (%rs1)",
+      disasm: "amominu.d", "%rd, %rs2, (%rs1)",
       semfunc: "&AAmominud", "&RV64::RiscVILdChild";
   }
 }
@@ -621,11 +621,11 @@
   resources ThreeOp = { next_pc, frs1, frs2 : frd[0..]};
   resources FourOp = { next_pc, frs1, frs2, frs3 : frd[0..]};
   opcodes {
-    flw{(: rs1, I_imm12 : ), (: : frd)},
+    flw{(: rs1, %reloc(I_imm12) : ), (: : frd)},
       resources: { next_pc, rs1 : frd[0..]},
       semfunc: "&RV64::RiscVILw", "&RiscVIFlwChild",
       disasm: "flw", "%frd, %I_imm12(%rs1)";
-    fsw{: rs1, S_imm12, frs2},
+    fsw{: rs1, %reloc(S_imm12), frs2},
       resources: { next_pc, rs1, frs2},
       semfunc: "&RV64::RiscVFSw",
       disasm: "fsw", "%frs2, %S_imm12(%rs1)";
@@ -752,11 +752,11 @@
   default size = 4;
   default latency = global_latency;
   opcodes {
-    fld{(: rs1, I_imm12 : ), (: : drd)},
+    fld{(: rs1, %reloc(I_imm12) : ), (: : drd)},
       resources: {next_pc, rs1 : drd[0..]},
       semfunc: "&RV64::RiscVILd", "&RV64::RiscVILdChild",
       disasm: "fld", "%drd, %I_imm12(%rs1)";
-    fsd{: rs1, S_imm12, drs2},
+    fsd{: rs1, %reloc(S_imm12), drs2},
       resources: {next_pc, rs1, drs2},
       semfunc: "&RV64::RiscVDSd",
       disasm: "fsd", "%drs2, %S_imm12(%rs1)";
@@ -936,7 +936,7 @@
       resources: {next_pc,c3rs1, c3drs2},
       disasm: "c.fsd", "%c3drs2, %I_cl_uimm5x8(%c3rs1)",
       semfunc: "&RV64::RiscVISd";
-    cj{: I_cj_imm11, x0 : next_pc, x0},
+    cj{: %reloc(I_cj_imm11), x0 : next_pc, x0},
       resources: {next_pc,x0 : next_pc[0..], x0[0..]},
       disasm: "c.j", "%(@+I_cj_imm11:08x)",
       semfunc: "&RV64::RiscVIJal";
@@ -948,11 +948,11 @@
       resources: {next_pc,crs1, x0 : next_pc[0..], x1[0..]},
       disasm: "c.jalr", "%crs1",
       semfunc: "&RV64::RiscVIJalr";
-    cbeqz{: c3rs1, x0, I_cb_imm8 : next_pc},
+    cbeqz{: c3rs1, x0, %reloc(I_cb_imm8) : next_pc},
       resources: {next_pc,c3rs1, x0 : next_pc[0..]},
       disasm: "c.beqz", "%c3rs1, %(@+I_cb_imm8:08x)",
       semfunc: "&RV64::RiscVIBeq";
-    cbnez{: c3rs1, x0, I_cb_imm8 : next_pc},
+    cbnez{: c3rs1, x0, %reloc(I_cb_imm8) : next_pc},
       resources: {next_pc,c3rs1, x0 : next_pc[0..]},
       disasm: "c.bnez", "%c3rs1, %(@+I_cb_imm8:08x)",
       semfunc: "&RV64::RiscVIBne";
@@ -962,7 +962,7 @@
       semfunc: "&RV64::RiscVIAdd";
     clui{: I_ci_imm6_12 : rd},
       resources: {next_pc : rd[0..]},
-      disasm: "c.lui", "%rd, 0x%(I_ci_imm6_12:x)",
+      disasm: "c.lui", "%rd, %(I_ci_imm6_12:x)",
       semfunc: "&RV64::RiscVILui";
     caddi{: rd, I_ci_imm6 : rd},
       resources: {next_pc, rd : rd[0..]},
@@ -982,15 +982,15 @@
       semfunc: "&RV64::RiscVIAdd";
     cslli{: rd, I_ci_uimm6 : rd},
       resources: {next_pc, rd : rd[0..]},
-      disasm: "c.slli", "%rd, %rd, 0x%(I_ci_uimm6:x)",
+      disasm: "c.slli", "%rd, %rd, %(I_ci_uimm6:x)",
       semfunc: "&RV64::RiscVISll";
     csrli{: c3rs1, I_ci_uimm6 : c3rs1},
       resources: {next_pc, c3rs1 : c3rs1[0..]},
-      disasm: "c.srli", "%c3rs1, %c3rs1, 0x%(I_ci_uimm6:x)",
+      disasm: "c.srli", "%c3rs1, %c3rs1, %(I_ci_uimm6:x)",
       semfunc: "&RV64::RiscVISrl";
     csrai{: c3rs1, I_ci_uimm6 : c3rs1},
       resources: {next_pc, c3rs1 : c3rs1[0..]},
-      disasm: "c.srai", "%c3rs1, %c3rs1, 0x%(I_ci_uimm6:x)",
+      disasm: "c.srai", "%c3rs1, %c3rs1, %(I_ci_uimm6:x)",
       semfunc: "&RV64::RiscVISra";
     candi{: c3rs1, I_ci_imm6 : c3rs1},
       resources: {next_pc, c3rs1 : c3rs1[0..]},
diff --git a/riscv/riscv64g_as_main.cc b/riscv/riscv64g_as_main.cc
new file mode 100644
index 0000000..fc1ff3e
--- /dev/null
+++ b/riscv/riscv64g_as_main.cc
@@ -0,0 +1,189 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+#include <fstream>
+#include <iostream>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "absl/log/check.h"
+#include "absl/log/log.h"
+#include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "elfio/elf_types.hpp"
+#include "elfio/elfio.hpp"
+#include "elfio/elfio_dump.hpp"
+#include "mpact/sim/generic/type_helpers.h"
+#include "mpact/sim/util/asm/opcode_assembler_interface.h"
+#include "mpact/sim/util/asm/resolver_interface.h"
+#include "mpact/sim/util/asm/simple_assembler.h"
+#include "riscv/riscv64g_bin_encoder_interface.h"
+#include "riscv/riscv64g_encoder.h"
+
+using ::mpact::sim::riscv::isa64::RiscV64GBinEncoderInterface;
+using ::mpact::sim::riscv::isa64::Riscv64gSlotMatcher;
+using ::mpact::sim::util::assembler::SimpleAssembler;
+
+// This file implements the main function for the generated assembler for
+// RiscV64G.
+
+namespace {
+
+using ::mpact::sim::generic::operator*;  // NOLINT(misc-unused-using-decls)
+using ::mpact::sim::util::assembler::OpcodeAssemblerInterface;
+using ::mpact::sim::util::assembler::RelocationInfo;
+using ::mpact::sim::util::assembler::ResolverInterface;
+
+// This class implements the byte oriented OpcodeAssemblerInterface, converting
+// from the bit interface provided by the SlotMatcher interface.
+class RiscV64GAssembler : public OpcodeAssemblerInterface {
+ public:
+  RiscV64GAssembler(Riscv64gSlotMatcher* matcher) : matcher_(matcher) {};
+  ~RiscV64GAssembler() override = default;
+  absl::Status Encode(uint64_t address, absl::string_view text,
+                      ResolverInterface* resolver, std::vector<uint8_t>& bytes,
+                      std::vector<RelocationInfo>& relocations) override {
+    auto res = matcher_->Encode(address, text, 0, resolver, relocations);
+    if (!res.status().ok()) return res.status();
+    auto [value, size] = res.value();
+    union {
+      uint64_t i;
+      uint8_t b[sizeof(uint64_t)];
+    } u;
+    u.i = value;
+    for (int i = 0; i < size / 8; ++i) {
+      bytes.push_back(u.b[i]);
+    }
+    return absl::OkStatus();
+  }
+
+ private:
+  Riscv64gSlotMatcher* matcher_;
+};
+
+}  // namespace
+
+// This flag dumps the info for the generated ELF file.
+ABSL_FLAG(bool, dump_elf, false, "Dump the ELF file");
+// Produce a relocatable file as opposed to an executable.
+ABSL_FLAG(bool, c, false, "Produce a relocatable file");
+// Specify the output file name.
+ABSL_FLAG(std::optional<std::string>, o, std::nullopt, "Output file name");
+
+// Supported RiscV ELF flags (TSO memory ordering and compact encodings).
+enum class RiscVElfFlags {
+  kNone = 0,
+  kRiscvTso = 0x0001,
+  kRiscvRvc = 0x0010,
+};
+
+int main(int argc, char* argv[]) {
+  auto arg_vec = absl::ParseCommandLine(argc, argv);
+
+  if (arg_vec.size() > 2) {
+    std::cout << "Too many arguments\n";
+    return 1;
+  }
+
+  std::istream* is;
+
+  if (arg_vec.size() == 1) {
+    is = &std::cin;
+  } else {
+    is = new std::fstream(arg_vec[1], std::fstream::in);
+  }
+
+  RiscV64GBinEncoderInterface bin_encoder_interface;
+  Riscv64gSlotMatcher matcher(&bin_encoder_interface);
+  RiscV64GAssembler riscv_64g_assembler(&matcher);
+  CHECK_OK(matcher.Initialize());
+  // Instantiate the assembler.
+  SimpleAssembler assembler("#", ELFCLASS64, ELFOSABI_LINUX, EM_RISCV,
+                            &riscv_64g_assembler);
+  // Set the appropriate ELF flags.
+  assembler.writer().set_flags(*RiscVElfFlags::kRiscvTso |
+                               *RiscVElfFlags::kRiscvRvc);
+  // Perform the first pass of parsing the assembly code.
+  auto status = assembler.Parse(*is);
+  if (!status.ok()) {
+    std::cout << "Failed to parse assembly: " << status.message() << "\n";
+    return 1;
+  }
+  // Perform the second pass of parsing the assembly code. This pass will either
+  // generate an executable or a relocatable file.
+  std::string output_file_name;
+  if (absl::GetFlag(FLAGS_c)) {
+    status = assembler.CreateRelocatable();
+    if (arg_vec.size() == 1) {
+      output_file_name = "stdin.o";
+    } else {
+      std::string input_file_name = arg_vec[1];
+      auto dot_pos = input_file_name.find_last_of('.');
+      if (dot_pos == std::string::npos) {
+        output_file_name = absl::StrCat(input_file_name, ".o");
+      } else {
+        output_file_name =
+            absl::StrCat(input_file_name.substr(0, dot_pos), ".o");
+      }
+    }
+  } else {
+    status = assembler.CreateExecutable(0x1000, "main");
+    output_file_name = "a.out";
+  }
+  if (!status.ok()) {
+    std::cout << "Assembly failure: " << status.message() << "\n";
+    return 1;
+  }
+  // Write out the output file.
+  if (absl::GetFlag(FLAGS_o).has_value()) {
+    output_file_name = absl::GetFlag(FLAGS_o).value();
+  }
+  std::ofstream output_file(output_file_name);
+  if (!output_file.is_open()) {
+    std::cout << "Failed to open output file: " << output_file_name << "\n";
+    return 1;
+  }
+  status = assembler.Write(output_file);
+  if (!status.ok()) {
+    std::cout << "Failed to write output file: " << status.message() << "\n";
+    return 1;
+  }
+  output_file.close();
+  if (is != &std::cin) delete is;
+
+  // Dump the ELF info if requested.
+  if (absl::GetFlag(FLAGS_dump_elf)) {
+    ELFIO::elfio reader;
+    if (!reader.load(output_file_name)) {
+      std::cout << "Failed to load output file: " << output_file_name << "\n";
+      return 1;
+    }
+
+    ELFIO::dump::header(std::cout, reader);
+    ELFIO::dump::section_headers(std::cout, reader);
+    ELFIO::dump::segment_headers(std::cout, reader);
+    ELFIO::dump::symbol_tables(std::cout, reader);
+    ELFIO::dump::notes(std::cout, reader);
+    ELFIO::dump::modinfo(std::cout, reader);
+    ELFIO::dump::dynamic_tags(std::cout, reader);
+    ELFIO::dump::section_datas(std::cout, reader);
+    ELFIO::dump::segment_datas(std::cout, reader);
+  }
+  return 0;
+}
diff --git a/riscv/riscv64g_bin_encoder_interface.cc b/riscv/riscv64g_bin_encoder_interface.cc
new file mode 100644
index 0000000..7e02c18
--- /dev/null
+++ b/riscv/riscv64g_bin_encoder_interface.cc
@@ -0,0 +1,135 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv/riscv64g_bin_encoder_interface.h"
+
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "mpact/sim/util/asm/resolver_interface.h"
+#include "riscv/riscv64g_bin_encoder.h"
+#include "riscv/riscv64g_encoder.h"
+#include "riscv/riscv64g_enums.h"
+#include "riscv/riscv_bin_setters.h"
+
+namespace mpact {
+namespace sim {
+namespace riscv {
+namespace isa64 {
+
+using ::mpact::sim::generic::operator*;  // NOLINT(misc-unused-using-decls)
+using ::mpact::sim::util::assembler::ResolverInterface;
+
+RiscV64GBinEncoderInterface::RiscV64GBinEncoderInterface() {
+  AddRiscvSourceOpBinSetters<SourceOpEnum, OpMap, encoding64::Encoder>(
+      source_op_map_);
+  AddRiscvDestOpBinSetters<DestOpEnum, OpMap, encoding64::Encoder>(
+      dest_op_map_);
+  AddRiscvSourceOpRelocationSetters<OpcodeEnum, SourceOpEnum, RelocationMap>(
+      relocation_source_op_map_);
+}
+
+absl::StatusOr<std::tuple<uint64_t, int>>
+RiscV64GBinEncoderInterface::GetOpcodeEncoding(SlotEnum slot, int entry,
+                                               OpcodeEnum opcode,
+                                               ResolverInterface *resolver) {
+  return encoding64::kOpcodeEncodings->at(opcode);
+}
+
+absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetSrcOpEncoding(
+    uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+    OpcodeEnum opcode, SourceOpEnum source_op, int source_num,
+    ResolverInterface *resolver) {
+  auto iter = source_op_map_.find(*source_op);
+  if (iter == source_op_map_.end()) {
+    return absl::NotFoundError(absl::StrCat(
+        "Source operand not found for op enum value ", *source_op));
+  }
+  return iter->second(address, text, resolver);
+}
+
+absl::Status RiscV64GBinEncoderInterface::AppendSrcOpRelocation(
+    uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+    OpcodeEnum opcode, SourceOpEnum source_op, int source_num,
+    ResolverInterface *resolver, std::vector<RelocationInfo> &relocations) {
+  auto iter = relocation_source_op_map_.find(std::tie(opcode, source_op));
+  if (iter == relocation_source_op_map_.end()) return absl::OkStatus();
+  return iter->second(address, text, resolver, relocations);
+}
+
+absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetDestOpEncoding(
+    uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+    OpcodeEnum opcode, DestOpEnum dest_op, int dest_num,
+    ResolverInterface *resolver) {
+  auto iter = dest_op_map_.find(*dest_op);
+  if (iter == dest_op_map_.end()) {
+    return absl::NotFoundError(
+        absl::StrCat("Dest operand not found for op enum value ", *dest_op));
+  }
+  return iter->second(address, text, resolver);
+}
+
+absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetListDestOpEncoding(
+    uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+    OpcodeEnum opcode, ListDestOpEnum dest_op, int dest_num,
+    ResolverInterface *resolver) {
+  auto iter = list_dest_op_map_.find(*dest_op);
+  if (iter == list_dest_op_map_.end()) {
+    return absl::NotFoundError(absl::StrCat(
+        "List dest operand not found for op enum value ", *dest_op));
+  }
+  return iter->second(address, text, resolver);
+}
+
+absl::Status RiscV64GBinEncoderInterface::AppendDestOpRelocation(
+    uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+    OpcodeEnum opcode, DestOpEnum dest_op, int dest_num,
+    ResolverInterface *resolver, std::vector<RelocationInfo> &relocations) {
+  // There are no destination operands that require relocation.
+  return absl::OkStatus();
+}
+
+absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetListSrcOpEncoding(
+    uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+    OpcodeEnum opcode, ListSourceOpEnum source_op, int source_num,
+    ResolverInterface *resolver) {
+  auto iter = list_source_op_map_.find(*source_op);
+  if (iter == list_source_op_map_.end()) {
+    return absl::NotFoundError(absl::StrCat(
+        "List source operand not found for op enum value ", *source_op));
+  }
+  return iter->second(address, text, resolver);
+}
+
+absl::StatusOr<uint64_t> RiscV64GBinEncoderInterface::GetPredOpEncoding(
+    uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+    OpcodeEnum opcode, PredOpEnum pred_op, ResolverInterface *resolver) {
+  auto iter = pred_op_map_.find(*pred_op);
+  if (iter == pred_op_map_.end()) {
+    return absl::NotFoundError(absl::StrCat(
+        "Predicate operand not found for op enum value ", *pred_op));
+  }
+  return iter->second(address, text, resolver);
+}
+
+}  // namespace isa64
+}  // namespace riscv
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv/riscv64g_bin_encoder_interface.h b/riscv/riscv64g_bin_encoder_interface.h
new file mode 100644
index 0000000..f25fe1d
--- /dev/null
+++ b/riscv/riscv64g_bin_encoder_interface.h
@@ -0,0 +1,110 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_MPACT_RISCV_RISCV64G_BIN_ENCODER_INTERFACE_H_
+#define THIRD_PARTY_MPACT_RISCV_RISCV64G_BIN_ENCODER_INTERFACE_H_
+
+#include <cstdint>
+#include <functional>
+#include <tuple>
+#include <vector>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/string_view.h"
+#include "mpact/sim/util/asm/opcode_assembler_interface.h"
+#include "mpact/sim/util/asm/resolver_interface.h"
+#include "riscv/riscv64g_encoder.h"
+#include "riscv/riscv64g_enums.h"
+
+// This file defines the class that implements the RiscV64GEncoderInterfaceBase.
+// This class is used to convert from a text representation to the binary
+// encoding of opcodes, source and destination operands.
+
+namespace mpact {
+namespace sim {
+namespace riscv {
+namespace isa64 {
+
+using ::mpact::sim::util::assembler::RelocationInfo;
+using ::mpact::sim::util::assembler::ResolverInterface;
+
+class RiscV64GBinEncoderInterface : public RiscV64GEncoderInterfaceBase {
+ public:
+  RiscV64GBinEncoderInterface();
+  RiscV64GBinEncoderInterface(const RiscV64GBinEncoderInterface &) = delete;
+  RiscV64GBinEncoderInterface &operator=(const RiscV64GBinEncoderInterface &) =
+      delete;
+  ~RiscV64GBinEncoderInterface() override = default;
+
+  absl::StatusOr<std::tuple<uint64_t, int>> GetOpcodeEncoding(
+      SlotEnum slot, int entry, OpcodeEnum opcode,
+      ResolverInterface *resolver) override;
+  absl::StatusOr<uint64_t> GetSrcOpEncoding(
+      uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+      OpcodeEnum opcode, SourceOpEnum source_op, int source_num,
+      ResolverInterface *resolver) override;
+  absl::Status AppendSrcOpRelocation(
+      uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+      OpcodeEnum opcode, SourceOpEnum source_op, int source_num,
+      ResolverInterface *resolver,
+      std::vector<RelocationInfo> &relocations) override;
+  absl::StatusOr<uint64_t> GetDestOpEncoding(
+      uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+      OpcodeEnum opcode, DestOpEnum dest_op, int dest_num,
+      ResolverInterface *resolver) override;
+  absl::Status AppendDestOpRelocation(
+      uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+      OpcodeEnum opcode, DestOpEnum dest_op, int dest_num,
+      ResolverInterface *resolver,
+      std::vector<RelocationInfo> &relocations) override;
+  absl::StatusOr<uint64_t> GetListSrcOpEncoding(
+      uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+      OpcodeEnum opcode, ListSourceOpEnum source_op, int source_num,
+      ResolverInterface *resolver) override;
+  absl::StatusOr<uint64_t> GetListDestOpEncoding(
+      uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+      OpcodeEnum opcode, ListDestOpEnum dest_op, int dest_num,
+      ResolverInterface *resolver) override;
+  absl::StatusOr<uint64_t> GetPredOpEncoding(
+      uint64_t address, absl::string_view text, SlotEnum slot, int entry,
+      OpcodeEnum opcode, PredOpEnum pred_op,
+      ResolverInterface *resolver) override;
+
+ private:
+  using OpMap = absl::flat_hash_map<
+      int, std::function<absl::StatusOr<uint64_t>(uint64_t, absl::string_view,
+                                                  ResolverInterface *)>>;
+
+  using RelocationMap =
+      absl::flat_hash_map<std::tuple<OpcodeEnum, SourceOpEnum>,
+                          std::function<absl::Status(
+                              uint64_t, absl::string_view, ResolverInterface *,
+                              std::vector<RelocationInfo> &)>>;
+
+  OpMap source_op_map_;
+  RelocationMap relocation_source_op_map_;
+  OpMap dest_op_map_;
+  OpMap list_dest_op_map_;
+  OpMap list_source_op_map_;
+  OpMap pred_op_map_;
+};
+
+}  // namespace isa64
+}  // namespace riscv
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // THIRD_PARTY_MPACT_RISCV_RISCV64G_BIN_ENCODER_INTERFACE_H_
diff --git a/riscv/riscv_arm_semihost.cc b/riscv/riscv_arm_semihost.cc
index 5a79e1e..b3ba038 100644
--- a/riscv/riscv_arm_semihost.cc
+++ b/riscv/riscv_arm_semihost.cc
@@ -238,7 +238,9 @@
     LOG(ERROR) << "Program exit not caught in ARM semihosting - no callback";
     return absl::NotFoundError("Exit callback not valid in ARM semihosting");
   }
-  LOG(ERROR) << "Exceptions other than ApplicationExit are not implemented";
+  LOG(ERROR) << absl::StrCat(
+      "Exception ", absl::Hex(param_value), " other than ApplicationExit (",
+      absl::Hex(kAdpStoppedApplicationExit), ") are not implemented");
   return absl::UnimplementedError(
       "Exceptions other than ApplicationExit are not implemented");
 }
diff --git a/riscv/riscv_arm_semihost.h b/riscv/riscv_arm_semihost.h
index 82ee35a..19d89bd 100644
--- a/riscv/riscv_arm_semihost.h
+++ b/riscv/riscv_arm_semihost.h
@@ -15,6 +15,7 @@
 #ifndef MPACT_RISCV_RISCV_RISCV_ARM_SEMIHOST_H_
 #define MPACT_RISCV_RISCV_RISCV_ARM_SEMIHOST_H_
 
+#include <cstdint>
 #include <functional>
 
 #include "absl/container/flat_hash_map.h"
diff --git a/riscv/riscv_bin_setters.cc b/riscv/riscv_bin_setters.cc
new file mode 100644
index 0000000..6d6684d
--- /dev/null
+++ b/riscv/riscv_bin_setters.cc
@@ -0,0 +1,145 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "riscv/riscv_bin_setters.h"
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "absl/base/no_destructor.h"
+#include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "re2/re2.h"
+
+namespace mpact {
+namespace sim {
+namespace riscv {
+
+namespace internal {
+
+// Current set of relocation types supported by the assembler.
+enum class RelocType {
+  kNone = 0,
+  kBranch = 16,
+  kJal = 17,
+  kPcrelHi20 = 23,
+  kPcrelLo12I = 24,
+  kPcrelLo12S = 25,
+  kHi20 = 26,
+  kLo12I = 27,
+  kLo12S = 28,
+};
+
+using ::mpact::sim::generic::operator*;  // NOLINT(misc-unused-using-decls)
+
+// This regular expression matches the text for a symbol. The first capture
+// group is the relocation type (if present), the second capture group is the
+// symbol name.
+absl::NoDestructor<RE2> kSymRe(
+    "^\\s*(%[a-zA-Z0-9_]+)?\\s*\\(?([a-zA-Z_][^)]+)\\)?\\s*$");
+
+absl::Status RelocateAddiIImm12(uint64_t address, absl::string_view text,
+                                ResolverInterface *resolver,
+                                std::vector<RelocationInfo> &relocations) {
+  std::string relo;
+  std::string sym;
+  if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus();
+  if (relo == "%lo") {
+    relocations.emplace_back(0, sym, *RelocType::kLo12I, 0, 0);
+    return absl::OkStatus();
+  }
+  if (relo == "%pcrel_lo") {
+    relocations.emplace_back(0, sym, *RelocType::kPcrelLo12I, 0, 0);
+    return absl::OkStatus();
+  }
+  if (!relo.empty()) {
+    return absl::InvalidArgumentError(
+        absl::StrCat("Invalid relocation: '", relo, "'"));
+  }
+  return absl::OkStatus();
+}
+
+absl::Status RelocateJJImm20(uint64_t address, absl::string_view text,
+                             ResolverInterface *resolver,
+                             std::vector<RelocationInfo> &relocations) {
+  std::string relo;
+  std::string sym;
+  if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus();
+  relocations.emplace_back(0, sym, *RelocType::kJal, 0, 0);
+  return absl::OkStatus();
+}
+
+absl::Status RelocateJrJImm12(uint64_t address, absl::string_view text,
+                              ResolverInterface *resolver,
+                              std::vector<RelocationInfo> &relocations) {
+  std::string relo;
+  std::string sym;
+  if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus();
+  if (relo == "%pcrel_lo") {
+    relocations.emplace_back(0, sym, *RelocType::kPcrelLo12I, 0, 0);
+  }
+  return absl::OkStatus();
+}
+
+absl::Status RelocateLuiUImm20(uint64_t address, absl::string_view text,
+                               ResolverInterface *resolver,
+                               std::vector<RelocationInfo> &relocations) {
+  std::string relo;
+  std::string sym;
+  if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus();
+  relocations.emplace_back(0, sym, *RelocType::kHi20, 0, 0);
+  return absl::OkStatus();
+}
+
+absl::Status RelocateSdSImm12(uint64_t address, absl::string_view text,
+                              ResolverInterface *resolver,
+                              std::vector<RelocationInfo> &relocations) {
+  std::string relo;
+  std::string sym;
+  if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus();
+  if (relo == "%lo") {
+    relocations.emplace_back(0, sym, *RelocType::kLo12S, 0, 0);
+    return absl::OkStatus();
+  }
+  if (relo == "%pcrel_lo") {
+    relocations.emplace_back(0, sym, *RelocType::kPcrelLo12S, 0, 0);
+    return absl::OkStatus();
+  }
+  if (!relo.empty()) {
+    return absl::InvalidArgumentError(
+        absl::StrCat("Invalid relocation: '", relo, "'"));
+  }
+  return absl::OkStatus();
+}
+
+absl::Status RelocateAuipcUImm20(uint64_t address, absl::string_view text,
+                                 ResolverInterface *resolver,
+                                 std::vector<RelocationInfo> &relocations) {
+  std::string relo;
+  std::string sym;
+  if (!RE2::FullMatch(text, *kSymRe, &relo, &sym)) return absl::OkStatus();
+  if (relo == "%pcrel_hi") {
+    relocations.emplace_back(0, sym, *RelocType::kPcrelHi20, 0, 0);
+  }
+  return absl::OkStatus();
+}
+
+}  // namespace internal
+
+}  // namespace riscv
+}  // namespace sim
+}  // namespace mpact
diff --git a/riscv/riscv_bin_setters.h b/riscv/riscv_bin_setters.h
new file mode 100644
index 0000000..4eb312c
--- /dev/null
+++ b/riscv/riscv_bin_setters.h
@@ -0,0 +1,1040 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_BIN_SETTERS_H_
+#define THIRD_PARTY_MPACT_RISCV_RISCV_BIN_SETTERS_H_
+
+#include <cstdint>
+#include <initializer_list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "mpact/sim/util/asm/opcode_assembler_interface.h"
+#include "mpact/sim/util/asm/resolver_interface.h"
+#include "re2/re2.h"
+#include "riscv/riscv_getter_helpers.h"
+
+// This file contains various setters for the RiscV binary encoder that is used
+// by the assembler to map from operand text strings to integer values.
+
+namespace mpact {
+namespace sim {
+namespace riscv {
+
+using ::mpact::sim::util::assembler::RelocationInfo;
+using ::mpact::sim::util::assembler::ResolverInterface;
+
+// Initializer lists for maps for different subsets of registers, mapping from
+// the register name to the register number.
+constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>>
+    kRegisterList = {
+        {"x0", 0},   {"x1", 1},   {"x2", 2},   {"x3", 3},   {"x4", 4},
+        {"x5", 5},   {"x6", 6},   {"x7", 7},   {"x8", 8},   {"x9", 9},
+        {"x10", 10}, {"x11", 11}, {"x12", 12}, {"x13", 13}, {"x14", 14},
+        {"x15", 15}, {"x16", 16}, {"x17", 17}, {"x18", 18}, {"x19", 19},
+        {"x20", 20}, {"x21", 21}, {"x22", 22}, {"x23", 23}, {"x24", 24},
+        {"x25", 25}, {"x26", 26}, {"x27", 27}, {"x28", 28}, {"x29", 29},
+        {"x30", 30}, {"x31", 31}, {"zero", 0}, {"ra", 1},   {"sp", 2},
+        {"gp", 3},   {"tp", 4},   {"t0", 5},   {"t1", 6},   {"t2", 7},
+        {"s0", 8},   {"s1", 9},   {"a0", 10},  {"a1", 11},  {"a2", 12},
+        {"a3", 13},  {"a4", 14},  {"a5", 15},  {"a6", 16},  {"a7", 17},
+        {"s2", 18},  {"s3", 19},  {"s4", 20},  {"s5", 21},  {"s6", 22},
+        {"s7", 23},  {"s8", 24},  {"s9", 25},  {"s10", 26}, {"s11", 27},
+        {"t3", 28},  {"t4", 29},  {"t5", 30},  {"t6", 31}};
+
+constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>>
+    kCRegisterList = {
+        {"x8", 8},   {"x9", 9},   {"x10", 10}, {"x11", 11},
+        {"x12", 12}, {"x13", 13}, {"x14", 14}, {"x15", 15},
+        {"s0", 8},   {"s1", 9},   {"a0", 10},  {"a1", 11},
+        {"a2", 12},  {"a3", 13},  {"a4", 14},  {"a5", 15},
+};
+
+constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>>
+    kFRegisterList = {
+        {"f0", 0},   {"f1", 1},   {"f2", 2},    {"f3", 3},    {"f4", 4},
+        {"f5", 5},   {"f6", 6},   {"f7", 7},    {"f8", 8},    {"f9", 9},
+        {"f10", 10}, {"f11", 11}, {"f12", 12},  {"f13", 13},  {"f14", 14},
+        {"f15", 15}, {"f16", 16}, {"f17", 17},  {"f18", 18},  {"f19", 19},
+        {"f20", 20}, {"f21", 21}, {"f22", 22},  {"f23", 23},  {"f24", 24},
+        {"f25", 25}, {"f26", 26}, {"f27", 27},  {"f28", 28},  {"f29", 29},
+        {"f30", 30}, {"f31", 31}, {"ft0", 0},   {"ft1", 1},   {"ft2", 2},
+        {"ft3", 3},  {"ft4", 4},  {"ft5", 5},   {"ft6", 6},   {"ft7", 7},
+        {"fs0", 8},  {"fs1", 9},  {"fa0", 10},  {"fa1", 11},  {"fa2", 12},
+        {"fa3", 13}, {"fa4", 14}, {"fa5", 15},  {"fa6", 16},  {"fa7", 17},
+        {"fs2", 18}, {"fs3", 19}, {"fs4", 20},  {"fs5", 21},  {"fs6", 22},
+        {"fs7", 23}, {"fs8", 24}, {"fs9", 25},  {"fs10", 26}, {"fs11", 27},
+        {"ft8", 28}, {"ft9", 29}, {"ft10", 30}, {"ft11", 31}};
+
+constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>>
+    kDRegisterList = {
+        {"d0", 0},   {"d1", 1},   {"d2", 2},    {"d3", 3},    {"d4", 4},
+        {"d5", 5},   {"d6", 6},   {"d7", 7},    {"d8", 8},    {"d9", 9},
+        {"d10", 10}, {"d11", 11}, {"d12", 12},  {"d13", 13},  {"d14", 14},
+        {"d15", 15}, {"d16", 16}, {"d17", 17},  {"d18", 18},  {"d19", 19},
+        {"d20", 20}, {"d21", 21}, {"d22", 22},  {"d23", 23},  {"d24", 24},
+        {"d25", 25}, {"d26", 26}, {"d27", 27},  {"d28", 28},  {"d29", 29},
+        {"d30", 30}, {"d31", 31}, {"dt0", 0},   {"dt1", 1},   {"dt2", 2},
+        {"dt3", 3},  {"dt4", 4},  {"dt5", 5},   {"dt6", 6},   {"dt7", 7},
+        {"ds0", 8},  {"ds1", 9},  {"da0", 10},  {"da1", 11},  {"da2", 12},
+        {"da3", 13}, {"da4", 14}, {"da5", 15},  {"da6", 16},  {"da7", 17},
+        {"ds2", 18}, {"ds3", 19}, {"ds4", 20},  {"ds5", 21},  {"ds6", 22},
+        {"ds7", 23}, {"ds8", 24}, {"ds9", 25},  {"ds10", 26}, {"ds11", 27},
+        {"dt8", 28}, {"dt9", 29}, {"dt10", 30}, {"dt11", 31}};
+
+constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>>
+    kDCRegisterList = {
+        {"d8", 8},   {"d9", 9},   {"d10", 10}, {"d11", 11},
+        {"d12", 12}, {"d13", 13}, {"d14", 14}, {"d15", 15},
+        {"ds0", 8},  {"ds1", 9},  {"da0", 10}, {"da1", 11},
+        {"da2", 12}, {"da3", 13}, {"da4", 14}, {"da5", 15},
+};
+
+// This is the initializer list for the map from CSR register names to register
+// numbers.
+constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>>
+    kCsrRegisterList = {
+        {"fflags", 0x001},
+        {"frm", 0x002},
+        {"fcsr", 0x003},
+        {"cycle", 0xc00},
+        {"time", 0xc01},
+        {"instret", 0xc02},
+        {"hpmcounter3", 0xc03},
+        {"hpmcounter4", 0xc04},
+        {"hpmcounter5", 0xc05},
+        {"hpmcounter6", 0xc06},
+        {"hpmcounter7", 0xc07},
+        {"hpmcounter8", 0xc08},
+        {"hpmcounter9", 0xc09},
+        {"hpmcounter10", 0xc0a},
+        {"hpmcounter11", 0xc0b},
+        {"hpmcounter12", 0xc0c},
+        {"hpmcounter13", 0xc0d},
+        {"hpmcounter14", 0xc0e},
+        {"hpmcounter15", 0xc0f},
+        {"hpmcounter16", 0xc10},
+        {"hpmcounter17", 0xc11},
+        {"hpmcounter18", 0xc12},
+        {"hpmcounter19", 0xc13},
+        {"hpmcounter20", 0xc14},
+        {"hpmcounter21", 0xc15},
+        {"hpmcounter22", 0xc16},
+        {"hpmcounter23", 0xc17},
+        {"hpmcounter24", 0xc18},
+        {"hpmcounter25", 0xc19},
+        {"hpmcounter26", 0xc1a},
+        {"hpmcounter27", 0xc1b},
+        {"hpmcounter28", 0xc1c},
+        {"hpmcounter29", 0xc1d},
+        {"hpmcounter30", 0xc1e},
+        {"hpmcounter31", 0xc1f},
+        {"cycleh", 0xc80},
+        {"timeh", 0xc81},
+        {"instreth", 0xc82},
+        {"hpmcounter3h", 0xc83},
+        {"hpmcounter4h", 0xc84},
+        {"hpmcounter5h", 0xc85},
+        {"hpmcounter6h", 0xc86},
+        {"hpmcounter7h", 0xc87},
+        {"hpmcounter8h", 0xc88},
+        {"hpmcounter9h", 0xc89},
+        {"hpmcounter10h", 0xc8a},
+        {"hpmcounter11h", 0xc8b},
+        {"hpmcounter12h", 0xc8c},
+        {"hpmcounter13h", 0xc8d},
+        {"hpmcounter14h", 0xc8e},
+        {"hpmcounter15h", 0xc8f},
+        {"hpmcounter16h", 0xc90},
+        {"hpmcounter17h", 0xc91},
+        {"hpmcounter18h", 0xc92},
+        {"hpmcounter19h", 0xc93},
+        {"hpmcounter20h", 0xc94},
+        {"hpmcounter2h1", 0xc95},
+        {"hpmcounter22h", 0xc96},
+        {"hpmcounter23h", 0xc97},
+        {"hpmcounter24h", 0xc98},
+        {"hpmcounter25h", 0xc99},
+        {"hpmcounter26h", 0xc9a},
+        {"hpmcounter27h", 0xc9b},
+        {"hpmcounter28h", 0xc9c},
+        {"hpmcounter29h", 0xc9d},
+        {"hpmcounter30h", 0xc9e},
+        {"hpmcounter3h1", 0xc9f},
+        {"sstatus", 0x100},
+        {"sie", 0x104},
+        {"stvec", 0x105},
+        {"stcounteren", 0x106},
+        {"senvcfg", 0x10a},
+        {"scountinhibit", 0x120},
+        {"sscratch", 0x140},
+        {"sepc", 0x141},
+        {"scause", 0x142},
+        {"stval", 0x143},
+        {"sip", 0x144},
+        {"scountovf", 0xda0},
+        {"satp", 0x180},
+        {"scontext", 0x5a8},
+        {"sstateen0", 0x10c},
+        {"sstateen1", 0x10d},
+        {"sstateen2", 0x10e},
+        {"sstateen3", 0x10f},
+        {"hstatus", 0x600},
+        {"hedeleg", 0x602},
+        {"hideleg", 0x603},
+        {"hie", 0x604},
+        {"hcounteren", 0x606},
+        {"hgeie", 0x607},
+        {"hedelegh", 0x612},
+        {"htval", 0x643},
+        {"hip", 0x644},
+        {"hvip", 0x645},
+        {"htinst", 0x64a},
+        {"hgeip", 0xe12},
+        {"henvcfg", 0x60a},
+        {"henvcfgh", 0x61a},
+        {"hgatp", 0x680},
+        {"hcontext", 0x6a8},
+        {"htimedelta", 0x605},
+        {"htimedeltah", 0x615},
+        {"hstateen0", 0x60c},
+        {"hstateen1", 0x60d},
+        {"hstateen2", 0x60e},
+        {"hstateen3", 0x60f},
+        {"hstateen0h", 0x61c},
+        {"hstateen1h", 0x61d},
+        {"hstateen2h", 0x61e},
+        {"hstateen3h", 0x61f},
+        {"vsstatus", 0x200},
+        {"vsie", 0x204},
+        {"vstvec", 0x205},
+        {"vsscratch", 0x240},
+        {"vsepc", 0x241},
+        {"vscause", 0x242},
+        {"vstval", 0x243},
+        {"vsip", 0x244},
+        {"vsatp", 0x280},
+        {"mvendorid", 0xf11},
+        {"marchid", 0xf12},
+        {"mimpid", 0xf13},
+        {"mhartid", 0xf14},
+        {"mconfigptr", 0xf15},
+        {"mstatus", 0x300},
+        {"misa", 0x301},
+        {"medeleg", 0x302},
+        {"mideleg", 0x303},
+        {"mie", 0x304},
+        {"mcounteren", 0x306},
+        {"mstatush", 0x310},
+        {"medelegh", 0x312},
+        {"mscratch", 0x340},
+        {"mepc", 0x341},
+        {"mcause", 0x342},
+        {"mtval", 0x343},
+        {"mip", 0x344},
+        {"mtinst", 0x34a},
+        {"mtval2", 0x34b},
+        {"menvcfg", 0x30a},
+        {"menvcfgh", 0x31a},
+        {"mseccfg", 0x747},
+        {"mseccfgh", 0x757},
+        {"pmpcfg0", 0x3a0},
+        {"pmpcfg1", 0x3a1},
+        {"pmpcfg2", 0x3a2},
+        {"pmpcfg3", 0x3a3},
+        {"pmpcfg4", 0x3a4},
+        {"pmpcfg5", 0x3a5},
+        {"pmpcfg6", 0x3a6},
+        {"pmpcfg7", 0x3a7},
+        {"pmpcfg8", 0x3a8},
+        {"pmpcfg9", 0x3a9},
+        {"pmpcfg10", 0x3aa},
+        {"pmpcfg11", 0x3ab},
+        {"pmpcfg12", 0x3ac},
+        {"pmpcfg13", 0x3ad},
+        {"pmpcfg14", 0x3ae},
+        {"pmpcfg15", 0x3af},
+        {"pmpaddr0", 0x3b0},
+        {"pmpaddr1", 0x3b1},
+        {"pmpaddr2", 0x3b2},
+        {"pmpaddr3", 0x3b3},
+        {"pmpaddr4", 0x3b4},
+        {"pmpaddr5", 0x3b5},
+        {"pmpaddr6", 0x3b6},
+        {"pmpaddr7", 0x3b7},
+        {"pmpaddr8", 0x3b8},
+        {"pmpaddr9", 0x3b9},
+        {"pmpaddr10", 0x3ba},
+        {"pmpaddr11", 0x3bb},
+        {"pmpaddr12", 0x3bc},
+        {"pmpaddr13", 0x3bd},
+        {"pmpaddr14", 0x3be},
+        {"pmpaddr15", 0x3bf},
+        {"pmpaddr16", 0x3c0},
+        {"pmpaddr17", 0x3c1},
+        {"pmpaddr18", 0x3c2},
+        {"pmpaddr19", 0x3c3},
+        {"pmpaddr20", 0x3c4},
+        {"pmpaddr21", 0x3c5},
+        {"pmpaddr22", 0x3c6},
+        {"pmpaddr23", 0x3c7},
+        {"pmpaddr24", 0x3c8},
+        {"pmpaddr25", 0x3c9},
+        {"pmpaddr26", 0x3ca},
+        {"pmpaddr27", 0x3cb},
+        {"pmpaddr28", 0x3cc},
+        {"pmpaddr29", 0x3cd},
+        {"pmpaddr30", 0x3ce},
+        {"pmpaddr31", 0x3cf},
+        {"pmpaddr32", 0x3d0},
+        {"pmpaddr33", 0x3d1},
+        {"pmpaddr34", 0x3d2},
+        {"pmpaddr35", 0x3d3},
+        {"pmpaddr36", 0x3d4},
+        {"pmpaddr37", 0x3d5},
+        {"pmpaddr38", 0x3d6},
+        {"pmpaddr39", 0x3d7},
+        {"pmpaddr40", 0x3d8},
+        {"pmpaddr41", 0x3d9},
+        {"pmpaddr42", 0x3da},
+        {"pmpaddr43", 0x3db},
+        {"pmpaddr44", 0x3dc},
+        {"pmpaddr45", 0x3dd},
+        {"pmpaddr46", 0x3de},
+        {"pmpaddr47", 0x3df},
+        {"pmpaddr48", 0x3e0},
+        {"pmpaddr49", 0x3e1},
+        {"pmpaddr50", 0x3e2},
+        {"pmpaddr51", 0x3e3},
+        {"pmpaddr52", 0x3e4},
+        {"pmpaddr53", 0x3e5},
+        {"pmpaddr54", 0x3e6},
+        {"pmpaddr55", 0x3e7},
+        {"pmpaddr56", 0x3e8},
+        {"pmpaddr57", 0x3e9},
+        {"pmpaddr58", 0x3ea},
+        {"pmpaddr59", 0x3eb},
+        {"pmpaddr60", 0x3ec},
+        {"pmpaddr61", 0x3ed},
+        {"pmpaddr62", 0x3ee},
+        {"pmpaddr63", 0x3ef},
+        {"mstateen0", 0x30c},
+        {"mstateen1", 0x30d},
+        {"mstateen2", 0x30e},
+        {"mstateen3", 0x30f},
+        {"mstateen0h", 0x31c},
+        {"mstateen1h", 0x31d},
+        {"mstateen2h", 0x31e},
+        {"mstateen3h", 0x31f},
+        {"mnscratch", 0x740},
+        {"mnepc", 0x741},
+        {"mncause", 0x742},
+        {"mnstatus", 0x743},
+        {"mcycle", 0xb00},
+        {"minstret", 0xb02},
+        {"mhpmcounter3", 0xb03},
+        {"mhpmcounter4", 0xb04},
+        {"mhpmcounter5", 0xb05},
+        {"mhpmcounter6", 0xb06},
+        {"mhpmcounter7", 0xb07},
+        {"mhpmcounter8", 0xb08},
+        {"mhpmcounter9", 0xb09},
+        {"mhpmcounter10", 0xb0a},
+        {"mhpmcounter11", 0xb0b},
+        {"mhpmcounter12", 0xb0c},
+        {"mhpmcounter13", 0xb0d},
+        {"mhpmcounter14", 0xb0e},
+        {"mhpmcounter15", 0xb0f},
+        {"mhpmcounter16", 0xb10},
+        {"mhpmcounter17", 0xb11},
+        {"mhpmcounter18", 0xb12},
+        {"mhpmcounter19", 0xb13},
+        {"mhpmcounter20", 0xb14},
+        {"mhpmcounter21", 0xb15},
+        {"mhpmcounter22", 0xb16},
+        {"mhpmcounter23", 0xb17},
+        {"mhpmcounter24", 0xb18},
+        {"mhpmcounter25", 0xb19},
+        {"mhpmcounter26", 0xb1a},
+        {"mhpmcounter27", 0xb1b},
+        {"mhpmcounter28", 0xb1c},
+        {"mhpmcounter29", 0xb1d},
+        {"mhpmcounter30", 0xb1e},
+        {"mhpmcounter31", 0xb1f},
+        {"mcycleh", 0xb80},
+        {"minstreth", 0xb82},
+        {"mhpmcounter3h", 0xb83},
+        {"mhpmcounter4h", 0xb84},
+        {"mhpmcounter5h", 0xb85},
+        {"mhpmcounter6h", 0xb86},
+        {"mhpmcounter7h", 0xb87},
+        {"mhpmcounter8h", 0xb88},
+        {"mhpmcounter9h", 0xb89},
+        {"mhpmcounter10h", 0xb8a},
+        {"mhpmcounter11h", 0xb8b},
+        {"mhpmcounter12h", 0xb8c},
+        {"mhpmcounter13h", 0xb8d},
+        {"mhpmcounter14h", 0xb8e},
+        {"mhpmcounter15h", 0xb8f},
+        {"mhpmcounter16h", 0xb90},
+        {"mhpmcounter17h", 0xb91},
+        {"mhpmcounter18h", 0xb92},
+        {"mhpmcounter19h", 0xb93},
+        {"mhpmcounter20h", 0xb94},
+        {"mhpmcounter21h", 0xb95},
+        {"mhpmcounter22h", 0xb96},
+        {"mhpmcounter23h", 0xb97},
+        {"mhpmcounter24h", 0xb98},
+        {"mhpmcounter25h", 0xb99},
+        {"mhpmcounter26h", 0xb9a},
+        {"mhpmcounter27h", 0xb9b},
+        {"mhpmcounter28h", 0xb9c},
+        {"mhpmcounter29h", 0xb9d},
+        {"mhpmcounter30h", 0xb9e},
+        {"mhpmcounter31h", 0xb9f},
+        {"mcountinhibit", 0x320},
+        {"mhpmevent3", 0x323},
+        {"mhpmevent4", 0x324},
+        {"mhpmevent5", 0x325},
+        {"mhpmevent6", 0x326},
+        {"mhpmevent7", 0x327},
+        {"mhpmevent8", 0x328},
+        {"mhpmevent9", 0x329},
+        {"mhpmevent10", 0x32a},
+        {"mhpmevent11", 0x32b},
+        {"mhpmevent12", 0x32c},
+        {"mhpmevent13", 0x32d},
+        {"mhpmevent14", 0x32e},
+        {"mhpmevent15", 0x32f},
+        {"mhpmevent16", 0x330},
+        {"mhpmevent17", 0x331},
+        {"mhpmevent18", 0x332},
+        {"mhpmevent19", 0x333},
+        {"mhpmevent20", 0x334},
+        {"mhpmevent21", 0x335},
+        {"mhpmevent22", 0x336},
+        {"mhpmevent23", 0x337},
+        {"mhpmevent24", 0x338},
+        {"mhpmevent25", 0x339},
+        {"mhpmevent26", 0x33a},
+        {"mhpmevent27", 0x33b},
+        {"mhpmevent28", 0x33c},
+        {"mhpmevent29", 0x33d},
+        {"mhpmevent30", 0x33e},
+        {"mhpmevent31", 0x33f},
+        {"mhpmevent3h", 0x723},
+        {"mhpmevent4h", 0x724},
+        {"mhpmevent5h", 0x725},
+        {"mhpmevent6h", 0x726},
+        {"mhpmevent7h", 0x727},
+        {"mhpmevent8h", 0x728},
+        {"mhpmevent9h", 0x729},
+        {"mhpmevent10h", 0x72a},
+        {"mhpmevent11h", 0x72b},
+        {"mhpmevent12h", 0x72c},
+        {"mhpmevent13h", 0x72d},
+        {"mhpmevent14h", 0x72e},
+        {"mhpmevent15h", 0x72f},
+        {"mhpmevent16h", 0x730},
+        {"mhpmevent17h", 0x731},
+        {"mhpmevent18h", 0x732},
+        {"mhpmevent19h", 0x733},
+        {"mhpmevent20h", 0x734},
+        {"mhpmevent21h", 0x735},
+        {"mhpmevent22h", 0x736},
+        {"mhpmevent23h", 0x737},
+        {"mhpmevent24h", 0x738},
+        {"mhpmevent25h", 0x739},
+        {"mhpmevent26h", 0x73a},
+        {"mhpmevent27h", 0x73b},
+        {"mhpmevent28h", 0x73c},
+        {"mhpmevent29h", 0x73d},
+        {"mhpmevent30h", 0x73e},
+        {"mhpmevent31h", 0x73f},
+};
+
+// A helper function to convert a text string to an integer. The function takes
+// either numeric literals (hexadecimal or decimal), symbol names, or relocation
+// functions, e.g., %hi(<symbol name>).
+template <typename T>
+absl::StatusOr<T> SimpleTextToInt(absl::string_view op_text,
+                                  ResolverInterface *resolver) {
+  T value;
+  static RE2 hex_re("^\\s*0x([0-9a-fA-F]+)\\s*$");
+  static RE2 dec_re("^\\s*(-?[0-9]+)\\s*$");
+  static RE2 relo_re("^\\s*\\%[a-zA-Z0-9_]+\\s*\\(([a-zA-Z0-9_]+)\\s*\\)\\s*$");
+  static RE2 symbol_re("^\\s*([a-zA-Z0-9_]+)\\s*$");
+  std::string str;
+  std::string text(op_text);
+  // First see if the operand is a relocation function, and extract the text
+  // argument. A relocation function is on the form of %name(arg).
+  if (RE2::FullMatch(op_text, relo_re, &str)) {
+    text = str;
+  }
+  // Extract the hex immediate.
+  if (RE2::FullMatch(text, hex_re, &str)) {
+    if (absl::SimpleHexAtoi(str, &value)) return value;
+    return absl::InvalidArgumentError(
+        absl::StrCat("Invalid hexadecimal immediate: ", text));
+  }
+  // Extract the decimal immediate.
+  if (RE2::FullMatch(text, dec_re, &str)) {
+    if (absl::SimpleAtoi(str, &value)) return value;
+    return absl::InvalidArgumentError(
+        absl::StrCat("Invalid decimal immediate: ", text));
+  }
+  // Extract the symbol.
+  if (RE2::FullMatch(text, symbol_re, &str)) {
+    if (resolver != nullptr) {
+      auto res = resolver->Resolve(str);
+      if (!res.ok()) {
+        return res.status();
+      }
+      return static_cast<T>(res.value());
+    }
+  }
+  return absl::InvalidArgumentError(absl::StrCat("Invalid argument: ", text));
+}
+
+using ValueMap = absl::flat_hash_map<absl::string_view, uint64_t>;
+
+// This function adds the bin setters for the source operands to the given map.
+template <typename Enum, typename Map, typename Encoder>
+void AddRiscvSourceOpBinSetters(Map &map) {
+  Insert(map, *Enum::kAAq,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map = {{"", 0}, {".aq", 1}};
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::AType::InsertAq(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kARl,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map = {{"", 0}, {".rl", 1}};
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::AType::InsertRl(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kBImm12,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           uint32_t delta = res.value() - address;
+           return Encoder::BType::InsertBImm(delta, 0ULL);
+         });
+  Insert(map, *Enum::kC3drs2,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kDCRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::CS::InsertCsRs2(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kC3rs1,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kCRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::CL::InsertClRs1(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kC3rs2,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kCRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::CS::InsertCsRs2(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kCSRUimm5,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::IType::InsertIUimm5(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kCdrs2,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kDRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::CR::InsertRs2(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kCrs1,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::CR::InsertRs1(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kCrs2,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::CR::InsertRs2(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kCsr,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kCsrRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::IType::InsertUImm12(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kDrs1,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kDRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::RType::InsertRs1(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kDrs2,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kDRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::RType::InsertRs2(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kDrs3,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kDRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::R4Type::InsertRs3(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kFrs1,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kFRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::RType::InsertRs1(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kFrs2,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kFRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::RType::InsertRs1(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kFrs3,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kFRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::R4Type::InsertRs3(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kICbImm8,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           uint32_t delta = res.value() - address;
+           return Encoder::CB::InsertBimm(delta, 0ULL);
+         });
+  Insert(map, *Enum::kICiImm6,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CI::InsertImm6(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICiImm612,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CI::InsertImm18(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICiImm6x16,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CI::InsertCiImm10(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICiUimm6,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CI::InsertUimm6(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICiUimm6x4,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CI::InsertCiImmW(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICiUimm6x8,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CI::InsertCiImmD(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICiwUimm8x4,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CIW::InsertCiwImm10(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICjImm11,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           auto delta = res.value() - address;
+           return Encoder::CJ::InsertJimm(delta, 0ULL);
+         });
+  Insert(map, *Enum::kIClUimm5x4,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CL::InsertClImmW(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kIClUimm5x8,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CL::InsertClImmD(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICssUimm6x4,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CS::InsertCsImmW(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kICssUimm6x8,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::CS::InsertCsImmD(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kIImm12,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::IType::InsertImm12(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kIUimm5,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::RType::InsertRUimm5(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kIUimm6,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::RSType::InsertRUimm6(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kJImm12,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::IType::InsertImm12(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kJImm20,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<int32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           uint32_t delta = res.value() - address;
+           auto value = Encoder::JType::InsertJImm(delta, 0ULL);
+           return value;
+         });
+  Insert(map, *Enum::kPred,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::Fence::InsertPred(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kRd,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::RType::InsertRd(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kRm,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::R4Type::InsertRs3(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kRs1,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::RType::InsertRs1(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kRs2,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::RType::InsertRs2(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kSImm12,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::SType::InsertSImm(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kSucc,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::Fence::InsertSucc(res.value(), 0ULL);
+         });
+  Insert(map, *Enum::kUImm20,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           auto res = SimpleTextToInt<uint32_t>(text, resolver);
+           if (!res.ok()) return res.status();
+           return Encoder::UType::InsertUImm(res.value(), 0ULL);
+         });
+}
+
+// This function adds the destination operand setters for the RiscV ISA to the
+// given map.
+template <typename Enum, typename Map, typename Encoder>
+void AddRiscvDestOpBinSetters(Map &map) {
+  Insert(map, *Enum::kC3drd,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kDCRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid destination operand: ", text));
+           }
+           return Encoder::CL::InsertClRd(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kC3rd,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kCRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid destination operand: ", text));
+           }
+           return Encoder::CL::InsertClRd(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kC3rs1,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kCRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid destination operand: ", text));
+           }
+           return Encoder::CS::InsertCsRs1(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kCsr,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kCsrRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid source operand: ", text));
+           }
+           return Encoder::IType::InsertUImm12(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kDrd,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kDRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid destination operand: ", text));
+           }
+           return Encoder::RType::InsertRd(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kFrd,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kFRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid destination operand: ", text));
+           }
+           return Encoder::RType::InsertRd(iter->second, 0ULL);
+         });
+  Insert(map, *Enum::kRd,
+         [](uint64_t address, absl::string_view text,
+            ResolverInterface *resolver) -> absl::StatusOr<uint64_t> {
+           static ValueMap map(kRegisterList);
+           auto iter = map.find(text);
+           if (iter == map.end()) {
+             return absl::InvalidArgumentError(
+                 absl::StrCat("Invalid destination operand: ", text));
+           }
+           return Encoder::RType::InsertRd(iter->second, 0ULL);
+         });
+}
+
+// These functions add the appropriate relocation entry to the relocations
+// vector if the operand (in text) requires it.
+namespace internal {
+
+absl::Status RelocateAddiIImm12(uint64_t address, absl::string_view text,
+                                ResolverInterface *resolver,
+                                std::vector<RelocationInfo> &relocations);
+absl::Status RelocateJJImm20(uint64_t address, absl::string_view text,
+                             ResolverInterface *resolver,
+                             std::vector<RelocationInfo> &relocations);
+absl::Status RelocateJrJImm12(uint64_t address, absl::string_view text,
+                              ResolverInterface *resolver,
+                              std::vector<RelocationInfo> &relocations);
+absl::Status RelocateLuiUImm20(uint64_t address, absl::string_view text,
+                               ResolverInterface *resolver,
+                               std::vector<RelocationInfo> &relocations);
+absl::Status RelocateSdSImm12(uint64_t address, absl::string_view text,
+                              ResolverInterface *resolver,
+                              std::vector<RelocationInfo> &relocations);
+absl::Status RelocateAuipcUImm20(uint64_t address, absl::string_view text,
+                                 ResolverInterface *resolver,
+                                 std::vector<RelocationInfo> &relocations);
+
+}  // namespace internal
+
+// This function adds the source operand relocation setters for the RiscV ISA to
+// the given map. Notice that the key in the map is the tuple consisting of the
+// opcode and the source operand enum values.
+template <typename OpcodeEnum, typename SourceOpEnum, typename Map>
+void AddRiscvSourceOpRelocationSetters(Map &map) {
+  Insert(map, OpcodeEnum::kAddi, SourceOpEnum::kIImm12,
+         internal::RelocateAddiIImm12);
+  Insert(map, OpcodeEnum::kJal, SourceOpEnum::kJImm20,
+         internal::RelocateJJImm20);
+  Insert(map, OpcodeEnum::kJ, SourceOpEnum::kJImm20, internal::RelocateJJImm20);
+  Insert(map, OpcodeEnum::kJr, SourceOpEnum::kJImm12,
+         internal::RelocateJrJImm12);
+  Insert(map, OpcodeEnum::kLui, SourceOpEnum::kUImm20,
+         internal::RelocateLuiUImm20);
+  Insert(map, OpcodeEnum::kSd, SourceOpEnum::kSImm12,
+         internal::RelocateSdSImm12);
+  Insert(map, OpcodeEnum::kJalr, SourceOpEnum::kJImm12,
+         internal::RelocateJrJImm12);
+  Insert(map, OpcodeEnum::kAuipc, SourceOpEnum::kUImm20,
+         internal::RelocateAuipcUImm20);
+}
+
+}  // namespace riscv
+}  // namespace sim
+}  // namespace mpact
+
+#endif  // THIRD_PARTY_MPACT_RISCV_RISCV_BIN_SETTERS_H_
diff --git a/riscv/riscv_getter_helpers.h b/riscv/riscv_getter_helpers.h
index dc3f91d..408da03 100644
--- a/riscv/riscv_getter_helpers.h
+++ b/riscv/riscv_getter_helpers.h
@@ -63,6 +63,16 @@
   }
 }
 
+template <typename M, typename E1, typename E2, typename G>
+inline void Insert(M &map, E1 entry1, E2 entry2, G getter) {
+  auto key = std::tie(entry1, entry2);
+  if (!map.contains(key)) {
+    map.insert(std::make_pair(key, getter));
+  } else {
+    map.at(key) = getter;
+  }
+}
+
 // Generic helper functions to create register operands.
 template <typename RegType>
 inline DestinationOperandInterface *GetRegisterDestinationOp(RiscVState *state,