github.com/bir3/gocompiler@v0.3.205/src/cmd/internal/obj/loong64/obj.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package loong64
     6  
     7  import (
     8  	"github.com/bir3/gocompiler/src/cmd/internal/obj"
     9  	"github.com/bir3/gocompiler/src/cmd/internal/objabi"
    10  	"github.com/bir3/gocompiler/src/cmd/internal/sys"
    11  	"log"
    12  	"math"
    13  )
    14  
    15  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    16  	// Rewrite JMP/JAL to symbol as TYPE_BRANCH.
    17  	switch p.As {
    18  	case AJMP,
    19  		AJAL,
    20  		ARET,
    21  		obj.ADUFFZERO,
    22  		obj.ADUFFCOPY:
    23  		if p.To.Sym != nil {
    24  			p.To.Type = obj.TYPE_BRANCH
    25  		}
    26  	}
    27  
    28  	// Rewrite float constants to values stored in memory.
    29  	switch p.As {
    30  	case AMOVF:
    31  		if p.From.Type == obj.TYPE_FCONST {
    32  			f32 := float32(p.From.Val.(float64))
    33  			if math.Float32bits(f32) == 0 {
    34  				p.As = AMOVW
    35  				p.From.Type = obj.TYPE_REG
    36  				p.From.Reg = REGZERO
    37  				break
    38  			}
    39  			p.From.Type = obj.TYPE_MEM
    40  			p.From.Sym = ctxt.Float32Sym(f32)
    41  			p.From.Name = obj.NAME_EXTERN
    42  			p.From.Offset = 0
    43  		}
    44  
    45  	case AMOVD:
    46  		if p.From.Type == obj.TYPE_FCONST {
    47  			f64 := p.From.Val.(float64)
    48  			if math.Float64bits(f64) == 0 {
    49  				p.As = AMOVV
    50  				p.From.Type = obj.TYPE_REG
    51  				p.From.Reg = REGZERO
    52  				break
    53  			}
    54  			p.From.Type = obj.TYPE_MEM
    55  			p.From.Sym = ctxt.Float64Sym(f64)
    56  			p.From.Name = obj.NAME_EXTERN
    57  			p.From.Offset = 0
    58  		}
    59  	}
    60  
    61  	// Rewrite SUB constants into ADD.
    62  	switch p.As {
    63  	case ASUB:
    64  		if p.From.Type == obj.TYPE_CONST {
    65  			p.From.Offset = -p.From.Offset
    66  			p.As = AADD
    67  		}
    68  
    69  	case ASUBU:
    70  		if p.From.Type == obj.TYPE_CONST {
    71  			p.From.Offset = -p.From.Offset
    72  			p.As = AADDU
    73  		}
    74  
    75  	case ASUBV:
    76  		if p.From.Type == obj.TYPE_CONST {
    77  			p.From.Offset = -p.From.Offset
    78  			p.As = AADDV
    79  		}
    80  
    81  	case ASUBVU:
    82  		if p.From.Type == obj.TYPE_CONST {
    83  			p.From.Offset = -p.From.Offset
    84  			p.As = AADDVU
    85  		}
    86  	}
    87  }
    88  
    89  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
    90  	c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
    91  
    92  	p := c.cursym.Func().Text
    93  	textstksiz := p.To.Offset
    94  
    95  	if textstksiz < 0 {
    96  		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
    97  	}
    98  	if p.From.Sym.NoFrame() {
    99  		if textstksiz != 0 {
   100  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   101  		}
   102  	}
   103  
   104  	c.cursym.Func().Args = p.To.Val.(int32)
   105  	c.cursym.Func().Locals = int32(textstksiz)
   106  
   107  	/*
   108  	 * find leaf subroutines
   109  	 * expand RET
   110  	 */
   111  
   112  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   113  		switch p.As {
   114  		case obj.ATEXT:
   115  			p.Mark |= LABEL | LEAF | SYNC
   116  			if p.Link != nil {
   117  				p.Link.Mark |= LABEL
   118  			}
   119  
   120  		case AMOVW,
   121  			AMOVV:
   122  			if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
   123  				p.Mark |= LABEL | SYNC
   124  				break
   125  			}
   126  			if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
   127  				p.Mark |= LABEL | SYNC
   128  			}
   129  
   130  		case ASYSCALL,
   131  			AWORD:
   132  			p.Mark |= LABEL | SYNC
   133  
   134  		case ANOR:
   135  			if p.To.Type == obj.TYPE_REG {
   136  				if p.To.Reg == REGZERO {
   137  					p.Mark |= LABEL | SYNC
   138  				}
   139  			}
   140  
   141  		case AJAL,
   142  			obj.ADUFFZERO,
   143  			obj.ADUFFCOPY:
   144  			c.cursym.Func().Text.Mark &^= LEAF
   145  			fallthrough
   146  
   147  		case AJMP,
   148  			ABEQ,
   149  			ABGEU,
   150  			ABLTU,
   151  			ABLTZ,
   152  			ABNE,
   153  			ABFPT, ABFPF:
   154  			p.Mark |= BRANCH
   155  			q1 := p.To.Target()
   156  			if q1 != nil {
   157  				for q1.As == obj.ANOP {
   158  					q1 = q1.Link
   159  					p.To.SetTarget(q1)
   160  				}
   161  
   162  				if q1.Mark&LEAF == 0 {
   163  					q1.Mark |= LABEL
   164  				}
   165  			}
   166  			q1 = p.Link
   167  			if q1 != nil {
   168  				q1.Mark |= LABEL
   169  			}
   170  
   171  		case ARET:
   172  			if p.Link != nil {
   173  				p.Link.Mark |= LABEL
   174  			}
   175  		}
   176  	}
   177  
   178  	var mov, add obj.As
   179  
   180  	add = AADDV
   181  	mov = AMOVV
   182  
   183  	var q *obj.Prog
   184  	var q1 *obj.Prog
   185  	autosize := int32(0)
   186  	var p1 *obj.Prog
   187  	var p2 *obj.Prog
   188  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   189  		o := p.As
   190  		switch o {
   191  		case obj.ATEXT:
   192  			autosize = int32(textstksiz)
   193  
   194  			if p.Mark&LEAF != 0 && autosize == 0 {
   195  				// A leaf function with no locals has no frame.
   196  				p.From.Sym.Set(obj.AttrNoFrame, true)
   197  			}
   198  
   199  			if !p.From.Sym.NoFrame() {
   200  				// If there is a stack frame at all, it includes
   201  				// space to save the LR.
   202  				autosize += int32(c.ctxt.Arch.FixedFrameSize)
   203  			}
   204  
   205  			if autosize&4 != 0 {
   206  				autosize += 4
   207  			}
   208  
   209  			if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
   210  				if c.cursym.Func().Text.From.Sym.NoSplit() {
   211  					if ctxt.Debugvlog {
   212  						ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
   213  					}
   214  
   215  					c.cursym.Func().Text.Mark |= LEAF
   216  				}
   217  			}
   218  
   219  			p.To.Offset = int64(autosize) - ctxt.Arch.FixedFrameSize
   220  
   221  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   222  				c.cursym.Set(obj.AttrLeaf, true)
   223  				if p.From.Sym.NoFrame() {
   224  					break
   225  				}
   226  			}
   227  
   228  			if !p.From.Sym.NoSplit() {
   229  				p = c.stacksplit(p, autosize) // emit split check
   230  			}
   231  
   232  			q = p
   233  
   234  			if autosize != 0 {
   235  				// Make sure to save link register for non-empty frame, even if
   236  				// it is a leaf function, so that traceback works.
   237  				// Store link register before decrement SP, so if a signal comes
   238  				// during the execution of the function prologue, the traceback
   239  				// code will not see a half-updated stack frame.
   240  				// This sequence is not async preemptible, as if we open a frame
   241  				// at the current SP, it will clobber the saved LR.
   242  				q = c.ctxt.StartUnsafePoint(q, c.newprog)
   243  
   244  				q = obj.Appendp(q, newprog)
   245  				q.As = mov
   246  				q.Pos = p.Pos
   247  				q.From.Type = obj.TYPE_REG
   248  				q.From.Reg = REGLINK
   249  				q.To.Type = obj.TYPE_MEM
   250  				q.To.Offset = int64(-autosize)
   251  				q.To.Reg = REGSP
   252  
   253  				q = obj.Appendp(q, newprog)
   254  				q.As = add
   255  				q.Pos = p.Pos
   256  				q.From.Type = obj.TYPE_CONST
   257  				q.From.Offset = int64(-autosize)
   258  				q.To.Type = obj.TYPE_REG
   259  				q.To.Reg = REGSP
   260  				q.Spadj = +autosize
   261  
   262  				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
   263  			}
   264  
   265  			if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 {
   266  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   267  				//
   268  				//	MOV	g_panic(g), R1
   269  				//	BEQ	R1, end
   270  				//	MOV	panic_argp(R1), R2
   271  				//	ADD	$(autosize+FIXED_FRAME), R29, R3
   272  				//	BNE	R2, R3, end
   273  				//	ADD	$FIXED_FRAME, R29, R2
   274  				//	MOV	R2, panic_argp(R1)
   275  				// end:
   276  				//	NOP
   277  				//
   278  				// The NOP is needed to give the jumps somewhere to land.
   279  				// It is a liblink NOP, not an hardware NOP: it encodes to 0 instruction bytes.
   280  				//
   281  				// We don't generate this for leafs because that means the wrapped
   282  				// function was inlined into the wrapper.
   283  
   284  				q = obj.Appendp(q, newprog)
   285  
   286  				q.As = mov
   287  				q.From.Type = obj.TYPE_MEM
   288  				q.From.Reg = REGG
   289  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   290  				q.To.Type = obj.TYPE_REG
   291  				q.To.Reg = REG_R19
   292  
   293  				q = obj.Appendp(q, newprog)
   294  				q.As = ABEQ
   295  				q.From.Type = obj.TYPE_REG
   296  				q.From.Reg = REG_R19
   297  				q.To.Type = obj.TYPE_BRANCH
   298  				q.Mark |= BRANCH
   299  				p1 = q
   300  
   301  				q = obj.Appendp(q, newprog)
   302  				q.As = mov
   303  				q.From.Type = obj.TYPE_MEM
   304  				q.From.Reg = REG_R19
   305  				q.From.Offset = 0 // Panic.argp
   306  				q.To.Type = obj.TYPE_REG
   307  				q.To.Reg = REG_R4
   308  
   309  				q = obj.Appendp(q, newprog)
   310  				q.As = add
   311  				q.From.Type = obj.TYPE_CONST
   312  				q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize
   313  				q.Reg = REGSP
   314  				q.To.Type = obj.TYPE_REG
   315  				q.To.Reg = REG_R5
   316  
   317  				q = obj.Appendp(q, newprog)
   318  				q.As = ABNE
   319  				q.From.Type = obj.TYPE_REG
   320  				q.From.Reg = REG_R4
   321  				q.Reg = REG_R5
   322  				q.To.Type = obj.TYPE_BRANCH
   323  				q.Mark |= BRANCH
   324  				p2 = q
   325  
   326  				q = obj.Appendp(q, newprog)
   327  				q.As = add
   328  				q.From.Type = obj.TYPE_CONST
   329  				q.From.Offset = ctxt.Arch.FixedFrameSize
   330  				q.Reg = REGSP
   331  				q.To.Type = obj.TYPE_REG
   332  				q.To.Reg = REG_R4
   333  
   334  				q = obj.Appendp(q, newprog)
   335  				q.As = mov
   336  				q.From.Type = obj.TYPE_REG
   337  				q.From.Reg = REG_R4
   338  				q.To.Type = obj.TYPE_MEM
   339  				q.To.Reg = REG_R19
   340  				q.To.Offset = 0 // Panic.argp
   341  
   342  				q = obj.Appendp(q, newprog)
   343  
   344  				q.As = obj.ANOP
   345  				p1.To.SetTarget(q)
   346  				p2.To.SetTarget(q)
   347  			}
   348  
   349  		case ARET:
   350  			if p.From.Type == obj.TYPE_CONST {
   351  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   352  				break
   353  			}
   354  
   355  			retSym := p.To.Sym
   356  			p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
   357  			p.To.Sym = nil
   358  
   359  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   360  				if autosize == 0 {
   361  					p.As = AJMP
   362  					p.From = obj.Addr{}
   363  					if retSym != nil { // retjmp
   364  						p.To.Type = obj.TYPE_BRANCH
   365  						p.To.Name = obj.NAME_EXTERN
   366  						p.To.Sym = retSym
   367  					} else {
   368  						p.To.Type = obj.TYPE_MEM
   369  						p.To.Reg = REGLINK
   370  						p.To.Offset = 0
   371  					}
   372  					p.Mark |= BRANCH
   373  					break
   374  				}
   375  
   376  				p.As = add
   377  				p.From.Type = obj.TYPE_CONST
   378  				p.From.Offset = int64(autosize)
   379  				p.To.Type = obj.TYPE_REG
   380  				p.To.Reg = REGSP
   381  				p.Spadj = -autosize
   382  
   383  				q = c.newprog()
   384  				q.As = AJMP
   385  				q.Pos = p.Pos
   386  				if retSym != nil { // retjmp
   387  					q.To.Type = obj.TYPE_BRANCH
   388  					q.To.Name = obj.NAME_EXTERN
   389  					q.To.Sym = retSym
   390  				} else {
   391  					q.To.Type = obj.TYPE_MEM
   392  					q.To.Offset = 0
   393  					q.To.Reg = REGLINK
   394  				}
   395  				q.Mark |= BRANCH
   396  				q.Spadj = +autosize
   397  
   398  				q.Link = p.Link
   399  				p.Link = q
   400  				break
   401  			}
   402  
   403  			p.As = mov
   404  			p.From.Type = obj.TYPE_MEM
   405  			p.From.Offset = 0
   406  			p.From.Reg = REGSP
   407  			p.To.Type = obj.TYPE_REG
   408  			p.To.Reg = REGLINK
   409  
   410  			if autosize != 0 {
   411  				q = c.newprog()
   412  				q.As = add
   413  				q.Pos = p.Pos
   414  				q.From.Type = obj.TYPE_CONST
   415  				q.From.Offset = int64(autosize)
   416  				q.To.Type = obj.TYPE_REG
   417  				q.To.Reg = REGSP
   418  				q.Spadj = -autosize
   419  
   420  				q.Link = p.Link
   421  				p.Link = q
   422  			}
   423  
   424  			q1 = c.newprog()
   425  			q1.As = AJMP
   426  			q1.Pos = p.Pos
   427  			if retSym != nil { // retjmp
   428  				q1.To.Type = obj.TYPE_BRANCH
   429  				q1.To.Name = obj.NAME_EXTERN
   430  				q1.To.Sym = retSym
   431  			} else {
   432  				q1.To.Type = obj.TYPE_MEM
   433  				q1.To.Offset = 0
   434  				q1.To.Reg = REGLINK
   435  			}
   436  			q1.Mark |= BRANCH
   437  			q1.Spadj = +autosize
   438  
   439  			q1.Link = q.Link
   440  			q.Link = q1
   441  
   442  		case AADD,
   443  			AADDU,
   444  			AADDV,
   445  			AADDVU:
   446  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   447  				p.Spadj = int32(-p.From.Offset)
   448  			}
   449  
   450  		case obj.AGETCALLERPC:
   451  			if cursym.Leaf() {
   452  				// MOV LR, Rd
   453  				p.As = mov
   454  				p.From.Type = obj.TYPE_REG
   455  				p.From.Reg = REGLINK
   456  			} else {
   457  				// MOV (RSP), Rd
   458  				p.As = mov
   459  				p.From.Type = obj.TYPE_MEM
   460  				p.From.Reg = REGSP
   461  			}
   462  		}
   463  
   464  		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
   465  			f := c.cursym.Func()
   466  			if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
   467  				c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
   468  				if ctxt.Debugvlog || !ctxt.IsAsm {
   469  					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
   470  					if !ctxt.IsAsm {
   471  						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
   472  						ctxt.DiagFlush()
   473  						log.Fatalf("bad SPWRITE")
   474  					}
   475  				}
   476  			}
   477  		}
   478  	}
   479  }
   480  
   481  func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   482  	var mov, add obj.As
   483  
   484  	add = AADDV
   485  	mov = AMOVV
   486  	if c.ctxt.Flag_maymorestack != "" {
   487  		// Save LR and REGCTXT.
   488  		frameSize := 2 * c.ctxt.Arch.PtrSize
   489  
   490  		p = c.ctxt.StartUnsafePoint(p, c.newprog)
   491  
   492  		// MOV	REGLINK, -8/-16(SP)
   493  		p = obj.Appendp(p, c.newprog)
   494  		p.As = mov
   495  		p.From.Type = obj.TYPE_REG
   496  		p.From.Reg = REGLINK
   497  		p.To.Type = obj.TYPE_MEM
   498  		p.To.Offset = int64(-frameSize)
   499  		p.To.Reg = REGSP
   500  
   501  		// MOV	REGCTXT, -4/-8(SP)
   502  		p = obj.Appendp(p, c.newprog)
   503  		p.As = mov
   504  		p.From.Type = obj.TYPE_REG
   505  		p.From.Reg = REGCTXT
   506  		p.To.Type = obj.TYPE_MEM
   507  		p.To.Offset = -int64(c.ctxt.Arch.PtrSize)
   508  		p.To.Reg = REGSP
   509  
   510  		// ADD	$-8/$-16, SP
   511  		p = obj.Appendp(p, c.newprog)
   512  		p.As = add
   513  		p.From.Type = obj.TYPE_CONST
   514  		p.From.Offset = int64(-frameSize)
   515  		p.To.Type = obj.TYPE_REG
   516  		p.To.Reg = REGSP
   517  		p.Spadj = int32(frameSize)
   518  
   519  		// JAL	maymorestack
   520  		p = obj.Appendp(p, c.newprog)
   521  		p.As = AJAL
   522  		p.To.Type = obj.TYPE_BRANCH
   523  		// See ../x86/obj6.go
   524  		p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
   525  		p.Mark |= BRANCH
   526  
   527  		// Restore LR and REGCTXT.
   528  
   529  		// MOV	0(SP), REGLINK
   530  		p = obj.Appendp(p, c.newprog)
   531  		p.As = mov
   532  		p.From.Type = obj.TYPE_MEM
   533  		p.From.Offset = 0
   534  		p.From.Reg = REGSP
   535  		p.To.Type = obj.TYPE_REG
   536  		p.To.Reg = REGLINK
   537  
   538  		// MOV	4/8(SP), REGCTXT
   539  		p = obj.Appendp(p, c.newprog)
   540  		p.As = mov
   541  		p.From.Type = obj.TYPE_MEM
   542  		p.From.Offset = int64(c.ctxt.Arch.PtrSize)
   543  		p.From.Reg = REGSP
   544  		p.To.Type = obj.TYPE_REG
   545  		p.To.Reg = REGCTXT
   546  
   547  		// ADD	$8/$16, SP
   548  		p = obj.Appendp(p, c.newprog)
   549  		p.As = add
   550  		p.From.Type = obj.TYPE_CONST
   551  		p.From.Offset = int64(frameSize)
   552  		p.To.Type = obj.TYPE_REG
   553  		p.To.Reg = REGSP
   554  		p.Spadj = int32(-frameSize)
   555  
   556  		p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
   557  	}
   558  
   559  	// Jump back to here after morestack returns.
   560  	startPred := p
   561  
   562  	// MOV	g_stackguard(g), R19
   563  	p = obj.Appendp(p, c.newprog)
   564  
   565  	p.As = mov
   566  	p.From.Type = obj.TYPE_MEM
   567  	p.From.Reg = REGG
   568  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   569  	if c.cursym.CFunc() {
   570  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   571  	}
   572  	p.To.Type = obj.TYPE_REG
   573  	p.To.Reg = REG_R19
   574  
   575  	// Mark the stack bound check and morestack call async nonpreemptible.
   576  	// If we get preempted here, when resumed the preemption request is
   577  	// cleared, but we'll still call morestack, which will double the stack
   578  	// unnecessarily. See issue #35470.
   579  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
   580  
   581  	var q *obj.Prog
   582  	if framesize <= objabi.StackSmall {
   583  		// small stack: SP < stackguard
   584  		//	AGTU	SP, stackguard, R19
   585  		p = obj.Appendp(p, c.newprog)
   586  
   587  		p.As = ASGTU
   588  		p.From.Type = obj.TYPE_REG
   589  		p.From.Reg = REGSP
   590  		p.Reg = REG_R19
   591  		p.To.Type = obj.TYPE_REG
   592  		p.To.Reg = REG_R19
   593  	} else {
   594  		// large stack: SP-framesize < stackguard-StackSmall
   595  		offset := int64(framesize) - objabi.StackSmall
   596  		if framesize > objabi.StackBig {
   597  			// Such a large stack we need to protect against underflow.
   598  			// The runtime guarantees SP > objabi.StackBig, but
   599  			// framesize is large enough that SP-framesize may
   600  			// underflow, causing a direct comparison with the
   601  			// stack guard to incorrectly succeed. We explicitly
   602  			// guard against underflow.
   603  			//
   604  			//      SGTU    $(framesize-StackSmall), SP, R4
   605  			//      BNE     R4, label-of-call-to-morestack
   606  
   607  			p = obj.Appendp(p, c.newprog)
   608  			p.As = ASGTU
   609  			p.From.Type = obj.TYPE_CONST
   610  			p.From.Offset = offset
   611  			p.Reg = REGSP
   612  			p.To.Type = obj.TYPE_REG
   613  			p.To.Reg = REG_R4
   614  
   615  			p = obj.Appendp(p, c.newprog)
   616  			q = p
   617  			p.As = ABNE
   618  			p.From.Type = obj.TYPE_REG
   619  			p.From.Reg = REG_R4
   620  			p.To.Type = obj.TYPE_BRANCH
   621  			p.Mark |= BRANCH
   622  		}
   623  
   624  		p = obj.Appendp(p, c.newprog)
   625  
   626  		p.As = add
   627  		p.From.Type = obj.TYPE_CONST
   628  		p.From.Offset = -offset
   629  		p.Reg = REGSP
   630  		p.To.Type = obj.TYPE_REG
   631  		p.To.Reg = REG_R4
   632  
   633  		p = obj.Appendp(p, c.newprog)
   634  		p.As = ASGTU
   635  		p.From.Type = obj.TYPE_REG
   636  		p.From.Reg = REG_R4
   637  		p.Reg = REG_R19
   638  		p.To.Type = obj.TYPE_REG
   639  		p.To.Reg = REG_R19
   640  	}
   641  
   642  	// q1: BNE	R19, done
   643  	p = obj.Appendp(p, c.newprog)
   644  	q1 := p
   645  
   646  	p.As = ABNE
   647  	p.From.Type = obj.TYPE_REG
   648  	p.From.Reg = REG_R19
   649  	p.To.Type = obj.TYPE_BRANCH
   650  	p.Mark |= BRANCH
   651  
   652  	// MOV	LINK, R5
   653  	p = obj.Appendp(p, c.newprog)
   654  
   655  	p.As = mov
   656  	p.From.Type = obj.TYPE_REG
   657  	p.From.Reg = REGLINK
   658  	p.To.Type = obj.TYPE_REG
   659  	p.To.Reg = REG_R5
   660  	if q != nil {
   661  		q.To.SetTarget(p)
   662  		p.Mark |= LABEL
   663  	}
   664  
   665  	p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
   666  
   667  	// JAL	runtime.morestack(SB)
   668  	p = obj.Appendp(p, c.newprog)
   669  
   670  	p.As = AJAL
   671  	p.To.Type = obj.TYPE_BRANCH
   672  	if c.cursym.CFunc() {
   673  		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
   674  	} else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
   675  		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
   676  	} else {
   677  		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
   678  	}
   679  	p.Mark |= BRANCH
   680  
   681  	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
   682  
   683  	// JMP	start
   684  	p = obj.Appendp(p, c.newprog)
   685  
   686  	p.As = AJMP
   687  	p.To.Type = obj.TYPE_BRANCH
   688  	p.To.SetTarget(startPred.Link)
   689  	startPred.Link.Mark |= LABEL
   690  	p.Mark |= BRANCH
   691  
   692  	// placeholder for q1's jump target
   693  	p = obj.Appendp(p, c.newprog)
   694  
   695  	p.As = obj.ANOP // zero-width place holder
   696  	q1.To.SetTarget(p)
   697  
   698  	return p
   699  }
   700  
   701  func (c *ctxt0) addnop(p *obj.Prog) {
   702  	q := c.newprog()
   703  	q.As = ANOOP
   704  	q.Pos = p.Pos
   705  	q.Link = p.Link
   706  	p.Link = q
   707  }
   708  
   709  var Linkloong64 = obj.LinkArch{
   710  	Arch:           sys.ArchLoong64,
   711  	Init:           buildop,
   712  	Preprocess:     preprocess,
   713  	Assemble:       span0,
   714  	Progedit:       progedit,
   715  	DWARFRegisters: LOONG64DWARFRegisters,
   716  }