]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/obj/loong64: recheck jump offset boundary after auto-aligning loop heads
authorGuoqi Chen <chenguoqi@loongson.cn>
Tue, 15 Aug 2023 18:09:49 +0000 (02:09 +0800)
committerCherry Mui <cherryyz@google.com>
Mon, 15 Apr 2024 17:39:37 +0000 (17:39 +0000)
After the alignment of the loop header is performed, the offset of the checked
conditional branch instruction may overflow, so it needs to be checked again.

When checking whether the offset of the branch jump instruction overflows, it
can be classified and processed according to the range of the immediate field
of the specific instruction, which can reduce the introduction of unnecessary
jump instructions.

Fixes #61819

Change-Id: I772a5b5b8b8de21c78d7566be30be8ff65fdbce8
Reviewed-on: https://go-review.googlesource.com/c/go/+/519915
Reviewed-by: sophie zhao <zhaoxiaolin@loongson.cn>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Meidan Li <limeidan@loongson.cn>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Run-TryBot: qiu laidongfeng2 <2645477756@qq.com>
Reviewed-by: WANG Xuerui <git@xen0n.name>
src/cmd/internal/obj/loong64/asm.go
src/cmd/internal/obj/loong64/asm_test.go [new file with mode: 0644]

index f0f8abb59c062b7d24d3fffc160bd97768850169..6b950f8d05b7fe1dac25fe9de90a73e4751b2281 100644 (file)
@@ -449,11 +449,8 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
        }
 
        // Run these passes until convergence.
-       bflag := 1
-       var otxt int64
-       var q *obj.Prog
-       for bflag != 0 {
-               bflag = 0
+       for {
+               rescan := false
                pc = 0
                prev := c.cursym.Func().Text
                for p = prev.Link; p != nil; prev, p = p, p.Link {
@@ -468,7 +465,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                        // because pc will be adjusted if padding happens.
                        if p.Mark&branchLoopHead != 0 && pc&(loopAlign-1) != 0 &&
                                !(prev.As == obj.APCALIGN && prev.From.Offset >= loopAlign) {
-                               q = c.newprog()
+                               q := c.newprog()
                                prev.Link = q
                                q.Link = p
                                q.Pc = pc
@@ -484,18 +481,29 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                // since this loop iteration is for p.
                                pc += int64(pcAlignPadLength(ctxt, pc, loopAlign))
                                p.Pc = pc
+                               rescan = true
                        }
 
                        // very large conditional branches
                        //
-                       // if any procedure is large enough to
-                       // generate a large SBRA branch, then
-                       // generate extra passes putting branches
-                       // around jmps to fix. this is rare.
+                       // if any procedure is large enough to generate a large SBRA branch, then
+                       // generate extra passes putting branches around jmps to fix. this is rare.
                        if o.type_ == 6 && p.To.Target() != nil {
-                               otxt = p.To.Target().Pc - pc
-                               if otxt < -(1<<17)+10 || otxt >= (1<<17)-10 {
-                                       q = c.newprog()
+                               otxt := p.To.Target().Pc - pc
+
+                               // On loong64, the immediate value field of the conditional branch instructions
+                               // BFPT and BFPT is 21 bits, and the others are 16 bits. The jump target address
+                               // is to logically shift the immediate value in the instruction code to the left
+                               // by 2 bits and then sign extend.
+                               bound := int64(1 << (18 - 1))
+
+                               switch p.As {
+                               case ABFPT, ABFPF:
+                                       bound = int64(1 << (23 - 1))
+                               }
+
+                               if otxt < -bound || otxt >= bound {
+                                       q := c.newprog()
                                        q.Link = p.Link
                                        p.Link = q
                                        q.As = AJMP
@@ -510,7 +518,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                                        q.Pos = p.Pos
                                        q.To.Type = obj.TYPE_BRANCH
                                        q.To.SetTarget(q.Link.Link)
-                                       bflag = 1
+                                       rescan = true
                                }
                        }
 
@@ -532,7 +540,12 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                }
 
                c.cursym.Size = pc
+
+               if !rescan {
+                       break
+               }
        }
+
        pc += -pc & (FuncAlign - 1)
        c.cursym.Size = pc
 
diff --git a/src/cmd/internal/obj/loong64/asm_test.go b/src/cmd/internal/obj/loong64/asm_test.go
new file mode 100644 (file)
index 0000000..a35de61
--- /dev/null
@@ -0,0 +1,85 @@
+// Copyright 2023 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.
+
+package loong64
+
+import (
+       "bytes"
+       "fmt"
+       "internal/testenv"
+       "os"
+       "path/filepath"
+       "testing"
+)
+
+const genBufSize = (1024 * 1024 * 32) // 32MB
+
+// TestLargeBranch generates a large function with a very far conditional
+// branch, in order to ensure that it assembles successfully.
+func TestLargeBranch(t *testing.T) {
+       if testing.Short() {
+               t.Skip("Skipping test in short mode")
+       }
+       testenv.MustHaveGoBuild(t)
+
+       dir, err := os.MkdirTemp("", "testlargebranch")
+       if err != nil {
+               t.Fatalf("Could not create directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       // Generate a very large function.
+       buf := bytes.NewBuffer(make([]byte, 0, genBufSize))
+       genLargeBranch(buf)
+
+       tmpfile := filepath.Join(dir, "x.s")
+       if err := os.WriteFile(tmpfile, buf.Bytes(), 0644); err != nil {
+               t.Fatalf("Failed to write file: %v", err)
+       }
+
+       // Assemble generated file.
+       cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
+       cmd.Env = append(os.Environ(), "GOARCH=loong64", "GOOS=linux")
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Errorf("Build failed: %v, output: %s", err, out)
+       }
+}
+
+func genLargeBranch(buf *bytes.Buffer) {
+       genSize1 := (1 << 16) + 16
+       genSize2 := (1 << 21) + 16
+
+       fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
+       fmt.Fprintln(buf, "BEQ R5, R6, label18")
+       fmt.Fprintln(buf, "BNE R5, R6, label18")
+       fmt.Fprintln(buf, "BGE R5, R6, label18")
+
+       fmt.Fprintln(buf, "BGEU R5, R6, label18")
+       fmt.Fprintln(buf, "BLTU R5, R6, label18")
+
+       fmt.Fprintln(buf, "BLEZ R5, label18")
+       fmt.Fprintln(buf, "BGEZ R5, label18")
+       fmt.Fprintln(buf, "BLTZ R5, label18")
+       fmt.Fprintln(buf, "BGTZ R5, label18")
+
+       fmt.Fprintln(buf, "BFPT label23")
+       fmt.Fprintln(buf, "BFPF label23")
+
+       fmt.Fprintln(buf, "BEQ R5, label23")
+       fmt.Fprintln(buf, "BNE R5, label23")
+
+       for i := 0; i <= genSize1; i++ {
+               fmt.Fprintln(buf, "ADDV $0, R0, R0")
+       }
+
+       fmt.Fprintln(buf, "label18:")
+       for i := 0; i <= (genSize2 - genSize1); i++ {
+               fmt.Fprintln(buf, "ADDV $0, R0, R0")
+       }
+
+       fmt.Fprintln(buf, "label23:")
+       fmt.Fprintln(buf, "ADDV $0, R0, R0")
+       fmt.Fprintln(buf, "RET")
+}