Source file src/cmd/internal/obj/loong64/obj.go

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

View as plain text