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)
}