[go] cmd/asm,cmd/internal/obj/riscv: add support for riscv compressed instructions

6 views
Skip to first unread message

Joel Sing (Gerrit)

unread,
Oct 20, 2025, 12:40:25 AMOct 20
to Meng Zhuo, Mark Ryan, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Mark Ryan and Meng Zhuo

Joel Sing has uploaded the change for review

Joel Sing would like Meng Zhuo and Mark Ryan to review this change.

Commit message

cmd/asm,cmd/internal/obj/riscv: add support for riscv compressed instructions

Add support for compressed instructions in the RISC-V assembler. This
implements instruction validation and encoding for all instructions in
the "C" extension.

It is worth noting that the validation and encoding of these instructions
is far more convoluted then the typical instruction validation and
encoding. While the current model has been followed for now, it would be
worth revisiting this in the future and potentially switching to a table
based or even per-instruction implementation.

Additionally, the current instruction encoding is lacking some of the bits
needed for compressed instructions - this is solved by compressedEncoding,
which provides the missing information. This will also be addressed in the
future, likely by changing the instruction encoding format.

Updates #71105
Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d

Change diff

diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go
index 8481a8f..fb9e785 100644
--- a/src/cmd/asm/internal/arch/arch.go
+++ b/src/cmd/asm/internal/arch/arch.go
@@ -92,7 +92,8 @@
func jumpRISCV(word string) bool {
switch word {
case "BEQ", "BEQZ", "BGE", "BGEU", "BGEZ", "BGT", "BGTU", "BGTZ", "BLE", "BLEU", "BLEZ",
- "BLT", "BLTU", "BLTZ", "BNE", "BNEZ", "CALL", "JAL", "JALR", "JMP":
+ "BLT", "BLTU", "BLTZ", "BNE", "BNEZ", "CALL", "CBEQZ", "CBNEZ", "CJ", "CJALR", "CJR",
+ "JAL", "JALR", "JMP":
return true
}
return false
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s
index 702b822..4615119 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64.s
@@ -372,6 +372,76 @@
// 21.7: Double-Precision Floating-Point Classify Instruction
FCLASSD F0, X5 // d31200e2

+ //
+ // "C" Extension for Compressed Instructions, Version 2.0
+ //
+
+ // 26.3.1: Compressed Stack-Pointer-Based Loads and Stores
+ CLWSP 20(SP), X10 // 5245
+ CLDSP 24(SP), X10 // 6265
+ CFLDSP 32(SP), F10 // 0235
+ CSWSP X10, 20(SP) // 2aca
+ CSDSP X10, 24(SP) // 2aec
+ CFSDSP F10, 32(SP) // 2ab0
+
+ // 26.3.2: Compressed Register-Based Loads and Stores
+ CLW 20(X10), X11 // 4c49
+ CLD 24(X10), X11 // 0c6d
+ CFLD 32(X10), F11 // 0c31
+ CSW X11, 20(X10) // 4cc9
+ CSD X11, 24(X10) // 0ced
+ CFSD F11, 32(X10) // 0cb1
+
+ // 26.4: Compressed Control Transfer Instructions
+ CJ 1(PC) // 09a0
+ CJR X5 // 8282
+ CJALR X5 // 8292
+ CBEQZ X10, 1(PC) // 09c1
+ CBNEZ X10, 1(PC) // 09e1
+
+ // 26.5.1: Compressed Integer Constant-Generation Instructions
+ CLI $-32, X5 // 8152
+ CLI $31, X5 // fd42
+ CLUI $-32, X5 // 8172
+ CLUI $31, X5 // fd62
+
+ // 26.5.2: Compressed Integer Register-Immediate Operations
+ CADD $-32, X5 // 8112
+ CADD $31, X5 // fd02
+ CADDI $-32, X5 // 8112
+ CADDI $31, X5 // fd02
+ CADDW $-32, X5 // 8132
+ CADDW $31, X5 // fd22
+ CADDIW $-32, X5 // 8132
+ CADDIW $31, X5 // fd22
+ CADDI16SP $-512, SP // 0171
+ CADDI16SP $496, SP // 7d61
+ CADDI4SPN $4, SP, X10 // 4800
+ CADDI4SPN $1020, SP, X10 // e81f
+ CSLLI $63, X5 // fe12
+ CSRLI $63, X10 // 7d91
+ CSRAI $63, X10 // 7d95
+ CAND $-32, X10 // 0199
+ CAND $31, X10 // 7d89
+ CANDI $-32, X10 // 0199
+ CANDI $31, X10 // 7d89
+
+ // 26.5.3: Compressed Integer Register-Register Operations
+ CMV X6, X5 // 9a82
+ CADD X9, X8 // 2694
+ CAND X9, X8 // 658c
+ COR X9, X8 // 458c
+ CXOR X9, X8 // 258c
+ CSUB X9, X8 // 058c
+ CADDW X9, X8 // 259c
+ CSUBW X9, X8 // 059c
+
+ // 26.5.5: Compressed NOP Instruction
+ CNOP // 0100
+
+ // 26.5.6: Compressed Breakpoint Instruction
+ CEBREAK // 0290
+
// 28.4.1: Address Generation Instructions (Zba)
ADDUW X10, X11, X12 // 3b86a508
ADDUW X10, X11 // bb85a508
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64validation.s b/src/cmd/asm/internal/asm/testdata/riscv64validation.s
index c8ae4c9..713c5cb 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64validation.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64validation.s
@@ -16,6 +16,138 @@
WORD $0x100000000 // ERROR "must be in range [0x0, 0xffffffff]"

//
+ // "C" Extension for Compressed Instructions, Version 2.0
+ //
+ CLWSP 20(X5), X10 // ERROR "rs2 must be SP/X2"
+ CLWSP 20(SP), X0 // ERROR "cannot use register X0"
+ CLWSP 20(SP), F10 // ERROR "expected integer register in rd position"
+ CLWSP 22(SP), X10 // ERROR "must be a multiple of 4"
+ CLDSP 24(X5), X10 // ERROR "rs2 must be SP/X2"
+ CLDSP 24(SP), X0 // ERROR "cannot use register X0"
+ CLDSP 24(SP), F10 // ERROR "expected integer register in rd position"
+ CLDSP 28(SP), X10 // ERROR "must be a multiple of 8"
+ CFLDSP 32(X5), F10 // ERROR "rs2 must be SP/X2"
+ CFLDSP 32(SP), X10 // ERROR "expected float register in rd position"
+ CFLDSP 36(SP), F10 // ERROR "must be a multiple of 8"
+ CSWSP X10, 20(X5) // ERROR "rd must be SP/X2"
+ CSWSP F10, 20(SP) // ERROR "expected integer register in rs2 position"
+ CSWSP X10, 22(SP) // ERROR "must be a multiple of 4"
+ CSDSP X10, 24(X5) // ERROR "rd must be SP/X2"
+ CSDSP F10, 24(SP) // ERROR "expected integer register in rs2 position"
+ CSDSP X10, 28(SP) // ERROR "must be a multiple of 8"
+ CFSDSP F10, 32(X5) // ERROR "rd must be SP/X2"
+ CFSDSP X10, 32(SP) // ERROR "expected float register in rs2 position"
+ CFSDSP F10, 36(SP) // ERROR "must be a multiple of 8"
+ CLW 20(X10), F11 // ERROR "expected integer prime register in rd position"
+ CLW 20(X5), X11 // ERROR "expected integer prime register in rs1 position"
+ CLW 20(X10), X5 // ERROR "expected integer prime register in rd position"
+ CLW -1(X10), X11 // ERROR "must be in range [0, 127]"
+ CLW 22(X10), X11 // ERROR "must be a multiple of 4"
+ CLW 128(X10), X11 // ERROR "must be in range [0, 127]"
+ CLD 24(X10), F11 // ERROR "expected integer prime register in rd position"
+ CLD 24(X5), X11 // ERROR "expected integer prime register in rs1 position"
+ CLD -1(X10), X11 // ERROR "must be in range [0, 255]"
+ CLD 30(X10), X11 // ERROR "must be a multiple of 8"
+ CLD 256(X10), X11 // ERROR "must be in range [0, 255]"
+ CFLD 32(X10), X11 // ERROR "expected float prime register in rd position"
+ CFLD 32(X5), F11 // ERROR "expected integer prime register in rs1 position"
+ CFLD -1(X10), F11 // ERROR "must be in range [0, 255]"
+ CFLD 34(X10), F11 // ERROR "must be a multiple of 8"
+ CFLD 256(X10), F11 // ERROR "must be in range [0, 255]"
+ CSW F11, 20(X10) // ERROR "expected integer prime register in rs2 position"
+ CSW X11, -1(X10) // ERROR "must be in range [0, 127]"
+ CSW X11, 22(X10) // ERROR "must be a multiple of 4"
+ CSW X11, 128(X10) // ERROR "must be in range [0, 127]"
+ CSD F11, 24(X10) // ERROR "expected integer prime register in rs2 position"
+ CSD X11, -1(X10) // ERROR "must be in range [0, 255]"
+ CSD X11, 28(X10) // ERROR "must be a multiple of 8"
+ CSD X11, 256(X10) // ERROR "must be in range [0, 255]"
+ CFSD X11, 32(X10) // ERROR "expected float prime register in rs2 position"
+ CFSD F11, -1(X10) // ERROR "must be in range [0, 255]"
+ CFSD F11, 36(X10) // ERROR "must be a multiple of 8"
+ CFSD F11, 256(X10) // ERROR "must be in range [0, 255]"
+ CJR X0 // ERROR "cannot use register X0 in rs1"
+ CJR X10, X11 // ERROR "expected no register in rs2"
+ CJALR X0 // ERROR "cannot use register X0 in rs1"
+ CJALR X10, X11 // ERROR "expected no register in rd"
+ CBEQZ X5, 1(PC) // ERROR "expected integer prime register in rs1"
+ CBNEZ X5, 1(PC) // ERROR "expected integer prime register in rs1"
+ CLI $3, X0 // ERROR "cannot use register X0 in rd"
+ CLI $-33, X5 // ERROR "must be in range [-32, 31]"
+ CLI $32, X5 // ERROR "must be in range [-32, 31]"
+ CLUI $3, X0 // ERROR "cannot use register X0 in rd"
+ CLUI $3, X2 // ERROR "cannot use register SP/X2 in rd"
+ CLUI $-33, X5 // ERROR "must be in range [-32, 31]"
+ CLUI $32, X5 // ERROR "must be in range [-32, 31]"
+ CADD $31, X5, X6 // ERROR "rd must be the same as rs1"
+ CADD $-33, X5 // ERROR "must be in range [-32, 31]"
+ CADD $32, X5 // ERROR "must be in range [-32, 31]"
+ CADDI $31, X5, X6 // ERROR "rd must be the same as rs1"
+ CADDI $-33, X5 // ERROR "must be in range [-32, 31]"
+ CADDI $32, X5 // ERROR "must be in range [-32, 31]"
+ CADDW $-33, X5 // ERROR "must be in range [-32, 31]"
+ CADDW $32, X5 // ERROR "must be in range [-32, 31]"
+ CADDIW $-33, X5 // ERROR "must be in range [-32, 31]"
+ CADDIW $32, X5 // ERROR "must be in range [-32, 31]"
+ CADDI16SP $16, X5 // ERROR "rd must be SP/X2"
+ CADDI16SP $-513, SP // ERROR "must be in range [-512, 511]"
+ CADDI16SP $20, SP // ERROR "must be a multiple of 16"
+ CADDI16SP $512, SP // ERROR "must be in range [-512, 511]"
+ CADDI4SPN $4, SP, X5 // ERROR "expected integer prime register in rd"
+ CADDI4SPN $4, X5, X10 // ERROR "SP/X2 must be in rs1"
+ CADDI4SPN $-1, SP, X10 // ERROR "must be in range [0, 1023]"
+ CADDI4SPN $0, SP, X10 // ERROR "immediate cannot be zero"
+ CADDI4SPN $6, SP, X10 // ERROR "must be a multiple of 4"
+ CADDI4SPN $1024, SP, X10 // ERROR "must be in range [0, 1023]"
+ CSLLI $63, X5, X6 // ERROR "rd must be the same as rs1"
+ CSLLI $-1, X5 // ERROR "must be in range [0, 63]"
+ CSLLI $64, X5 // ERROR "must be in range [0, 63]"
+ CSRLI $63, X10, X11 // ERROR "rd must be the same as rs1"
+ CSRLI $63, X5 // ERROR "expected integer prime register in rd"
+ CSRLI $-1, X10 // ERROR "must be in range [0, 63]"
+ CSRLI $64, X10 // ERROR "must be in range [0, 63]"
+ CSRAI $63, X10, X11 // ERROR "rd must be the same as rs1"
+ CSRAI $63, X5 // ERROR "expected integer prime register in rd"
+ CSRAI $-1, X10 // ERROR "must be in range [0, 63]"
+ CSRAI $64, X10 // ERROR "must be in range [0, 63]"
+ CAND $1, X10, X11 // ERROR "rd must be the same as rs1"
+ CAND $1, X5 // ERROR "expected integer prime register in rd"
+ CAND $-64, X10 // ERROR "must be in range [-32, 31]"
+ CAND $63, X10 // ERROR "must be in range [-32, 31]"
+ CANDI $1, X10, X11 // ERROR "rd must be the same as rs1"
+ CANDI $1, X5 // ERROR "expected integer prime register in rd"
+ CANDI $-64, X10 // ERROR "must be in range [-32, 31]"
+ CANDI $63, X10 // ERROR "must be in range [-32, 31]"
+ CMV X0, X5 // ERROR "cannot use register X0 in rs2"
+ CMV X5, X6, X7 // ERROR "expected no register in rs1"
+ CMV X5, X0 // ERROR "cannot use register X0 in rd"
+ CMV F1, X5 // ERROR "expected integer register in rs2"
+ CMV X5, F1 // ERROR "expected integer register in rd"
+ CADD X5, X6, X7 // ERROR "rd must be the same as rs1"
+ CADD X0, X8 // ERROR "cannot use register X0 in rs2"
+ CADD X8, X0 // ERROR "cannot use register X0 in rd"
+ CAND X10, X11, X12 // ERROR "rd must be the same as rs1"
+ CAND X5, X11 // ERROR "expected integer prime register in rs2"
+ CAND X10, X5 // ERROR "expected integer prime register in rd"
+ COR X10, X11, X12 // ERROR "rd must be the same as rs1"
+ COR X5, X11 // ERROR "expected integer prime register in rs2"
+ COR X10, X5 // ERROR "expected integer prime register in rd"
+ CXOR X10, X11, X12 // ERROR "rd must be the same as rs1"
+ CXOR X5, X11 // ERROR "expected integer prime register in rs2"
+ CXOR X10, X5 // ERROR "expected integer prime register in rd"
+ CSUB X10, X11, X12 // ERROR "rd must be the same as rs1"
+ CSUB X5, X11 // ERROR "expected integer prime register in rs2"
+ CSUB X10, X5 // ERROR "expected integer prime register in rd"
+ CADDW X10, X11, X12 // ERROR "rd must be the same as rs1"
+ CADDW X5, X11 // ERROR "expected integer prime register in rs2"
+ CADDW X10, X5 // ERROR "expected integer prime register in rd"
+ CSUBW X10, X11, X12 // ERROR "rd must be the same as rs1"
+ CSUBW X5, X11 // ERROR "expected integer prime register in rs2"
+ CSUBW X10, X5 // ERROR "expected integer prime register in rd"
+ CNOP X10 // ERROR "expected no register in rs2"
+ CEBREAK X10 // ERROR "expected no register in rs2"
+
+ //
// "V" Standard Extension for Vector Operations, Version 1.0
//
VSETVLI $32, E16, M1, TU, MU, X12 // ERROR "must be in range [0, 31] (5 bits)"
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index 7f72a60..3216f25 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -70,6 +70,10 @@
// form of the instruction.
if p.From.Type == obj.TYPE_CONST {
switch p.As {
+ case ACSUB:
+ p.As, p.From.Offset = ACADDI, -p.From.Offset
+ case ACSUBW:
+ p.As, p.From.Offset = ACADDIW, -p.From.Offset
case ASUB:
p.As, p.From.Offset = AADDI, -p.From.Offset
case ASUBW:
@@ -381,6 +385,10 @@
return ABEQ
case ABNEZ:
return ABEQZ
+ case ACBEQZ:
+ return ACBNEZ
+ case ACBNEZ:
+ return ACBEQZ
default:
panic("InvertBranch: not a branch")
}
@@ -394,7 +402,7 @@
switch p.As {
case obj.ACALL:
return true
- case AJAL, AJALR:
+ case ACJALR, AJAL, AJALR:
if p.From.Type == obj.TYPE_REG && p.From.Reg == REG_LR {
return true
}
@@ -670,7 +678,7 @@

for p := cursym.Func().Text; p != nil; p = p.Link {
switch p.As {
- case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
+ case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, ACBEQZ, ACBNEZ, ACJ:
if p.To.Type != obj.TYPE_BRANCH {
ctxt.Diag("%v: instruction with branch-like opcode lacks destination", p)
break
@@ -752,7 +760,7 @@
// instructions will break everything--don't do it!
for p := cursym.Func().Text; p != nil; p = p.Link {
switch p.As {
- case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
+ case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, ACBEQZ, ACBNEZ, ACJ:
switch p.To.Type {
case obj.TYPE_BRANCH:
p.To.Type, p.To.Offset = obj.TYPE_CONST, p.To.Target().Pc-p.Pc
@@ -1042,6 +1050,16 @@
return r - min
}

+// regCI returns an integer register for use in a compressed instruction.
+func regCI(r uint32) uint32 {
+ return regVal(r, REG_X8, REG_X15)
+}
+
+// regCF returns a float register for use in a compressed instruction.
+func regCF(r uint32) uint32 {
+ return regVal(r, REG_F8, REG_F15)
+}
+
// regI returns an integer register.
func regI(r uint32) uint32 {
return regVal(r, REG_X0, REG_X31)
@@ -1123,6 +1141,24 @@
}
}

+func wantScaledImm(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64, signed bool) {
+ if err := immFits(imm, nbits, signed); err != nil {
+ ctxt.Diag("%v: %v", ins, err)
+ return
+ }
+ if imm%scale != 0 {
+ ctxt.Diag("%v: unsigned immediate %d must be a multiple of %d", ins, imm, scale)
+ }
+}
+
+func wantScaledImmI(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64) {
+ wantScaledImm(ctxt, ins, imm, nbits, scale, true)
+}
+
+func wantScaledImmU(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64) {
+ wantScaledImm(ctxt, ins, imm, nbits, scale, false)
+}
+
func wantReg(ctxt *obj.Link, ins *instruction, pos string, descr string, r, min, max uint32) {
if r < min || r > max {
var suffix string
@@ -1144,11 +1180,23 @@
wantReg(ctxt, ins, pos, "integer", r, REG_X0, REG_X31)
}

+// wantIntPrimeReg checks that r is an integer register that can be used
+// in a prime register field of a compressed instruction.
+func wantIntPrimeReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
+ wantReg(ctxt, ins, pos, "integer prime", r, REG_X8, REG_X15)
+}
+
// wantFloatReg checks that r is a floating-point register.
func wantFloatReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
wantReg(ctxt, ins, pos, "float", r, REG_F0, REG_F31)
}

+// wantFloatPrimeReg checks that r is an floating-point register that can
+// be used in a prime register field of a compressed instruction.
+func wantFloatPrimeReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
+ wantReg(ctxt, ins, pos, "float prime", r, REG_F8, REG_F15)
+}
+
// wantVectorReg checks that r is a vector register.
func wantVectorReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
wantReg(ctxt, ins, pos, "vector", r, REG_V0, REG_V31)
@@ -1161,6 +1209,202 @@
}
}

+func validateCA(ctxt *obj.Link, ins *instruction) {
+ wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
+ if ins.rd != ins.rs1 {
+ ctxt.Diag("%v: rd must be the same as rs1", ins)
+ }
+ wantIntPrimeReg(ctxt, ins, "rs2", ins.rs2)
+}
+
+func validateCB(ctxt *obj.Link, ins *instruction) {
+ if ins.as == ACSRAI || ins.as == ACSRLI {
+ wantImmU(ctxt, ins, ins.imm, 6)
+ } else if ins.as == ACBEQZ || ins.as == ACBNEZ {
+ wantImmI(ctxt, ins, ins.imm, 9)
+ } else {
+ wantImmI(ctxt, ins, ins.imm, 6)
+ }
+ if ins.as == ACBEQZ || ins.as == ACBNEZ {
+ wantNoneReg(ctxt, ins, "rd", ins.rd)
+ wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
+ } else {
+ wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
+ if ins.rd != ins.rs1 {
+ ctxt.Diag("%v: rd must be the same as rs1", ins)
+ }
+ }
+ wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+}
+
+func validateCI(ctxt *obj.Link, ins *instruction) {
+ if ins.as != ACNOP && ins.rd == REG_X0 {
+ ctxt.Diag("%v: cannot use register X0 in rd", ins)
+ }
+ if ins.as == ACLUI && ins.rd == REG_X2 {
+ ctxt.Diag("%v: cannot use register SP/X2 in rd", ins)
+ }
+ if ins.as != ACLI && ins.as != ACLUI && ins.as != ACLWSP && ins.as != ACLDSP && ins.as != ACFLDSP && ins.rd != ins.rs1 {
+ ctxt.Diag("%v: rd must be the same as rs1", ins)
+ }
+ if ins.as == ACADDI16SP && ins.rd != REG_SP {
+ ctxt.Diag("%v: rd must be SP/X2", ins)
+ }
+ if (ins.as == ACLWSP || ins.as == ACLDSP || ins.as == ACFLDSP) && ins.rs2 != REG_SP {
+ ctxt.Diag("%v: rs2 must be SP/X2", ins)
+ }
+ if ins.as == ACSLLI {
+ wantImmU(ctxt, ins, ins.imm, 6)
+ } else if ins.as == ACLWSP {
+ wantScaledImmU(ctxt, ins, ins.imm, 8, 4)
+ } else if ins.as == ACLDSP || ins.as == ACFLDSP {
+ wantScaledImmU(ctxt, ins, ins.imm, 9, 8)
+ } else if ins.as == ACADDI16SP {
+ wantScaledImmI(ctxt, ins, ins.imm, 10, 16)
+ } else {
+ wantImmI(ctxt, ins, ins.imm, 6)
+ }
+ switch ins.as {
+ case ACNOP, ACADDI, ACADDIW, ACSLLI:
+ wantIntReg(ctxt, ins, "rd", ins.rd)
+ wantIntReg(ctxt, ins, "rs1", ins.rs1)
+ wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+ case ACLWSP, ACLDSP:
+ wantIntReg(ctxt, ins, "rd", ins.rd)
+ wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+ wantIntReg(ctxt, ins, "rs2", ins.rs2)
+ case ACFLDSP:
+ wantFloatReg(ctxt, ins, "rd", ins.rd)
+ wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+ wantIntReg(ctxt, ins, "rs2", ins.rs2)
+ case ACADDI16SP:
+ wantIntReg(ctxt, ins, "rd", ins.rd)
+ wantIntReg(ctxt, ins, "rs1", ins.rs1)
+ wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+ default:
+ wantIntReg(ctxt, ins, "rd", ins.rd)
+ wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+ wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+ }
+}
+
+func validateCIW(ctxt *obj.Link, ins *instruction) {
+ wantScaledImmU(ctxt, ins, ins.imm, 10, 4)
+ wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
+ wantIntReg(ctxt, ins, "rs1", ins.rs1)
+ wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+ if ins.imm == 0 {
+ ctxt.Diag("%v: immediate cannot be zero", ins)
+ }
+ if ins.rs1 != REG_SP {
+ ctxt.Diag("%v: SP/X2 must be in rs1", ins)
+ }
+}
+
+func validateCJ(ctxt *obj.Link, ins *instruction) {
+ wantEvenOffset(ctxt, ins, ins.imm)
+ wantImmI(ctxt, ins, ins.imm, 12)
+ if ins.as != ACJ {
+ wantNoneReg(ctxt, ins, "rd", ins.rd)
+ wantIntReg(ctxt, ins, "rs1", ins.rs1)
+ wantIntReg(ctxt, ins, "rs2", ins.rs2)
+ if ins.rs1 == REG_X0 {
+ ctxt.Diag("%v: cannot use register X0 in rs1", ins)
+ }
+ }
+}
+
+func validateCL(ctxt *obj.Link, ins *instruction) {
+ if ins.as == ACLW {
+ wantScaledImmU(ctxt, ins, ins.imm, 7, 4)
+ } else if ins.as == ACLD || ins.as == ACFLD {
+ wantScaledImmU(ctxt, ins, ins.imm, 8, 8)
+ } else {
+ wantImmI(ctxt, ins, ins.imm, 5)
+ }
+ if ins.as == ACFLD {
+ wantFloatPrimeReg(ctxt, ins, "rd", ins.rd)
+ } else {
+ wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
+ }
+ wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
+ wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+}
+
+func validateCR(ctxt *obj.Link, ins *instruction) {
+ switch ins.as {
+ case ACJR, ACJALR:
+ wantNoneReg(ctxt, ins, "rd", ins.rd)
+ wantIntReg(ctxt, ins, "rs1", ins.rs1)
+ wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+ if ins.rs1 == REG_X0 {
+ ctxt.Diag("%v: cannot use register X0 in rs1", ins)
+ }
+ case ACMV:
+ wantIntReg(ctxt, ins, "rd", ins.rd)
+ wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+ wantIntReg(ctxt, ins, "rs2", ins.rs2)
+ if ins.rd == REG_X0 {
+ ctxt.Diag("%v: cannot use register X0 in rd", ins)
+ }
+ if ins.rs2 == REG_X0 {
+ ctxt.Diag("%v: cannot use register X0 in rs2", ins)
+ }
+ case ACEBREAK:
+ wantNoneReg(ctxt, ins, "rd", ins.rd)
+ wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+ wantNoneReg(ctxt, ins, "rs2", ins.rs2)
+ case ACADD:
+ wantIntReg(ctxt, ins, "rd", ins.rd)
+ if ins.rd == REG_X0 {
+ ctxt.Diag("%v: cannot use register X0 in rd", ins)
+ }
+ if ins.rd != ins.rs1 {
+ ctxt.Diag("%v: rd must be the same as rs1", ins)
+ }
+ wantIntReg(ctxt, ins, "rs2", ins.rs2)
+ if ins.rs2 == REG_X0 {
+ ctxt.Diag("%v: cannot use register X0 in rs2", ins)
+ }
+ }
+}
+
+func validateCS(ctxt *obj.Link, ins *instruction) {
+ if ins.as == ACSW {
+ wantScaledImmU(ctxt, ins, ins.imm, 7, 4)
+ } else if ins.as == ACSD || ins.as == ACFSD {
+ wantScaledImmU(ctxt, ins, ins.imm, 8, 8)
+ } else {
+ wantImmI(ctxt, ins, ins.imm, 5)
+ }
+ wantNoneReg(ctxt, ins, "rd", ins.rd)
+ wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
+ if ins.as == ACFSD {
+ wantFloatPrimeReg(ctxt, ins, "rs2", ins.rs2)
+ } else {
+ wantIntPrimeReg(ctxt, ins, "rs2", ins.rs2)
+ }
+}
+
+func validateCSS(ctxt *obj.Link, ins *instruction) {
+ if ins.rd != REG_SP {
+ ctxt.Diag("%v: rd must be SP/X2", ins)
+ }
+ if ins.as == ACSWSP {
+ wantScaledImmU(ctxt, ins, ins.imm, 8, 4)
+ } else if ins.as == ACSDSP || ins.as == ACFSDSP {
+ wantScaledImmU(ctxt, ins, ins.imm, 9, 8)
+ } else {
+ wantImmI(ctxt, ins, ins.imm, 6)
+ }
+ wantNoneReg(ctxt, ins, "rs1", ins.rs1)
+ if ins.as == ACFSDSP {
+ wantFloatReg(ctxt, ins, "rs2", ins.rs2)
+ } else {
+ wantIntReg(ctxt, ins, "rs2", ins.rs2)
+ }
+}
+
func validateRII(ctxt *obj.Link, ins *instruction) {
wantIntReg(ctxt, ins, "rd", ins.rd)
wantIntReg(ctxt, ins, "rs1", ins.rs1)
@@ -1422,6 +1666,91 @@
wantImmU(ctxt, ins, ins.imm, 32)
}

+// compressedEncoding returns the fixed instruction encoding for a compressed
+// instruction.
+func compressedEncoding(as obj.As) uint32 {
+ enc := encode(as)
+ if enc == nil {
+ panic("compressedEncoding: could not encode instruction")
+ }
+
+ // TODO: this can be removed once encode is reworked to return the
+ // necessary bits.
+ op := uint32(0)
+ switch as {
+ case ACSUB:
+ op = 0b100011<<10 | 0b00<<5
+ case ACXOR:
+ op = 0b100011<<10 | 0b01<<5
+ case ACOR:
+ op = 0b100011<<10 | 0b10<<5
+ case ACAND:
+ op = 0b100011<<10 | 0b11<<5
+ case ACSUBW:
+ op = 0b100111<<10 | 0b00<<5
+ case ACADDW:
+ op = 0b100111<<10 | 0b01<<5
+ case ACBEQZ:
+ op = 0b110 << 13
+ case ACBNEZ:
+ op = 0b111 << 13
+ case ACANDI:
+ op = 0b100<<13 | 0b10<<10
+ case ACSRAI:
+ op = 0b100<<13 | 0b01<<10
+ case ACSRLI:
+ op = 0b100<<13 | 0b00<<10
+ case ACLI:
+ op = 0b010 << 13
+ case ACLUI:
+ op = 0b011 << 13
+ case ACLWSP:
+ op = 0b010 << 13
+ case ACLDSP:
+ op = 0b011 << 13
+ case ACFLDSP:
+ op = 0b001 << 13
+ case ACADDIW:
+ op = 0b001 << 13
+ case ACADDI16SP:
+ op = 0b011 << 13
+ case ACADDI4SPN:
+ op = 0b000 << 13
+ case ACJ:
+ op = 0b101 << 13
+ case ACLW:
+ op = 0b010 << 13
+ case ACLD:
+ op = 0b011 << 13
+ case ACFLD:
+ op = 0b001 << 13
+ case ACJR:
+ op = 0b1000 << 12
+ case ACMV:
+ op = 0b1000 << 12
+ case ACEBREAK:
+ op = 0b1001 << 12
+ case ACJALR:
+ op = 0b1001 << 12
+ case ACADD:
+ op = 0b1001 << 12
+ case ACSW:
+ op = 0b110 << 13
+ case ACSD:
+ op = 0b111 << 13
+ case ACFSD:
+ op = 0b101 << 13
+ case ACSWSP:
+ op = 0b110 << 13
+ case ACSDSP:
+ op = 0b111 << 13
+ case ACFSDSP:
+ op = 0b101 << 13
+ }
+
+ return op | enc.opcode
+}
+
// encodeBitPattern encodes an immediate value by extracting the specified
// bit pattern from the given immediate.
func encodeBitPattern(imm uint32, pattern []int) uint32 {
@@ -1432,6 +1761,149 @@
return outImm
}

+// encodeCA encodes a compressed arithmetic (CA-type) instruction.
+func encodeCA(ins *instruction) uint32 {
+ return compressedEncoding(ins.as) | regCI(ins.rd)<<7 | regCI(ins.rs2)<<2
+}
+
+// encodeCBImmediate encodes an immediate for a CB-type RISC-V instruction.
+func encodeCBImmediate(imm uint32) uint32 {
+ // Bit order - [8|4:3|7:6|2:1|5]
+ bits := encodeBitPattern(imm, []int{8, 4, 3, 7, 6, 2, 1, 5})
+ return (bits>>5)<<10 | (bits&0x1f)<<2
+}
+
+// encodeCB encodes a compressed branch (CB-type) instruction.
+func encodeCB(ins *instruction) uint32 {
+ imm := uint32(0)
+ if ins.as == ACBEQZ || ins.as == ACBNEZ {
+ imm = immI(ins.as, ins.imm, 9)
+ imm = encodeBitPattern(imm, []int{8, 4, 3, 7, 6, 2, 1, 5})
+ } else if ins.as == ACANDI {
+ imm = immI(ins.as, ins.imm, 6)
+ imm = (imm>>5)<<7 | imm&0x1f
+ } else if ins.as == ACSRAI || ins.as == ACSRLI {
+ imm = immU(ins.as, ins.imm, 6)
+ imm = (imm>>5)<<7 | imm&0x1f
+ }
+ return compressedEncoding(ins.as) | (imm>>5)<<10 | regCI(ins.rs1)<<7 | (imm&0x1f)<<2
+}
+
+// encodeCI encodes a compressed immediate (CI-type) instruction.
+func encodeCI(ins *instruction) uint32 {
+ imm := uint32(ins.imm)
+ if ins.as == ACLWSP {
+ // Bit order [5:2|7:6]
+ imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 7, 6})
+ } else if ins.as == ACLDSP || ins.as == ACFLDSP {
+ // Bit order [5:3|8:6]
+ imm = encodeBitPattern(imm, []int{5, 4, 3, 8, 7, 6})
+ } else if ins.as == ACADDI16SP {
+ // Bit order [9|4|6|8:7|5]
+ imm = encodeBitPattern(imm, []int{9, 4, 6, 8, 7, 5})
+ }
+ rd := uint32(0)
+ if ins.as == ACFLDSP {
+ rd = regF(ins.rd)
+ } else {
+ rd = regI(ins.rd)
+ }
+ return compressedEncoding(ins.as) | ((imm>>5)&0x1)<<12 | rd<<7 | (imm&0x1f)<<2
+}
+
+// encodeCIW encodes a compressed immediate wide (CIW-type) instruction.
+func encodeCIW(ins *instruction) uint32 {
+ imm := uint32(ins.imm)
+ if ins.as == ACADDI4SPN {
+ // Bit order [5:4|9:6|2|3]
+ imm = encodeBitPattern(imm, []int{5, 4, 9, 8, 7, 6, 2, 3})
+ }
+ return compressedEncoding(ins.as) | imm<<5 | regCI(ins.rd)<<2
+}
+
+// encodeCJImmediate encodes an immediate for a CJ-type RISC-V instruction.
+func encodeCJImmediate(imm uint32) uint32 {
+ // Bit order - [11|4|9:8|10|6|7|3:1|5]
+ bits := encodeBitPattern(imm, []int{11, 4, 9, 8, 10, 6, 7, 3, 2, 1, 5})
+ return bits << 2
+}
+
+// encodeCJ encodes a compressed jump (CJ-type) instruction.
+func encodeCJ(ins *instruction) uint32 {
+ return compressedEncoding(ins.as) | encodeCJImmediate(uint32(ins.imm))
+}
+
+// encodeCL encodes a compressed load (CL-type) instruction.
+func encodeCL(ins *instruction) uint32 {
+ imm := uint32(ins.imm)
+ if ins.as == ACLW {
+ // Bit order [5:2|6]
+ imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 6})
+ } else if ins.as == ACLD || ins.as == ACFLD {
+ // Bit order [5:3|7:6]
+ imm = encodeBitPattern(imm, []int{5, 4, 3, 7, 6})
+ }
+ rd := uint32(0)
+ if ins.as == ACFLD {
+ rd = regCF(ins.rd)
+ } else {
+ rd = regCI(ins.rd)
+ }
+ return compressedEncoding(ins.as) | (imm>>2)<<10 | regCI(ins.rs1)<<7 | (imm&0x3)<<5 | rd<<2
+}
+
+// encodeCR encodes a compressed register (CR-type) instruction.
+func encodeCR(ins *instruction) uint32 {
+ rs1, rs2 := uint32(0), uint32(0)
+ switch ins.as {
+ case ACJR, ACJALR:
+ rs1 = regI(ins.rs1)
+ case ACMV:
+ rs1, rs2 = regI(ins.rd), regI(ins.rs2)
+ case ACADD:
+ rs1, rs2 = regI(ins.rs1), regI(ins.rs2)
+ }
+ return compressedEncoding(ins.as) | rs1<<7 | rs2<<2
+}
+
+// encodeCS encodes a compressed store (CS-type) instruction.
+func encodeCS(ins *instruction) uint32 {
+ imm := uint32(ins.imm)
+ if ins.as == ACSW {
+ // Bit order [5:3|2|6]
+ imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 6})
+ } else if ins.as == ACSD || ins.as == ACFSD {
+ // Bit order [5:3|7:6]
+ imm = encodeBitPattern(imm, []int{5, 4, 3, 7, 6})
+ }
+ rs2 := uint32(0)
+ if ins.as == ACFSD {
+ rs2 = regCF(ins.rs2)
+ } else {
+ rs2 = regCI(ins.rs2)
+ }
+ return compressedEncoding(ins.as) | ((imm>>2)&0x7)<<10 | regCI(ins.rs1)<<7 | (imm&3)<<5 | rs2<<2
+}
+
+// encodeCSS encodes a compressed stack-relative store (CSS-type) instruction.
+func encodeCSS(ins *instruction) uint32 {
+ imm := uint32(ins.imm)
+ if ins.as == ACSWSP {
+ // Bit order [5:2|7:6]
+ imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 7, 6})
+ } else if ins.as == ACSDSP || ins.as == ACFSDSP {
+ // Bit order [5:3|8:6]
+ imm = encodeBitPattern(imm, []int{5, 4, 3, 8, 7, 6})
+ }
+ rs2 := uint32(0)
+ if ins.as == ACFSDSP {
+ rs2 = regF(ins.rs2)
+ } else {
+ rs2 = regI(ins.rs2)
+ }
+ return compressedEncoding(ins.as) | imm<<7 | rs2<<2
+}
+
// encodeR encodes an R-type RISC-V instruction.
func encodeR(as obj.As, rs1, rs2, rd, funct3, funct7 uint32) uint32 {
enc := encode(as)
@@ -1651,20 +2123,6 @@
return encodeJImmediate(imm) | rd<<7 | enc.opcode
}

