]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/obj/riscv: implement control transfer instructions
authorJoel Sing <joel@sing.id.au>
Wed, 18 Sep 2019 17:53:50 +0000 (03:53 +1000)
committerJoel Sing <joel@sing.id.au>
Thu, 26 Sep 2019 17:37:02 +0000 (17:37 +0000)
Add support for assembling control transfer instructions.

Based on the riscv-go port.

Updates #27532

Change-Id: I205d3ccd0a48deeaace0f20fca8516f382a83fae
Reviewed-on: https://go-review.googlesource.com/c/go/+/196841
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/asm/internal/asm/testdata/riscvenc.s
src/cmd/internal/obj/riscv/obj.go

index c58bb0fe0755435333c2b920c3ec8db550ff952a..11fe827ec6f60766a6e7ac9f29b3afce4dcabcf4 100644 (file)
@@ -5,7 +5,7 @@
 #include "../../../../../runtime/textflag.h"
 
 TEXT asmtest(SB),DUPOK|NOSPLIT,$0
-
+start:
        // Unprivileged ISA
 
        // 2.4: Integer Computational Instructions
@@ -83,6 +83,22 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
        SRA     $1, X5, X6                              // 13d31240
        SRA     $1, X5                                  // 93d21240
 
+       // 2.5: Control Transfer Instructions
+
+       // These jumps and branches get printed as a jump or branch
+       // to 2 because they transfer control to the second instruction
+       // in the function (the first instruction being an invisible
+       // stack pointer adjustment).
+       JAL     X5, start       // JAL  X5, 2           // eff2dff0
+       JALR    X6, (X5)                                // 67830200
+       JALR    X6, 4(X5)                               // 67834200
+       BEQ     X5, X6, start   // BEQ  X5, X6, 2       // e38062f0
+       BNE     X5, X6, start   // BNE  X5, X6, 2       // e39e62ee
+       BLT     X5, X6, start   // BLT  X5, X6, 2       // e3cc62ee
+       BLTU    X5, X6, start   // BLTU X5, X6, 2       // e3ea62ee
+       BGE     X5, X6, start   // BGE  X5, X6, 2       // e3d862ee
+       BGEU    X5, X6, start   // BGEU X5, X6, 2       // e3f662ee
+
        // 2.6: Load and Store Instructions
        LW      $0, X5, X6                              // 03a30200
        LW      $4, X5, X6                              // 03a34200
index 543a3b4a048c636f50a60b2667960651c37b06d4..1930bf9313275ee1160b166a67143aaffce03da2 100644 (file)
@@ -31,6 +31,24 @@ var RISCV64DWARFRegisters = map[int16]int16{}
 
 func buildop(ctxt *obj.Link) {}
 
+// lowerJALR normalizes a JALR instruction.
+func lowerJALR(p *obj.Prog) {
+       if p.As != AJALR {
+               panic("lowerJALR: not a JALR")
+       }
+
+       // JALR gets parsed like JAL - the linkage pointer goes in From,
+       // and the target is in To. However, we need to assemble it as an
+       // I-type instruction, so place the linkage pointer in To, the
+       // target register in Reg, and the offset in From.
+       p.Reg = p.To.Reg
+       p.From, p.To = p.To, p.From
+
+       // Reset Reg so the string looks correct.
+       p.From.Type = obj.TYPE_CONST
+       p.From.Reg = obj.REG_NONE
+}
+
 // 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) {
