}
// 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 {
// 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
// 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
q.Pos = p.Pos
q.To.Type = obj.TYPE_BRANCH
q.To.SetTarget(q.Link.Link)
- bflag = 1
+ rescan = true
}
}
}
c.cursym.Size = pc
+
+ if !rescan {
+ break
+ }
}
+
pc += -pc & (FuncAlign - 1)
c.cursym.Size = pc
--- /dev/null
+// 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")
+}