-// encodeCBImmediate encodes an immediate for a CB-type RISC-V instruction.
-func encodeCBImmediate(imm uint32) uint32 {
- // Bit order - [8|4:3|7:6|2:1|5]
- bits := encodeBitPattern(imm, []int{8, 4, 3, 7, 6, 2, 1, 5})
- return (bits>>5)<<10 | (bits&0x1f)<<2
-}
-
-// encodeCJImmediate encodes an immediate for a CJ-type RISC-V instruction.
-func encodeCJImmediate(imm uint32) uint32 {
- // Bit order - [11|4|9:8|10|6|7|3:1|5]
- bits := encodeBitPattern(imm, []int{11, 4, 9, 8, 10, 6, 7, 3, 2, 1, 5})
- return bits << 2
-}
-
func encodeVset(as obj.As, rs1, rs2, rd uint32) uint32 {
enc := encode(as)
if enc == nil {
@@ -1779,7 +2237,7 @@
type encoding struct {
encode func(*instruction) uint32 // encode returns the machine code for an instruction
validate func(*obj.Link, *instruction) // validate validates an instruction
- length int // length of encoded instruction; 0 for pseudo-ops, 4 otherwise
+ length int // length of encoded instruction; 0 for pseudo-ops, 2 for compressed instructions, 4 otherwise
}

var (
@@ -1829,6 +2287,17 @@
uEncoding = encoding{encode: encodeU, validate: validateU, length: 4}
jEncoding = encoding{encode: encodeJ, validate: validateJ, length: 4}

+ // Compressed encodings.
+ caEncoding = encoding{encode: encodeCA, validate: validateCA, length: 2}
+ cbEncoding = encoding{encode: encodeCB, validate: validateCB, length: 2}
+ ciEncoding = encoding{encode: encodeCI, validate: validateCI, length: 2}
+ ciwEncoding = encoding{encode: encodeCIW, validate: validateCIW, length: 2}
+ cjEncoding = encoding{encode: encodeCJ, validate: validateCJ, length: 2}
+ clEncoding = encoding{encode: encodeCL, validate: validateCL, length: 2}
+ crEncoding = encoding{encode: encodeCR, validate: validateCR, length: 2}
+ csEncoding = encoding{encode: encodeCS, validate: validateCS, length: 2}
+ cssEncoding = encoding{encode: encodeCSS, validate: validateCSS, length: 2}
+
// Encodings for vector configuration setting instruction.
vsetvliEncoding = encoding{encode: encodeVsetvli, validate: validateVsetvli, length: 4}
vsetivliEncoding = encoding{encode: encodeVsetivli, validate: validateVsetivli, length: 4}
@@ -2058,6 +2527,63 @@
AFCLASSD & obj.AMask: {enc: rFIEncoding},

//
+ // "C" Extension for Compressed Instructions, Version 2.0
+ //
+
+ // 26.3.1: Compressed Stack-Pointer-Based Loads and Stores
+ ACLWSP & obj.AMask: {enc: ciEncoding},
+ ACLDSP & obj.AMask: {enc: ciEncoding},
+ ACFLDSP & obj.AMask: {enc: ciEncoding},
+ ACSWSP & obj.AMask: {enc: cssEncoding},
+ ACSDSP & obj.AMask: {enc: cssEncoding},
+ ACFSDSP & obj.AMask: {enc: cssEncoding},
+
+ // 26.3.2: Compressed Register-Based Loads and Stores
+ ACLW & obj.AMask: {enc: clEncoding},
+ ACLD & obj.AMask: {enc: clEncoding},
+ ACFLD & obj.AMask: {enc: clEncoding},
+ ACSW & obj.AMask: {enc: csEncoding},
+ ACSD & obj.AMask: {enc: csEncoding},
+ ACFSD & obj.AMask: {enc: csEncoding},
+
+ // 26.4: Compressed Control Transfer Instructions
+ ACJ & obj.AMask: {enc: cjEncoding},
+ ACJR & obj.AMask: {enc: crEncoding},
+ ACJALR & obj.AMask: {enc: crEncoding},
+ ACBEQZ & obj.AMask: {enc: cbEncoding},
+ ACBNEZ & obj.AMask: {enc: cbEncoding},
+
+ // 26.5.1: Compressed Integer Constant-Generation Instructions
+ ACLI & obj.AMask: {enc: ciEncoding},
+ ACLUI & obj.AMask: {enc: ciEncoding},
+
+ // 26.5.2: Compressed Integer Register-Immediate Operations
+ ACADDI & obj.AMask: {enc: ciEncoding, ternary: true},
+ ACADDIW & obj.AMask: {enc: ciEncoding, ternary: true},
+ ACADDI16SP & obj.AMask: {enc: ciEncoding, ternary: true},
+ ACADDI4SPN & obj.AMask: {enc: ciwEncoding, ternary: true},
+ ACSLLI & obj.AMask: {enc: ciEncoding, ternary: true},
+ ACSRLI & obj.AMask: {enc: cbEncoding, ternary: true},
+ ACSRAI & obj.AMask: {enc: cbEncoding, ternary: true},
+ ACANDI & obj.AMask: {enc: cbEncoding, ternary: true},
+
+ // 26.5.3: Compressed Integer Register-Register Operations
+ ACMV & obj.AMask: {enc: crEncoding},
+ ACADD & obj.AMask: {enc: crEncoding, immForm: ACADDI, ternary: true},
+ ACAND & obj.AMask: {enc: caEncoding, immForm: ACANDI, ternary: true},
+ ACOR & obj.AMask: {enc: caEncoding, ternary: true},
+ ACXOR & obj.AMask: {enc: caEncoding, ternary: true},
+ ACSUB & obj.AMask: {enc: caEncoding, ternary: true},
+ ACADDW & obj.AMask: {enc: caEncoding, immForm: ACADDIW, ternary: true},
+ ACSUBW & obj.AMask: {enc: caEncoding, ternary: true},
+
+ // 26.5.5: Compressed NOP Instruction
+ ACNOP & obj.AMask: {enc: ciEncoding},
+
+ // 26.5.6: Compressed Breakpoint Instruction
+ ACEBREAK & obj.AMask: {enc: crEncoding},
+
+ //
// "B" Extension for Bit Manipulation, Version 1.0.0
//

@@ -3540,7 +4066,7 @@
}

switch ins.as {
- case AJAL, AJALR:
+ case ACJALR, AJAL, AJALR:
ins.rd, ins.rs1, ins.rs2 = uint32(p.From.Reg), uint32(p.To.Reg), obj.REG_NONE
ins.imm = p.To.Offset

@@ -3751,6 +4277,32 @@
ins.as = AFSGNJND
ins.rs1 = uint32(p.From.Reg)

+ case ACLW, ACLD, ACFLD:
+ ins.rs1, ins.rs2 = ins.rs2, obj.REG_NONE
+
+ case ACSW, ACSD, ACFSD:
+ ins.rs1, ins.rd = ins.rd, obj.REG_NONE
+ ins.imm = p.To.Offset
+
+ case ACSWSP, ACSDSP, ACFSDSP:
+ ins.imm = p.To.Offset
+
+ case ACANDI, ACSRLI, ACSRAI:
+ ins.rs1, ins.rd = ins.rd, ins.rs1
+
+ case ACBEQZ, ACBNEZ:
+ ins.rd, ins.rs1, ins.rs2 = obj.REG_NONE, uint32(p.From.Reg), obj.REG_NONE
+ ins.imm = p.To.Offset
+
+ case ACJR:
+ ins.rd, ins.rs1 = obj.REG_NONE, uint32(p.To.Reg)
+
+ case ACJ:
+ ins.imm = p.To.Offset
+
+ case ACNOP:
+ ins.rd, ins.rs1 = REG_ZERO, REG_ZERO
+
case AROL, AROLW, AROR, ARORW:
inss = instructionsForRotate(p, ins)

@@ -4185,7 +4737,8 @@
Add: p.To.Offset,
})
}
- case AJALR:
+
+ case ACJALR, AJALR:
if p.To.Sym != nil {
ctxt.Diag("%v: unexpected AJALR with to symbol", p)
}