@@ -70,6 +88,9 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
        }
 
        switch p.As {
+       case AJALR:
+               lowerJALR(p)
+
        case obj.AUNDEF, AECALL, AEBREAK, ASCALL, ASBREAK, ARDCYCLE, ARDTIME, ARDINSTRET:
                switch p.As {
                case obj.AUNDEF:
@@ -151,6 +172,19 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 
        setPCs(cursym.Func.Text, 0)
 
+       // Resolve branch and jump targets.
+       for p := cursym.Func.Text; p != nil; p = p.Link {
+               switch p.As {
+               case AJAL, ABEQ, ABNE, ABLT, ABLTU, ABGE, ABGEU:
+                       switch p.To.Type {
+                       case obj.TYPE_BRANCH:
+                               p.To.Type, p.To.Offset = obj.TYPE_CONST, p.Pcond.Pc-p.Pc
+                       case obj.TYPE_MEM:
+                               panic("unhandled type")
+                       }
+               }
+       }
+
        // Validate all instructions - this provides nice error messages.
        for p := cursym.Func.Text; p != nil; p = p.Link {
                encodingForProg(p).validate(p)
@@ -290,6 +324,13 @@ func wantFloatRegAddr(p *obj.Prog, pos string, a *obj.Addr) {
        wantRegAddr(p, pos, a, "float", REG_F0, REG_F31)
 }
 
+// wantEvenJumpOffset checks that the jump offset is a multiple of two.
+func wantEvenJumpOffset(p *obj.Prog) {
+       if p.To.Offset%1 != 0 {
+               p.Ctxt.Diag("%v\tjump offset %v must be even", p, obj.Dconv(p, &p.To))
+       }
+}
+
 func validateRIII(p *obj.Prog) {
        wantIntRegAddr(p, "from", &p.From)
        wantIntReg(p, "reg", p.Reg)
@@ -347,11 +388,28 @@ func validateSF(p *obj.Prog) {
        wantIntRegAddr(p, "to", &p.To)
 }
 
+func validateB(p *obj.Prog) {
+       // Offsets are multiples of two, so accept 13 bit immediates for the
+       // 12 bit slot. We implicitly drop the least significant bit in encodeB.
+       wantEvenJumpOffset(p)
+       wantImmI(p, "to", p.To, 13)
+       wantIntReg(p, "reg", p.Reg)
+       wantIntRegAddr(p, "from", &p.From)
+}
+
 func validateU(p *obj.Prog) {
        wantImmU(p, "from", p.From, 20)
        wantIntRegAddr(p, "to", &p.To)
 }
 
+func validateJ(p *obj.Prog) {
+       // Offsets are multiples of two, so accept 21 bit immediates for the
+       // 20 bit slot. We implicitly drop the least significant bit in encodeJ.
+       wantEvenJumpOffset(p)
+       wantImmI(p, "to", p.To, 21)
+       wantIntRegAddr(p, "from", &p.From)
+}
+
 func validateRaw(p *obj.Prog) {
        // Treat the raw value specially as a 32-bit unsigned integer.
        // Nobody wants to enter negative machine code.
@@ -443,6 +501,18 @@ func encodeSF(p *obj.Prog) uint32 {
        return encodeS(p, regF(p.Reg))
 }
 
+// encodeB encodes a B-type RISC-V instruction.
+func encodeB(p *obj.Prog) uint32 {
+       imm := immI(p.To, 13)
+       rs2 := regI(p.Reg)
+       rs1 := regIAddr(p.From)
+       ins := encode(p.As)
+       if ins == nil {
+               panic("encodeB: could not encode instruction")
+       }
+       return (imm>>12)<<31 | ((imm>>5)&0x3f)<<25 | rs2<<20 | rs1<<15 | ins.funct3<<12 | ((imm>>1)&0xf)<<8 | ((imm>>11)&0x1)<<7 | ins.opcode
+}
+
 // encodeU encodes a U-type RISC-V instruction.
 func encodeU(p *obj.Prog) uint32 {
        // The immediates for encodeU are the upper 20 bits of a 32 bit value.
@@ -458,6 +528,17 @@ func encodeU(p *obj.Prog) uint32 {
        return imm<<12 | rd<<7 | ins.opcode
 }
 
+// encodeJ encodes a J-type RISC-V instruction.
+func encodeJ(p *obj.Prog) uint32 {
+       imm := immI(p.To, 21)
+       rd := regIAddr(p.From)
+       ins := encode(p.As)
+       if ins == nil {
+               panic("encodeJ: could not encode instruction")
+       }
+       return (imm>>20)<<31 | ((imm>>1)&0x3ff)<<21 | ((imm>>11)&0x1)<<20 | ((imm>>12)&0xff)<<12 | rd<<7 | ins.opcode
+}
+
 // encodeRaw encodes a raw instruction value.
 func encodeRaw(p *obj.Prog) uint32 {
        // Treat the raw value specially as a 32-bit unsigned integer.
@@ -481,7 +562,7 @@ type encoding struct {
 var (
        // Encodings have the following naming convention:
        //
-       //  1. the instruction encoding (R/I/S/SB/U/UJ), in lowercase
+       //  1. the instruction encoding (R/I/S/B/U/J), in lowercase
        //  2. zero or more register operand identifiers (I = integer
        //     register, F = float register), in uppercase
        //  3. the word "Encoding"
@@ -503,7 +584,9 @@ var (
        sIEncoding = encoding{encode: encodeSI, validate: validateSI, length: 4}
        sFEncoding = encoding{encode: encodeSF, validate: validateSF, length: 4}
 
+       bEncoding = encoding{encode: encodeB, validate: validateB, length: 4}
        uEncoding = encoding{encode: encodeU, validate: validateU, length: 4}
+       jEncoding = encoding{encode: encodeJ, validate: validateJ, length: 4}
 
        // rawEncoding encodes a raw instruction byte sequence.
        rawEncoding = encoding{encode: encodeRaw, validate: validateRaw, length: 4}
@@ -519,7 +602,6 @@ var (
 // encodingForAs contains the encoding for a RISC-V instruction.
 // Instructions are masked with obj.AMask to keep indices small.
 var encodingForAs = [ALAST & obj.AMask]encoding{
-       // TODO(jsing): Implement remaining instructions.
 
        // Unprivileged ISA
 
@@ -546,6 +628,16 @@ var encodingForAs = [ALAST & obj.AMask]encoding{
        ASUB & obj.AMask:   rIIIEncoding,
        ASRA & obj.AMask:   rIIIEncoding,
 
+       // 2.5: Control Transfer Instructions
+       AJAL & obj.AMask:  jEncoding,
+       AJALR & obj.AMask: iIEncoding,
+       ABEQ & obj.AMask:  bEncoding,
+       ABNE & obj.AMask:  bEncoding,
+       ABLT & obj.AMask:  bEncoding,
+       ABLTU & obj.AMask: bEncoding,
+       ABGE & obj.AMask:  bEncoding,
+       ABGEU & obj.AMask: bEncoding,
+
        // 2.6: Load and Store Instructions
        ALW & obj.AMask:  iIEncoding,
        ALWU & obj.AMask: iIEncoding,