1
0
mirror of https://github.com/golang/go synced 2024-11-17 08:24:43 -07:00

cmd/asm,cmd/internal/obj/riscv: provide branch pseudo-instructions

Implement various branch pseudo-instructions for riscv64. These make it easier
to read/write assembly and will also make it easier for the compiler to generate
optimised code.

Change-Id: Ic31a7748c0e1495522ebecf34b440842b8d12c04
Reviewed-on: https://go-review.googlesource.com/c/go/+/226397
Run-TryBot: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Joel Sing 2020-03-31 01:57:52 +11:00
parent d98023ebb5
commit a3d8c210ad
8 changed files with 311 additions and 12 deletions

View File

@ -88,7 +88,8 @@ func jumpX86(word string) bool {
func jumpRISCV(word string) bool {
switch word {
case "BEQ", "BNE", "BLT", "BGE", "BLTU", "BGEU", "CALL", "JAL", "JALR", "JMP":
case "BEQ", "BEQZ", "BGE", "BGEU", "BGEZ", "BGT", "BGTU", "BGTZ", "BLE", "BLEU", "BLEZ",
"BLT", "BLTU", "BLTZ", "BNE", "BNEZ", "CALL", "JAL", "JALR", "JMP":
return true
}
return false

View File

@ -330,6 +330,19 @@ start:
CALL asmtest(SB) // 970f0000
JMP asmtest(SB) // 970f0000
// Branch pseudo-instructions
BEQZ X5, start // BEQZ X5, 2 // e38a02c2
BGEZ X5, start // BGEZ X5, 2 // e3d802c2
BGT X5, X6, start // BGT X5, X6, 2 // e3c662c2
BGTU X5, X6, start // BGTU X5, X6, 2 // e3e462c2
BGTZ X5, start // BGTZ X5, 2 // e34250c2
BLE X5, X6, start // BLE X5, X6, 2 // e3d062c2
BLEU X5, X6, start // BLEU X5, X6, 2 // e3fe62c0
BLEZ X5, start // BLEZ X5, 2 // e35c50c0
BLTZ X5, start // BLTZ X5, 2 // e3ca02c0
BNEZ X5, start // BNEZ X5, 2 // e39802c0
// Set pseudo-instructions
SEQZ X15, X15 // 93b71700
SNEZ X15, X15 // b337f000

View File

@ -226,6 +226,16 @@ var Anames = []string{
"HFENCEGVMA",
"HFENCEVVMA",
"WORD",
"BEQZ",
"BGEZ",
"BGT",
"BGTU",
"BGTZ",
"BLE",
"BLEU",
"BLEZ",
"BLTZ",
"BNEZ",
"FNEGD",
"FNEGS",
"FNED",

View File

@ -12,6 +12,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
)
@ -131,3 +132,20 @@ TEXT _stub(SB),$0-0
t.Errorf("%v\n%s", err, out)
}
}
func TestBranch(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
if runtime.GOARCH != "riscv64" {
t.Skip("Requires riscv64 to run")
}
testenv.MustHaveGoBuild(t)
cmd := exec.Command(testenv.GoToolPath(t), "test")
cmd.Dir = "testdata/testbranch"
if out, err := testenv.CleanCmdEnv(cmd).CombinedOutput(); err != nil {
t.Errorf("Branch test failed: %v\n%s", err, out)
}
}

View File

