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

View as plain text