Commit 434e4caf authored by Joel Sing's avatar Joel Sing

cmd/internal/obj/riscv: implement floating point instructions

Add support for assembling various single-precision and double-precision
floating point instructions.

Based on the riscv-go port.

Updates #27532

Change-Id: Iac1aec9b03bb6cbf116b229daeef944d4df550fa
Reviewed-on: https://go-review.googlesource.com/c/go/+/196839Reviewed-by: default avatarCherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 8f5755e7
...@@ -95,7 +95,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 ...@@ -95,7 +95,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
SB $0, X5, X6 // 23005300 SB $0, X5, X6 // 23005300
SB $4, X5, X6 // 23025300 SB $4, X5, X6 // 23025300
// 5.2: Integer Computational Instructions (RV64I) // 5.2: Integer Computational Instructions (RV64I)
ADDIW $1, X5, X6 // 1b831200 ADDIW $1, X5, X6 // 1b831200
SLLIW $1, X5, X6 // 1b931200 SLLIW $1, X5, X6 // 1b931200
SRLIW $1, X5, X6 // 1bd31200 SRLIW $1, X5, X6 // 1bd31200
...@@ -132,6 +132,75 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 ...@@ -132,6 +132,75 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
RDTIME X5 // f32210c0 RDTIME X5 // f32210c0
RDINSTRET X5 // f32220c0 RDINSTRET X5 // f32220c0
// 11.5: Single-Precision Load and Store Instructions
FLW $0, X5, F0 // 07a00200
FLW $4, X5, F0 // 07a04200
FSW $0, F0, X5 // 27a00200
FSW $4, F0, X5 // 27a20200
// 11.6: Single-Precision Floating-Point Computational Instructions
FADDS F1, F0, F2 // 53011000
FSUBS F1, F0, F2 // 53011008
FMULS F1, F0, F2 // 53011010
FDIVS F1, F0, F2 // 53011018
FMINS F1, F0, F2 // 53011028
FMAXS F1, F0, F2 // 53111028
FSQRTS F0, F1 // d3000058
// 11.7: Single-Precision Floating-Point Conversion and Move Instructions
FCVTWS F0, X5 // d31200c0
FCVTLS F0, X5 // d31220c0
FCVTSW X5, F0 // 538002d0
FCVTSL X5, F0 // 538022d0
FCVTWUS F0, X5 // d31210c0
FCVTLUS F0, X5 // d31230c0
FCVTSWU X5, F0 // 538012d0
FCVTSLU X5, F0 // 538032d0
FSGNJS F1, F0, F2 // 53011020
FSGNJNS F1, F0, F2 // 53111020
FSGNJXS F1, F0, F2 // 53211020
FMVXS F0, X5 // d30200e0
FMVSX X5, F0 // 538002f0
FMVXW F0, X5 // d30200e0
FMVWX X5, F0 // 538002f0
// 11.8: Single-Precision Floating-Point Compare Instructions
FEQS F0, F1, X7 // d3a300a0
FLTS F0, F1, X7 // d39300a0
FLES F0, F1, X7 // d38300a0
// 12.3: Double-Precision Load and Store Instructions
FLD $0, X5, F0 // 07b00200
FLD $4, X5, F0 // 07b04200
FSD $0, F0, X5 // 27b00200
FSD $4, F0, X5 // 27b20200
// 12.4: Double-Precision Floating-Point Computational Instructions
FADDD F1, F0, F2 // 53011002
FSUBD F1, F0, F2 // 5301100a
FMULD F1, F0, F2 // 53011012
FDIVD F1, F0, F2 // 5301101a
FMIND F1, F0, F2 // 5301102a
FMAXD F1, F0, F2 // 5311102a
FSQRTD F0, F1 // d300005a
// 12.5: Double-Precision Floating-Point Conversion and Move Instructions
FCVTWD F0, X5 // d31200c2
FCVTLD F0, X5 // d31220c2
FCVTDW X5, F0 // 538002d2
FCVTDL X5, F0 // 538022d2
FCVTWUD F0, X5 // d31210c2
FCVTLUD F0, X5 // d31230c2
FCVTDWU X5, F0 // 538012d2
FCVTDLU X5, F0 // 538032d2
FCVTSD F0, F1 // d3001040
FCVTDS F0, F1 // d3000042
FSGNJD F1, F0, F2 // 53011022
FSGNJND F1, F0, F2 // 53111022
FSGNJXD F1, F0, F2 // 53211022
FMVXD F0, X5 // d30200e2
FMVDX X5, F0 // 538002f2
// Privileged ISA // Privileged ISA
// 3.2.1: Environment Call and Breakpoint // 3.2.1: Environment Call and Breakpoint
......
...@@ -133,10 +133,10 @@ var Anames = []string{ ...@@ -133,10 +133,10 @@ var Anames = []string{
"FSGNJS", "FSGNJS",
"FSGNJNS", "FSGNJNS",
"FSGNJXS", "FSGNJXS",
"FMVSX",
"FMVXS", "FMVXS",
"FMVWX", "FMVSX",
"FMVXW", "FMVXW",
"FMVWX",
"FEQS", "FEQS",
"FLTS", "FLTS",
"FLES", "FLES",
......
...@@ -368,10 +368,10 @@ const ( ...@@ -368,10 +368,10 @@ const (
AFSGNJS AFSGNJS
AFSGNJNS AFSGNJNS
AFSGNJXS AFSGNJXS
AFMVSX
AFMVXS AFMVXS
AFMVWX AFMVSX
AFMVXW AFMVXW
AFMVWX
// 11.8: Single-Precision Floating-Point Compare Instructions // 11.8: Single-Precision Floating-Point Compare Instructions
AFEQS AFEQS
......
...@@ -96,6 +96,16 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { ...@@ -96,6 +96,16 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
if p.To.Type == obj.TYPE_NONE { if p.To.Type == obj.TYPE_NONE {
p.To.Type, p.To.Reg = obj.TYPE_REG, REG_ZERO p.To.Type, p.To.Reg = obj.TYPE_REG, REG_ZERO
} }
case AFSQRTS, AFSQRTD:
// These instructions expect a zero (i.e. float register 0)
// to be the second input operand.
p.Reg = p.From.Reg
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_F0}
case AFCVTWS, AFCVTLS, AFCVTWUS, AFCVTLUS, AFCVTWD, AFCVTLD, AFCVTWUD, AFCVTLUD:
// Set the rounding mode in funct3 to round to zero.
p.Scond = 1
} }
} }
...@@ -159,6 +169,11 @@ func regI(r int16) uint32 { ...@@ -159,6 +169,11 @@ func regI(r int16) uint32 {
return regVal(r, REG_X0, REG_X31) return regVal(r, REG_X0, REG_X31)
} }
// regF returns a float register.
func regF(r int16) uint32 {
return regVal(r, REG_F0, REG_F31)
}
// regAddr extracts a register from an Addr. // regAddr extracts a register from an Addr.
func regAddr(a obj.Addr, min, max int16) uint32 { func regAddr(a obj.Addr, min, max int16) uint32 {
if a.Type != obj.TYPE_REG { if a.Type != obj.TYPE_REG {
...@@ -172,6 +187,11 @@ func regIAddr(a obj.Addr) uint32 { ...@@ -172,6 +187,11 @@ func regIAddr(a obj.Addr) uint32 {
return regAddr(a, REG_X0, REG_X31) return regAddr(a, REG_X0, REG_X31)
} }
// regFAddr extracts the float register from an Addr.
func regFAddr(a obj.Addr) uint32 {
return regAddr(a, REG_F0, REG_F31)
}
// immFits reports whether immediate value x fits in nbits bits as a // immFits reports whether immediate value x fits in nbits bits as a
// signed integer. // signed integer.
func immFits(x int64, nbits uint) bool { func immFits(x int64, nbits uint) bool {
...@@ -213,6 +233,11 @@ func wantIntReg(p *obj.Prog, pos string, r int16) { ...@@ -213,6 +233,11 @@ func wantIntReg(p *obj.Prog, pos string, r int16) {
wantReg(p, pos, "integer", r, REG_X0, REG_X31) wantReg(p, pos, "integer", r, REG_X0, REG_X31)
} }
// wantFloatReg checks that r is a floating-point register.
func wantFloatReg(p *obj.Prog, pos string, r int16) {
wantReg(p, pos, "float", r, REG_F0, REG_F31)
}
func wantRegAddr(p *obj.Prog, pos string, a *obj.Addr, descr string, min int16, max int16) { func wantRegAddr(p *obj.Prog, pos string, a *obj.Addr, descr string, min int16, max int16) {
if a == nil { if a == nil {
p.Ctxt.Diag("%v\texpected register in %s position but got nothing", p, pos) p.Ctxt.Diag("%v\texpected register in %s position but got nothing", p, pos)
...@@ -232,24 +257,68 @@ func wantIntRegAddr(p *obj.Prog, pos string, a *obj.Addr) { ...@@ -232,24 +257,68 @@ func wantIntRegAddr(p *obj.Prog, pos string, a *obj.Addr) {
wantRegAddr(p, pos, a, "integer", REG_X0, REG_X31) wantRegAddr(p, pos, a, "integer", REG_X0, REG_X31)
} }
// wantFloatRegAddr checks that a contains a floating-point register.
func wantFloatRegAddr(p *obj.Prog, pos string, a *obj.Addr) {
wantRegAddr(p, pos, a, "float", REG_F0, REG_F31)
}
func validateRIII(p *obj.Prog) { func validateRIII(p *obj.Prog) {
wantIntRegAddr(p, "from", &p.From) wantIntRegAddr(p, "from", &p.From)
wantIntReg(p, "reg", p.Reg) wantIntReg(p, "reg", p.Reg)
wantIntRegAddr(p, "to", &p.To) wantIntRegAddr(p, "to", &p.To)
} }
func validateRFFF(p *obj.Prog) {
wantFloatRegAddr(p, "from", &p.From)
wantFloatReg(p, "reg", p.Reg)
wantFloatRegAddr(p, "to", &p.To)
}
func validateRFFI(p *obj.Prog) {
wantFloatRegAddr(p, "from", &p.From)
wantFloatReg(p, "reg", p.Reg)
wantIntRegAddr(p, "to", &p.To)
}
func validateRFI(p *obj.Prog) {
wantFloatRegAddr(p, "from", &p.From)
wantIntRegAddr(p, "to", &p.To)
}
func validateRIF(p *obj.Prog) {
wantIntRegAddr(p, "from", &p.From)
wantFloatRegAddr(p, "to", &p.To)
}
func validateRFF(p *obj.Prog) {
wantFloatRegAddr(p, "from", &p.From)
wantFloatRegAddr(p, "to", &p.To)
}
func validateII(p *obj.Prog) { func validateII(p *obj.Prog) {
wantImm(p, "from", p.From, 12) wantImm(p, "from", p.From, 12)
wantIntReg(p, "reg", p.Reg) wantIntReg(p, "reg", p.Reg)
wantIntRegAddr(p, "to", &p.To) wantIntRegAddr(p, "to", &p.To)
} }
func validateIF(p *obj.Prog) {
wantImm(p, "from", p.From, 12)
wantIntReg(p, "reg", p.Reg)
wantFloatRegAddr(p, "to", &p.To)
}
func validateSI(p *obj.Prog) { func validateSI(p *obj.Prog) {
wantImm(p, "from", p.From, 12) wantImm(p, "from", p.From, 12)
wantIntReg(p, "reg", p.Reg) wantIntReg(p, "reg", p.Reg)
wantIntRegAddr(p, "to", &p.To) wantIntRegAddr(p, "to", &p.To)
} }
func validateSF(p *obj.Prog) {
wantImm(p, "from", p.From, 12)
wantFloatReg(p, "reg", p.Reg)
wantIntRegAddr(p, "to", &p.To)
}
func validateRaw(p *obj.Prog) { func validateRaw(p *obj.Prog) {
// Treat the raw value specially as a 32-bit unsigned integer. // Treat the raw value specially as a 32-bit unsigned integer.
// Nobody wants to enter negative machine code. // Nobody wants to enter negative machine code.
...@@ -282,6 +351,26 @@ func encodeRIII(p *obj.Prog) uint32 { ...@@ -282,6 +351,26 @@ func encodeRIII(p *obj.Prog) uint32 {
return encodeR(p, regI(p.Reg), regIAddr(p.From), regIAddr(p.To)) return encodeR(p, regI(p.Reg), regIAddr(p.From), regIAddr(p.To))
} }
func encodeRFFF(p *obj.Prog) uint32 {
return encodeR(p, regF(p.Reg), regFAddr(p.From), regFAddr(p.To))
}
func encodeRFFI(p *obj.Prog) uint32 {
return encodeR(p, regF(p.Reg), regFAddr(p.From), regIAddr(p.To))
}
func encodeRFI(p *obj.Prog) uint32 {
return encodeR(p, regFAddr(p.From), 0, regIAddr(p.To))
}
func encodeRIF(p *obj.Prog) uint32 {
return encodeR(p, regIAddr(p.From), 0, regFAddr(p.To))
}
func encodeRFF(p *obj.Prog) uint32 {
return encodeR(p, regFAddr(p.From), 0, regFAddr(p.To))
}
// encodeI encodes an I-type RISC-V instruction. // encodeI encodes an I-type RISC-V instruction.
func encodeI(p *obj.Prog, rd uint32) uint32 { func encodeI(p *obj.Prog, rd uint32) uint32 {
imm := immI(p.From, 12) imm := immI(p.From, 12)
...@@ -298,6 +387,10 @@ func encodeII(p *obj.Prog) uint32 { ...@@ -298,6 +387,10 @@ func encodeII(p *obj.Prog) uint32 {
return encodeI(p, regIAddr(p.To)) return encodeI(p, regIAddr(p.To))
} }
func encodeIF(p *obj.Prog) uint32 {
return encodeI(p, regFAddr(p.To))
}
// encodeS encodes an S-type RISC-V instruction. // encodeS encodes an S-type RISC-V instruction.
func encodeS(p *obj.Prog, rs2 uint32) uint32 { func encodeS(p *obj.Prog, rs2 uint32) uint32 {
imm := immI(p.From, 12) imm := immI(p.From, 12)
...@@ -313,6 +406,10 @@ func encodeSI(p *obj.Prog) uint32 { ...@@ -313,6 +406,10 @@ func encodeSI(p *obj.Prog) uint32 {
return encodeS(p, regI(p.Reg)) return encodeS(p, regI(p.Reg))
} }
func encodeSF(p *obj.Prog) uint32 {
return encodeS(p, regF(p.Reg))
}
// encodeRaw encodes a raw instruction value. // encodeRaw encodes a raw instruction value.
func encodeRaw(p *obj.Prog) uint32 { func encodeRaw(p *obj.Prog) uint32 {
// Treat the raw value specially as a 32-bit unsigned integer. // Treat the raw value specially as a 32-bit unsigned integer.
...@@ -346,10 +443,17 @@ var ( ...@@ -346,10 +443,17 @@ var (
// indicates an S-type instruction with rs2 being a float register. // indicates an S-type instruction with rs2 being a float register.
rIIIEncoding = encoding{encode: encodeRIII, validate: validateRIII, length: 4} rIIIEncoding = encoding{encode: encodeRIII, validate: validateRIII, length: 4}
rFFFEncoding = encoding{encode: encodeRFFF, validate: validateRFFF, length: 4}
rFFIEncoding = encoding{encode: encodeRFFI, validate: validateRFFI, length: 4}
rFIEncoding = encoding{encode: encodeRFI, validate: validateRFI, length: 4}
rIFEncoding = encoding{encode: encodeRIF, validate: validateRIF, length: 4}
rFFEncoding = encoding{encode: encodeRFF, validate: validateRFF, length: 4}
iIEncoding = encoding{encode: encodeII, validate: validateII, length: 4} iIEncoding = encoding{encode: encodeII, validate: validateII, length: 4}
iFEncoding = encoding{encode: encodeIF, validate: validateIF, length: 4}
sIEncoding = encoding{encode: encodeSI, validate: validateSI, length: 4} sIEncoding = encoding{encode: encodeSI, validate: validateSI, length: 4}
sFEncoding = encoding{encode: encodeSF, validate: validateSF, length: 4}
// rawEncoding encodes a raw instruction byte sequence. // rawEncoding encodes a raw instruction byte sequence.
rawEncoding = encoding{encode: encodeRaw, validate: validateRaw, length: 4} rawEncoding = encoding{encode: encodeRaw, validate: validateRaw, length: 4}
...@@ -436,6 +540,76 @@ var encodingForAs = [ALAST & obj.AMask]encoding{ ...@@ -436,6 +540,76 @@ var encodingForAs = [ALAST & obj.AMask]encoding{
ARDTIME & obj.AMask: iIEncoding, ARDTIME & obj.AMask: iIEncoding,
ARDINSTRET & obj.AMask: iIEncoding, ARDINSTRET & obj.AMask: iIEncoding,
// 11.5: Single-Precision Load and Store Instructions
AFLW & obj.AMask: iFEncoding,
AFSW & obj.AMask: sFEncoding,
// 11.6: Single-Precision Floating-Point Computational Instructions
AFADDS & obj.AMask: rFFFEncoding,
AFSUBS & obj.AMask: rFFFEncoding,
AFMULS & obj.AMask: rFFFEncoding,
AFDIVS & obj.AMask: rFFFEncoding,
AFMINS & obj.AMask: rFFFEncoding,
AFMAXS & obj.AMask: rFFFEncoding,
AFSQRTS & obj.AMask: rFFFEncoding,
// 11.7: Single-Precision Floating-Point Conversion and Move Instructions
AFCVTWS & obj.AMask: rFIEncoding,
AFCVTLS & obj.AMask: rFIEncoding,
AFCVTSW & obj.AMask: rIFEncoding,
AFCVTSL & obj.AMask: rIFEncoding,
AFCVTWUS & obj.AMask: rFIEncoding,
AFCVTLUS & obj.AMask: rFIEncoding,
AFCVTSWU & obj.AMask: rIFEncoding,
AFCVTSLU & obj.AMask: rIFEncoding,
AFSGNJS & obj.AMask: rFFFEncoding,
AFSGNJNS & obj.AMask: rFFFEncoding,
AFSGNJXS & obj.AMask: rFFFEncoding,
AFMVXS & obj.AMask: rFIEncoding,
AFMVSX & obj.AMask: rIFEncoding,
AFMVXW & obj.AMask: rFIEncoding,
AFMVWX & obj.AMask: rIFEncoding,
// 11.8: Single-Precision Floating-Point Compare Instructions
AFEQS & obj.AMask: rFFIEncoding,
AFLTS & obj.AMask: rFFIEncoding,
AFLES & obj.AMask: rFFIEncoding,
// 12.3: Double-Precision Load and Store Instructions
AFLD & obj.AMask: iFEncoding,
AFSD & obj.AMask: sFEncoding,
// 12.4: Double-Precision Floating-Point Computational Instructions
AFADDD & obj.AMask: rFFFEncoding,
AFSUBD & obj.AMask: rFFFEncoding,
AFMULD & obj.AMask: rFFFEncoding,
AFDIVD & obj.AMask: rFFFEncoding,
AFMIND & obj.AMask: rFFFEncoding,
AFMAXD & obj.AMask: rFFFEncoding,
AFSQRTD & obj.AMask: rFFFEncoding,
// 12.5: Double-Precision Floating-Point Conversion and Move Instructions
AFCVTWD & obj.AMask: rFIEncoding,
AFCVTLD & obj.AMask: rFIEncoding,
AFCVTDW & obj.AMask: rIFEncoding,
AFCVTDL & obj.AMask: rIFEncoding,
AFCVTWUD & obj.AMask: rFIEncoding,
AFCVTLUD & obj.AMask: rFIEncoding,
AFCVTDWU & obj.AMask: rIFEncoding,
AFCVTDLU & obj.AMask: rIFEncoding,
AFCVTSD & obj.AMask: rFFEncoding,
AFCVTDS & obj.AMask: rFFEncoding,
AFSGNJD & obj.AMask: rFFFEncoding,
AFSGNJND & obj.AMask: rFFFEncoding,
AFSGNJXD & obj.AMask: rFFFEncoding,
AFMVXD & obj.AMask: rFIEncoding,
AFMVDX & obj.AMask: rIFEncoding,
// 12.6: Double-Precision Floating-Point Compare Instructions
AFEQD & obj.AMask: rFFIEncoding,
AFLTD & obj.AMask: rFFIEncoding,
AFLED & obj.AMask: rFFIEncoding,
// Privileged ISA // Privileged ISA
// 3.2.1: Environment Call and Breakpoint // 3.2.1: Environment Call and Breakpoint
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment