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  	var p1 *obj.Prog
   305  	var p2 *obj.Prog
   306  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   307  		o := p.As
   308  		switch o {
   309  		case obj.ATEXT:
   310  			autosize = int32(textstksiz)
   311  
   312  			if p.Mark&LEAF != 0 && autosize == 0 {
   313  				// A leaf function with no locals has no frame.
   314  				p.From.Sym.Set(obj.AttrNoFrame, true)
   315  			}
   316  
   317  			if !p.From.Sym.NoFrame() {
   318  				// If there is a stack frame at all, it includes
   319  				// space to save the LR.
   320  				autosize += int32(c.ctxt.Arch.FixedFrameSize)
   321  			}
   322  
   323  			if p.Mark&LEAF != 0 && autosize < abi.StackSmall {
   324  				// A leaf function with a small stack can be marked
   325  				// NOSPLIT, avoiding a stack check.
   326  				p.From.Sym.Set(obj.AttrNoSplit, true)
   327  			}
   328  
   329  			if autosize&4 != 0 {
   330  				autosize += 4
   331  			}
   332  
   333  			if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
   334  				if c.cursym.Func().Text.From.Sym.NoSplit() {
   335  					if ctxt.Debugvlog {
   336  						ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
   337  					}
   338  
   339  					c.cursym.Func().Text.Mark |= LEAF
   340  				}
   341  			}
   342  
   343  			p.To.Offset = int64(autosize) - ctxt.Arch.FixedFrameSize
   344  
   345  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   346  				c.cursym.Set(obj.AttrLeaf, true)
   347  				if p.From.Sym.NoFrame() {
   348  					break
   349  				}
   350  			}
   351  
   352  			if !p.From.Sym.NoSplit() {
   353  				p = c.stacksplit(p, autosize) // emit split check
   354  			}
   355  
   356  			q = p
   357  
   358  			if autosize != 0 {
   359  				// Make sure to save link register for non-empty frame, even if
   360  				// it is a leaf function, so that traceback works.
   361  				// Store link register before decrement SP, so if a signal comes
   362  				// during the execution of the function prologue, the traceback
   363  				// code will not see a half-updated stack frame.
   364  				// This sequence is not async preemptible, as if we open a frame
   365  				// at the current SP, it will clobber the saved LR.
   366  				q = c.ctxt.StartUnsafePoint(q, c.newprog)
   367  
   368  				q = obj.Appendp(q, newprog)
   369  				q.As = mov
   370  				q.Pos = p.Pos
   371  				q.From.Type = obj.TYPE_REG
   372  				q.From.Reg = REGLINK
   373  				q.To.Type = obj.TYPE_MEM
   374  				q.To.Offset = int64(-autosize)
   375  				q.To.Reg = REGSP
   376  
   377  				q = obj.Appendp(q, newprog)
   378  				q.As = add
   379  				q.Pos = p.Pos
   380  				q.Pos = q.Pos.WithXlogue(src.PosPrologueEnd)
   381  				q.From.Type = obj.TYPE_CONST
   382  				q.From.Offset = int64(-autosize)
   383  				q.To.Type = obj.TYPE_REG
   384  				q.To.Reg = REGSP
   385  				q.Spadj = +autosize
   386  
   387  				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
   388  
   389  				// On Linux, in a cgo binary we may get a SIGSETXID signal early on
   390  				// before the signal stack is set, as glibc doesn't allow us to block
   391  				// SIGSETXID. So a signal may land on the current stack and clobber
   392  				// the content below the SP. We store the LR again after the SP is
   393  				// decremented.
   394  				q = obj.Appendp(q, newprog)
   395  				q.As = mov
   396  				q.Pos = p.Pos
   397  				q.From.Type = obj.TYPE_REG
   398  				q.From.Reg = REGLINK
   399  				q.To.Type = obj.TYPE_MEM
   400  				q.To.Offset = 0
   401  				q.To.Reg = REGSP
   402  			}
   403  
   404  			if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 {
   405  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   406  				//
   407  				//	MOV	g_panic(g), R20
   408  				//	BEQ	R20, end
   409  				//	MOV	panic_argp(R20), R24
   410  				//	ADD	$(autosize+FIXED_FRAME), R3, R30
   411  				//	BNE	R24, R30, end
   412  				//	ADD	$FIXED_FRAME, R3, R24
   413  				//	MOV	R24, panic_argp(R20)
   414  				// end:
   415  				//	NOP
   416  				//
   417  				// The NOP is needed to give the jumps somewhere to land.
   418  				// It is a liblink NOP, not a hardware NOP: it encodes to 0 instruction bytes.
   419  				//
   420  				// We don't generate this for leaves because that means the wrapped
   421  				// function was inlined into the wrapper.
   422  
   423  				q = obj.Appendp(q, newprog)
   424  
   425  				q.As = mov
   426  				q.From.Type = obj.TYPE_MEM
   427  				q.From.Reg = REGG
   428  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   429  				q.To.Type = obj.TYPE_REG
   430  				q.To.Reg = REG_R20
   431  
   432  				q = obj.Appendp(q, newprog)
   433  				q.As = ABEQ
   434  				q.From.Type = obj.TYPE_REG
   435  				q.From.Reg = REG_R20
   436  				q.To.Type = obj.TYPE_BRANCH
   437  				q.Mark |= BRANCH
   438  				p1 = q
   439  
   440  				q = obj.Appendp(q, newprog)
   441  				q.As = mov
   442  				q.From.Type = obj.TYPE_MEM
   443  				q.From.Reg = REG_R20
   444  				q.From.Offset = 0 // Panic.argp
   445  				q.To.Type = obj.TYPE_REG
   446  				q.To.Reg = REG_R24
   447  
   448  				q = obj.Appendp(q, newprog)
   449  				q.As = add
   450  				q.From.Type = obj.TYPE_CONST
   451  				q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize
   452  				q.Reg = REGSP
   453  				q.To.Type = obj.TYPE_REG
   454  				q.To.Reg = REG_R30
   455  
   456  				q = obj.Appendp(q, newprog)
   457  				q.As = ABNE
   458  				q.From.Type = obj.TYPE_REG
   459  				q.From.Reg = REG_R24
   460  				q.Reg = REG_R30
   461  				q.To.Type = obj.TYPE_BRANCH
   462  				q.Mark |= BRANCH
   463  				p2 = q
   464  
   465  				q = obj.Appendp(q, newprog)
   466  				q.As = add
   467  				q.From.Type = obj.TYPE_CONST
   468  				q.From.Offset = ctxt.Arch.FixedFrameSize
   469  				q.Reg = REGSP
   470  				q.To.Type = obj.TYPE_REG
   471  				q.To.Reg = REG_R24
   472  
   473  				q = obj.Appendp(q, newprog)
   474  				q.As = mov
   475  				q.From.Type = obj.TYPE_REG
   476  				q.From.Reg = REG_R24
   477  				q.To.Type = obj.TYPE_MEM
   478  				q.To.Reg = REG_R20
   479  				q.To.Offset = 0 // Panic.argp
   480  
   481  				q = obj.Appendp(q, newprog)
   482  
   483  				q.As = obj.ANOP
   484  				p1.To.SetTarget(q)
   485  				p2.To.SetTarget(q)
   486  			}
   487  
   488  		case ARET:
   489  			if p.From.Type == obj.TYPE_CONST {
   490  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   491  				break
   492  			}
   493  
   494  			retSym := p.To.Sym
   495  			p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
   496  			p.To.Sym = nil
   497  
   498  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   499  				if autosize == 0 {
   500  					p.As = AJMP
   501  					p.From = obj.Addr{}
   502  					if retSym != nil { // retjmp
   503  						p.To.Type = obj.TYPE_BRANCH
   504  						p.To.Name = obj.NAME_EXTERN
   505  						p.To.Sym = retSym
   506  					} else {
   507  						p.To.Type = obj.TYPE_MEM
   508  						p.To.Reg = REGLINK
   509  						p.To.Offset = 0
   510  					}
   511  					p.Mark |= BRANCH
   512  					break
   513  				}
   514  
   515  				p.As = add
   516  				p.From.Type = obj.TYPE_CONST
   517  				p.From.Offset = int64(autosize)
   518  				p.To.Type = obj.TYPE_REG
   519  				p.To.Reg = REGSP
   520  				p.Spadj = -autosize
   521  
   522  				q = c.newprog()
   523  				q.As = AJMP
   524  				q.Pos = p.Pos
   525  				if retSym != nil { // retjmp
   526  					q.To.Type = obj.TYPE_BRANCH
   527  					q.To.Name = obj.NAME_EXTERN
   528  					q.To.Sym = retSym
   529  				} else {
   530  					q.To.Type = obj.TYPE_MEM
   531  					q.To.Offset = 0
   532  					q.To.Reg = REGLINK
   533  				}
   534  				q.Mark |= BRANCH
   535  				q.Spadj = +autosize
   536  
   537  				q.Link = p.Link
   538  				p.Link = q
   539  				break
   540  			}
   541  
   542  			p.As = mov
   543  			p.From.Type = obj.TYPE_MEM
   544  			p.From.Offset = 0
   545  			p.From.Reg = REGSP
   546  			p.To.Type = obj.TYPE_REG
   547  			p.To.Reg = REGLINK
   548  
   549  			if autosize != 0 {
   550  				q = c.newprog()
   551  				q.As = add
   552  				q.Pos = p.Pos
   553  				q.From.Type = obj.TYPE_CONST
   554  				q.From.Offset = int64(autosize)
   555  				q.To.Type = obj.TYPE_REG
   556  				q.To.Reg = REGSP
   557  				q.Spadj = -autosize
   558  
   559  				q.Link = p.Link
   560  				p.Link = q
   561  			}
   562  
   563  			q1 = c.newprog()
   564  			q1.As = AJMP
   565  			q1.Pos = p.Pos
   566  			if retSym != nil { // retjmp
   567  				q1.To.Type = obj.TYPE_BRANCH
   568  				q1.To.Name = obj.NAME_EXTERN
   569  				q1.To.Sym = retSym
   570  			} else {
   571  				q1.To.Type = obj.TYPE_MEM
   572  				q1.To.Offset = 0
   573  				q1.To.Reg = REGLINK
   574  			}
   575  			q1.Mark |= BRANCH
   576  			q1.Spadj = +autosize
   577  
   578  			q1.Link = q.Link
   579  			q.Link = q1
   580  
   581  		case AADD,
   582  			AADDU,
   583  			AADDV,
   584  			AADDVU:
   585  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   586  				p.Spadj = int32(-p.From.Offset)
   587  			}
   588  
   589  		case obj.AGETCALLERPC:
   590  			if cursym.Leaf() {
   591  				// MOV LR, Rd
   592  				p.As = mov
   593  				p.From.Type = obj.TYPE_REG
   594  				p.From.Reg = REGLINK
   595  			} else {
   596  				// MOV (RSP), Rd
   597  				p.As = mov
   598  				p.From.Type = obj.TYPE_MEM
   599  				p.From.Reg = REGSP
   600  			}
   601  		}
   602  
   603  		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
   604  			f := c.cursym.Func()
   605  			if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
   606  				c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
   607  				if ctxt.Debugvlog || !ctxt.IsAsm {
   608  					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
   609  					if !ctxt.IsAsm {
   610  						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
   611  						ctxt.DiagFlush()
   612  						log.Fatalf("bad SPWRITE")
   613  					}
   614  				}
   615  			}
   616  		}
   617  	}
   618  }
   619  
   620  func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   621  	var mov, add obj.As
   622  
   623  	add = AADDV
   624  	mov = AMOVV
   625  	if c.ctxt.Flag_maymorestack != "" {
   626  		// Save LR and REGCTXT.
   627  		frameSize := 2 * c.ctxt.Arch.PtrSize
   628  
   629  		p = c.ctxt.StartUnsafePoint(p, c.newprog)
   630  
   631  		// Spill Arguments. This has to happen before we open
   632  		// any more frame space.
   633  		p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
   634  
   635  		// MOV	REGLINK, -8/-16(SP)
   636  		p = obj.Appendp(p, c.newprog)
   637  		p.As = mov
   638  		p.From.Type = obj.TYPE_REG
   639  		p.From.Reg = REGLINK
   640  		p.To.Type = obj.TYPE_MEM
   641  		p.To.Offset = int64(-frameSize)
   642  		p.To.Reg = REGSP
   643  
   644  		// MOV	REGCTXT, -4/-8(SP)
   645  		p = obj.Appendp(p, c.newprog)
   646  		p.As = mov
   647  		p.From.Type = obj.TYPE_REG
   648  		p.From.Reg = REGCTXT
   649  		p.To.Type = obj.TYPE_MEM
   650  		p.To.Offset = -int64(c.ctxt.Arch.PtrSize)
   651  		p.To.Reg = REGSP
   652  
   653  		// ADD	$-8/$-16, SP
   654  		p = obj.Appendp(p, c.newprog)
   655  		p.As = add
   656  		p.From.Type = obj.TYPE_CONST
   657  		p.From.Offset = int64(-frameSize)
   658  		p.To.Type = obj.TYPE_REG
   659  		p.To.Reg = REGSP
   660  		p.Spadj = int32(frameSize)
   661  
   662  		// JAL	maymorestack
   663  		p = obj.Appendp(p, c.newprog)
   664  		p.As = AJAL
   665  		p.To.Type = obj.TYPE_BRANCH
   666  		// See ../x86/obj6.go
   667  		p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
   668  		p.Mark |= BRANCH
   669  
   670  		// Restore LR and REGCTXT.
   671  
   672  		// MOV	0(SP), REGLINK
   673  		p = obj.Appendp(p, c.newprog)
   674  		p.As = mov
   675  		p.From.Type = obj.TYPE_MEM
   676  		p.From.Offset = 0
   677  		p.From.Reg = REGSP
   678  		p.To.Type = obj.TYPE_REG
   679  		p.To.Reg = REGLINK
   680  
   681  		// MOV	4/8(SP), REGCTXT
   682  		p = obj.Appendp(p, c.newprog)
   683  		p.As = mov
   684  		p.From.Type = obj.TYPE_MEM
   685  		p.From.Offset = int64(c.ctxt.Arch.PtrSize)
   686  		p.From.Reg = REGSP
   687  		p.To.Type = obj.TYPE_REG
   688  		p.To.Reg = REGCTXT
   689  
   690  		// ADD	$8/$16, SP
   691  		p = obj.Appendp(p, c.newprog)
   692  		p.As = add
   693  		p.From.Type = obj.TYPE_CONST
   694  		p.From.Offset = int64(frameSize)
   695  		p.To.Type = obj.TYPE_REG
   696  		p.To.Reg = REGSP
   697  		p.Spadj = int32(-frameSize)
   698  
   699  		// Unspill arguments
   700  		p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
   701  		p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
   702  	}
   703  
   704  	// Jump back to here after morestack returns.
   705  	startPred := p
   706  
   707  	// MOV	g_stackguard(g), R20
   708  	p = obj.Appendp(p, c.newprog)
   709  
   710  	p.As = mov
   711  	p.From.Type = obj.TYPE_MEM
   712  	p.From.Reg = REGG
   713  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   714  	if c.cursym.CFunc() {
   715  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   716  	}
   717  	p.To.Type = obj.TYPE_REG
   718  	p.To.Reg = REG_R20
   719  
   720  	// Mark the stack bound check and morestack call async nonpreemptible.
   721  	// If we get preempted here, when resumed the preemption request is
   722  	// cleared, but we'll still call morestack, which will double the stack
   723  	// unnecessarily. See issue #35470.
   724  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
   725  
   726  	var q *obj.Prog
   727  	if framesize <= abi.StackSmall {
   728  		// small stack: SP < stackguard
   729  		//	SGTU	SP, stackguard, R20
   730  		p = obj.Appendp(p, c.newprog)
   731  
   732  		p.As = ASGTU
   733  		p.From.Type = obj.TYPE_REG
   734  		p.From.Reg = REGSP
   735  		p.Reg = REG_R20
   736  		p.To.Type = obj.TYPE_REG
   737  		p.To.Reg = REG_R20
   738  	} else {
   739  		// large stack: SP-framesize < stackguard-StackSmall
   740  		offset := int64(framesize) - abi.StackSmall
   741  		if framesize > abi.StackBig {
   742  			// Such a large stack we need to protect against underflow.
   743  			// The runtime guarantees SP > objabi.StackBig, but
   744  			// framesize is large enough that SP-framesize may
   745  			// underflow, causing a direct comparison with the
   746  			// stack guard to incorrectly succeed. We explicitly
   747  			// guard against underflow.
   748  			//
   749  			//      SGTU    $(framesize-StackSmall), SP, R24
   750  			//      BNE     R24, label-of-call-to-morestack
   751  
   752  			p = obj.Appendp(p, c.newprog)
   753  			p.As = ASGTU
   754  			p.From.Type = obj.TYPE_CONST
   755  			p.From.Offset = offset
   756  			p.Reg = REGSP
   757  			p.To.Type = obj.TYPE_REG
   758  			p.To.Reg = REG_R24
   759  
   760  			p = obj.Appendp(p, c.newprog)
   761  			q = p
   762  			p.As = ABNE
   763  			p.From.Type = obj.TYPE_REG
   764  			p.From.Reg = REG_R24
   765  			p.To.Type = obj.TYPE_BRANCH
   766  			p.Mark |= BRANCH
   767  		}
   768  
   769  		p = obj.Appendp(p, c.newprog)
   770  
   771  		p.As = add
   772  		p.From.Type = obj.TYPE_CONST
   773  		p.From.Offset = -offset
   774  		p.Reg = REGSP
   775  		p.To.Type = obj.TYPE_REG
   776  		p.To.Reg = REG_R24
   777  
   778  		p = obj.Appendp(p, c.newprog)
   779  		p.As = ASGTU
   780  		p.From.Type = obj.TYPE_REG
   781  		p.From.Reg = REG_R24
   782  		p.Reg = REG_R20
   783  		p.To.Type = obj.TYPE_REG
   784  		p.To.Reg = REG_R20
   785  	}
   786  
   787  	// q1: BEQ	R20, morestack
   788  	p = obj.Appendp(p, c.newprog)
   789  	q1 := p
   790  
   791  	p.As = ABEQ
   792  	p.From.Type = obj.TYPE_REG
   793  	p.From.Reg = REG_R20
   794  	p.To.Type = obj.TYPE_BRANCH
   795  	p.Mark |= BRANCH
   796  
   797  	end := c.ctxt.EndUnsafePoint(p, c.newprog, -1)
   798  
   799  	var last *obj.Prog
   800  	for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
   801  	}
   802  
   803  	// Now we are at the end of the function, but logically
   804  	// we are still in function prologue. We need to fix the
   805  	// SP data and PCDATA.
   806  	spfix := obj.Appendp(last, c.newprog)
   807  	spfix.As = obj.ANOP
   808  	spfix.Spadj = -framesize
   809  
   810  	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
   811  	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
   812  
   813  	if q != nil {
   814  		q.To.SetTarget(pcdata)
   815  	}
   816  	q1.To.SetTarget(pcdata)
   817  
   818  	p = c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
   819  
   820  	// MOV  LINK, R31
   821  	p = obj.Appendp(p, c.newprog)
   822  	p.As = mov
   823  	p.From.Type = obj.TYPE_REG
   824  	p.From.Reg = REGLINK
   825  	p.To.Type = obj.TYPE_REG
   826  	p.To.Reg = REG_R31
   827  	if q != nil {
   828  		q.To.SetTarget(p)
   829  		p.Mark |= LABEL
   830  	}
   831  
   832  	// JAL runtime.morestack(SB)
   833  	call := obj.Appendp(p, c.newprog)
   834  	call.As = AJAL
   835  	call.To.Type = obj.TYPE_BRANCH
   836  
   837  	if c.cursym.CFunc() {
   838  		call.To.Sym = c.ctxt.Lookup("runtime.morestackc")
   839  	} else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
   840  		call.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
   841  	} else {
   842  		call.To.Sym = c.ctxt.Lookup("runtime.morestack")
   843  	}
   844  	call.Mark |= BRANCH
   845  
   846  	// The instructions which unspill regs should be preemptible.
   847  	pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
   848  	unspill := c.cursym.Func().UnspillRegisterArgs(pcdata, c.newprog)
   849  
   850  	// JMP start
   851  	jmp := obj.Appendp(unspill, c.newprog)
   852  	jmp.As = AJMP
   853  	jmp.To.Type = obj.TYPE_BRANCH
   854  	jmp.To.SetTarget(startPred.Link)
   855  	jmp.Spadj = +framesize
   856  
   857  	return end
   858  }
   859  
   860  func (c *ctxt0) addnop(p *obj.Prog) {
   861  	q := c.newprog()
   862  	q.As = ANOOP
   863  	q.Pos = p.Pos
   864  	q.Link = p.Link
   865  	p.Link = q
   866  }
   867  
   868  var Linkloong64 = obj.LinkArch{
   869  	Arch:           sys.ArchLoong64,
   870  	Init:           buildop,
   871  	Preprocess:     preprocess,
   872  	Assemble:       span0,
   873  	Progedit:       progedit,
   874  	DWARFRegisters: LOONG64DWARFRegisters,
   875  }
   876  

View as plain text