@@ -17,7 +17,8 @@ import (
1717)
1818
1919// TestLargeBranch generates a large function with a very far conditional
20- // branch, in order to ensure that it assembles successfully.
20+ // branch, in order to ensure that it assembles correctly. This requires
21+ // inverting the branch and using a jump to reach the target.
2122func TestLargeBranch (t * testing.T ) {
2223 if testing .Short () {
2324 t .Skip ("Skipping test in short mode" )
@@ -26,6 +27,23 @@ func TestLargeBranch(t *testing.T) {
2627
2728 dir := t .TempDir ()
2829
30+ if err := os .WriteFile (filepath .Join (dir , "go.mod" ), []byte ("module largecall" ), 0644 ); err != nil {
31+ t .Fatalf ("Failed to write file: %v\n " , err )
32+ }
33+ main := `package main
34+
35+ import "fmt"
36+
37+ func main() {
38+ fmt.Print(x())
39+ }
40+
41+ func x() uint64
42+ `
43+ if err := os .WriteFile (filepath .Join (dir , "x.go" ), []byte (main ), 0644 ); err != nil {
44+ t .Fatalf ("failed to write main: %v\n " , err )
45+ }
46+
2947 // Generate a very large function.
3048 buf := bytes .NewBuffer (make ([]byte , 0 , 7000000 ))
3149 genLargeBranch (buf )
@@ -36,27 +54,62 @@ func TestLargeBranch(t *testing.T) {
3654 }
3755
3856 // Assemble generated file.
39- cmd := testenv .Command (t , testenv .GoToolPath (t ), "tool" , "asm" , "-o" , filepath .Join (dir , "x.o" ), tmpfile )
57+ cmd := exec .Command (testenv .GoToolPath (t ), "tool" , "asm" , "-o" , filepath .Join (dir , "x.o" ), "-S" , tmpfile )
4058 cmd .Env = append (os .Environ (), "GOARCH=riscv64" , "GOOS=linux" )
4159 out , err := cmd .CombinedOutput ()
60+ if err != nil {
61+ t .Errorf ("Failed to assemble: %v\n %s" , err , out )
62+ }
63+
64+ // The expected instruction sequence for the long branch is:
65+ // BNEZ
66+ // AUIPC $..., X31
67+ // JALR X0, $..., X31
68+ want := regexp .MustCompile (`\sBNEZ\s.*\s.*\n.*\n.*AUIPC\s\$\d+, X31.*\n.*JALR\sX0, \$\d+, ?X31` )
69+ if ! want .Match (out ) {
70+ t .Error ("Missing assembly instructions" )
71+ }
72+
73+ // Build generated files.
74+ cmd = testenv .Command (t , testenv .GoToolPath (t ), "build" , "-o" , "x.exe" , "-ldflags=-linkmode=internal" )
75+ cmd .Dir = dir
76+ cmd .Env = append (os .Environ (), "GOARCH=riscv64" , "GOOS=linux" )
77+ out , err = cmd .CombinedOutput ()
4278 if err != nil {
4379 t .Errorf ("Build failed: %v, output: %s" , err , out )
4480 }
81+
82+ if runtime .GOARCH == "riscv64" && runtime .GOOS == "linux" {
83+ cmd = testenv .Command (t , filepath .Join (dir , "x.exe" ))
84+ out , err = cmd .CombinedOutput ()
85+ if err != nil {
86+ t .Errorf ("Failed to run test binary: %v" , err )
87+ }
88+ if string (out ) != "1" {
89+ t .Errorf (`Got test output %q, want "2"` , string (out ))
90+ }
91+ }
4592}
4693
4794func genLargeBranch (buf * bytes.Buffer ) {
48- fmt .Fprintln (buf , "TEXT f(SB),0,$0-0" )
49- fmt .Fprintln (buf , "BEQ X0, X0, label" )
50- for i := 0 ; i < 1 << 19 ; i ++ {
95+ fmt .Fprintln (buf , "TEXT ·x(SB),0,$0-8" )
96+ fmt .Fprintln (buf , "MOV X0, X10" )
97+ fmt .Fprintln (buf , "BEQZ X10, label" )
98+ for i := 0 ; i < 1 << 18 ; i ++ {
99+ // Use a non-compressable instruction.
51100 fmt .Fprintln (buf , "ADD $0, X5, X0" )
52101 }
102+ fmt .Fprintln (buf , "ADD $1, X10, X10" )
53103 fmt .Fprintln (buf , "label:" )
54- fmt .Fprintln (buf , "ADD $0, X5, X0" )
104+ fmt .Fprintln (buf , "ADD $1, X10, X10" )
105+ fmt .Fprintln (buf , "MOV X10, r+0(FP)" )
106+ fmt .Fprintln (buf , "RET" )
55107}
56108
57109// TestLargeCall generates a large function (>1MB of text) with a call to
58110// a following function, in order to ensure that it assembles and links
59- // correctly.
111+ // correctly. This requires the use of AUIPC+JALR instruction sequences,
112+ // which are fixed up by the linker.
60113func TestLargeCall (t * testing.T ) {
61114 if testing .Short () {
62115 t .Skip ("Skipping test in short mode" )
@@ -69,12 +122,15 @@ func TestLargeCall(t *testing.T) {
69122 t .Fatalf ("Failed to write file: %v\n " , err )
70123 }
71124 main := `package main
125+
126+ import "fmt"
127+
72128func main() {
73- x( )
129+ fmt.Print(x() )
74130}
75131
76- func x()
77- func y()
132+ func x() uint64
133+ func y() uint64
78134`
79135 if err := os .WriteFile (filepath .Join (dir , "x.go" ), []byte (main ), 0644 ); err != nil {
80136 t .Fatalf ("failed to write main: %v\n " , err )
@@ -84,51 +140,94 @@ func y()
84140 buf := bytes .NewBuffer (make ([]byte , 0 , 7000000 ))
85141 genLargeCall (buf )
86142
87- if err := os .WriteFile (filepath .Join (dir , "x.s" ), buf .Bytes (), 0644 ); err != nil {
143+ tmpfile := filepath .Join (dir , "x.s" )
144+ if err := os .WriteFile (tmpfile , buf .Bytes (), 0644 ); err != nil {
88145 t .Fatalf ("Failed to write file: %v\n " , err )
89146 }
90147
148+ // Assemble generated file.
149+ cmd := exec .Command (testenv .GoToolPath (t ), "tool" , "asm" , "-o" , filepath .Join (dir , "x.o" ), "-S" , tmpfile )
150+ cmd .Env = append (os .Environ (), "GOARCH=riscv64" , "GOOS=linux" )
151+ out , err := cmd .CombinedOutput ()
152+ if err != nil {
153+ t .Errorf ("Failed to assemble: %v\n %s" , err , out )
154+ }
155+
156+ // The expected instruction sequence for the long call is:
157+ // AUIPC $0, $0, X31
158+ // JALR X.., X31
159+ want := regexp .MustCompile (`\sAUIPC\s\$0, \$0, X31.*\n.*\sJALR\sX.*, X31` )
160+ if ! want .Match (out ) {
161+ t .Error ("Missing assembly instructions" )
162+ }
163+
91164 // Build generated files.
92- cmd : = testenv .Command (t , testenv .GoToolPath (t ), "build" , "-ldflags=-linkmode=internal" )
165+ cmd = testenv .Command (t , testenv .GoToolPath (t ), "build" , "-o" , "x.exe " , "-ldflags=-linkmode=internal" )
93166 cmd .Dir = dir
94167 cmd .Env = append (os .Environ (), "GOARCH=riscv64" , "GOOS=linux" )
95- out , err : = cmd .CombinedOutput ()
168+ out , err = cmd .CombinedOutput ()
96169 if err != nil {
97170 t .Errorf ("Build failed: %v, output: %s" , err , out )
98171 }
99172
173+ if runtime .GOARCH == "riscv64" && runtime .GOOS == "linux" {
174+ cmd = testenv .Command (t , filepath .Join (dir , "x.exe" ))
175+ out , err = cmd .CombinedOutput ()
176+ if err != nil {
177+ t .Errorf ("Failed to run test binary: %v" , err )
178+ }
179+ if string (out ) != "2" {
180+ t .Errorf (`Got test output %q, want "2"` , string (out ))
181+ }
182+ }
183+
100184 if runtime .GOARCH == "riscv64" && testenv .HasCGO () {
101- cmd := testenv .Command (t , testenv .GoToolPath (t ), "build" , "-ldflags=-linkmode=external" )
185+ cmd := testenv .Command (t , testenv .GoToolPath (t ), "build" , "-o" , "x.exe" , "- ldflags=-linkmode=external" )
102186 cmd .Dir = dir
103187 cmd .Env = append (os .Environ (), "GOARCH=riscv64" , "GOOS=linux" )
104188 out , err := cmd .CombinedOutput ()
105189 if err != nil {
106190 t .Errorf ("Build failed: %v, output: %s" , err , out )
107191 }
192+
193+ if runtime .GOARCH == "riscv64" && runtime .GOOS == "linux" {
194+ cmd = testenv .Command (t , filepath .Join (dir , "x.exe" ))
195+ out , err = cmd .CombinedOutput ()
196+ if err != nil {
197+ t .Errorf ("Failed to run test binary: %v" , err )
198+ }
199+ if string (out ) != "2" {
200+ t .Errorf (`Got test output %q, want "2"` , string (out ))
201+ }
202+ }
108203 }
109204}
110205
111206func genLargeCall (buf * bytes.Buffer ) {
112- fmt .Fprintln (buf , "TEXT ·x(SB),0,$0-0" )
207+ fmt .Fprintln (buf , "TEXT ·x(SB),0,$0-8" )
208+ fmt .Fprintln (buf , "MOV X0, X10" )
113209 fmt .Fprintln (buf , "CALL ·y(SB)" )
114- for i := 0 ; i < 1 << 19 ; i ++ {
210+ fmt .Fprintln (buf , "ADD $1, X10, X10" )
211+ fmt .Fprintln (buf , "MOV X10, r+0(FP)" )
212+ fmt .Fprintln (buf , "RET" )
213+ for i := 0 ; i < 1 << 18 ; i ++ {
214+ // Use a non-compressable instruction.
115215 fmt .Fprintln (buf , "ADD $0, X5, X0" )
116216 }
217+ fmt .Fprintln (buf , "ADD $1, X10, X10" )
117218 fmt .Fprintln (buf , "RET" )
118219 fmt .Fprintln (buf , "TEXT ·y(SB),0,$0-0" )
119- fmt .Fprintln (buf , "ADD $0, X5, X0 " )
220+ fmt .Fprintln (buf , "ADD $1, X10, X10 " )
120221 fmt .Fprintln (buf , "RET" )
121222}
122223
123224// TestLargeJump generates a large jump (>1MB of text) with a JMP to the
124225// end of the function, in order to ensure that it assembles correctly.
226+ // This requires the use of AUIPC+JALR instruction sequences.
125227func TestLargeJump (t * testing.T ) {
126228 if testing .Short () {
127229 t .Skip ("Skipping test in short mode" )
128230 }
129- if runtime .GOARCH != "riscv64" {
130- t .Skip ("Require riscv64 to run" )
131- }
132231 testenv .MustHaveGoBuild (t )
133232
134233 dir := t .TempDir ()
@@ -154,22 +253,46 @@ func x() uint64
154253 buf := bytes .NewBuffer (make ([]byte , 0 , 7000000 ))
155254 genLargeJump (buf )
156255
157- if err := os .WriteFile (filepath .Join (dir , "x.s" ), buf .Bytes (), 0644 ); err != nil {
256+ tmpfile := filepath .Join (dir , "x.s" )
257+ if err := os .WriteFile (tmpfile , buf .Bytes (), 0644 ); err != nil {
158258 t .Fatalf ("Failed to write file: %v\n " , err )
159259 }
160260
261+ // Assemble generated file.
262+ cmd := exec .Command (testenv .GoToolPath (t ), "tool" , "asm" , "-o" , filepath .Join (dir , "x.o" ), "-S" , tmpfile )
263+ cmd .Env = append (os .Environ (), "GOARCH=riscv64" , "GOOS=linux" )
264+ out , err := cmd .CombinedOutput ()
265+ if err != nil {
266+ t .Errorf ("Failed to assemble: %v\n %s" , err , out )
267+ }
268+
269+ // The expected instruction sequence for the long call is:
270+ // AUIPC $..., X31
271+ // JALR X0, $.., X31
272+ want := regexp .MustCompile (`\sAUIPC\s\$\d+, X31.*\n.*\sJALR\sX0, \$\d+, ?X31` )
273+ if ! want .Match (out ) {
274+ t .Error ("Missing assembly instructions" )
275+ t .Errorf ("%s" , out )
276+ }
277+
161278 // Build generated files.
162- cmd : = testenv .Command (t , testenv .GoToolPath (t ), "build" , "-o" , "x.exe" )
279+ cmd = testenv .Command (t , testenv .GoToolPath (t ), "build" , "-o" , "x.exe" )
163280 cmd .Dir = dir
164- out , err := cmd .CombinedOutput ()
281+ cmd .Env = append (os .Environ (), "GOARCH=riscv64" , "GOOS=linux" )
282+ out , err = cmd .CombinedOutput ()
165283 if err != nil {
166284 t .Errorf ("Build failed: %v, output: %s" , err , out )
167285 }
168286
169- cmd = testenv .Command (t , filepath .Join (dir , "x.exe" ))
170- out , err = cmd .CombinedOutput ()
171- if string (out ) != "1" {
172- t .Errorf (`Got test output %q, want "1"` , string (out ))
287+ if runtime .GOARCH == "riscv64" && runtime .GOOS == "linux" {
288+ cmd = testenv .Command (t , filepath .Join (dir , "x.exe" ))
289+ out , err = cmd .CombinedOutput ()
290+ if err != nil {
291+ t .Errorf ("Failed to run test binary: %v" , err )
292+ }
293+ if string (out ) != "1" {
294+ t .Errorf (`Got test output %q, want "1"` , string (out ))
295+ }
173296 }
174297}
175298
@@ -178,8 +301,10 @@ func genLargeJump(buf *bytes.Buffer) {
178301 fmt .Fprintln (buf , "MOV X0, X10" )
179302 fmt .Fprintln (buf , "JMP end" )
180303 for i := 0 ; i < 1 << 18 ; i ++ {
181- fmt .Fprintln (buf , "ADD $1, X10, X10" )
304+ // Use a non-compressable instruction.
305+ fmt .Fprintln (buf , "ADD $0, X5, X0" )
182306 }
307+ fmt .Fprintln (buf , "ADD $1, X10, X10" )
183308 fmt .Fprintln (buf , "end:" )
184309 fmt .Fprintln (buf , "ADD $1, X10, X10" )
185310 fmt .Fprintln (buf , "MOV X10, r+0(FP)" )
0 commit comments