Change information

Files:
  • M src/cmd/asm/internal/arch/arch.go
  • M src/cmd/asm/internal/asm/testdata/riscv64.s
  • M src/cmd/asm/internal/asm/testdata/riscv64validation.s
  • M src/cmd/internal/obj/riscv/obj.go
Change size: L
Delta: 4 files changed, 777 insertions(+), 21 deletions(-)
Open in Gerrit

Related details

Attention is currently required from:
  • Mark Ryan
  • Meng Zhuo
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
Gerrit-Change-Number: 713020
Gerrit-PatchSet: 1
Gerrit-Owner: Joel Sing <jo...@sing.id.au>
Gerrit-Reviewer: Mark Ryan <mark...@rivosinc.com>
Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
Gerrit-Attention: Mark Ryan <mark...@rivosinc.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Joel Sing (Gerrit)

unread,
Oct 20, 2025, 12:43:28 AMOct 20
to goph...@pubsubhelper.golang.org, Meng Zhuo, Mark Ryan, golang-co...@googlegroups.com
Attention needed from Mark Ryan and Meng Zhuo

Joel Sing voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention is currently required from:
  • Mark Ryan
  • Meng Zhuo
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
Gerrit-Change-Number: 713020
Gerrit-PatchSet: 1
Gerrit-Owner: Joel Sing <jo...@sing.id.au>
Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
Gerrit-Reviewer: Mark Ryan <mark...@rivosinc.com>
Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
Gerrit-Attention: Mark Ryan <mark...@rivosinc.com>
Gerrit-Comment-Date: Mon, 20 Oct 2025 04:43:22 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Cherry Mui (Gerrit)

