...

Source file src/pkg/cmd/internal/obj/ppc64/obj9.go

     1	// cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
     2	//
     3	//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     4	//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     5	//	Portions Copyright © 1997-1999 Vita Nuova Limited
     6	//	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
     7	//	Portions Copyright © 2004,2006 Bruce Ellis
     8	//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
     9	//	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
    10	//	Portions Copyright © 2009 The Go Authors. All rights reserved.
    11	//
    12	// Permission is hereby granted, free of charge, to any person obtaining a copy
    13	// of this software and associated documentation files (the "Software"), to deal
    14	// in the Software without restriction, including without limitation the rights
    15	// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    16	// copies of the Software, and to permit persons to whom the Software is
    17	// furnished to do so, subject to the following conditions:
    18	//
    19	// The above copyright notice and this permission notice shall be included in
    20	// all copies or substantial portions of the Software.
    21	//
    22	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    23	// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    24	// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    25	// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    26	// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    27	// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    28	// THE SOFTWARE.
    29	
    30	package ppc64
    31	
    32	import (
    33		"cmd/internal/obj"
    34		"cmd/internal/objabi"
    35		"cmd/internal/sys"
    36	)
    37	
    38	func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    39		p.From.Class = 0
    40		p.To.Class = 0
    41	
    42		c := ctxt9{ctxt: ctxt, newprog: newprog}
    43	
    44		// Rewrite BR/BL to symbol as TYPE_BRANCH.
    45		switch p.As {
    46		case ABR,
    47			ABL,
    48			obj.ARET,
    49			obj.ADUFFZERO,
    50			obj.ADUFFCOPY:
    51			if p.To.Sym != nil {
    52				p.To.Type = obj.TYPE_BRANCH
    53			}
    54		}
    55	
    56		// Rewrite float constants to values stored in memory.
    57		switch p.As {
    58		case AFMOVS:
    59			if p.From.Type == obj.TYPE_FCONST {
    60				f32 := float32(p.From.Val.(float64))
    61				p.From.Type = obj.TYPE_MEM
    62				p.From.Sym = ctxt.Float32Sym(f32)
    63				p.From.Name = obj.NAME_EXTERN
    64				p.From.Offset = 0
    65			}
    66	
    67		case AFMOVD:
    68			if p.From.Type == obj.TYPE_FCONST {
    69				f64 := p.From.Val.(float64)
    70				// Constant not needed in memory for float +/- 0
    71				if f64 != 0 {
    72					p.From.Type = obj.TYPE_MEM
    73					p.From.Sym = ctxt.Float64Sym(f64)
    74					p.From.Name = obj.NAME_EXTERN
    75					p.From.Offset = 0
    76				}
    77			}
    78	
    79			// Put >32-bit constants in memory and load them
    80		case AMOVD:
    81			if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset {
    82				p.From.Type = obj.TYPE_MEM
    83				p.From.Sym = ctxt.Int64Sym(p.From.Offset)
    84				p.From.Name = obj.NAME_EXTERN
    85				p.From.Offset = 0
    86			}
    87		}
    88	
    89		// Rewrite SUB constants into ADD.
    90		switch p.As {
    91		case ASUBC:
    92			if p.From.Type == obj.TYPE_CONST {
    93				p.From.Offset = -p.From.Offset
    94				p.As = AADDC
    95			}
    96	
    97		case ASUBCCC:
    98			if p.From.Type == obj.TYPE_CONST {
    99				p.From.Offset = -p.From.Offset
   100				p.As = AADDCCC
   101			}
   102	
   103		case ASUB:
   104			if p.From.Type == obj.TYPE_CONST {
   105				p.From.Offset = -p.From.Offset
   106				p.As = AADD
   107			}
   108		}
   109		if c.ctxt.Flag_dynlink {
   110			c.rewriteToUseGot(p)
   111		} else if c.ctxt.Headtype == objabi.Haix {
   112			c.rewriteToUseTOC(p)
   113		}
   114	}
   115	
   116	// Rewrite p, if necessary, to access a symbol using its TOC anchor.
   117	// This code is for AIX only.
   118	func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) {
   119		if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   120			return
   121		}
   122	
   123		var source *obj.Addr
   124		if p.From.Name == obj.NAME_EXTERN || p.From.Name == obj.NAME_STATIC {
   125			if p.From.Type == obj.TYPE_ADDR {
   126				if p.As == ADWORD {
   127					// ADWORD $sym doesn't need TOC anchor
   128					return
   129				}
   130				if p.As != AMOVD {
   131					c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v", p)
   132					return
   133				}
   134				if p.To.Type != obj.TYPE_REG {
   135					c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v", p)
   136					return
   137				}
   138			} else if p.From.Type != obj.TYPE_MEM {
   139				c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
   140				return
   141			}
   142			source = &p.From
   143	
   144		} else if p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC {
   145			if p.To.Type != obj.TYPE_MEM {
   146				c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
   147				return
   148			}
   149			if source != nil {
   150				c.ctxt.Diag("cannot handle symbols on both sides in %v", p)
   151				return
   152			}
   153			source = &p.To
   154		} else {
   155			return
   156	
   157		}
   158	
   159		if source.Sym == nil {
   160			c.ctxt.Diag("do not know how to handle nil symbol in %v", p)
   161			return
   162		}
   163	
   164		if source.Sym.Type == objabi.STLSBSS {
   165			return
   166		}
   167	
   168		// Retrieve or create the TOC anchor.
   169		symtoc := c.ctxt.LookupInit("TOC."+source.Sym.Name, func(s *obj.LSym) {
   170			s.Type = objabi.SDATA
   171			s.Set(obj.AttrDuplicateOK, true)
   172			c.ctxt.Data = append(c.ctxt.Data, s)
   173			s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0)
   174		})
   175	
   176		if source.Type == obj.TYPE_ADDR {
   177			// MOVD $sym, Rx becomes MOVD symtoc, Rx
   178			// MOVD $sym+<off>, Rx becomes MOVD symtoc, Rx; ADD <off>, Rx
   179			p.From.Type = obj.TYPE_MEM
   180			p.From.Sym = symtoc
   181			p.From.Name = obj.NAME_TOCREF
   182	
   183			if p.From.Offset != 0 {
   184				q := obj.Appendp(p, c.newprog)
   185				q.As = AADD
   186				q.From.Type = obj.TYPE_CONST
   187				q.From.Offset = p.From.Offset
   188				p.From.Offset = 0
   189				q.To = p.To
   190			}
   191			return
   192	
   193		}
   194	
   195		// MOVx sym, Ry becomes MOVD symtoc, REGTMP; MOVx (REGTMP), Ry
   196		// MOVx Ry, sym becomes MOVD symtoc, REGTMP; MOVx Ry, (REGTMP)
   197		// An addition may be inserted between the two MOVs if there is an offset.
   198	
   199		q := obj.Appendp(p, c.newprog)
   200		q.As = AMOVD
   201		q.From.Type = obj.TYPE_MEM
   202		q.From.Sym = symtoc
   203		q.From.Name = obj.NAME_TOCREF
   204		q.To.Type = obj.TYPE_REG
   205		q.To.Reg = REGTMP
   206	
   207		q = obj.Appendp(q, c.newprog)
   208		q.As = p.As
   209		q.From = p.From
   210		q.To = p.To
   211		if p.From.Name != obj.NAME_NONE {
   212			q.From.Type = obj.TYPE_MEM
   213			q.From.Reg = REGTMP
   214			q.From.Name = obj.NAME_NONE
   215			q.From.Sym = nil
   216		} else if p.To.Name != obj.NAME_NONE {
   217			q.To.Type = obj.TYPE_MEM
   218			q.To.Reg = REGTMP
   219			q.To.Name = obj.NAME_NONE
   220			q.To.Sym = nil
   221		} else {
   222			c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p)
   223		}
   224	
   225		obj.Nopout(p)
   226	}
   227	
   228	// Rewrite p, if necessary, to access global data via the global offset table.
   229	func (c *ctxt9) rewriteToUseGot(p *obj.Prog) {
   230		if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   231			//     ADUFFxxx $offset
   232			// becomes
   233			//     MOVD runtime.duffxxx@GOT, R12
   234			//     ADD $offset, R12
   235			//     MOVD R12, CTR
   236			//     BL (CTR)
   237			var sym *obj.LSym
   238			if p.As == obj.ADUFFZERO {
   239				sym = c.ctxt.Lookup("runtime.duffzero")
   240			} else {
   241				sym = c.ctxt.Lookup("runtime.duffcopy")
   242			}
   243			offset := p.To.Offset
   244			p.As = AMOVD
   245			p.From.Type = obj.TYPE_MEM
   246			p.From.Name = obj.NAME_GOTREF
   247			p.From.Sym = sym
   248			p.To.Type = obj.TYPE_REG
   249			p.To.Reg = REG_R12
   250			p.To.Name = obj.NAME_NONE
   251			p.To.Offset = 0
   252			p.To.Sym = nil
   253			p1 := obj.Appendp(p, c.newprog)
   254			p1.As = AADD
   255			p1.From.Type = obj.TYPE_CONST
   256			p1.From.Offset = offset
   257			p1.To.Type = obj.TYPE_REG
   258			p1.To.Reg = REG_R12
   259			p2 := obj.Appendp(p1, c.newprog)
   260			p2.As = AMOVD
   261			p2.From.Type = obj.TYPE_REG
   262			p2.From.Reg = REG_R12
   263			p2.To.Type = obj.TYPE_REG
   264			p2.To.Reg = REG_CTR
   265			p3 := obj.Appendp(p2, c.newprog)
   266			p3.As = obj.ACALL
   267			p3.From.Type = obj.TYPE_REG
   268			p3.From.Reg = REG_R12
   269			p3.To.Type = obj.TYPE_REG
   270			p3.To.Reg = REG_CTR
   271		}
   272	
   273		// We only care about global data: NAME_EXTERN means a global
   274		// symbol in the Go sense, and p.Sym.Local is true for a few
   275		// internally defined symbols.
   276		if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   277			// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   278			// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   279			if p.As != AMOVD {
   280				c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   281			}
   282			if p.To.Type != obj.TYPE_REG {
   283				c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   284			}
   285			p.From.Type = obj.TYPE_MEM
   286			p.From.Name = obj.NAME_GOTREF
   287			if p.From.Offset != 0 {
   288				q := obj.Appendp(p, c.newprog)
   289				q.As = AADD
   290				q.From.Type = obj.TYPE_CONST
   291				q.From.Offset = p.From.Offset
   292				q.To = p.To
   293				p.From.Offset = 0
   294			}
   295		}
   296		if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   297			c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   298		}
   299		var source *obj.Addr
   300		// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   301		// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
   302		// An addition may be inserted between the two MOVs if there is an offset.
   303		if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   304			if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   305				c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   306			}
   307			source = &p.From
   308		} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   309			source = &p.To
   310		} else {
   311			return
   312		}
   313		if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   314			return
   315		}
   316		if source.Sym.Type == objabi.STLSBSS {
   317			return
   318		}
   319		if source.Type != obj.TYPE_MEM {
   320			c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   321		}
   322		p1 := obj.Appendp(p, c.newprog)
   323		p2 := obj.Appendp(p1, c.newprog)
   324	
   325		p1.As = AMOVD
   326		p1.From.Type = obj.TYPE_MEM
   327		p1.From.Sym = source.Sym
   328		p1.From.Name = obj.NAME_GOTREF
   329		p1.To.Type = obj.TYPE_REG
   330		p1.To.Reg = REGTMP
   331	
   332		p2.As = p.As
   333		p2.From = p.From
   334		p2.To = p.To
   335		if p.From.Name == obj.NAME_EXTERN {
   336			p2.From.Reg = REGTMP
   337			p2.From.Name = obj.NAME_NONE
   338			p2.From.Sym = nil
   339		} else if p.To.Name == obj.NAME_EXTERN {
   340			p2.To.Reg = REGTMP
   341			p2.To.Name = obj.NAME_NONE
   342			p2.To.Sym = nil
   343		} else {
   344			return
   345		}
   346		obj.Nopout(p)
   347	}
   348	
   349	func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   350		// TODO(minux): add morestack short-cuts with small fixed frame-size.
   351		if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   352			return
   353		}
   354	
   355		c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog}
   356	
   357		p := c.cursym.Func.Text
   358		textstksiz := p.To.Offset
   359		if textstksiz == -8 {
   360			// Compatibility hack.
   361			p.From.Sym.Set(obj.AttrNoFrame, true)
   362			textstksiz = 0
   363		}
   364		if textstksiz%8 != 0 {
   365			c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
   366		}
   367		if p.From.Sym.NoFrame() {
   368			if textstksiz != 0 {
   369				c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   370			}
   371		}
   372	
   373		c.cursym.Func.Args = p.To.Val.(int32)
   374		c.cursym.Func.Locals = int32(textstksiz)
   375	
   376		/*
   377		 * find leaf subroutines
   378		 * strip NOPs
   379		 * expand RET
   380		 * expand BECOME pseudo
   381		 */
   382	
   383		var q *obj.Prog
   384		var q1 *obj.Prog
   385		for p := c.cursym.Func.Text; p != nil; p = p.Link {
   386			switch p.As {
   387			/* too hard, just leave alone */
   388			case obj.ATEXT:
   389				q = p
   390	
   391				p.Mark |= LABEL | LEAF | SYNC
   392				if p.Link != nil {
   393					p.Link.Mark |= LABEL
   394				}
   395	
   396			case ANOR:
   397				q = p
   398				if p.To.Type == obj.TYPE_REG {
   399					if p.To.Reg == REGZERO {
   400						p.Mark |= LABEL | SYNC
   401					}
   402				}
   403	
   404			case ALWAR,
   405				ALBAR,
   406				ASTBCCC,
   407				ASTWCCC,
   408				AECIWX,
   409				AECOWX,
   410				AEIEIO,
   411				AICBI,
   412				AISYNC,
   413				ATLBIE,
   414				ATLBIEL,
   415				ASLBIA,
   416				ASLBIE,
   417				ASLBMFEE,
   418				ASLBMFEV,
   419				ASLBMTE,
   420				ADCBF,
   421				ADCBI,
   422				ADCBST,
   423				ADCBT,
   424				ADCBTST,
   425				ADCBZ,
   426				ASYNC,
   427				ATLBSYNC,
   428				APTESYNC,
   429				ALWSYNC,
   430				ATW,
   431				AWORD,
   432				ARFI,
   433				ARFCI,
   434				ARFID,
   435				AHRFID:
   436				q = p
   437				p.Mark |= LABEL | SYNC
   438				continue
   439	
   440			case AMOVW, AMOVWZ, AMOVD:
   441				q = p
   442				if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
   443					p.Mark |= LABEL | SYNC
   444				}
   445				continue
   446	
   447			case AFABS,
   448				AFABSCC,
   449				AFADD,
   450				AFADDCC,
   451				AFCTIW,
   452				AFCTIWCC,
   453				AFCTIWZ,
   454				AFCTIWZCC,
   455				AFDIV,
   456				AFDIVCC,
   457				AFMADD,
   458				AFMADDCC,
   459				AFMOVD,
   460				AFMOVDU,
   461				/* case AFMOVDS: */
   462				AFMOVS,
   463				AFMOVSU,
   464	
   465				/* case AFMOVSD: */
   466				AFMSUB,
   467				AFMSUBCC,
   468				AFMUL,
   469				AFMULCC,
   470				AFNABS,
   471				AFNABSCC,
   472				AFNEG,
   473				AFNEGCC,
   474				AFNMADD,
   475				AFNMADDCC,
   476				AFNMSUB,
   477				AFNMSUBCC,
   478				AFRSP,
   479				AFRSPCC,
   480				AFSUB,
   481				AFSUBCC:
   482				q = p
   483	
   484				p.Mark |= FLOAT
   485				continue
   486	
   487			case ABL,
   488				ABCL,
   489				obj.ADUFFZERO,
   490				obj.ADUFFCOPY:
   491				c.cursym.Func.Text.Mark &^= LEAF
   492				fallthrough
   493	
   494			case ABC,
   495				ABEQ,
   496				ABGE,
   497				ABGT,
   498				ABLE,
   499				ABLT,
   500				ABNE,
   501				ABR,
   502				ABVC,
   503				ABVS:
   504				p.Mark |= BRANCH
   505				q = p
   506				q1 = p.Pcond
   507				if q1 != nil {
   508					for q1.As == obj.ANOP {
   509						q1 = q1.Link
   510						p.Pcond = q1
   511					}
   512	
   513					if q1.Mark&LEAF == 0 {
   514						q1.Mark |= LABEL
   515					}
   516				} else {
   517					p.Mark |= LABEL
   518				}
   519				q1 = p.Link
   520				if q1 != nil {
   521					q1.Mark |= LABEL
   522				}
   523				continue
   524	
   525			case AFCMPO, AFCMPU:
   526				q = p
   527				p.Mark |= FCMP | FLOAT
   528				continue
   529	
   530			case obj.ARET:
   531				q = p
   532				if p.Link != nil {
   533					p.Link.Mark |= LABEL
   534				}
   535				continue
   536	
   537			case obj.ANOP:
   538				q1 = p.Link
   539				q.Link = q1 /* q is non-nop */
   540				q1.Mark |= p.Mark
   541				continue
   542	
   543			default:
   544				q = p
   545				continue
   546			}
   547		}
   548	
   549		autosize := int32(0)
   550		var p1 *obj.Prog
   551		var p2 *obj.Prog
   552		for p := c.cursym.Func.Text; p != nil; p = p.Link {
   553			o := p.As
   554			switch o {
   555			case obj.ATEXT:
   556				autosize = int32(textstksiz)
   557	
   558				if p.Mark&LEAF != 0 && autosize == 0 {
   559					// A leaf function with no locals has no frame.
   560					p.From.Sym.Set(obj.AttrNoFrame, true)
   561				}
   562	
   563				if !p.From.Sym.NoFrame() {
   564					// If there is a stack frame at all, it includes
   565					// space to save the LR.
   566					autosize += int32(c.ctxt.FixedFrameSize())
   567				}
   568	
   569				if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
   570					// A leaf function with a small stack can be marked
   571					// NOSPLIT, avoiding a stack check.
   572					p.From.Sym.Set(obj.AttrNoSplit, true)
   573				}
   574	
   575				p.To.Offset = int64(autosize)
   576	
   577				q = p
   578	
   579				if c.ctxt.Flag_shared && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" {
   580					// When compiling Go into PIC, all functions must start
   581					// with instructions to load the TOC pointer into r2:
   582					//
   583					//	addis r2, r12, .TOC.-func@ha
   584					//	addi r2, r2, .TOC.-func@l+4
   585					//
   586					// We could probably skip this prologue in some situations
   587					// but it's a bit subtle. However, it is both safe and
   588					// necessary to leave the prologue off duffzero and
   589					// duffcopy as we rely on being able to jump to a specific
   590					// instruction offset for them.
   591					//
   592					// These are AWORDS because there is no (afaict) way to
   593					// generate the addis instruction except as part of the
   594					// load of a large constant, and in that case there is no
   595					// way to use r12 as the source.
   596					//
   597					// Note that the same condition is tested in
   598					// putelfsym in cmd/link/internal/ld/symtab.go
   599					// where we set the st_other field to indicate
   600					// the presence of these instructions.
   601					q = obj.Appendp(q, c.newprog)
   602					q.As = AWORD
   603					q.Pos = p.Pos
   604					q.From.Type = obj.TYPE_CONST
   605					q.From.Offset = 0x3c4c0000
   606					q = obj.Appendp(q, c.newprog)
   607					q.As = AWORD
   608					q.Pos = p.Pos
   609					q.From.Type = obj.TYPE_CONST
   610					q.From.Offset = 0x38420000
   611					rel := obj.Addrel(c.cursym)
   612					rel.Off = 0
   613					rel.Siz = 8
   614					rel.Sym = c.ctxt.Lookup(".TOC.")
   615					rel.Type = objabi.R_ADDRPOWER_PCREL
   616				}
   617	
   618				if !c.cursym.Func.Text.From.Sym.NoSplit() {
   619					q = c.stacksplit(q, autosize) // emit split check
   620				}
   621	
   622				// Special handling of the racecall thunk. Assume that its asm code will
   623				// save the link register and update the stack, since that code is
   624				// called directly from C/C++ and can't clobber REGTMP (R31).
   625				if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" {
   626					// Save the link register and update the SP.  MOVDU is used unless
   627					// the frame size is too large.  The link register must be saved
   628					// even for non-empty leaf functions so that traceback works.
   629					if autosize >= -BIG && autosize <= BIG {
   630						// Use MOVDU to adjust R1 when saving R31, if autosize is small.
   631						q = obj.Appendp(q, c.newprog)
   632						q.As = AMOVD
   633						q.Pos = p.Pos
   634						q.From.Type = obj.TYPE_REG
   635						q.From.Reg = REG_LR
   636						q.To.Type = obj.TYPE_REG
   637						q.To.Reg = REGTMP
   638	
   639						q = obj.Appendp(q, c.newprog)
   640						q.As = AMOVDU
   641						q.Pos = p.Pos
   642						q.From.Type = obj.TYPE_REG
   643						q.From.Reg = REGTMP
   644						q.To.Type = obj.TYPE_MEM
   645						q.To.Offset = int64(-autosize)
   646						q.To.Reg = REGSP
   647						q.Spadj = autosize
   648					} else {
   649						// Frame size is too large for a MOVDU instruction.
   650						// Store link register before decrementing SP, so if a signal comes
   651						// during the execution of the function prologue, the traceback
   652						// code will not see a half-updated stack frame.
   653						q = obj.Appendp(q, c.newprog)
   654						q.As = AMOVD
   655						q.Pos = p.Pos
   656						q.From.Type = obj.TYPE_REG
   657						q.From.Reg = REG_LR
   658						q.To.Type = obj.TYPE_REG
   659						q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
   660	
   661						q = obj.Appendp(q, c.newprog)
   662						q.As = AMOVD
   663						q.Pos = p.Pos
   664						q.From.Type = obj.TYPE_REG
   665						q.From.Reg = REG_R29
   666						q.To.Type = obj.TYPE_MEM
   667						q.To.Offset = int64(-autosize)
   668						q.To.Reg = REGSP
   669	
   670						q = obj.Appendp(q, c.newprog)
   671						q.As = AADD
   672						q.Pos = p.Pos
   673						q.From.Type = obj.TYPE_CONST
   674						q.From.Offset = int64(-autosize)
   675						q.To.Type = obj.TYPE_REG
   676						q.To.Reg = REGSP
   677						q.Spadj = +autosize
   678					}
   679				} else if c.cursym.Func.Text.Mark&LEAF == 0 {
   680					// A very few functions that do not return to their caller
   681					// (e.g. gogo) are not identified as leaves but still have
   682					// no frame.
   683					c.cursym.Func.Text.Mark |= LEAF
   684				}
   685	
   686				if c.cursym.Func.Text.Mark&LEAF != 0 {
   687					c.cursym.Set(obj.AttrLeaf, true)
   688					break
   689				}
   690	
   691				if c.ctxt.Flag_shared {
   692					q = obj.Appendp(q, c.newprog)
   693					q.As = AMOVD
   694					q.Pos = p.Pos
   695					q.From.Type = obj.TYPE_REG
   696					q.From.Reg = REG_R2
   697					q.To.Type = obj.TYPE_MEM
   698					q.To.Reg = REGSP
   699					q.To.Offset = 24
   700				}
   701	
   702				if c.cursym.Func.Text.From.Sym.Wrapper() {
   703					// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   704					//
   705					//	MOVD g_panic(g), R3
   706					//	CMP R0, R3
   707					//	BEQ end
   708					//	MOVD panic_argp(R3), R4
   709					//	ADD $(autosize+8), R1, R5
   710					//	CMP R4, R5
   711					//	BNE end
   712					//	ADD $8, R1, R6
   713					//	MOVD R6, panic_argp(R3)
   714					// end:
   715					//	NOP
   716					//
   717					// The NOP is needed to give the jumps somewhere to land.
   718					// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
   719	
   720					q = obj.Appendp(q, c.newprog)
   721	
   722					q.As = AMOVD
   723					q.From.Type = obj.TYPE_MEM
   724					q.From.Reg = REGG
   725					q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   726					q.To.Type = obj.TYPE_REG
   727					q.To.Reg = REG_R3
   728	
   729					q = obj.Appendp(q, c.newprog)
   730					q.As = ACMP
   731					q.From.Type = obj.TYPE_REG
   732					q.From.Reg = REG_R0
   733					q.To.Type = obj.TYPE_REG
   734					q.To.Reg = REG_R3
   735	
   736					q = obj.Appendp(q, c.newprog)
   737					q.As = ABEQ
   738					q.To.Type = obj.TYPE_BRANCH
   739					p1 = q
   740	
   741					q = obj.Appendp(q, c.newprog)
   742					q.As = AMOVD
   743					q.From.Type = obj.TYPE_MEM
   744					q.From.Reg = REG_R3
   745					q.From.Offset = 0 // Panic.argp
   746					q.To.Type = obj.TYPE_REG
   747					q.To.Reg = REG_R4
   748	
   749					q = obj.Appendp(q, c.newprog)
   750					q.As = AADD
   751					q.From.Type = obj.TYPE_CONST
   752					q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
   753					q.Reg = REGSP
   754					q.To.Type = obj.TYPE_REG
   755					q.To.Reg = REG_R5
   756	
   757					q = obj.Appendp(q, c.newprog)
   758					q.As = ACMP
   759					q.From.Type = obj.TYPE_REG
   760					q.From.Reg = REG_R4
   761					q.To.Type = obj.TYPE_REG
   762					q.To.Reg = REG_R5
   763	
   764					q = obj.Appendp(q, c.newprog)
   765					q.As = ABNE
   766					q.To.Type = obj.TYPE_BRANCH
   767					p2 = q
   768	
   769					q = obj.Appendp(q, c.newprog)
   770					q.As = AADD
   771					q.From.Type = obj.TYPE_CONST
   772					q.From.Offset = c.ctxt.FixedFrameSize()
   773					q.Reg = REGSP
   774					q.To.Type = obj.TYPE_REG
   775					q.To.Reg = REG_R6
   776	
   777					q = obj.Appendp(q, c.newprog)
   778					q.As = AMOVD
   779					q.From.Type = obj.TYPE_REG
   780					q.From.Reg = REG_R6
   781					q.To.Type = obj.TYPE_MEM
   782					q.To.Reg = REG_R3
   783					q.To.Offset = 0 // Panic.argp
   784	
   785					q = obj.Appendp(q, c.newprog)
   786	
   787					q.As = obj.ANOP
   788					p1.Pcond = q
   789					p2.Pcond = q
   790				}
   791	
   792			case obj.ARET:
   793				if p.From.Type == obj.TYPE_CONST {
   794					c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   795					break
   796				}
   797	
   798				retTarget := p.To.Sym
   799	
   800				if c.cursym.Func.Text.Mark&LEAF != 0 {
   801					if autosize == 0 || c.cursym.Name == "runtime.racecallbackthunk" {
   802						p.As = ABR
   803						p.From = obj.Addr{}
   804						if retTarget == nil {
   805							p.To.Type = obj.TYPE_REG
   806							p.To.Reg = REG_LR
   807						} else {
   808							p.To.Type = obj.TYPE_BRANCH
   809							p.To.Sym = retTarget
   810						}
   811						p.Mark |= BRANCH
   812						break
   813					}
   814	
   815					p.As = AADD
   816					p.From.Type = obj.TYPE_CONST
   817					p.From.Offset = int64(autosize)
   818					p.To.Type = obj.TYPE_REG
   819					p.To.Reg = REGSP
   820					p.Spadj = -autosize
   821	
   822					q = c.newprog()
   823					q.As = ABR
   824					q.Pos = p.Pos
   825					q.To.Type = obj.TYPE_REG
   826					q.To.Reg = REG_LR
   827					q.Mark |= BRANCH
   828					q.Spadj = +autosize
   829	
   830					q.Link = p.Link
   831					p.Link = q
   832					break
   833				}
   834	
   835				p.As = AMOVD
   836				p.From.Type = obj.TYPE_MEM
   837				p.From.Offset = 0
   838				p.From.Reg = REGSP
   839				p.To.Type = obj.TYPE_REG
   840				p.To.Reg = REGTMP
   841	
   842				q = c.newprog()
   843				q.As = AMOVD
   844				q.Pos = p.Pos
   845				q.From.Type = obj.TYPE_REG
   846				q.From.Reg = REGTMP
   847				q.To.Type = obj.TYPE_REG
   848				q.To.Reg = REG_LR
   849	
   850				q.Link = p.Link
   851				p.Link = q
   852				p = q
   853	
   854				if false {
   855					// Debug bad returns
   856					q = c.newprog()
   857	
   858					q.As = AMOVD
   859					q.Pos = p.Pos
   860					q.From.Type = obj.TYPE_MEM
   861					q.From.Offset = 0
   862					q.From.Reg = REGTMP
   863					q.To.Type = obj.TYPE_REG
   864					q.To.Reg = REGTMP
   865	
   866					q.Link = p.Link
   867					p.Link = q
   868					p = q
   869				}
   870				prev := p
   871				if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" {
   872					q = c.newprog()
   873					q.As = AADD
   874					q.Pos = p.Pos
   875					q.From.Type = obj.TYPE_CONST
   876					q.From.Offset = int64(autosize)
   877					q.To.Type = obj.TYPE_REG
   878					q.To.Reg = REGSP
   879					q.Spadj = -autosize
   880	
   881					q.Link = p.Link
   882					prev.Link = q
   883					prev = q
   884				}
   885	
   886				q1 = c.newprog()
   887				q1.As = ABR
   888				q1.Pos = p.Pos
   889				if retTarget == nil {
   890					q1.To.Type = obj.TYPE_REG
   891					q1.To.Reg = REG_LR
   892				} else {
   893					q1.To.Type = obj.TYPE_BRANCH
   894					q1.To.Sym = retTarget
   895				}
   896				q1.Mark |= BRANCH
   897				q1.Spadj = +autosize
   898	
   899				q1.Link = q.Link
   900				prev.Link = q1
   901			case AADD:
   902				if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   903					p.Spadj = int32(-p.From.Offset)
   904				}
   905			case obj.AGETCALLERPC:
   906				if cursym.Leaf() {
   907					/* MOVD LR, Rd */
   908					p.As = AMOVD
   909					p.From.Type = obj.TYPE_REG
   910					p.From.Reg = REG_LR
   911				} else {
   912					/* MOVD (RSP), Rd */
   913					p.As = AMOVD
   914					p.From.Type = obj.TYPE_MEM
   915					p.From.Reg = REGSP
   916				}
   917			}
   918		}
   919	}
   920	
   921	/*
   922	// instruction scheduling
   923		if(debug['Q'] == 0)
   924			return;
   925	
   926		curtext = nil;
   927		q = nil;	// p - 1
   928		q1 = firstp;	// top of block
   929		o = 0;		// count of instructions
   930		for(p = firstp; p != nil; p = p1) {
   931			p1 = p->link;
   932			o++;
   933			if(p->mark & NOSCHED){
   934				if(q1 != p){
   935					sched(q1, q);
   936				}
   937				for(; p != nil; p = p->link){
   938					if(!(p->mark & NOSCHED))
   939						break;
   940					q = p;
   941				}
   942				p1 = p;
   943				q1 = p;
   944				o = 0;
   945				continue;
   946			}
   947			if(p->mark & (LABEL|SYNC)) {
   948				if(q1 != p)
   949					sched(q1, q);
   950				q1 = p;
   951				o = 1;
   952			}
   953			if(p->mark & (BRANCH|SYNC)) {
   954				sched(q1, p);
   955				q1 = p1;
   956				o = 0;
   957			}
   958			if(o >= NSCHED) {
   959				sched(q1, p);
   960				q1 = p1;
   961				o = 0;
   962			}
   963			q = p;
   964		}
   965	*/
   966	func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   967		p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
   968	
   969		// MOVD	g_stackguard(g), R3
   970		p = obj.Appendp(p, c.newprog)
   971	
   972		p.As = AMOVD
   973		p.From.Type = obj.TYPE_MEM
   974		p.From.Reg = REGG
   975		p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   976		if c.cursym.CFunc() {
   977			p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   978		}
   979		p.To.Type = obj.TYPE_REG
   980		p.To.Reg = REG_R3
   981	
   982		var q *obj.Prog
   983		if framesize <= objabi.StackSmall {
   984			// small stack: SP < stackguard
   985			//	CMP	stackguard, SP
   986			p = obj.Appendp(p, c.newprog)
   987	
   988			p.As = ACMPU
   989			p.From.Type = obj.TYPE_REG
   990			p.From.Reg = REG_R3
   991			p.To.Type = obj.TYPE_REG
   992			p.To.Reg = REGSP
   993		} else if framesize <= objabi.StackBig {
   994			// large stack: SP-framesize < stackguard-StackSmall
   995			//	ADD $-(framesize-StackSmall), SP, R4
   996			//	CMP stackguard, R4
   997			p = obj.Appendp(p, c.newprog)
   998	
   999			p.As = AADD
  1000			p.From.Type = obj.TYPE_CONST
  1001			p.From.Offset = -(int64(framesize) - objabi.StackSmall)
  1002			p.Reg = REGSP
  1003			p.To.Type = obj.TYPE_REG
  1004			p.To.Reg = REG_R4
  1005	
  1006			p = obj.Appendp(p, c.newprog)
  1007			p.As = ACMPU
  1008			p.From.Type = obj.TYPE_REG
  1009			p.From.Reg = REG_R3
  1010			p.To.Type = obj.TYPE_REG
  1011			p.To.Reg = REG_R4
  1012		} else {
  1013			// Such a large stack we need to protect against wraparound.
  1014			// If SP is close to zero:
  1015			//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
  1016			// The +StackGuard on both sides is required to keep the left side positive:
  1017			// SP is allowed to be slightly below stackguard. See stack.h.
  1018			//
  1019			// Preemption sets stackguard to StackPreempt, a very large value.
  1020			// That breaks the math above, so we have to check for that explicitly.
  1021			//	// stackguard is R3
  1022			//	CMP	R3, $StackPreempt
  1023			//	BEQ	label-of-call-to-morestack
  1024			//	ADD	$StackGuard, SP, R4
  1025			//	SUB	R3, R4
  1026			//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
  1027			//	CMPU	R31, R4
  1028			p = obj.Appendp(p, c.newprog)
  1029	
  1030			p.As = ACMP
  1031			p.From.Type = obj.TYPE_REG
  1032			p.From.Reg = REG_R3
  1033			p.To.Type = obj.TYPE_CONST
  1034			p.To.Offset = objabi.StackPreempt
  1035	
  1036			p = obj.Appendp(p, c.newprog)
  1037			q = p
  1038			p.As = ABEQ
  1039			p.To.Type = obj.TYPE_BRANCH
  1040	
  1041			p = obj.Appendp(p, c.newprog)
  1042			p.As = AADD
  1043			p.From.Type = obj.TYPE_CONST
  1044			p.From.Offset = int64(objabi.StackGuard)
  1045			p.Reg = REGSP
  1046			p.To.Type = obj.TYPE_REG
  1047			p.To.Reg = REG_R4
  1048	
  1049			p = obj.Appendp(p, c.newprog)
  1050			p.As = ASUB
  1051			p.From.Type = obj.TYPE_REG
  1052			p.From.Reg = REG_R3
  1053			p.To.Type = obj.TYPE_REG
  1054			p.To.Reg = REG_R4
  1055	
  1056			p = obj.Appendp(p, c.newprog)
  1057			p.As = AMOVD
  1058			p.From.Type = obj.TYPE_CONST
  1059			p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
  1060			p.To.Type = obj.TYPE_REG
  1061			p.To.Reg = REGTMP
  1062	
  1063			p = obj.Appendp(p, c.newprog)
  1064			p.As = ACMPU
  1065			p.From.Type = obj.TYPE_REG
  1066			p.From.Reg = REGTMP
  1067			p.To.Type = obj.TYPE_REG
  1068			p.To.Reg = REG_R4
  1069		}
  1070	
  1071		// q1: BLT	done
  1072		p = obj.Appendp(p, c.newprog)
  1073		q1 := p
  1074	
  1075		p.As = ABLT
  1076		p.To.Type = obj.TYPE_BRANCH
  1077	
  1078		// MOVD	LR, R5
  1079		p = obj.Appendp(p, c.newprog)
  1080	
  1081		p.As = AMOVD
  1082		p.From.Type = obj.TYPE_REG
  1083		p.From.Reg = REG_LR
  1084		p.To.Type = obj.TYPE_REG
  1085		p.To.Reg = REG_R5
  1086		if q != nil {
  1087			q.Pcond = p
  1088		}
  1089	
  1090		p = c.ctxt.EmitEntryLiveness(c.cursym, p, c.newprog)
  1091	
  1092		var morestacksym *obj.LSym
  1093		if c.cursym.CFunc() {
  1094			morestacksym = c.ctxt.Lookup("runtime.morestackc")
  1095		} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
  1096			morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt")
  1097		} else {
  1098			morestacksym = c.ctxt.Lookup("runtime.morestack")
  1099		}
  1100	
  1101		if c.ctxt.Flag_shared {
  1102			// In PPC64 PIC code, R2 is used as TOC pointer derived from R12
  1103			// which is the address of function entry point when entering
  1104			// the function. We need to preserve R2 across call to morestack.
  1105			// Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
  1106			// the caller's frame, but not used (0(SP) is caller's saved LR,
  1107			// 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
  1108	
  1109			// MOVD R12, 8(SP)
  1110			p = obj.Appendp(p, c.newprog)
  1111			p.As = AMOVD
  1112			p.From.Type = obj.TYPE_REG
  1113			p.From.Reg = REG_R2
  1114			p.To.Type = obj.TYPE_MEM
  1115			p.To.Reg = REGSP
  1116			p.To.Offset = 8
  1117		}
  1118	
  1119		if c.ctxt.Flag_dynlink {
  1120			// Avoid calling morestack via a PLT when dynamically linking. The
  1121			// PLT stubs generated by the system linker on ppc64le when "std r2,
  1122			// 24(r1)" to save the TOC pointer in their callers stack
  1123			// frame. Unfortunately (and necessarily) morestack is called before
  1124			// the function that calls it sets up its frame and so the PLT ends
  1125			// up smashing the saved TOC pointer for its caller's caller.
  1126			//
  1127			// According to the ABI documentation there is a mechanism to avoid
  1128			// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
  1129			// relocation on the nop after the call to morestack) but at the time
  1130			// of writing it is not supported at all by gold and my attempt to
  1131			// use it with ld.bfd caused an internal linker error. So this hack
  1132			// seems preferable.
  1133	
  1134			// MOVD $runtime.morestack(SB), R12
  1135			p = obj.Appendp(p, c.newprog)
  1136			p.As = AMOVD
  1137			p.From.Type = obj.TYPE_MEM
  1138			p.From.Sym = morestacksym
  1139			p.From.Name = obj.NAME_GOTREF
  1140			p.To.Type = obj.TYPE_REG
  1141			p.To.Reg = REG_R12
  1142	
  1143			// MOVD R12, CTR
  1144			p = obj.Appendp(p, c.newprog)
  1145			p.As = AMOVD
  1146			p.From.Type = obj.TYPE_REG
  1147			p.From.Reg = REG_R12
  1148			p.To.Type = obj.TYPE_REG
  1149			p.To.Reg = REG_CTR
  1150	
  1151			// BL CTR
  1152			p = obj.Appendp(p, c.newprog)
  1153			p.As = obj.ACALL
  1154			p.From.Type = obj.TYPE_REG
  1155			p.From.Reg = REG_R12
  1156			p.To.Type = obj.TYPE_REG
  1157			p.To.Reg = REG_CTR
  1158		} else {
  1159			// BL	runtime.morestack(SB)
  1160			p = obj.Appendp(p, c.newprog)
  1161	
  1162			p.As = ABL
  1163			p.To.Type = obj.TYPE_BRANCH
  1164			p.To.Sym = morestacksym
  1165		}
  1166	
  1167		if c.ctxt.Flag_shared {
  1168			// MOVD 8(SP), R2
  1169			p = obj.Appendp(p, c.newprog)
  1170			p.As = AMOVD
  1171			p.From.Type = obj.TYPE_MEM
  1172			p.From.Reg = REGSP
  1173			p.From.Offset = 8
  1174			p.To.Type = obj.TYPE_REG
  1175			p.To.Reg = REG_R2
  1176		}
  1177	
  1178		// BR	start
  1179		p = obj.Appendp(p, c.newprog)
  1180		p.As = ABR
  1181		p.To.Type = obj.TYPE_BRANCH
  1182		p.Pcond = p0.Link
  1183	
  1184		// placeholder for q1's jump target
  1185		p = obj.Appendp(p, c.newprog)
  1186	
  1187		p.As = obj.ANOP // zero-width place holder
  1188		q1.Pcond = p
  1189	
  1190		return p
  1191	}
  1192	
  1193	var Linkppc64 = obj.LinkArch{
  1194		Arch:           sys.ArchPPC64,
  1195		Init:           buildop,
  1196		Preprocess:     preprocess,
  1197		Assemble:       span9,
  1198		Progedit:       progedit,
  1199		DWARFRegisters: PPC64DWARFRegisters,
  1200	}
  1201	
  1202	var Linkppc64le = obj.LinkArch{
  1203		Arch:           sys.ArchPPC64LE,
  1204		Init:           buildop,
  1205		Preprocess:     preprocess,
  1206		Assemble:       span9,
  1207		Progedit:       progedit,
  1208		DWARFRegisters: PPC64DWARFRegisters,
  1209	}
  1210	

View as plain text