diff options
| author | Joel Sing <joel@sing.id.au> | 2021-10-16 03:59:41 +1100 |
|---|---|---|
| committer | Joel Sing <joel@sing.id.au> | 2021-10-19 11:52:34 +0000 |
| commit | bde0463da3f31934791b0bb2ccacdf6206314073 (patch) | |
| tree | ec3cb0594eac99f4da7e8ed0cf2f9c0ed4fc81f2 /src/cmd/internal/obj/riscv/obj.go | |
| parent | b0351bfd7d5f0d367c27aa07789b2e6317442ece (diff) | |
| download | go-bde0463da3f31934791b0bb2ccacdf6206314073.tar.xz | |
cmd/internal/obj/riscv: fix trampoline calls from large functions
On riscv64, the JAL instruction is only capable of reaching +/-1MB. In the case where
a single function and its trampolines exceeds this size, it is possible that the JAL
is unable to reach the trampoline, which is laid down after the function text. In the
case of large functions, switch back to using a AUIPC+JALR pairs rather than using
trampolines.
Fixes #48791
Change-Id: I119cf3bc20ce4933a9b7ab41a8e514437c6addb9
Reviewed-on: https://go-review.googlesource.com/c/go/+/356250
Trust: Joel Sing <joel@sing.id.au>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/cmd/internal/obj/riscv/obj.go')
| -rw-r--r-- | src/cmd/internal/obj/riscv/obj.go | 36 |
1 files changed, 32 insertions, 4 deletions
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index b346b13577..d98806edb5 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -280,14 +280,15 @@ func containsCall(sym *obj.LSym) bool { } // setPCs sets the Pc field in all instructions reachable from p. -// It uses pc as the initial value. -func setPCs(p *obj.Prog, pc int64) { +// It uses pc as the initial value and returns the next available pc. +func setPCs(p *obj.Prog, pc int64) int64 { for ; p != nil; p = p.Link { p.Pc = pc for _, ins := range instructionsForProg(p) { pc += int64(ins.length()) } } + return pc } // stackOffset updates Addr offsets based on the current stack size. @@ -582,17 +583,26 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } + var callCount int for p := cursym.Func().Text; p != nil; p = p.Link { markRelocs(p) + if p.Mark&NEED_CALL_RELOC == NEED_CALL_RELOC { + callCount++ + } } + const callTrampSize = 8 // 2 machine instructions. + maxTrampSize := int64(callCount * callTrampSize) // Compute instruction addresses. Once we do that, we need to check for // overextended jumps and branches. Within each iteration, Pc differences // are always lower bounds (since the program gets monotonically longer, // a fixed point will be reached). No attempt to handle functions > 2GiB. for { - rescan := false - setPCs(cursym.Func().Text, 0) + big, rescan := false, false + maxPC := setPCs(cursym.Func().Text, 0) + if maxPC+maxTrampSize > (1 << 20) { + big = true + } for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { @@ -619,6 +629,24 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case AJAL: // Linker will handle the intersymbol case and trampolines. if p.To.Target() == nil { + if !big { + break + } + // This function is going to be too large for JALs + // to reach trampolines. Replace with AUIPC+JALR. + jmp := obj.Appendp(p, newprog) + jmp.As = AJALR + jmp.From = p.From + jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP} + + p.As = AAUIPC + p.Mark = (p.Mark &^ NEED_CALL_RELOC) | NEED_PCREL_ITYPE_RELOC + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym}) + p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0} + p.Reg = obj.REG_NONE + p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP} + + rescan = true break } offset := p.To.Target().Pc - p.Pc |
