// 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" "log" ) 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+, Rx becomes MOVD symtoc, Rx; ADD , 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+, Rx becomes MOVD sym@GOT, Rx; ADD , 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 } } if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 && p.As != ACMPU { f := c.cursym.Func() if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 { c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE if ctxt.Debugvlog || !ctxt.IsAsm { ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p) if !ctxt.IsAsm { ctxt.Diag("invalid auto-SPWRITE in non-assembly") ctxt.DiagFlush() log.Fatalf("bad SPWRITE") } } } } } } /* // 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 { // large stack: SP-framesize < stackguard-StackSmall offset := int64(framesize) - objabi.StackSmall if framesize > objabi.StackBig { // Such a large stack we need to protect against underflow. // The runtime guarantees SP > objabi.StackBig, but // framesize is large enough that SP-framesize may // underflow, causing a direct comparison with the // stack guard to incorrectly succeed. We explicitly // guard against underflow. // // CMPU SP, $(framesize-StackSmall) // BLT label-of-call-to-morestack if offset <= 0xffff { p = obj.Appendp(p, c.newprog) p.As = ACMPU p.From.Type = obj.TYPE_REG p.From.Reg = REGSP p.To.Type = obj.TYPE_CONST p.To.Offset = offset } else { // Constant is too big for CMPU. p = obj.Appendp(p, c.newprog) p.As = AMOVD p.From.Type = obj.TYPE_CONST p.From.Offset = offset 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 = REGSP p.To.Type = obj.TYPE_REG p.To.Reg = REG_R4 } p = obj.Appendp(p, c.newprog) q = p p.As = ABLT p.To.Type = obj.TYPE_BRANCH } // Check against the stack guard. We've ensured this won't underflow. // ADD $-(framesize-StackSmall), SP, R4 // CMPU stackguard, R4 p = obj.Appendp(p, c.newprog) p.As = AADD p.From.Type = obj.TYPE_CONST p.From.Offset = -offset 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 } // 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, }