@ -576,6 +576,16 @@ const (
// Pseudo-instructions. These get translated by the assembler into other
// instructions, based on their operands.
ABEQZ
ABGEZ
ABGT
ABGTU
ABGTZ
ABLE
ABLEU
ABLEZ
ABLTZ
ABNEZ
AFNEGD
AFNEGS
AFNED

View File

@ -406,20 +406,40 @@ func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) {
}
// InvertBranch inverts the condition of a conditional branch.
func InvertBranch(i obj.As) obj.As {
switch i {
func InvertBranch(as obj.As) obj.As {
switch as {
case ABEQ:
return ABNE
case ABNE:
return ABEQ
case ABLT:
return ABGE
case ABEQZ:
return ABNEZ
case ABGE:
return ABLT
case ABLTU:
return ABGEU
case ABGEU:
return ABLTU
case ABGEZ:
return ABLTZ
case ABGT:
return ABLE
case ABGTU:
return ABLEU
case ABGTZ:
return ABLEZ
case ABLE:
return ABGT
case ABLEU:
return ABGTU
case ABLEZ:
return ABGTZ
case ABLT:
return ABGE
case ABLTU:
return ABGEU
case ABLTZ:
return ABGEZ
case ABNE:
return ABEQ
case ABNEZ:
return ABEQZ
default:
panic("InvertBranch: not a branch")
}
@ -860,7 +880,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
for p := cursym.Func.Text; p != nil; p = p.Link {
switch p.As {
case ABEQ, ABNE, ABLT, ABGE, ABLTU, ABGEU:
case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
if p.To.Type != obj.TYPE_BRANCH {
panic("assemble: instruction with branch-like opcode lacks destination")
}
@ -917,7 +937,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
// instructions will break everything--don't do it!
for p := cursym.Func.Text; p != nil; p = p.Link {
switch p.As {
case AJAL, ABEQ, ABNE, ABLT, ABLTU, ABGE, ABGEU:
case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, AJAL:
switch p.To.Type {
case obj.TYPE_BRANCH:
p.To.Type, p.To.Offset = obj.TYPE_CONST, p.Pcond.Pc-p.Pc
@ -1778,7 +1798,29 @@ func instructionsForProg(p *obj.Prog) []*instruction {
ins.rd, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
ins.imm = p.To.Offset
case ABEQ, ABNE, ABLT, ABGE, ABLTU, ABGEU:
case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
switch ins.as {
case ABEQZ:
ins.as, ins.rs1, ins.rs2 = ABEQ, REG_ZERO, uint32(p.From.Reg)
case ABGEZ:
ins.as, ins.rs1, ins.rs2 = ABGE, REG_ZERO, uint32(p.From.Reg)
case ABGT:
ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.Reg), uint32(p.From.Reg)
case ABGTU:
ins.as, ins.rs1, ins.rs2 = ABLTU, uint32(p.Reg), uint32(p.From.Reg)
case ABGTZ:
ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.From.Reg), REG_ZERO
case ABLE:
ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.Reg), uint32(p.From.Reg)
case ABLEU:
ins.as, ins.rs1, ins.rs2 = ABGEU, uint32(p.Reg), uint32(p.From.Reg)
case ABLEZ:
ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.From.Reg), REG_ZERO
case ABLTZ:
ins.as, ins.rs1, ins.rs2 = ABLT, REG_ZERO, uint32(p.From.Reg)
case ABNEZ:
ins.as, ins.rs1, ins.rs2 = ABNE, REG_ZERO, uint32(p.From.Reg)
}
ins.imm = p.To.Offset
case ALW, ALWU, ALH, ALHU, ALB, ALBU, ALD, AFLW, AFLD:

View File

@ -0,0 +1,94 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build riscv64
package testbranch
import (
"testing"
)
func testBEQZ(a int64) (r bool)
func testBGEZ(a int64) (r bool)
func testBGT(a, b int64) (r bool)
func testBGTU(a, b int64) (r bool)
func testBGTZ(a int64) (r bool)
func testBLE(a, b int64) (r bool)
func testBLEU(a, b int64) (r bool)
func testBLEZ(a int64) (r bool)
func testBLTZ(a int64) (r bool)
func testBNEZ(a int64) (r bool)
func TestBranchCondition(t *testing.T) {
tests := []struct{
ins string
a int64
b int64
fn func(a, b int64) bool
want bool
}{
{"BGT", 0, 1, testBGT, true},
{"BGT", 0, 0, testBGT, false},
{"BGT", 0, -1, testBGT, false},
{"BGT", -1, 0, testBGT, true},
{"BGT", 1, 0, testBGT, false},
{"BGTU", 0, 1, testBGTU, true},
{"BGTU", 0, -1, testBGTU, true},
{"BGTU", -1, 0, testBGTU, false},
{"BGTU", 1, 0, testBGTU, false},
{"BLE", 0, 1, testBLE, false},
{"BLE", 0, -1, testBLE, true},
{"BLE", 0, 0, testBLE, true},
{"BLE", -1, 0, testBLE, false},
{"BLE", 1, 0, testBLE, true},
{"BLEU", 0, 1, testBLEU, false},
{"BLEU", 0, -1, testBLEU, false},
{"BLEU", 0, 0, testBLEU, true},
{"BLEU", -1, 0, testBLEU, true},
{"BLEU", 1, 0, testBLEU, true},
}
for _, test := range tests {
t.Run(test.ins, func(t *testing.T) {
if got := test.fn(test.a, test.b); got != test.want {
t.Errorf("%v %v, %v = %v, want %v", test.ins, test.a, test.b, got, test.want)
}
})
}
}
func TestBranchZero(t *testing.T) {
tests := []struct{
ins string
a int64
fn func(a int64) bool
want bool
}{
{"BEQZ", -1, testBEQZ, false},
{"BEQZ", 0, testBEQZ, true},
{"BEQZ", 1, testBEQZ, false},
{"BGEZ", -1, testBGEZ, false},
{"BGEZ", 0, testBGEZ, true},
{"BGEZ", 1, testBGEZ, true},
{"BGTZ", -1, testBGTZ, false},
{"BGTZ", 0, testBGTZ, false},
{"BGTZ", 1, testBGTZ, true},
{"BLEZ", -1, testBLEZ, true},
{"BLEZ", 0, testBLEZ, true},
{"BLEZ", 1, testBLEZ, false},
{"BLTZ", -1, testBLTZ, true},
{"BLTZ", 0, testBLTZ, false},
{"BLTZ", 1, testBLTZ, false},
{"BNEZ", -1, testBNEZ, true},
{"BNEZ", 0, testBNEZ, false},
{"BNEZ", 1, testBNEZ, true},
}
for _, test := range tests {
t.Run(test.ins, func(t *testing.T) {
if got := test.fn(test.a); got != test.want {
t.Errorf("%v %v = %v, want %v", test.ins, test.a, got, test.want)
}
})
}
}

View File

@ -0,0 +1,111 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build riscv64
#include "textflag.h"
// func testBEQZ(a int64) (r bool)
TEXT ·testBEQZ(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV $1, X6
BEQZ X5, b
MOV $0, X6
b:
MOV X6, r+8(FP)
RET
// func testBGEZ(a int64) (r bool)
TEXT ·testBGEZ(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV $1, X6
BGEZ X5, b
MOV $0, X6
b:
MOV X6, r+8(FP)
RET
// func testBGT(a, b int64) (r bool)
TEXT ·testBGT(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV b+8(FP), X6
MOV $1, X7
BGT X5, X6, b
MOV $0, X7
b:
MOV X7, r+16(FP)
RET
// func testBGTU(a, b int64) (r bool)
TEXT ·testBGTU(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV b+8(FP), X6
MOV $1, X7
BGTU X5, X6, b
MOV $0, X7
b:
MOV X7, r+16(FP)
RET
// func testBGTZ(a int64) (r bool)
TEXT ·testBGTZ(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV $1, X6
BGTZ X5, b
MOV $0, X6
b:
MOV X6, r+8(FP)
RET
// func testBLE(a, b int64) (r bool)
TEXT ·testBLE(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV b+8(FP), X6
MOV $1, X7
BLE X5, X6, b
MOV $0, X7
b:
MOV X7, r+16(FP)
RET
// func testBLEU(a, b int64) (r bool)
TEXT ·testBLEU(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV b+8(FP), X6
MOV $1, X7
BLEU X5, X6, b
MOV $0, X7
b:
MOV X7, r+16(FP)
RET
// func testBLEZ(a int64) (r bool)
TEXT ·testBLEZ(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV $1, X6
BLEZ X5, b
MOV $0, X6
b:
MOV X6, r+8(FP)
RET
// func testBLTZ(a int64) (r bool)
TEXT ·testBLTZ(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV $1, X6
BLTZ X5, b
MOV $0, X6
b:
MOV X6, r+8(FP)
RET
// func testBNEZ(a int64) (r bool)
TEXT ·testBNEZ(SB),NOSPLIT,$0-0
MOV a+0(FP), X5
MOV $1, X6
BNEZ X5, b
MOV $0, X6
b:
MOV X6, r+8(FP)
RET