diff options
Diffstat (limited to 'src/cmd/internal/obj/ppc64/obj9.go')
-rw-r--r-- | src/cmd/internal/obj/ppc64/obj9.go | 1284 |
1 files changed, 1284 insertions, 0 deletions
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go new file mode 100644 index 0000000..fddf552 --- /dev/null +++ b/src/cmd/internal/obj/ppc64/obj9.go @@ -0,0 +1,1284 @@ +// cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" + "cmd/internal/sys" +) + +func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { + p.From.Class = 0 + p.To.Class = 0 + + c := ctxt9{ctxt: ctxt, newprog: newprog} + + // Rewrite BR/BL to symbol as TYPE_BRANCH. + switch p.As { + case ABR, + ABL, + obj.ARET, + obj.ADUFFZERO, + obj.ADUFFCOPY: + if p.To.Sym != nil { + p.To.Type = obj.TYPE_BRANCH + } + } + + // Rewrite float constants to values stored in memory. + switch p.As { + case AFMOVS: + if p.From.Type == obj.TYPE_FCONST { + f32 := float32(p.From.Val.(float64)) + p.From.Type = obj.TYPE_MEM + p.From.Sym = ctxt.Float32Sym(f32) + p.From.Name = obj.NAME_EXTERN + p.From.Offset = 0 + } + + case AFMOVD: + if p.From.Type == obj.TYPE_FCONST { + f64 := p.From.Val.(float64) + // Constant not needed in memory for float +/- 0 + if f64 != 0 { + p.From.Type = obj.TYPE_MEM + p.From.Sym = ctxt.Float64Sym(f64) + p.From.Name = obj.NAME_EXTERN + p.From.Offset = 0 + } + } + + // Put >32-bit constants in memory and load them + case AMOVD: + 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 { + p.From.Type = obj.TYPE_MEM + p.From.Sym = ctxt.Int64Sym(p.From.Offset) + p.From.Name = obj.NAME_EXTERN + p.From.Offset = 0 + } + } + + // Rewrite SUB constants into ADD. + switch p.As { + case ASUBC: + if p.From.Type == obj.TYPE_CONST { + p.From.Offset = -p.From.Offset + p.As = AADDC + } + + case ASUBCCC: + if p.From.Type == obj.TYPE_CONST { + p.From.Offset = -p.From.Offset + p.As = AADDCCC + } + + case ASUB: + if p.From.Type == obj.TYPE_CONST { + p.From.Offset = -p.From.Offset + p.As = AADD + } + } + if c.ctxt.Headtype == objabi.Haix { + c.rewriteToUseTOC(p) + } else if c.ctxt.Flag_dynlink { + c.rewriteToUseGot(p) + } +} + +// Rewrite p, if necessary, to access a symbol using its TOC anchor. +// This code is for AIX only. +func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) { + if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { + return + } + + if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { + // ADUFFZERO/ADUFFCOPY is considered as an ABL except in dynamic + // link where it should be an indirect call. + if !c.ctxt.Flag_dynlink { + return + } + // ADUFFxxx $offset + // becomes + // MOVD runtime.duffxxx@TOC, R12 + // ADD $offset, R12 + // MOVD R12, LR + // BL (LR) + var sym *obj.LSym + if p.As == obj.ADUFFZERO { + sym = c.ctxt.Lookup("runtime.duffzero") + } else { + sym = c.ctxt.Lookup("runtime.duffcopy") + } + // Retrieve or create the TOC anchor. + symtoc := c.ctxt.LookupInit("TOC."+sym.Name, func(s *obj.LSym) { + s.Type = objabi.SDATA + s.Set(obj.AttrDuplicateOK, true) + s.Set(obj.AttrStatic, true) + c.ctxt.Data = append(c.ctxt.Data, s) + s.WriteAddr(c.ctxt, 0, 8, sym, 0) + }) + + offset := p.To.Offset + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_TOCREF + p.From.Sym = symtoc + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R12 + p.To.Name = obj.NAME_NONE + p.To.Offset = 0 + p.To.Sym = nil + p1 := obj.Appendp(p, c.newprog) + p1.As = AADD + p1.From.Type = obj.TYPE_CONST + p1.From.Offset = offset + p1.To.Type = obj.TYPE_REG + p1.To.Reg = REG_R12 + p2 := obj.Appendp(p1, c.newprog) + p2.As = AMOVD + p2.From.Type = obj.TYPE_REG + p2.From.Reg = REG_R12 + p2.To.Type = obj.TYPE_REG + p2.To.Reg = REG_LR + p3 := obj.Appendp(p2, c.newprog) + p3.As = obj.ACALL + p3.To.Type = obj.TYPE_REG + p3.To.Reg = REG_LR + } + + var source *obj.Addr + if p.From.Name == obj.NAME_EXTERN || p.From.Name == obj.NAME_STATIC { + if p.From.Type == obj.TYPE_ADDR { + if p.As == ADWORD { + // ADWORD $sym doesn't need TOC anchor + return + } + if p.As != AMOVD { + c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v", p) + return + } + if p.To.Type != obj.TYPE_REG { + c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v", p) + return + } + } else if p.From.Type != obj.TYPE_MEM { + c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p) + return + } + source = &p.From + + } else if p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC { + if p.To.Type != obj.TYPE_MEM { + c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p) + return + } + if source != nil { + c.ctxt.Diag("cannot handle symbols on both sides in %v", p) + return + } + source = &p.To + } else { + return + + } + + if source.Sym == nil { + c.ctxt.Diag("do not know how to handle nil symbol in %v", p) + return + } + + if source.Sym.Type == objabi.STLSBSS { + return + } + + // Retrieve or create the TOC anchor. + symtoc := c.ctxt.LookupInit("TOC."+source.Sym.Name, func(s *obj.LSym) { + s.Type = objabi.SDATA + s.Set(obj.AttrDuplicateOK, true) + s.Set(obj.AttrStatic, true) + c.ctxt.Data = append(c.ctxt.Data, s) + s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0) + }) + + if source.Type == obj.TYPE_ADDR { + // MOVD $sym, Rx becomes MOVD symtoc, Rx + // MOVD $sym+<off>, Rx becomes MOVD symtoc, Rx; ADD <off>, Rx + p.From.Type = obj.TYPE_MEM + p.From.Sym = symtoc + p.From.Name = obj.NAME_TOCREF + + if p.From.Offset != 0 { + q := obj.Appendp(p, c.newprog) + q.As = AADD + q.From.Type = obj.TYPE_CONST + q.From.Offset = p.From.Offset + p.From.Offset = 0 + q.To = p.To + } + return + + } + + // MOVx sym, Ry becomes MOVD symtoc, REGTMP; MOVx (REGTMP), Ry + // MOVx Ry, sym becomes MOVD symtoc, REGTMP; MOVx Ry, (REGTMP) + // An addition may be inserted between the two MOVs if there is an offset. + + q := obj.Appendp(p, c.newprog) + q.As = AMOVD + q.From.Type = obj.TYPE_MEM + q.From.Sym = symtoc + q.From.Name = obj.NAME_TOCREF + q.To.Type = obj.TYPE_REG + q.To.Reg = REGTMP + + q = obj.Appendp(q, c.newprog) + q.As = p.As + q.From = p.From + q.To = p.To + if p.From.Name != obj.NAME_NONE { + q.From.Type = obj.TYPE_MEM + q.From.Reg = REGTMP + q.From.Name = obj.NAME_NONE + q.From.Sym = nil + } else if p.To.Name != obj.NAME_NONE { + q.To.Type = obj.TYPE_MEM + q.To.Reg = REGTMP + q.To.Name = obj.NAME_NONE + q.To.Sym = nil + } else { + c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p) + } + + obj.Nopout(p) +} + +// Rewrite p, if necessary, to access global data via the global offset table. +func (c *ctxt9) rewriteToUseGot(p *obj.Prog) { + if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { + // ADUFFxxx $offset + // becomes + // MOVD runtime.duffxxx@GOT, R12 + // ADD $offset, R12 + // MOVD R12, LR + // BL (LR) + var sym *obj.LSym + if p.As == obj.ADUFFZERO { + sym = c.ctxt.Lookup("runtime.duffzero") + } else { + sym = c.ctxt.Lookup("runtime.duffcopy") + } + offset := p.To.Offset + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_GOTREF + p.From.Sym = sym + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R12 + p.To.Name = obj.NAME_NONE + p.To.Offset = 0 + p.To.Sym = nil + p1 := obj.Appendp(p, c.newprog) + p1.As = AADD + p1.From.Type = obj.TYPE_CONST + p1.From.Offset = offset + p1.To.Type = obj.TYPE_REG + p1.To.Reg = REG_R12 + p2 := obj.Appendp(p1, c.newprog) + p2.As = AMOVD + p2.From.Type = obj.TYPE_REG + p2.From.Reg = REG_R12 + p2.To.Type = obj.TYPE_REG + p2.To.Reg = REG_LR + p3 := obj.Appendp(p2, c.newprog) + p3.As = obj.ACALL + p3.To.Type = obj.TYPE_REG + p3.To.Reg = REG_LR + } + + // We only care about global data: NAME_EXTERN means a global + // symbol in the Go sense, and p.Sym.Local is true for a few + // internally defined symbols. + if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { + // MOVD $sym, Rx becomes MOVD sym@GOT, Rx + // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx + if p.As != AMOVD { + c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) + } + if p.To.Type != obj.TYPE_REG { + c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) + } + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_GOTREF + if p.From.Offset != 0 { + q := obj.Appendp(p, c.newprog) + q.As = AADD + q.From.Type = obj.TYPE_CONST + q.From.Offset = p.From.Offset + q.To = p.To + p.From.Offset = 0 + } + } + if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { + c.ctxt.Diag("don't know how to handle %v with -dynlink", p) + } + var source *obj.Addr + // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry + // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP) + // An addition may be inserted between the two MOVs if there is an offset. + if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { + if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { + c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) + } + source = &p.From + } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { + source = &p.To + } else { + return + } + if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { + return + } + if source.Sym.Type == objabi.STLSBSS { + return + } + if source.Type != obj.TYPE_MEM { + c.ctxt.Diag("don't know how to handle %v with -dynlink", p) + } + p1 := obj.Appendp(p, c.newprog) + p2 := obj.Appendp(p1, c.newprog) + + p1.As = AMOVD + p1.From.Type = obj.TYPE_MEM + p1.From.Sym = source.Sym + p1.From.Name = obj.NAME_GOTREF + p1.To.Type = obj.TYPE_REG + p1.To.Reg = REGTMP + + p2.As = p.As + p2.From = p.From + p2.To = p.To + if p.From.Name == obj.NAME_EXTERN { + p2.From.Reg = REGTMP + p2.From.Name = obj.NAME_NONE + p2.From.Sym = nil + } else if p.To.Name == obj.NAME_EXTERN { + p2.To.Reg = REGTMP + p2.To.Name = obj.NAME_NONE + p2.To.Sym = nil + } else { + return + } + obj.Nopout(p) +} + +func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { + // TODO(minux): add morestack short-cuts with small fixed frame-size. + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { + return + } + + c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog} + + p := c.cursym.Func().Text + textstksiz := p.To.Offset + if textstksiz == -8 { + // Compatibility hack. + p.From.Sym.Set(obj.AttrNoFrame, true) + textstksiz = 0 + } + if textstksiz%8 != 0 { + c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz) + } + if p.From.Sym.NoFrame() { + if textstksiz != 0 { + c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) + } + } + + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) + + /* + * find leaf subroutines + * expand RET + * expand BECOME pseudo + */ + + var q *obj.Prog + var q1 *obj.Prog + for p := c.cursym.Func().Text; p != nil; p = p.Link { + switch p.As { + /* too hard, just leave alone */ + case obj.ATEXT: + q = p + + p.Mark |= LABEL | LEAF | SYNC + if p.Link != nil { + p.Link.Mark |= LABEL + } + + case ANOR: + q = p + if p.To.Type == obj.TYPE_REG { + if p.To.Reg == REGZERO { + p.Mark |= LABEL | SYNC + } + } + + case ALWAR, + ALBAR, + ASTBCCC, + ASTWCCC, + AECIWX, + AECOWX, + AEIEIO, + AICBI, + AISYNC, + ATLBIE, + ATLBIEL, + ASLBIA, + ASLBIE, + ASLBMFEE, + ASLBMFEV, + ASLBMTE, + ADCBF, + ADCBI, + ADCBST, + ADCBT, + ADCBTST, + ADCBZ, + ASYNC, + ATLBSYNC, + APTESYNC, + ALWSYNC, + ATW, + AWORD, + ARFI, + ARFCI, + ARFID, + AHRFID: + q = p + p.Mark |= LABEL | SYNC + continue + + case AMOVW, AMOVWZ, AMOVD: + q = p + if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL { + p.Mark |= LABEL | SYNC + } + continue + + case AFABS, + AFABSCC, + AFADD, + AFADDCC, + AFCTIW, + AFCTIWCC, + AFCTIWZ, + AFCTIWZCC, + AFDIV, + AFDIVCC, + AFMADD, + AFMADDCC, + AFMOVD, + AFMOVDU, + /* case AFMOVDS: */ + AFMOVS, + AFMOVSU, + + /* case AFMOVSD: */ + AFMSUB, + AFMSUBCC, + AFMUL, + AFMULCC, + AFNABS, + AFNABSCC, + AFNEG, + AFNEGCC, + AFNMADD, + AFNMADDCC, + AFNMSUB, + AFNMSUBCC, + AFRSP, + AFRSPCC, + AFSUB, + AFSUBCC: + q = p + + p.Mark |= FLOAT + continue + + case ABL, + ABCL, + obj.ADUFFZERO, + obj.ADUFFCOPY: + c.cursym.Func().Text.Mark &^= LEAF + fallthrough + + case ABC, + ABEQ, + ABGE, + ABGT, + ABLE, + ABLT, + ABNE, + ABR, + ABVC, + ABVS: + p.Mark |= BRANCH + q = p + q1 = p.To.Target() + if q1 != nil { + // NOPs are not removed due to #40689. + + if q1.Mark&LEAF == 0 { + q1.Mark |= LABEL + } + } else { + p.Mark |= LABEL + } + q1 = p.Link + if q1 != nil { + q1.Mark |= LABEL + } + continue + + case AFCMPO, AFCMPU: + q = p + p.Mark |= FCMP | FLOAT + continue + + case obj.ARET: + q = p + if p.Link != nil { + p.Link.Mark |= LABEL + } + continue + + case obj.ANOP: + // NOPs are not removed due to + // #40689 + continue + + default: + q = p + continue + } + } + + autosize := int32(0) + var p1 *obj.Prog + var p2 *obj.Prog + for p := c.cursym.Func().Text; p != nil; p = p.Link { + o := p.As + switch o { + case obj.ATEXT: + autosize = int32(textstksiz) + + if p.Mark&LEAF != 0 && autosize == 0 { + // A leaf function with no locals has no frame. + p.From.Sym.Set(obj.AttrNoFrame, true) + } + + if !p.From.Sym.NoFrame() { + // If there is a stack frame at all, it includes + // space to save the LR. + autosize += int32(c.ctxt.FixedFrameSize()) + } + + if p.Mark&LEAF != 0 && autosize < objabi.StackSmall { + // A leaf function with a small stack can be marked + // NOSPLIT, avoiding a stack check. + p.From.Sym.Set(obj.AttrNoSplit, true) + } + + p.To.Offset = int64(autosize) + + q = p + + if c.ctxt.Flag_shared && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" { + // When compiling Go into PIC, all functions must start + // with instructions to load the TOC pointer into r2: + // + // addis r2, r12, .TOC.-func@ha + // addi r2, r2, .TOC.-func@l+4 + // + // We could probably skip this prologue in some situations + // but it's a bit subtle. However, it is both safe and + // necessary to leave the prologue off duffzero and + // duffcopy as we rely on being able to jump to a specific + // instruction offset for them. + // + // These are AWORDS because there is no (afaict) way to + // generate the addis instruction except as part of the + // load of a large constant, and in that case there is no + // way to use r12 as the source. + // + // Note that the same condition is tested in + // putelfsym in cmd/link/internal/ld/symtab.go + // where we set the st_other field to indicate + // the presence of these instructions. + q = obj.Appendp(q, c.newprog) + q.As = AWORD + q.Pos = p.Pos + q.From.Type = obj.TYPE_CONST + q.From.Offset = 0x3c4c0000 + q = obj.Appendp(q, c.newprog) + q.As = AWORD + q.Pos = p.Pos + q.From.Type = obj.TYPE_CONST + q.From.Offset = 0x38420000 + rel := obj.Addrel(c.cursym) + rel.Off = 0 + rel.Siz = 8 + rel.Sym = c.ctxt.Lookup(".TOC.") + rel.Type = objabi.R_ADDRPOWER_PCREL + } + + if !c.cursym.Func().Text.From.Sym.NoSplit() { + q = c.stacksplit(q, autosize) // emit split check + } + + // Special handling of the racecall thunk. Assume that its asm code will + // save the link register and update the stack, since that code is + // called directly from C/C++ and can't clobber REGTMP (R31). + if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" { + var prologueEnd *obj.Prog + // Save the link register and update the SP. MOVDU is used unless + // the frame size is too large. The link register must be saved + // even for non-empty leaf functions so that traceback works. + if autosize >= -BIG && autosize <= BIG { + // Use MOVDU to adjust R1 when saving R31, if autosize is small. + q = obj.Appendp(q, c.newprog) + q.As = AMOVD + q.Pos = p.Pos + q.From.Type = obj.TYPE_REG + q.From.Reg = REG_LR + q.To.Type = obj.TYPE_REG + q.To.Reg = REGTMP + + prologueEnd = q + + q = obj.Appendp(q, c.newprog) + q.As = AMOVDU + q.Pos = p.Pos + q.From.Type = obj.TYPE_REG + q.From.Reg = REGTMP + q.To.Type = obj.TYPE_MEM + q.To.Offset = int64(-autosize) + q.To.Reg = REGSP + q.Spadj = autosize + } else { + // Frame size is too large for a MOVDU instruction. + // Store link register before decrementing SP, so if a signal comes + // during the execution of the function prologue, the traceback + // code will not see a half-updated stack frame. + // This sequence is not async preemptible, as if we open a frame + // at the current SP, it will clobber the saved LR. + q = obj.Appendp(q, c.newprog) + q.As = AMOVD + q.Pos = p.Pos + q.From.Type = obj.TYPE_REG + q.From.Reg = REG_LR + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction + + q = c.ctxt.StartUnsafePoint(q, c.newprog) + + q = obj.Appendp(q, c.newprog) + q.As = AMOVD + q.Pos = p.Pos + q.From.Type = obj.TYPE_REG + q.From.Reg = REG_R29 + q.To.Type = obj.TYPE_MEM + q.To.Offset = int64(-autosize) + q.To.Reg = REGSP + + prologueEnd = q + + q = obj.Appendp(q, c.newprog) + q.As = AADD + q.Pos = p.Pos + q.From.Type = obj.TYPE_CONST + q.From.Offset = int64(-autosize) + q.To.Type = obj.TYPE_REG + q.To.Reg = REGSP + q.Spadj = +autosize + + q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) + } + prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd) + } else if c.cursym.Func().Text.Mark&LEAF == 0 { + // A very few functions that do not return to their caller + // (e.g. gogo) are not identified as leaves but still have + // no frame. + c.cursym.Func().Text.Mark |= LEAF + } + + if c.cursym.Func().Text.Mark&LEAF != 0 { + c.cursym.Set(obj.AttrLeaf, true) + break + } + + if c.ctxt.Flag_shared { + q = obj.Appendp(q, c.newprog) + q.As = AMOVD + q.Pos = p.Pos + q.From.Type = obj.TYPE_REG + q.From.Reg = REG_R2 + q.To.Type = obj.TYPE_MEM + q.To.Reg = REGSP + q.To.Offset = 24 + } + + if c.cursym.Func().Text.From.Sym.Wrapper() { + // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame + // + // MOVD g_panic(g), R3 + // CMP R0, R3 + // BEQ end + // MOVD panic_argp(R3), R4 + // ADD $(autosize+8), R1, R5 + // CMP R4, R5 + // BNE end + // ADD $8, R1, R6 + // MOVD R6, panic_argp(R3) + // end: + // NOP + // + // The NOP is needed to give the jumps somewhere to land. + // It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes. + + q = obj.Appendp(q, c.newprog) + + q.As = AMOVD + q.From.Type = obj.TYPE_MEM + q.From.Reg = REGG + q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_R3 + + q = obj.Appendp(q, c.newprog) + q.As = ACMP + q.From.Type = obj.TYPE_REG + q.From.Reg = REG_R0 + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_R3 + + q = obj.Appendp(q, c.newprog) + q.As = ABEQ + q.To.Type = obj.TYPE_BRANCH + p1 = q + + q = obj.Appendp(q, c.newprog) + q.As = AMOVD + q.From.Type = obj.TYPE_MEM + q.From.Reg = REG_R3 + q.From.Offset = 0 // Panic.argp + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_R4 + + q = obj.Appendp(q, c.newprog) + q.As = AADD + q.From.Type = obj.TYPE_CONST + q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize() + q.Reg = REGSP + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_R5 + + q = obj.Appendp(q, c.newprog) + q.As = ACMP + q.From.Type = obj.TYPE_REG + q.From.Reg = REG_R4 + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_R5 + + q = obj.Appendp(q, c.newprog) + q.As = ABNE + q.To.Type = obj.TYPE_BRANCH + p2 = q + + q = obj.Appendp(q, c.newprog) + q.As = AADD + q.From.Type = obj.TYPE_CONST + q.From.Offset = c.ctxt.FixedFrameSize() + q.Reg = REGSP + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_R6 + + q = obj.Appendp(q, c.newprog) + q.As = AMOVD + q.From.Type = obj.TYPE_REG + q.From.Reg = REG_R6 + q.To.Type = obj.TYPE_MEM + q.To.Reg = REG_R3 + q.To.Offset = 0 // Panic.argp + + q = obj.Appendp(q, c.newprog) + + q.As = obj.ANOP + p1.To.SetTarget(q) + p2.To.SetTarget(q) + } + + case obj.ARET: + if p.From.Type == obj.TYPE_CONST { + c.ctxt.Diag("using BECOME (%v) is not supported!", p) + break + } + + retTarget := p.To.Sym + + if c.cursym.Func().Text.Mark&LEAF != 0 { + if autosize == 0 || c.cursym.Name == "runtime.racecallbackthunk" { + p.As = ABR + p.From = obj.Addr{} + if retTarget == nil { + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_LR + } else { + p.To.Type = obj.TYPE_BRANCH + p.To.Sym = retTarget + } + p.Mark |= BRANCH + break + } + + p.As = AADD + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(autosize) + p.To.Type = obj.TYPE_REG + p.To.Reg = REGSP + p.Spadj = -autosize + + q = c.newprog() + q.As = ABR + q.Pos = p.Pos + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_LR + q.Mark |= BRANCH + q.Spadj = +autosize + + q.Link = p.Link + p.Link = q + break + } + + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Offset = 0 + p.From.Reg = REGSP + p.To.Type = obj.TYPE_REG + p.To.Reg = REGTMP + + q = c.newprog() + q.As = AMOVD + q.Pos = p.Pos + q.From.Type = obj.TYPE_REG + q.From.Reg = REGTMP + q.To.Type = obj.TYPE_REG + q.To.Reg = REG_LR + + q.Link = p.Link + p.Link = q + p = q + + if false { + // Debug bad returns + q = c.newprog() + + q.As = AMOVD + q.Pos = p.Pos + q.From.Type = obj.TYPE_MEM + q.From.Offset = 0 + q.From.Reg = REGTMP + q.To.Type = obj.TYPE_REG + q.To.Reg = REGTMP + + q.Link = p.Link + p.Link = q + p = q + } + prev := p + if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" { + q = c.newprog() + q.As = AADD + q.Pos = p.Pos + q.From.Type = obj.TYPE_CONST + q.From.Offset = int64(autosize) + q.To.Type = obj.TYPE_REG + q.To.Reg = REGSP + q.Spadj = -autosize + + q.Link = p.Link + prev.Link = q + prev = q + } + + q1 = c.newprog() + q1.As = ABR + q1.Pos = p.Pos + if retTarget == nil { + q1.To.Type = obj.TYPE_REG + q1.To.Reg = REG_LR + } else { + q1.To.Type = obj.TYPE_BRANCH + q1.To.Sym = retTarget + } + q1.Mark |= BRANCH + q1.Spadj = +autosize + + q1.Link = q.Link + prev.Link = q1 + case AADD: + if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { + p.Spadj = int32(-p.From.Offset) + } + case AMOVDU: + if p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { + p.Spadj = int32(-p.To.Offset) + } + if p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP { + p.Spadj = int32(-p.From.Offset) + } + case obj.AGETCALLERPC: + if cursym.Leaf() { + /* MOVD LR, Rd */ + p.As = AMOVD + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_LR + } else { + /* MOVD (RSP), Rd */ + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Reg = REGSP + } + } + } +} + +/* +// instruction scheduling + if(debug['Q'] == 0) + return; + + curtext = nil; + q = nil; // p - 1 + q1 = firstp; // top of block + o = 0; // count of instructions + for(p = firstp; p != nil; p = p1) { + p1 = p->link; + o++; + if(p->mark & NOSCHED){ + if(q1 != p){ + sched(q1, q); + } + for(; p != nil; p = p->link){ + if(!(p->mark & NOSCHED)) + break; + q = p; + } + p1 = p; + q1 = p; + o = 0; + continue; + } + if(p->mark & (LABEL|SYNC)) { + if(q1 != p) + sched(q1, q); + q1 = p; + o = 1; + } + if(p->mark & (BRANCH|SYNC)) { + sched(q1, p); + q1 = p1; + o = 0; + } + if(o >= NSCHED) { + sched(q1, p); + q1 = p1; + o = 0; + } + q = p; + } +*/ +func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { + p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode + + // MOVD g_stackguard(g), R3 + p = obj.Appendp(p, c.newprog) + + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Reg = REGG + p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 + if c.cursym.CFunc() { + p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 + } + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R3 + + // Mark the stack bound check and morestack call async nonpreemptible. + // If we get preempted here, when resumed the preemption request is + // cleared, but we'll still call morestack, which will double the stack + // unnecessarily. See issue #35470. + p = c.ctxt.StartUnsafePoint(p, c.newprog) + + var q *obj.Prog + if framesize <= objabi.StackSmall { + // small stack: SP < stackguard + // CMP stackguard, SP + p = obj.Appendp(p, c.newprog) + + p.As = ACMPU + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_R3 + p.To.Type = obj.TYPE_REG + p.To.Reg = REGSP + } else if framesize <= objabi.StackBig { + // large stack: SP-framesize < stackguard-StackSmall + // ADD $-(framesize-StackSmall), SP, R4 + // CMP stackguard, R4 + p = obj.Appendp(p, c.newprog) + + p.As = AADD + p.From.Type = obj.TYPE_CONST + p.From.Offset = -(int64(framesize) - objabi.StackSmall) + p.Reg = REGSP + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R4 + + p = obj.Appendp(p, c.newprog) + p.As = ACMPU + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_R3 + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R4 + } else { + // Such a large stack we need to protect against wraparound. + // If SP is close to zero: + // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) + // The +StackGuard on both sides is required to keep the left side positive: + // SP is allowed to be slightly below stackguard. See stack.h. + // + // Preemption sets stackguard to StackPreempt, a very large value. + // That breaks the math above, so we have to check for that explicitly. + // // stackguard is R3 + // CMP R3, $StackPreempt + // BEQ label-of-call-to-morestack + // ADD $StackGuard, SP, R4 + // SUB R3, R4 + // MOVD $(framesize+(StackGuard-StackSmall)), R31 + // CMPU R31, R4 + p = obj.Appendp(p, c.newprog) + + p.As = ACMP + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_R3 + p.To.Type = obj.TYPE_CONST + p.To.Offset = objabi.StackPreempt + + p = obj.Appendp(p, c.newprog) + q = p + p.As = ABEQ + p.To.Type = obj.TYPE_BRANCH + + p = obj.Appendp(p, c.newprog) + p.As = AADD + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(objabi.StackGuard) + p.Reg = REGSP + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R4 + + p = obj.Appendp(p, c.newprog) + p.As = ASUB + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_R3 + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R4 + + p = obj.Appendp(p, c.newprog) + p.As = AMOVD + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall + p.To.Type = obj.TYPE_REG + p.To.Reg = REGTMP + + p = obj.Appendp(p, c.newprog) + p.As = ACMPU + p.From.Type = obj.TYPE_REG + p.From.Reg = REGTMP + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R4 + } + + // q1: BLT done + p = obj.Appendp(p, c.newprog) + q1 := p + + p.As = ABLT + p.To.Type = obj.TYPE_BRANCH + + // MOVD LR, R5 + p = obj.Appendp(p, c.newprog) + + p.As = AMOVD + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_LR + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R5 + if q != nil { + q.To.SetTarget(p) + } + + p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog) + + var morestacksym *obj.LSym + if c.cursym.CFunc() { + morestacksym = c.ctxt.Lookup("runtime.morestackc") + } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { + morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt") + } else { + morestacksym = c.ctxt.Lookup("runtime.morestack") + } + + if c.ctxt.Flag_shared { + // In PPC64 PIC code, R2 is used as TOC pointer derived from R12 + // which is the address of function entry point when entering + // the function. We need to preserve R2 across call to morestack. + // Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in + // the caller's frame, but not used (0(SP) is caller's saved LR, + // 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2. + + // MOVD R12, 8(SP) + p = obj.Appendp(p, c.newprog) + p.As = AMOVD + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_R2 + p.To.Type = obj.TYPE_MEM + p.To.Reg = REGSP + p.To.Offset = 8 + } + + if c.ctxt.Flag_dynlink { + // Avoid calling morestack via a PLT when dynamically linking. The + // PLT stubs generated by the system linker on ppc64le when "std r2, + // 24(r1)" to save the TOC pointer in their callers stack + // frame. Unfortunately (and necessarily) morestack is called before + // the function that calls it sets up its frame and so the PLT ends + // up smashing the saved TOC pointer for its caller's caller. + // + // According to the ABI documentation there is a mechanism to avoid + // the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE + // relocation on the nop after the call to morestack) but at the time + // of writing it is not supported at all by gold and my attempt to + // use it with ld.bfd caused an internal linker error. So this hack + // seems preferable. + + // MOVD $runtime.morestack(SB), R12 + p = obj.Appendp(p, c.newprog) + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Sym = morestacksym + p.From.Name = obj.NAME_GOTREF + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R12 + + // MOVD R12, LR + p = obj.Appendp(p, c.newprog) + p.As = AMOVD + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_R12 + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_LR + + // BL LR + p = obj.Appendp(p, c.newprog) + p.As = obj.ACALL + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_LR + } else { + // BL runtime.morestack(SB) + p = obj.Appendp(p, c.newprog) + + p.As = ABL + p.To.Type = obj.TYPE_BRANCH + p.To.Sym = morestacksym + } + + if c.ctxt.Flag_shared { + // MOVD 8(SP), R2 + p = obj.Appendp(p, c.newprog) + p.As = AMOVD + p.From.Type = obj.TYPE_MEM + p.From.Reg = REGSP + p.From.Offset = 8 + p.To.Type = obj.TYPE_REG + p.To.Reg = REG_R2 + } + + p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) + + // BR start + p = obj.Appendp(p, c.newprog) + p.As = ABR + p.To.Type = obj.TYPE_BRANCH + p.To.SetTarget(p0.Link) + + // placeholder for q1's jump target + p = obj.Appendp(p, c.newprog) + + p.As = obj.ANOP // zero-width place holder + q1.To.SetTarget(p) + + return p +} + +var Linkppc64 = obj.LinkArch{ + Arch: sys.ArchPPC64, + Init: buildop, + Preprocess: preprocess, + Assemble: span9, + Progedit: progedit, + DWARFRegisters: PPC64DWARFRegisters, +} + +var Linkppc64le = obj.LinkArch{ + Arch: sys.ArchPPC64LE, + Init: buildop, + Preprocess: preprocess, + Assemble: span9, + Progedit: progedit, + DWARFRegisters: PPC64DWARFRegisters, +} |