summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/obj/loong64/obj.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/internal/obj/loong64/obj.go
parentInitial commit. (diff)
downloadgolang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz
golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/internal/obj/loong64/obj.go')
-rw-r--r--src/cmd/internal/obj/loong64/obj.go716
1 files changed, 716 insertions, 0 deletions
diff --git a/src/cmd/internal/obj/loong64/obj.go b/src/cmd/internal/obj/loong64/obj.go
new file mode 100644
index 0000000..dc05e18
--- /dev/null
+++ b/src/cmd/internal/obj/loong64/obj.go
@@ -0,0 +1,716 @@
+// Copyright 2022 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 (
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "log"
+ "math"
+)
+
+func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
+ // Rewrite JMP/JAL to symbol as TYPE_BRANCH.
+ switch p.As {
+ case AJMP,
+ AJAL,
+ ARET,
+ obj.ADUFFZERO,
+ obj.ADUFFCOPY:
+ if p.To.Sym != nil {
+ p.To.Type = obj.TYPE_BRANCH
+ }
+ }
+
+ // Rewrite float constants to values stored in memory.
+ switch p.As {
+ case AMOVF:
+ if p.From.Type == obj.TYPE_FCONST {
+ f32 := float32(p.From.Val.(float64))
+ if math.Float32bits(f32) == 0 {
+ p.As = AMOVW
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGZERO
+ break
+ }
+ p.From.Type = obj.TYPE_MEM
+ p.From.Sym = ctxt.Float32Sym(f32)
+ p.From.Name = obj.NAME_EXTERN
+ p.From.Offset = 0
+ }
+
+ case AMOVD:
+ if p.From.Type == obj.TYPE_FCONST {
+ f64 := p.From.Val.(float64)
+ if math.Float64bits(f64) == 0 {
+ p.As = AMOVV
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGZERO
+ break
+ }
+ p.From.Type = obj.TYPE_MEM
+ p.From.Sym = ctxt.Float64Sym(f64)
+ p.From.Name = obj.NAME_EXTERN
+ p.From.Offset = 0
+ }
+ }
+
+ // Rewrite SUB constants into ADD.
+ switch p.As {
+ case ASUB:
+ if p.From.Type == obj.TYPE_CONST {
+ p.From.Offset = -p.From.Offset
+ p.As = AADD
+ }
+
+ case ASUBU:
+ if p.From.Type == obj.TYPE_CONST {
+ p.From.Offset = -p.From.Offset
+ p.As = AADDU
+ }
+
+ case ASUBV:
+ if p.From.Type == obj.TYPE_CONST {
+ p.From.Offset = -p.From.Offset
+ p.As = AADDV
+ }
+
+ case ASUBVU:
+ if p.From.Type == obj.TYPE_CONST {
+ p.From.Offset = -p.From.Offset
+ p.As = AADDVU
+ }
+ }
+}
+
+func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+ c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
+
+ p := c.cursym.Func().Text
+ textstksiz := p.To.Offset
+
+ if textstksiz < 0 {
+ c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
+ }
+ if p.From.Sym.NoFrame() {
+ if textstksiz != 0 {
+ c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
+ }
+ }
+
+ c.cursym.Func().Args = p.To.Val.(int32)
+ c.cursym.Func().Locals = int32(textstksiz)
+
+ /*
+ * find leaf subroutines
+ * expand RET
+ */
+
+ for p := c.cursym.Func().Text; p != nil; p = p.Link {
+ switch p.As {
+ case obj.ATEXT:
+ p.Mark |= LABEL | LEAF | SYNC
+ if p.Link != nil {
+ p.Link.Mark |= LABEL
+ }
+
+ case AMOVW,
+ AMOVV:
+ if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
+ p.Mark |= LABEL | SYNC
+ break
+ }
+ if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
+ p.Mark |= LABEL | SYNC
+ }
+
+ case ASYSCALL,
+ AWORD:
+ p.Mark |= LABEL | SYNC
+
+ case ANOR:
+ if p.To.Type == obj.TYPE_REG {
+ if p.To.Reg == REGZERO {
+ p.Mark |= LABEL | SYNC
+ }
+ }
+
+ case AJAL,
+ obj.ADUFFZERO,
+ obj.ADUFFCOPY:
+ c.cursym.Func().Text.Mark &^= LEAF
+ fallthrough
+
+ case AJMP,
+ ABEQ,
+ ABGEU,
+ ABLTU,
+ ABLTZ,
+ ABNE,
+ ABFPT, ABFPF:
+ p.Mark |= BRANCH
+ q1 := p.To.Target()
+ if q1 != nil {
+ for q1.As == obj.ANOP {
+ q1 = q1.Link
+ p.To.SetTarget(q1)
+ }
+
+ if q1.Mark&LEAF == 0 {
+ q1.Mark |= LABEL
+ }
+ }
+ q1 = p.Link
+ if q1 != nil {
+ q1.Mark |= LABEL
+ }
+
+ case ARET:
+ if p.Link != nil {
+ p.Link.Mark |= LABEL
+ }
+ }
+ }
+
+ var mov, add obj.As
+
+ add = AADDV
+ mov = AMOVV
+
+ var q *obj.Prog
+ var q1 *obj.Prog
+ autosize := int32(0)
+ var p1 *obj.Prog
+ var p2 *obj.Prog
+ for p := c.cursym.Func().Text; p != nil; p = p.Link {
+ o := p.As
+ switch o {
+ case obj.ATEXT:
+ autosize = int32(textstksiz)
+
+ if p.Mark&LEAF != 0 && autosize == 0 {
+ // A leaf function with no locals has no frame.
+ p.From.Sym.Set(obj.AttrNoFrame, true)
+ }
+
+ if !p.From.Sym.NoFrame() {
+ // If there is a stack frame at all, it includes
+ // space to save the LR.
+ autosize += int32(c.ctxt.Arch.FixedFrameSize)
+ }
+
+ if autosize&4 != 0 {
+ autosize += 4
+ }
+
+ if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
+ if c.cursym.Func().Text.From.Sym.NoSplit() {
+ if ctxt.Debugvlog {
+ ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
+ }
+
+ c.cursym.Func().Text.Mark |= LEAF
+ }
+ }
+
+ p.To.Offset = int64(autosize) - ctxt.Arch.FixedFrameSize
+
+ if c.cursym.Func().Text.Mark&LEAF != 0 {
+ c.cursym.Set(obj.AttrLeaf, true)
+ if p.From.Sym.NoFrame() {
+ break
+ }
+ }
+
+ if !p.From.Sym.NoSplit() {
+ p = c.stacksplit(p, autosize) // emit split check
+ }
+
+ q = p
+
+ if autosize != 0 {
+ // Make sure to save link register for non-empty frame, even if
+ // it is a leaf function, so that traceback works.
+ // Store link register before decrement SP, so if a signal comes
+ // during the execution of the function prologue, the traceback
+ // code will not see a half-updated stack frame.
+ // This sequence is not async preemptible, as if we open a frame
+ // at the current SP, it will clobber the saved LR.
+ q = c.ctxt.StartUnsafePoint(q, c.newprog)
+
+ q = obj.Appendp(q, newprog)
+ q.As = mov
+ q.Pos = p.Pos
+ q.From.Type = obj.TYPE_REG
+ q.From.Reg = REGLINK
+ q.To.Type = obj.TYPE_MEM
+ q.To.Offset = int64(-autosize)
+ q.To.Reg = REGSP
+
+ q = obj.Appendp(q, newprog)
+ q.As = add
+ q.Pos = p.Pos
+ q.From.Type = obj.TYPE_CONST
+ q.From.Offset = int64(-autosize)
+ q.To.Type = obj.TYPE_REG
+ q.To.Reg = REGSP
+ q.Spadj = +autosize
+
+ q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
+ }
+
+ if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 {
+ // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
+ //
+ // MOV g_panic(g), R1
+ // BEQ R1, end
+ // MOV panic_argp(R1), R2
+ // ADD $(autosize+FIXED_FRAME), R29, R3
+ // BNE R2, R3, end
+ // ADD $FIXED_FRAME, R29, R2
+ // MOV R2, panic_argp(R1)
+ // end:
+ // NOP
+ //
+ // The NOP is needed to give the jumps somewhere to land.
+ // It is a liblink NOP, not an hardware NOP: it encodes to 0 instruction bytes.
+ //
+ // We don't generate this for leafs because that means the wrapped
+ // function was inlined into the wrapper.
+
+ q = obj.Appendp(q, newprog)
+
+ q.As = mov
+ q.From.Type = obj.TYPE_MEM
+ q.From.Reg = REGG
+ q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
+ q.To.Type = obj.TYPE_REG
+ q.To.Reg = REG_R19
+
+ q = obj.Appendp(q, newprog)
+ q.As = ABEQ
+ q.From.Type = obj.TYPE_REG
+ q.From.Reg = REG_R19
+ q.To.Type = obj.TYPE_BRANCH
+ q.Mark |= BRANCH
+ p1 = q
+
+ q = obj.Appendp(q, newprog)
+ q.As = mov
+ q.From.Type = obj.TYPE_MEM
+ q.From.Reg = REG_R19
+ q.From.Offset = 0 // Panic.argp
+ q.To.Type = obj.TYPE_REG
+ q.To.Reg = REG_R4
+
+ q = obj.Appendp(q, newprog)
+ q.As = add
+ q.From.Type = obj.TYPE_CONST
+ q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize
+ q.Reg = REGSP
+ q.To.Type = obj.TYPE_REG
+ q.To.Reg = REG_R5
+
+ q = obj.Appendp(q, newprog)
+ q.As = ABNE
+ q.From.Type = obj.TYPE_REG
+ q.From.Reg = REG_R4
+ q.Reg = REG_R5
+ q.To.Type = obj.TYPE_BRANCH
+ q.Mark |= BRANCH
+ p2 = q
+
+ q = obj.Appendp(q, newprog)
+ q.As = add
+ q.From.Type = obj.TYPE_CONST
+ q.From.Offset = ctxt.Arch.FixedFrameSize
+ q.Reg = REGSP
+ q.To.Type = obj.TYPE_REG
+ q.To.Reg = REG_R4
+
+ q = obj.Appendp(q, newprog)
+ q.As = mov
+ q.From.Type = obj.TYPE_REG
+ q.From.Reg = REG_R4
+ q.To.Type = obj.TYPE_MEM
+ q.To.Reg = REG_R19
+ q.To.Offset = 0 // Panic.argp
+
+ q = obj.Appendp(q, newprog)
+
+ q.As = obj.ANOP
+ p1.To.SetTarget(q)
+ p2.To.SetTarget(q)
+ }
+
+ case ARET:
+ if p.From.Type == obj.TYPE_CONST {
+ ctxt.Diag("using BECOME (%v) is not supported!", p)
+ break
+ }
+
+ retSym := p.To.Sym
+ p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
+ p.To.Sym = nil
+
+ if c.cursym.Func().Text.Mark&LEAF != 0 {
+ if autosize == 0 {
+ p.As = AJMP
+ p.From = obj.Addr{}
+ if retSym != nil { // retjmp
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.Name = obj.NAME_EXTERN
+ p.To.Sym = retSym
+ } else {
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = REGLINK
+ p.To.Offset = 0
+ }
+ p.Mark |= BRANCH
+ break
+ }
+
+ p.As = add
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(autosize)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGSP
+ p.Spadj = -autosize
+
+ q = c.newprog()
+ q.As = AJMP
+ q.Pos = p.Pos
+ if retSym != nil { // retjmp
+ q.To.Type = obj.TYPE_BRANCH
+ q.To.Name = obj.NAME_EXTERN
+ q.To.Sym = retSym
+ } else {
+ q.To.Type = obj.TYPE_MEM
+ q.To.Offset = 0
+ q.To.Reg = REGLINK
+ }
+ q.Mark |= BRANCH
+ q.Spadj = +autosize
+
+ q.Link = p.Link
+ p.Link = q
+ break
+ }
+
+ p.As = mov
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = 0
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGLINK
+
+ if autosize != 0 {
+ q = c.newprog()
+ q.As = add
+ q.Pos = p.Pos
+ q.From.Type = obj.TYPE_CONST
+ q.From.Offset = int64(autosize)
+ q.To.Type = obj.TYPE_REG
+ q.To.Reg = REGSP
+ q.Spadj = -autosize
+
+ q.Link = p.Link
+ p.Link = q
+ }
+
+ q1 = c.newprog()
+ q1.As = AJMP
+ q1.Pos = p.Pos
+ if retSym != nil { // retjmp
+ q1.To.Type = obj.TYPE_BRANCH
+ q1.To.Name = obj.NAME_EXTERN
+ q1.To.Sym = retSym
+ } else {
+ q1.To.Type = obj.TYPE_MEM
+ q1.To.Offset = 0
+ q1.To.Reg = REGLINK
+ }
+ q1.Mark |= BRANCH
+ q1.Spadj = +autosize
+
+ q1.Link = q.Link
+ q.Link = q1
+
+ case AADD,
+ AADDU,
+ AADDV,
+ AADDVU:
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
+ p.Spadj = int32(-p.From.Offset)
+ }
+
+ case obj.AGETCALLERPC:
+ if cursym.Leaf() {
+ // MOV LR, Rd
+ p.As = mov
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGLINK
+ } else {
+ // MOV (RSP), Rd
+ p.As = mov
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = REGSP
+ }
+ }
+
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+ f := c.cursym.Func()
+ if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+ c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
+ }
+ }
+ }
+}
+
+func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
+ var mov, add obj.As
+
+ add = AADDV
+ mov = AMOVV
+ if c.ctxt.Flag_maymorestack != "" {
+ // Save LR and REGCTXT.
+ frameSize := 2 * c.ctxt.Arch.PtrSize
+
+ p = c.ctxt.StartUnsafePoint(p, c.newprog)
+
+ // MOV REGLINK, -8/-16(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = mov
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGLINK
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = int64(-frameSize)
+ p.To.Reg = REGSP
+
+ // MOV REGCTXT, -4/-8(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = mov
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGCTXT
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = -int64(c.ctxt.Arch.PtrSize)
+ p.To.Reg = REGSP
+
+ // ADD $-8/$-16, SP
+ p = obj.Appendp(p, c.newprog)
+ p.As = add
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(-frameSize)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGSP
+ p.Spadj = int32(frameSize)
+
+ // JAL maymorestack
+ p = obj.Appendp(p, c.newprog)
+ p.As = AJAL
+ p.To.Type = obj.TYPE_BRANCH
+ // See ../x86/obj6.go
+ p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
+ p.Mark |= BRANCH
+
+ // Restore LR and REGCTXT.
+
+ // MOV 0(SP), REGLINK
+ p = obj.Appendp(p, c.newprog)
+ p.As = mov
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = 0
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGLINK
+
+ // MOV 4/8(SP), REGCTXT
+ p = obj.Appendp(p, c.newprog)
+ p.As = mov
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = int64(c.ctxt.Arch.PtrSize)
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGCTXT
+
+ // ADD $8/$16, SP
+ p = obj.Appendp(p, c.newprog)
+ p.As = add
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(frameSize)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGSP
+ p.Spadj = int32(-frameSize)
+
+ p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
+ }
+
+ // Jump back to here after morestack returns.
+ startPred := p
+
+ // MOV g_stackguard(g), R19
+ p = obj.Appendp(p, c.newprog)
+
+ p.As = mov
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = REGG
+ p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
+ if c.cursym.CFunc() {
+ p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
+ }
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_R19
+
+ // Mark the stack bound check and morestack call async nonpreemptible.
+ // If we get preempted here, when resumed the preemption request is
+ // cleared, but we'll still call morestack, which will double the stack
+ // unnecessarily. See issue #35470.
+ p = c.ctxt.StartUnsafePoint(p, c.newprog)
+
+ var q *obj.Prog
+ if framesize <= objabi.StackSmall {
+ // small stack: SP < stackguard
+ // AGTU SP, stackguard, R19
+ p = obj.Appendp(p, c.newprog)
+
+ p.As = ASGTU
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGSP
+ p.Reg = REG_R19
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_R19
+ } else {
+ // large stack: SP-framesize < stackguard-StackSmall
+ offset := int64(framesize) - objabi.StackSmall
+ if framesize > objabi.StackBig {
+ // Such a large stack we need to protect against underflow.
+ // The runtime guarantees SP > objabi.StackBig, but
+ // framesize is large enough that SP-framesize may
+ // underflow, causing a direct comparison with the
+ // stack guard to incorrectly succeed. We explicitly
+ // guard against underflow.
+ //
+ // SGTU $(framesize-StackSmall), SP, R4
+ // BNE R4, label-of-call-to-morestack
+
+ p = obj.Appendp(p, c.newprog)
+ p.As = ASGTU
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = offset
+ p.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_R4
+
+ p = obj.Appendp(p, c.newprog)
+ q = p
+ p.As = ABNE
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REG_R4
+ p.To.Type = obj.TYPE_BRANCH
+ p.Mark |= BRANCH
+ }
+
+ p = obj.Appendp(p, c.newprog)
+
+ p.As = add
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = -offset
+ p.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_R4
+
+ p = obj.Appendp(p, c.newprog)
+ p.As = ASGTU
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REG_R4
+ p.Reg = REG_R19
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_R19
+ }
+
+ // q1: BNE R19, done
+ p = obj.Appendp(p, c.newprog)
+ q1 := p
+
+ p.As = ABNE
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REG_R19
+ p.To.Type = obj.TYPE_BRANCH
+ p.Mark |= BRANCH
+
+ // MOV LINK, R5
+ p = obj.Appendp(p, c.newprog)
+
+ p.As = mov
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGLINK
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_R5
+ if q != nil {
+ q.To.SetTarget(p)
+ p.Mark |= LABEL
+ }
+
+ p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
+
+ // JAL runtime.morestack(SB)
+ p = obj.Appendp(p, c.newprog)
+
+ p.As = AJAL
+ p.To.Type = obj.TYPE_BRANCH
+ if c.cursym.CFunc() {
+ p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
+ } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
+ p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
+ } else {
+ p.To.Sym = c.ctxt.Lookup("runtime.morestack")
+ }
+ p.Mark |= BRANCH
+
+ p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
+
+ // JMP start
+ p = obj.Appendp(p, c.newprog)
+
+ p.As = AJMP
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.SetTarget(startPred.Link)
+ startPred.Link.Mark |= LABEL
+ p.Mark |= BRANCH
+
+ // placeholder for q1's jump target
+ p = obj.Appendp(p, c.newprog)
+
+ p.As = obj.ANOP // zero-width place holder
+ q1.To.SetTarget(p)
+
+ return p
+}
+
+func (c *ctxt0) addnop(p *obj.Prog) {
+ q := c.newprog()
+ q.As = ANOOP
+ q.Pos = p.Pos
+ q.Link = p.Link
+ p.Link = q
+}
+
+var Linkloong64 = obj.LinkArch{
+ Arch: sys.ArchLoong64,
+ Init: buildop,
+ Preprocess: preprocess,
+ Assemble: span0,
+ Progedit: progedit,
+ DWARFRegisters: LOONG64DWARFRegisters,
+}