From: Joel Sing Date: Sat, 7 Sep 2019 18:11:07 +0000 (+1000) Subject: cmd/asm,cmd/internal/obj/riscv: implement integer computational instructions X-Git-Tag: go1.14beta1~1078 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c3c53661ba8823ea7a051110aebbdea2650c25d0;p=gostls13.git cmd/asm,cmd/internal/obj/riscv: implement integer computational instructions Add support for assembling integer computational instructions. Based on the riscv-go port. Updates #27532 Change-Id: Ibf02649eebd65ce96002a9ca0624266d96def2cd Reviewed-on: https://go-review.googlesource.com/c/go/+/195079 Run-TryBot: Joel Sing TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go index d83cfb2284..c6f07832a7 100644 --- a/src/cmd/asm/internal/asm/asm.go +++ b/src/cmd/asm/internal/asm/asm.go @@ -417,7 +417,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { prog.Reg = reg break } - if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 { + if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 || p.arch.Family == sys.RISCV64 { // 3-operand jumps. // First two must be registers target = &a[2] @@ -579,7 +579,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.To = a[1] case 3: switch p.arch.Family { - case sys.MIPS, sys.MIPS64: + case sys.MIPS, sys.MIPS64, sys.RISCV64: prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] diff --git a/src/cmd/asm/internal/asm/testdata/riscvenc.s b/src/cmd/asm/internal/asm/testdata/riscvenc.s index eea5738f2c..c05a05ea33 100644 --- a/src/cmd/asm/internal/asm/testdata/riscvenc.s +++ b/src/cmd/asm/internal/asm/testdata/riscvenc.s @@ -9,3 +9,71 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 // Arbitrary bytes (entered in little-endian mode) WORD $0x12345678 // WORD $305419896 // 78563412 WORD $0x9abcdef0 // WORD $2596069104 // f0debc9a + + // Unprivileged ISA + + // 2.4: Integer Computational Instructions + + ADDI $2047, X5, X6 // 1383f27f + ADDI $-2048, X5, X6 // 13830280 + ADDI $2047, X5 // 9382f27f + ADDI $-2048, X5 // 93820280 + + SLTI $55, X5, X7 // 93a37203 + SLTIU $55, X5, X7 // 93b37203 + + ANDI $1, X5, X6 // 13f31200 + ANDI $1, X5 // 93f21200 + ORI $1, X5, X6 // 13e31200 + ORI $1, X5 // 93e21200 + XORI $1, X5, X6 // 13c31200 + XORI $1, X5 // 93c21200 + + SLLI $1, X5, X6 // 13931200 + SLLI $1, X5 // 93921200 + SRLI $1, X5, X6 // 13d31200 + SRLI $1, X5 // 93d21200 + SRAI $1, X5, X6 // 13d31240 + SRAI $1, X5 // 93d21240 + + ADD X6, X5, X7 // b3836200 + ADD X5, X6 // 33035300 + ADD $2047, X5, X6 // 1383f27f + ADD $-2048, X5, X6 // 13830280 + ADD $2047, X5 // 9382f27f + ADD $-2048, X5 // 93820280 + + SLT X6, X5, X7 // b3a36200 + SLT $55, X5, X7 // 93a37203 + SLTU X6, X5, X7 // b3b36200 + SLTU $55, X5, X7 // 93b37203 + + AND X6, X5, X7 // b3f36200 + AND X5, X6 // 33735300 + AND $1, X5, X6 // 13f31200 + AND $1, X5 // 93f21200 + OR X6, X5, X7 // b3e36200 + OR X5, X6 // 33635300 + OR $1, X5, X6 // 13e31200 + OR $1, X5 // 93e21200 + XOR X6, X5, X7 // b3c36200 + XOR X5, X6 // 33435300 + XOR $1, X5, X6 // 13c31200 + XOR $1, X5 // 93c21200 + + SLL X6, X5, X7 // b3936200 + SLL X5, X6 // 33135300 + SLL $1, X5, X6 // 13931200 + SLL $1, X5 // 93921200 + SRL X6, X5, X7 // b3d36200 + SRL X5, X6 // 33535300 + SRL $1, X5, X6 // 13d31200 + SRL $1, X5 // 93d21200 + + SUB X6, X5, X7 // b3836240 + SUB X5, X6 // 33035340 + + SRA X6, X5, X7 // b3d36240 + SRA X5, X6 // 33535340 + SRA $1, X5, X6 // 13d31240 + SRA $1, X5 // 93d21240 diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index af07522cfd..3ce8cb8982 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -31,8 +31,43 @@ var RISCV64DWARFRegisters = map[int16]int16{} func buildop(ctxt *obj.Link) {} +// progedit is called individually for each *obj.Prog. It normalizes instruction +// formats and eliminates as many pseudo-instructions as possible. func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { - // TODO(jsing): Implement. + + // Expand binary instructions to ternary ones. + if p.Reg == 0 { + switch p.As { + case AADDI, ASLTI, ASLTIU, AANDI, AORI, AXORI, ASLLI, ASRLI, ASRAI, + AADD, AAND, AOR, AXOR, ASLL, ASRL, ASUB, ASRA: + p.Reg = p.To.Reg + } + } + + // Rewrite instructions with constant operands to refer to the immediate + // form of the instruction. + if p.From.Type == obj.TYPE_CONST { + switch p.As { + case AADD: + p.As = AADDI + case ASLT: + p.As = ASLTI + case ASLTU: + p.As = ASLTIU + case AAND: + p.As = AANDI + case AOR: + p.As = AORI + case AXOR: + p.As = AXORI + case ASLL: + p.As = ASLLI + case ASRL: + p.As = ASRLI + case ASRA: + p.As = ASRAI + } + } } // setPCs sets the Pc field in all instructions reachable from p. @@ -83,6 +118,103 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } +func regVal(r, min, max int16) uint32 { + if r < min || r > max { + panic(fmt.Sprintf("register out of range, want %d < %d < %d", min, r, max)) + } + return uint32(r - min) +} + +// regI returns an integer register. +func regI(r int16) uint32 { + return regVal(r, REG_X0, REG_X31) +} + +// regAddr extracts a register from an Addr. +func regAddr(a obj.Addr, min, max int16) uint32 { + if a.Type != obj.TYPE_REG { + panic(fmt.Sprintf("ill typed: %+v", a)) + } + return regVal(a.Reg, min, max) +} + +// regIAddr extracts the integer register from an Addr. +func regIAddr(a obj.Addr) uint32 { + return regAddr(a, REG_X0, REG_X31) +} + +// immFits reports whether immediate value x fits in nbits bits as a +// signed integer. +func immFits(x int64, nbits uint) bool { + nbits-- + var min int64 = -1 << nbits + var max int64 = 1< max { + p.Ctxt.Diag("%v\texpected %s register in %s position but got non-%s register %s", p, descr, pos, descr, regName(int(r))) + } +} + +// wantIntReg checks that r is an integer register. +func wantIntReg(p *obj.Prog, pos string, r int16) { + wantReg(p, pos, "integer", r, REG_X0, REG_X31) +} + +func wantRegAddr(p *obj.Prog, pos string, a *obj.Addr, descr string, min int16, max int16) { + if a == nil { + p.Ctxt.Diag("%v\texpected register in %s position but got nothing", p, pos) + return + } + if a.Type != obj.TYPE_REG { + p.Ctxt.Diag("%v\texpected register in %s position but got %s", p, pos, obj.Dconv(p, a)) + return + } + if a.Reg < min || a.Reg > max { + p.Ctxt.Diag("%v\texpected %s register in %s position but got non-%s register %s", p, descr, pos, descr, obj.Dconv(p, a)) + } +} + +// wantIntRegAddr checks that a contains an integer register. +func wantIntRegAddr(p *obj.Prog, pos string, a *obj.Addr) { + wantRegAddr(p, pos, a, "integer", REG_X0, REG_X31) +} + +func validateRIII(p *obj.Prog) { + wantIntRegAddr(p, "from", &p.From) + wantIntReg(p, "reg", p.Reg) + wantIntRegAddr(p, "to", &p.To) +} + +func validateII(p *obj.Prog) { + wantImm(p, "from", p.From, 12) + wantIntReg(p, "reg", p.Reg) + wantIntRegAddr(p, "to", &p.To) +} + func validateRaw(p *obj.Prog) { // Treat the raw value specially as a 32-bit unsigned integer. // Nobody wants to enter negative machine code. @@ -96,6 +228,42 @@ func validateRaw(p *obj.Prog) { } } +// encodeR encodes an R-type RISC-V instruction. +func encodeR(p *obj.Prog, rs1 uint32, rs2 uint32, rd uint32) uint32 { + ins := encode(p.As) + if ins == nil { + panic("encodeR: could not encode instruction") + } + if ins.rs2 != 0 && rs2 != 0 { + panic("encodeR: instruction uses rs2, but rs2 was nonzero") + } + + // Use Scond for the floating-point rounding mode override. + // TODO(sorear): Is there a more appropriate way to handle opcode extension bits like this? + return ins.funct7<<25 | ins.rs2<<20 | rs2<<20 | rs1<<15 | ins.funct3<<12 | uint32(p.Scond)<<12 | rd<<7 | ins.opcode +} + +func encodeRIII(p *obj.Prog) uint32 { + return encodeR(p, regI(p.Reg), regIAddr(p.From), regIAddr(p.To)) +} + +// encodeI encodes an I-type RISC-V instruction. +func encodeI(p *obj.Prog, rd uint32) uint32 { + imm := immI(p.From, 12) + rs1 := regI(p.Reg) + ins := encode(p.As) + if ins == nil { + panic("encodeI: could not encode instruction") + } + imm |= uint32(ins.csr) + return imm<<20 | rs1<<15 | ins.funct3<<12 | rd<<7 | ins.opcode +} + +func encodeII(p *obj.Prog) uint32 { + return encodeI(p, regIAddr(p.To)) +} + +// encodeRaw encodes a raw instruction value. func encodeRaw(p *obj.Prog) uint32 { // Treat the raw value specially as a 32-bit unsigned integer. // Nobody wants to enter negative machine code. @@ -116,6 +284,22 @@ type encoding struct { } var ( + // Encodings have the following naming convention: + // + // 1. the instruction encoding (R/I/S/SB/U/UJ), in lowercase + // 2. zero or more register operand identifiers (I = integer + // register, F = float register), in uppercase + // 3. the word "Encoding" + // + // For example, rIIIEncoding indicates an R-type instruction with two + // integer register inputs and an integer register output; sFEncoding + // indicates an S-type instruction with rs2 being a float register. + + rIIIEncoding = encoding{encode: encodeRIII, validate: validateRIII, length: 4} + + iIEncoding = encoding{encode: encodeII, validate: validateII, length: 4} + + // rawEncoding encodes a raw instruction byte sequence. rawEncoding = encoding{encode: encodeRaw, validate: validateRaw, length: 4} // pseudoOpEncoding panics if encoding is attempted, but does no validation. @@ -131,6 +315,29 @@ var ( var encodingForAs = [ALAST & obj.AMask]encoding{ // TODO(jsing): Implement remaining instructions. + // Unprivileged ISA + + // 2.4: Integer Computational Instructions + AADDI & obj.AMask: iIEncoding, + ASLTI & obj.AMask: iIEncoding, + ASLTIU & obj.AMask: iIEncoding, + AANDI & obj.AMask: iIEncoding, + AORI & obj.AMask: iIEncoding, + AXORI & obj.AMask: iIEncoding, + ASLLI & obj.AMask: iIEncoding, + ASRLI & obj.AMask: iIEncoding, + ASRAI & obj.AMask: iIEncoding, + AADD & obj.AMask: rIIIEncoding, + ASLT & obj.AMask: rIIIEncoding, + ASLTU & obj.AMask: rIIIEncoding, + AAND & obj.AMask: rIIIEncoding, + AOR & obj.AMask: rIIIEncoding, + AXOR & obj.AMask: rIIIEncoding, + ASLL & obj.AMask: rIIIEncoding, + ASRL & obj.AMask: rIIIEncoding, + ASUB & obj.AMask: rIIIEncoding, + ASRA & obj.AMask: rIIIEncoding, + // Escape hatch AWORD & obj.AMask: rawEncoding,