mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00: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:
parent
d98023ebb5
commit
a3d8c210ad
8 changed files with 311 additions and 12 deletions
|
|
@ -88,7 +88,8 @@ func jumpX86(word string) bool {
|
||||||
|
|
||||||
func jumpRISCV(word string) bool {
|
func jumpRISCV(word string) bool {
|
||||||
switch word {
|
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 true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
13
src/cmd/asm/internal/asm/testdata/riscvenc.s
vendored
13
src/cmd/asm/internal/asm/testdata/riscvenc.s
vendored
|
|
@ -330,6 +330,19 @@ start:
|
||||||
CALL asmtest(SB) // 970f0000
|
CALL asmtest(SB) // 970f0000
|
||||||
JMP 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
|
SEQZ X15, X15 // 93b71700
|
||||||
SNEZ X15, X15 // b337f000
|
SNEZ X15, X15 // b337f000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,16 @@ var Anames = []string{
|
||||||
"HFENCEGVMA",
|
"HFENCEGVMA",
|
||||||
"HFENCEVVMA",
|
"HFENCEVVMA",
|
||||||
"WORD",
|
"WORD",
|
||||||
|
"BEQZ",
|
||||||
|
"BGEZ",
|
||||||
|
"BGT",
|
||||||
|
"BGTU",
|
||||||
|
"BGTZ",
|
||||||
|
"BLE",
|
||||||
|
"BLEU",
|
||||||
|
"BLEZ",
|
||||||
|
"BLTZ",
|
||||||
|
"BNEZ",
|
||||||
"FNEGD",
|
"FNEGD",
|
||||||
"FNEGS",
|
"FNEGS",
|
||||||
"FNED",
|
"FNED",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -131,3 +132,20 @@ TEXT _stub(SB),$0-0
|
||||||
t.Errorf("%v\n%s", err, out)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -576,6 +576,16 @@ const (
|
||||||
|
|
||||||
// Pseudo-instructions. These get translated by the assembler into other
|
// Pseudo-instructions. These get translated by the assembler into other
|
||||||
// instructions, based on their operands.
|
// instructions, based on their operands.
|
||||||
|
ABEQZ
|
||||||
|
ABGEZ
|
||||||
|
ABGT
|
||||||
|
ABGTU
|
||||||
|
ABGTZ
|
||||||
|
ABLE
|
||||||
|
ABLEU
|
||||||
|
ABLEZ
|
||||||
|
ABLTZ
|
||||||
|
ABNEZ
|
||||||
AFNEGD
|
AFNEGD
|
||||||
AFNEGS
|
AFNEGS
|
||||||
AFNED
|
AFNED
|
||||||
|
|
|
||||||
|
|
@ -406,20 +406,40 @@ func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvertBranch inverts the condition of a conditional branch.
|
// InvertBranch inverts the condition of a conditional branch.
|
||||||
func InvertBranch(i obj.As) obj.As {
|
func InvertBranch(as obj.As) obj.As {
|
||||||
switch i {
|
switch as {
|
||||||
case ABEQ:
|
case ABEQ:
|
||||||
return ABNE
|
return ABNE
|
||||||
case ABNE:
|
case ABEQZ:
|
||||||
return ABEQ
|
return ABNEZ
|
||||||
case ABLT:
|
|
||||||
return ABGE
|
|
||||||
case ABGE:
|
case ABGE:
|
||||||
return ABLT
|
return ABLT
|
||||||
case ABLTU:
|
|
||||||
return ABGEU
|
|
||||||
case ABGEU:
|
case ABGEU:
|
||||||
return ABLTU
|
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:
|
default:
|
||||||
panic("InvertBranch: not a branch")
|
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 {
|
for p := cursym.Func.Text; p != nil; p = p.Link {
|
||||||
switch p.As {
|
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 {
|
if p.To.Type != obj.TYPE_BRANCH {
|
||||||
panic("assemble: instruction with branch-like opcode lacks destination")
|
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!
|
// instructions will break everything--don't do it!
|
||||||
for p := cursym.Func.Text; p != nil; p = p.Link {
|
for p := cursym.Func.Text; p != nil; p = p.Link {
|
||||||
switch p.As {
|
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 {
|
switch p.To.Type {
|
||||||
case obj.TYPE_BRANCH:
|
case obj.TYPE_BRANCH:
|
||||||
p.To.Type, p.To.Offset = obj.TYPE_CONST, p.Pcond.Pc-p.Pc
|
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.rd, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
|
||||||
ins.imm = p.To.Offset
|
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
|
ins.imm = p.To.Offset
|
||||||
|
|
||||||
case ALW, ALWU, ALH, ALHU, ALB, ALBU, ALD, AFLW, AFLD:
|
case ALW, ALWU, ALH, ALHU, ALB, ALBU, ALD, AFLW, AFLD:
|
||||||
|
|
|
||||||
94
src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go
vendored
Normal file
94
src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go
vendored
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.s
vendored
Normal file
111
src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.s
vendored
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue