Skip to content

Commit d88f93f

Browse files
committed
cmd/internal/obj/riscv,internal/bytealg: synthesize MIN/MAX/MINU/MAXU instructions
Provide a synthesized version of the MIN/MAX/MINU/MAXU instructions if they're not natively available. This allows these instructions to be used in assembly unconditionally. Use MIN in internal/bytealg.compare. Cq-Include-Trybots: luci.golang.try:gotip-linux-riscv64 Change-Id: I8a5a3a59f0a9205e136fc3d673b23eaf3ca469f8 Reviewed-on: https://go-review.googlesource.com/c/go/+/653295 Reviewed-by: Mark Ryan <markdryan@rivosinc.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Meng Zhuo <mengzhuo1203@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent d376248 commit d88f93f

File tree

6 files changed

+339
-14
lines changed

6 files changed

+339
-14
lines changed

src/cmd/asm/internal/asm/testdata/riscv64.s

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -376,14 +376,14 @@ start:
376376
CPOPW X23, X24 // 1b9c2b60
377377
CTZ X24, X25 // 931c1c60
378378
CTZW X25, X26 // 1b9d1c60
379-
MAX X26, X28, X29 // b36eae0b
380-
MAX X26, X28 // 336eae0b
381-
MAXU X28, X29, X30 // 33ffce0b
382-
MAXU X28, X29 // b3fece0b
383-
MIN X29, X30, X5 // b342df0b
384-
MIN X29, X30 // 334fdf0b
385-
MINU X30, X5, X6 // 33d3e20b
386-
MINU X30, X5 // b3d2e20b
379+
MAX X26, X28, X29 // b36eae0b or b32fae01b30ff041b34eae01b3fedf01b34ede01
380+
MAX X26, X28 // 336eae0b or b32fcd01b30ff041334ecd0133fecf01334ecd01
381+
MAXU X28, X29, X30 // 33ffce0b or b3bfce01b30ff04133cfce0133ffef0133cfee01
382+
MAXU X28, X29 // b3fece0b or b33fde01b30ff041b34ede01b3fedf01b34ede01
383+
MIN X29, X30, X5 // b342df0b or b3afee01b30ff041b342df01b3f25f00b3425f00
384+
MIN X29, X30 // 334fdf0b or b32fdf01b30ff04133cfee0133ffef0133cfee01
385+
MINU X30, X5, X6 // 33d3e20b or b33f5f00b30ff04133c3e20133f36f0033c36200
386+
MINU X30, X5 // b3d2e20b or b3bfe201b30ff041b3425f00b3f25f00b3425f00
387387
ORN X6, X7, X8 // 33e46340 or 1344f3ff33e48300
388388
ORN X6, X7 // b3e36340 or 934ff3ffb3e3f301
389389
SEXTB X16, X17 // 93184860

src/cmd/internal/obj/riscv/asm_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,20 @@ func TestBranch(t *testing.T) {
264264
}
265265
}
266266

267+
func TestMinMax(t *testing.T) {
268+
if runtime.GOARCH != "riscv64" {
269+
t.Skip("Requires riscv64 to run")
270+
}
271+
272+
testenv.MustHaveGoBuild(t)
273+
274+
cmd := testenv.Command(t, testenv.GoToolPath(t), "test")
275+
cmd.Dir = "testdata/testminmax"
276+
if out, err := testenv.CleanCmdEnv(cmd).CombinedOutput(); err != nil {
277+
t.Errorf("Min max test failed: %v\n%s", err, out)
278+
}
279+
}
280+
267281
func TestPCAlign(t *testing.T) {
268282
dir := t.TempDir()
269283
tmpfile := filepath.Join(dir, "x.s")

src/cmd/internal/obj/riscv/obj.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,6 +2785,47 @@ func instructionsForRotate(p *obj.Prog, ins *instruction) []*instruction {
27852785
}
27862786
}
27872787

2788+
// instructionsForMinMax returns the machine instructions for an integer minimum or maximum.
2789+
func instructionsForMinMax(p *obj.Prog, ins *instruction) []*instruction {
2790+
if buildcfg.GORISCV64 >= 22 {
2791+
// Minimum and maximum instructions are supported natively.
2792+
return []*instruction{ins}
2793+
}
2794+
2795+
// Generate a move for identical inputs.
2796+
if ins.rs1 == ins.rs2 {
2797+
ins.as, ins.rs2, ins.imm = AADDI, obj.REG_NONE, 0
2798+
return []*instruction{ins}
2799+
}
2800+
2801+
// Ensure that if one of the source registers is the same as the destination,
2802+
// it is processed first.
2803+
if ins.rs1 == ins.rd {
2804+
ins.rs1, ins.rs2 = ins.rs2, ins.rs1
2805+
}
2806+
sltReg1, sltReg2 := ins.rs2, ins.rs1
2807+
2808+
// MIN -> SLT/SUB/XOR/AND/XOR
2809+
// MAX -> SLT/SUB/XOR/AND/XOR with swapped inputs to SLT
2810+
switch ins.as {
2811+
case AMIN:
2812+
ins.as = ASLT
2813+
case AMAX:
2814+
ins.as, sltReg1, sltReg2 = ASLT, sltReg2, sltReg1
2815+
case AMINU:
2816+
ins.as = ASLTU
2817+
case AMAXU:
2818+
ins.as, sltReg1, sltReg2 = ASLTU, sltReg2, sltReg1
2819+
}
2820+
return []*instruction{
2821+
&instruction{as: ins.as, rs1: sltReg1, rs2: sltReg2, rd: REG_TMP},
2822+
&instruction{as: ASUB, rs1: REG_ZERO, rs2: REG_TMP, rd: REG_TMP},
2823+
&instruction{as: AXOR, rs1: ins.rs1, rs2: ins.rs2, rd: ins.rd},
2824+
&instruction{as: AAND, rs1: REG_TMP, rs2: ins.rd, rd: ins.rd},
2825+
&instruction{as: AXOR, rs1: ins.rs1, rs2: ins.rd, rd: ins.rd},
2826+
}
2827+
}
2828+
27882829
// instructionsForProg returns the machine instructions for an *obj.Prog.
27892830
func instructionsForProg(p *obj.Prog) []*instruction {
27902831
ins := instructionForProg(p)
@@ -3034,6 +3075,9 @@ func instructionsForProg(p *obj.Prog) []*instruction {
30343075
ins.as = AXOR
30353076
inss = append(inss, &instruction{as: AXORI, rs1: ins.rd, rs2: obj.REG_NONE, rd: ins.rd, imm: -1})
30363077

3078+
case AMIN, AMAX, AMINU, AMAXU:
3079+
inss = instructionsForMinMax(p, ins)
3080+
30373081
case AVSETVLI, AVSETIVLI:
30383082
ins.rs1, ins.rs2 = ins.rs2, obj.REG_NONE
30393083
vtype, err := EncodeVectorType(p.RestArgs[0].Offset, p.RestArgs[1].Offset, p.RestArgs[2].Offset, p.RestArgs[3].Offset)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build riscv64
6+
7+
package testminmax
8+
9+
import (
10+
"testing"
11+
)
12+
13+
func testMIN1(a int64) (r int64)
14+
func testMIN2(a, b int64) (r int64)
15+
func testMIN3(a, b int64) (r int64)
16+
func testMIN4(a, b int64) (r int64)
17+
func testMAX1(a int64) (r int64)
18+
func testMAX2(a, b int64) (r int64)
19+
func testMAX3(a, b int64) (r int64)
20+
func testMAX4(a, b int64) (r int64)
21+
func testMINU1(a int64) (r int64)
22+
func testMINU2(a, b int64) (r int64)
23+
func testMINU3(a, b int64) (r int64)
24+
func testMINU4(a, b int64) (r int64)
25+
func testMAXU1(a int64) (r int64)
26+
func testMAXU2(a, b int64) (r int64)
27+
func testMAXU3(a, b int64) (r int64)
28+
func testMAXU4(a, b int64) (r int64)
29+
30+
func TestMin(t *testing.T) {
31+
tests := []struct {
32+
a int64
33+
b int64
34+
want int64
35+
}{
36+
{1, 2, 1},
37+
{2, 1, 1},
38+
{2, 2, 2},
39+
{1, -1, -1},
40+
{-1, 1, -1},
41+
}
42+
for _, test := range tests {
43+
if got := testMIN1(test.a); got != test.a {
44+
t.Errorf("Assembly testMIN1 %v = %v, want %v", test.a, got, test.a)
45+
}
46+
if got := testMIN2(test.a, test.b); got != test.want {
47+
t.Errorf("Assembly testMIN2 %v, %v = %v, want %v", test.a, test.b, got, test.want)
48+
}
49+
if got := testMIN3(test.a, test.b); got != test.want {
50+
t.Errorf("Assembly testMIN3 %v, %v = %v, want %v", test.a, test.b, got, test.want)
51+
}
52+
if got := testMIN4(test.a, test.b); got != test.want {
53+
t.Errorf("Assembly testMIN4 %v, %v = %v, want %v", test.a, test.b, got, test.want)
54+
}
55+
}
56+
}
57+
58+
func TestMax(t *testing.T) {
59+
tests := []struct {
60+
a int64
61+
b int64
62+
want int64
63+
}{
64+
{1, 2, 2},
65+
{2, 1, 2},
66+
{2, 2, 2},
67+
{1, -1, 1},
68+
{-1, 1, 1},
69+
}
70+
for _, test := range tests {
71+
if got := testMAX1(test.a); got != test.a {
72+
t.Errorf("Assembly testMAX1 %v = %v, want %v", test.a, got, test.a)
73+
}
74+
if got := testMAX2(test.a, test.b); got != test.want {
75+
t.Errorf("Assembly testMAX2 %v, %v = %v, want %v", test.a, test.b, got, test.want)
76+
}
77+
if got := testMAX3(test.a, test.b); got != test.want {
78+
t.Errorf("Assembly testMAX3 %v, %v = %v, want %v", test.a, test.b, got, test.want)
79+
}
80+
if got := testMAX4(test.a, test.b); got != test.want {
81+
t.Errorf("Assembly testMAX4 %v, %v = %v, want %v", test.a, test.b, got, test.want)
82+
}
83+
}
84+
}
85+
86+
func TestMinU(t *testing.T) {
87+
tests := []struct {
88+
a int64
89+
b int64
90+
want int64
91+
}{
92+
{1, 2, 1},
93+
{2, 1, 1},
94+
{2, 2, 2},
95+
{1, -1, 1},
96+
{-1, 1, 1},
97+
}
98+
for _, test := range tests {
99+
if got := testMINU1(test.a); got != test.a {
100+
t.Errorf("Assembly testMINU1 %v = %v, want %v", test.a, got, test.a)
101+
}
102+
if got := testMINU2(test.a, test.b); got != test.want {
103+
t.Errorf("Assembly testMINU2 %v, %v = %v, want %v", test.a, test.b, got, test.want)
104+
}
105+
if got := testMINU3(test.a, test.b); got != test.want {
106+
t.Errorf("Assembly testMINU3 %v, %v = %v, want %v", test.a, test.b, got, test.want)
107+
}
108+
if got := testMINU4(test.a, test.b); got != test.want {
109+
t.Errorf("Assembly testMINU4 %v, %v = %v, want %v", test.a, test.b, got, test.want)
110+
}
111+
}
112+
}
113+
114+
func TestMaxU(t *testing.T) {
115+
tests := []struct {
116+
a int64
117+
b int64
118+
want int64
119+
}{
120+
{1, 2, 2},
121+
{2, 1, 2},
122+
{2, 2, 2},
123+
{1, -1, -1},
124+
{-1, 1, -1},
125+
}
126+
for _, test := range tests {
127+
if got := testMAXU1(test.a); got != test.a {
128+
t.Errorf("Assembly testMAXU1 %v = %v, want %v", test.a, got, test.a)
129+
}
130+
if got := testMAXU2(test.a, test.b); got != test.want {
131+
t.Errorf("Assembly testMAXU2 %v, %v = %v, want %v", test.a, test.b, got, test.want)
132+
}
133+
if got := testMAXU3(test.a, test.b); got != test.want {
134+
t.Errorf("Assembly testMAXU3 %v, %v = %v, want %v", test.a, test.b, got, test.want)
135+
}
136+
if got := testMAXU4(test.a, test.b); got != test.want {
137+
t.Errorf("Assembly testMAXU4 %v, %v = %v, want %v", test.a, test.b, got, test.want)
138+
}
139+
}
140+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build riscv64
6+
7+
#include "textflag.h"
8+
9+
// func testMIN1(a int64) (r int64)
10+
TEXT ·testMIN1(SB),NOSPLIT,$0-16
11+
MOV a+0(FP), X5
12+
MIN X5, X5, X6
13+
MOV X6, r+8(FP)
14+
RET
15+
16+
// func testMIN2(a, b int64) (r int64)
17+
TEXT ·testMIN2(SB),NOSPLIT,$0-24
18+
MOV a+0(FP), X5
19+
MOV b+8(FP), X6
20+
MIN X5, X6, X6
21+
MOV X6, r+16(FP)
22+
RET
23+
24+
// func testMIN3(a, b int64) (r int64)
25+
TEXT ·testMIN3(SB),NOSPLIT,$0-24
26+
MOV a+0(FP), X5
27+
MOV b+8(FP), X6
28+
MIN X6, X5, X5
29+
MOV X5, r+16(FP)
30+
RET
31+
32+
// func testMIN4(a, b int64) (r int64)
33+
TEXT ·testMIN4(SB),NOSPLIT,$0-24
34+
MOV a+0(FP), X5
35+
MOV b+8(FP), X6
36+
MIN X5, X6, X7
37+
MOV X7, r+16(FP)
38+
RET
39+
40+
// func testMAX1(a int64) (r int64)
41+
TEXT ·testMAX1(SB),NOSPLIT,$0-16
42+
MOV a+0(FP), X5
43+
MAX X5, X5, X6
44+
MOV X6, r+8(FP)
45+
RET
46+
47+
// func testMAX2(a, b int64) (r int64)
48+
TEXT ·testMAX2(SB),NOSPLIT,$0-24
49+
MOV a+0(FP), X5
50+
MOV b+8(FP), X6
51+
MAX X5, X6, X6
52+
MOV X6, r+16(FP)
53+
RET
54+
55+
// func testMAX3(a, b int64) (r int64)
56+
TEXT ·testMAX3(SB),NOSPLIT,$0-24
57+
MOV a+0(FP), X5
58+
MOV b+8(FP), X6
59+
MAX X6, X5, X5
60+
MOV X5, r+16(FP)
61+
RET
62+
63+
// func testMAX4(a, b int64) (r int64)
64+
TEXT ·testMAX4(SB),NOSPLIT,$0-24
65+
MOV a+0(FP), X5
66+
MOV b+8(FP), X6
67+
MAX X5, X6, X7
68+
MOV X7, r+16(FP)
69+
RET
70+
71+
// func testMINU1(a int64) (r int64)
72+
TEXT ·testMINU1(SB),NOSPLIT,$0-16
73+
MOV a+0(FP), X5
74+
MINU X5, X5, X6
75+
MOV X6, r+8(FP)
76+
RET
77+
78+
// func testMINU2(a, b int64) (r int64)
79+
TEXT ·testMINU2(SB),NOSPLIT,$0-24
80+
MOV a+0(FP), X5
81+
MOV b+8(FP), X6
82+
MINU X5, X6, X6
83+
MOV X6, r+16(FP)
84+
RET
85+
86+
// func testMINU3(a, b int64) (r int64)
87+
TEXT ·testMINU3(SB),NOSPLIT,$0-24
88+
MOV a+0(FP), X5
89+
MOV b+8(FP), X6
90+
MINU X6, X5, X5
91+
MOV X5, r+16(FP)
92+
RET
93+
94+
// func testMINU4(a, b int64) (r int64)
95+
TEXT ·testMINU4(SB),NOSPLIT,$0-24
96+
MOV a+0(FP), X5
97+
MOV b+8(FP), X6
98+
MINU X5, X6, X7
99+
MOV X7, r+16(FP)
100+
RET
101+
102+
// func testMAXU1(a int64) (r int64)
103+
TEXT ·testMAXU1(SB),NOSPLIT,$0-16
104+
MOV a+0(FP), X5
105+
MAXU X5, X5, X6
106+
MOV X6, r+8(FP)
107+
RET
108+
109+
// func testMAXU2(a, b int64) (r int64)
110+
TEXT ·testMAXU2(SB),NOSPLIT,$0-24
111+
MOV a+0(FP), X5
112+
MOV b+8(FP), X6
113+
MAXU X5, X6, X6
114+
MOV X6, r+16(FP)
115+
RET
116+
117+
// func testMAXU3(a, b int64) (r int64)
118+
TEXT ·testMAXU3(SB),NOSPLIT,$0-24
119+
MOV a+0(FP), X5
120+
MOV b+8(FP), X6
121+
MAXU X6, X5, X5
122+
MOV X5, r+16(FP)
123+
RET
124+
125+
// func testMAXU4(a, b int64) (r int64)
126+
TEXT ·testMAXU4(SB),NOSPLIT,$0-24
127+
MOV a+0(FP), X5
128+
MOV b+8(FP), X6
129+
MAXU X5, X6, X7
130+
MOV X7, r+16(FP)
131+
RET

src/internal/bytealg/compare_riscv64.s

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,11 @@ TEXT runtime·cmpstring<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-40
2828
// X11 length of a
2929
// X12 points to start of b
3030
// X13 length of b
31-
// for non-regabi X14 points to the address to store the return value (-1/0/1)
32-
// for regabi the return value in X10
31+
// return value in X10 (-1/0/1)
3332
TEXT compare<>(SB),NOSPLIT|NOFRAME,$0
3433
BEQ X10, X12, cmp_len
3534

36-
MOV X11, X5
37-
BGE X13, X5, use_a_len // X5 = min(len(a), len(b))
38-
MOV X13, X5
39-
use_a_len:
35+
MIN X11, X13, X5
4036
BEQZ X5, cmp_len
4137

4238
MOV $32, X6

0 commit comments

Comments
 (0)