blob: a2f16f2e2a643557de2d08510a3d137972591035 [file] [log] [blame]
// 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/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)
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)
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.Pcond
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" {
// 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
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
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)
}
} 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.Pcond = q
p2.Pcond = 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.Pcond = 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.Pcond = p0.Link
// placeholder for q1's jump target
p = obj.Appendp(p, c.newprog)
p.As = obj.ANOP // zero-width place holder
q1.Pcond = 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,
}