unread,
Oct 20, 2025, 9:08:21 AMOct 20
to Joel Sing, goph...@pubsubhelper.golang.org, Go LUCI, Meng Zhuo, Mark Ryan, golang-co...@googlegroups.com
Attention needed from Joel Sing, Mark Ryan and Meng Zhuo

Cherry Mui voted Code-Review+1

Code-Review+1
Open in Gerrit

Related details

Attention is currently required from:
  • Joel Sing
  • Mark Ryan
  • Meng Zhuo
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
    Gerrit-Change-Number: 713020
    Gerrit-PatchSet: 1
    Gerrit-Owner: Joel Sing <jo...@sing.id.au>
    Gerrit-Reviewer: Cherry Mui <cher...@google.com>
    Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
    Gerrit-Reviewer: Mark Ryan <mark...@rivosinc.com>
    Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
    Gerrit-Attention: Joel Sing <jo...@sing.id.au>
    Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
    Gerrit-Attention: Mark Ryan <mark...@rivosinc.com>
    Gerrit-Comment-Date: Mon, 20 Oct 2025 13:08:16 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: Yes
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Mark Ryan (Gerrit)

    unread,
    Nov 6, 2025, 6:53:50 AM (5 days ago) Nov 6
    to Joel Sing, goph...@pubsubhelper.golang.org, Cherry Mui, Go LUCI, Meng Zhuo, golang-co...@googlegroups.com
    Attention needed from Joel Sing and Meng Zhuo

    Mark Ryan voted and added 2 comments

    Votes added by Mark Ryan

    Code-Review+2

    2 comments

    Patchset-level comments
    File-level comment, Patchset 1 (Latest):
    Mark Ryan . resolved

    Looks good. Can't see any obvious issues.

    File src/cmd/internal/obj/riscv/obj.go
    Line 1241, Patchset 1 (Latest): if ins.as != ACNOP && ins.rd == REG_X0 {
    Mark Ryan . unresolved

    We could add a check for c.lu, c.addi and the shifts where the immediate is not allowed to be zero. Note, binutils doesn't seem to enforce this, but it doesn't disallow c.lui x0,1 either. Note an immediate of 0 is allowed with c.addiw.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Joel Sing
    • Meng Zhuo
    Submit Requirements:
    • requirement satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
    Gerrit-Change-Number: 713020
    Gerrit-PatchSet: 1
    Gerrit-Owner: Joel Sing <jo...@sing.id.au>
    Gerrit-Reviewer: Cherry Mui <cher...@google.com>
    Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
    Gerrit-Reviewer: Mark Ryan <mark...@rivosinc.com>
    Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
    Gerrit-Attention: Joel Sing <jo...@sing.id.au>
    Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
    Gerrit-Comment-Date: Thu, 06 Nov 2025 11:53:45 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: Yes
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Joel Sing (Gerrit)

    unread,
    Nov 8, 2025, 8:47:01 AM (3 days ago) Nov 8
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Joel Sing and Meng Zhuo

    Joel Sing uploaded new patchset

    Joel Sing uploaded patch set #2 to this change.
    Following approvals got outdated and were removed:
    • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Joel Sing
    • Meng Zhuo
    Submit Requirements:
      • requirement satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
      Gerrit-Change-Number: 713020
      Gerrit-PatchSet: 2
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Joel Sing (Gerrit)

      unread,
      Nov 8, 2025, 8:47:42 AM (3 days ago) Nov 8
      to goph...@pubsubhelper.golang.org, Mark Ryan, Cherry Mui, Go LUCI, Meng Zhuo, golang-co...@googlegroups.com
      Attention needed from Meng Zhuo

      Joel Sing voted and added 1 comment

      Votes added by Joel Sing

      Commit-Queue+1

      1 comment

      File src/cmd/internal/obj/riscv/obj.go
      Line 1241, Patchset 1: if ins.as != ACNOP && ins.rd == REG_X0 {
      Mark Ryan . resolved

      We could add a check for c.lu, c.addi and the shifts where the immediate is not allowed to be zero. Note, binutils doesn't seem to enforce this, but it doesn't disallow c.lui x0,1 either. Note an immediate of 0 is allowed with c.addiw.

      Joel Sing

      Good catch - I thought I'd checked the nzimm, but seems not. Done for CADDI, CADDI16SP and CLUI. While it makes little sense, as far as I can tell shift with a zero immediate is permitted so I've not checked that.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Meng Zhuo
      Submit Requirements:
      • requirement satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
      Gerrit-Change-Number: 713020
      Gerrit-PatchSet: 2
      Gerrit-Owner: Joel Sing <jo...@sing.id.au>
      Gerrit-Reviewer: Cherry Mui <cher...@google.com>
      Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
      Gerrit-Reviewer: Mark Ryan <mark...@rivosinc.com>
      Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
      Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
      Gerrit-Comment-Date: Sat, 08 Nov 2025 13:47:35 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      Comment-In-Reply-To: Mark Ryan <mark...@rivosinc.com>
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Joel Sing (Gerrit)

      unread,
      Nov 8, 2025, 11:41:29 AM (2 days ago) Nov 8
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Meng Zhuo

      Joel Sing uploaded new patchset

      Joel Sing uploaded patch set #3 to this change.
      Following approvals got outdated and were removed:
      • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Meng Zhuo
      Submit Requirements:
      • requirement satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
      Gerrit-Change-Number: 713020
      Gerrit-PatchSet: 3
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Joel Sing (Gerrit)

      unread,
      Nov 8, 2025, 11:42:42 AM (2 days ago) Nov 8
      to goph...@pubsubhelper.golang.org, Go LUCI, Mark Ryan, Cherry Mui, Meng Zhuo, golang-co...@googlegroups.com
      Attention needed from Meng Zhuo

      Joel Sing voted Commit-Queue+1

      Commit-Queue+1
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Meng Zhuo
      Submit Requirements:
      • requirement satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
      Gerrit-Change-Number: 713020
      Gerrit-PatchSet: 3
      Gerrit-Owner: Joel Sing <jo...@sing.id.au>
      Gerrit-Reviewer: Cherry Mui <cher...@google.com>
      Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
      Gerrit-Reviewer: Mark Ryan <mark...@rivosinc.com>
      Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
      Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
      Gerrit-Comment-Date: Sat, 08 Nov 2025 16:42:33 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Joel Sing (Gerrit)

      unread,
      Nov 8, 2025, 11:48:24 AM (2 days ago) Nov 8
      to goph...@pubsubhelper.golang.org, Go LUCI, Mark Ryan, Cherry Mui, Meng Zhuo, golang-co...@googlegroups.com
      Attention needed from Meng Zhuo

      Joel Sing added 1 comment

      File src/cmd/internal/obj/riscv/obj.go
      Line 1241, Patchset 1: if ins.as != ACNOP && ins.rd == REG_X0 {
      Mark Ryan . resolved

      We could add a check for c.lu, c.addi and the shifts where the immediate is not allowed to be zero. Note, binutils doesn't seem to enforce this, but it doesn't disallow c.lui x0,1 either. Note an immediate of 0 is allowed with c.addiw.

      Joel Sing

      Good catch - I thought I'd checked the nzimm, but seems not. Done for CADDI, CADDI16SP and CLUI. While it makes little sense, as far as I can tell shift with a zero immediate is permitted so I've not checked that.

      Joel Sing

      The compressed shifts with zero immediate are HINTs (which is is somewhat obfuscated in the specification) - have added checks for these as well.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Meng Zhuo
      Submit Requirements:
      • requirement satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
      Gerrit-Change-Number: 713020
      Gerrit-PatchSet: 3
      Gerrit-Owner: Joel Sing <jo...@sing.id.au>
      Gerrit-Reviewer: Cherry Mui <cher...@google.com>
      Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
      Gerrit-Reviewer: Mark Ryan <mark...@rivosinc.com>
      Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
      Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
      Gerrit-Comment-Date: Sat, 08 Nov 2025 16:48:15 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Joel Sing <jo...@sing.id.au>
      Comment-In-Reply-To: Mark Ryan <mark...@rivosinc.com>
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Meng Zhuo (Gerrit)

      unread,
      2:25 AM (20 hours ago) 2:25 AM
      to Joel Sing, goph...@pubsubhelper.golang.org, Meng Zhuo, Go LUCI, Mark Ryan, Cherry Mui, golang-co...@googlegroups.com
      Attention needed from Joel Sing

      Meng Zhuo voted Code-Review+2

      Code-Review+2
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Joel Sing
      Submit Requirements:
        • requirement satisfiedCode-Review
        • requirement satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: I0f9359d63f93ebbdc6e708e79429b2d61eae220d
        Gerrit-Change-Number: 713020
        Gerrit-PatchSet: 3
        Gerrit-Owner: Joel Sing <jo...@sing.id.au>
        Gerrit-Reviewer: Cherry Mui <cher...@google.com>
        Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
        Gerrit-Reviewer: Mark Ryan <mark...@rivosinc.com>
        Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
        Gerrit-Attention: Joel Sing <jo...@sing.id.au>
        Gerrit-Comment-Date: Mon, 10 Nov 2025 07:25:35 +0000
        Gerrit-HasComments: No
        Gerrit-Has-Labels: Yes
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy
        Reply all
        Reply to author
        Forward
        0 new messages