mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
We currently use two fields to store the targets of branches. Some phases use p.To.Val, some use p.Pcond. Rewrite so that every branch instruction uses p.To.Val. p.From.Val is also used in rare instances. Introduce a Pool link for use by arm/arm64, instead of repurposing Pcond. This is a cleanup CL in preparation for some stack frame CLs. Change-Id: If8239177e4b1ea2bccd0608eb39553d23210d405 Reviewed-on: https://go-review.googlesource.com/c/go/+/251437 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
348 lines
9.7 KiB
Go
348 lines
9.7 KiB
Go
// Derived from Inferno utils/6c/txt.c
|
|
// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6c/txt.c
|
|
//
|
|
// 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-2007 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-2007 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 gc
|
|
|
|
import (
|
|
"cmd/compile/internal/ssa"
|
|
"cmd/internal/obj"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/src"
|
|
)
|
|
|
|
var sharedProgArray = new([10000]obj.Prog) // *T instead of T to work around issue 19839
|
|
|
|
// Progs accumulates Progs for a function and converts them into machine code.
|
|
type Progs struct {
|
|
Text *obj.Prog // ATEXT Prog for this function
|
|
next *obj.Prog // next Prog
|
|
pc int64 // virtual PC; count of Progs
|
|
pos src.XPos // position to use for new Progs
|
|
curfn *Node // fn these Progs are for
|
|
progcache []obj.Prog // local progcache
|
|
cacheidx int // first free element of progcache
|
|
|
|
nextLive LivenessIndex // liveness index for the next Prog
|
|
prevLive LivenessIndex // last emitted liveness index
|
|
}
|
|
|
|
// newProgs returns a new Progs for fn.
|
|
// worker indicates which of the backend workers will use the Progs.
|
|
func newProgs(fn *Node, worker int) *Progs {
|
|
pp := new(Progs)
|
|
if Ctxt.CanReuseProgs() {
|
|
sz := len(sharedProgArray) / nBackendWorkers
|
|
pp.progcache = sharedProgArray[sz*worker : sz*(worker+1)]
|
|
}
|
|
pp.curfn = fn
|
|
|
|
// prime the pump
|
|
pp.next = pp.NewProg()
|
|
pp.clearp(pp.next)
|
|
|
|
pp.pos = fn.Pos
|
|
pp.settext(fn)
|
|
// PCDATA tables implicitly start with index -1.
|
|
pp.prevLive = LivenessIndex{-1, -1, false}
|
|
if go115ReduceLiveness {
|
|
pp.nextLive = pp.prevLive
|
|
} else {
|
|
pp.nextLive = LivenessInvalid
|
|
}
|
|
return pp
|
|
}
|
|
|
|
func (pp *Progs) NewProg() *obj.Prog {
|
|
var p *obj.Prog
|
|
if pp.cacheidx < len(pp.progcache) {
|
|
p = &pp.progcache[pp.cacheidx]
|
|
pp.cacheidx++
|
|
} else {
|
|
p = new(obj.Prog)
|
|
}
|
|
p.Ctxt = Ctxt
|
|
return p
|
|
}
|
|
|
|
// Flush converts from pp to machine code.
|
|
func (pp *Progs) Flush() {
|
|
plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.curfn}
|
|
obj.Flushplist(Ctxt, plist, pp.NewProg, myimportpath)
|
|
}
|
|
|
|
// Free clears pp and any associated resources.
|
|
func (pp *Progs) Free() {
|
|
if Ctxt.CanReuseProgs() {
|
|
// Clear progs to enable GC and avoid abuse.
|
|
s := pp.progcache[:pp.cacheidx]
|
|
for i := range s {
|
|
s[i] = obj.Prog{}
|
|
}
|
|
}
|
|
// Clear pp to avoid abuse.
|
|
*pp = Progs{}
|
|
}
|
|
|
|
// Prog adds a Prog with instruction As to pp.
|
|
func (pp *Progs) Prog(as obj.As) *obj.Prog {
|
|
if pp.nextLive.StackMapValid() && pp.nextLive.stackMapIndex != pp.prevLive.stackMapIndex {
|
|
// Emit stack map index change.
|
|
idx := pp.nextLive.stackMapIndex
|
|
pp.prevLive.stackMapIndex = idx
|
|
p := pp.Prog(obj.APCDATA)
|
|
Addrconst(&p.From, objabi.PCDATA_StackMapIndex)
|
|
Addrconst(&p.To, int64(idx))
|
|
}
|
|
if !go115ReduceLiveness {
|
|
if pp.nextLive.isUnsafePoint {
|
|
// Unsafe points are encoded as a special value in the
|
|
// register map.
|
|
pp.nextLive.regMapIndex = objabi.PCDATA_RegMapUnsafe
|
|
}
|
|
if pp.nextLive.regMapIndex != pp.prevLive.regMapIndex {
|
|
// Emit register map index change.
|
|
idx := pp.nextLive.regMapIndex
|
|
pp.prevLive.regMapIndex = idx
|
|
p := pp.Prog(obj.APCDATA)
|
|
Addrconst(&p.From, objabi.PCDATA_RegMapIndex)
|
|
Addrconst(&p.To, int64(idx))
|
|
}
|
|
} else {
|
|
if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint {
|
|
// Emit unsafe-point marker.
|
|
pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint
|
|
p := pp.Prog(obj.APCDATA)
|
|
Addrconst(&p.From, objabi.PCDATA_UnsafePoint)
|
|
if pp.nextLive.isUnsafePoint {
|
|
Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe)
|
|
} else {
|
|
Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe)
|
|
}
|
|
}
|
|
}
|
|
|
|
p := pp.next
|
|
pp.next = pp.NewProg()
|
|
pp.clearp(pp.next)
|
|
p.Link = pp.next
|
|
|
|
if !pp.pos.IsKnown() && Debug['K'] != 0 {
|
|
Warn("prog: unknown position (line 0)")
|
|
}
|
|
|
|
p.As = as
|
|
p.Pos = pp.pos
|
|
if pp.pos.IsStmt() == src.PosIsStmt {
|
|
// Clear IsStmt for later Progs at this pos provided that as can be marked as a stmt
|
|
if ssa.LosesStmtMark(as) {
|
|
return p
|
|
}
|
|
pp.pos = pp.pos.WithNotStmt()
|
|
}
|
|
return p
|
|
}
|
|
|
|
func (pp *Progs) clearp(p *obj.Prog) {
|
|
obj.Nopout(p)
|
|
p.As = obj.AEND
|
|
p.Pc = pp.pc
|
|
pp.pc++
|
|
}
|
|
|
|
func (pp *Progs) Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
|
|
q := pp.NewProg()
|
|
pp.clearp(q)
|
|
q.As = as
|
|
q.Pos = p.Pos
|
|
q.From.Type = ftype
|
|
q.From.Reg = freg
|
|
q.From.Offset = foffset
|
|
q.To.Type = ttype
|
|
q.To.Reg = treg
|
|
q.To.Offset = toffset
|
|
q.Link = p.Link
|
|
p.Link = q
|
|
return q
|
|
}
|
|
|
|
func (pp *Progs) settext(fn *Node) {
|
|
if pp.Text != nil {
|
|
Fatalf("Progs.settext called twice")
|
|
}
|
|
ptxt := pp.Prog(obj.ATEXT)
|
|
pp.Text = ptxt
|
|
|
|
fn.Func.lsym.Func.Text = ptxt
|
|
ptxt.From.Type = obj.TYPE_MEM
|
|
ptxt.From.Name = obj.NAME_EXTERN
|
|
ptxt.From.Sym = fn.Func.lsym
|
|
}
|
|
|
|
// initLSym defines f's obj.LSym and initializes it based on the
|
|
// properties of f. This includes setting the symbol flags and ABI and
|
|
// creating and initializing related DWARF symbols.
|
|
//
|
|
// initLSym must be called exactly once per function and must be
|
|
// called for both functions with bodies and functions without bodies.
|
|
func (f *Func) initLSym(hasBody bool) {
|
|
if f.lsym != nil {
|
|
Fatalf("Func.initLSym called twice")
|
|
}
|
|
|
|
if nam := f.Nname; !nam.isBlank() {
|
|
f.lsym = nam.Sym.Linksym()
|
|
if f.Pragma&Systemstack != 0 {
|
|
f.lsym.Set(obj.AttrCFunc, true)
|
|
}
|
|
|
|
var aliasABI obj.ABI
|
|
needABIAlias := false
|
|
defABI, hasDefABI := symabiDefs[f.lsym.Name]
|
|
if hasDefABI && defABI == obj.ABI0 {
|
|
// Symbol is defined as ABI0. Create an
|
|
// Internal -> ABI0 wrapper.
|
|
f.lsym.SetABI(obj.ABI0)
|
|
needABIAlias, aliasABI = true, obj.ABIInternal
|
|
} else {
|
|
// No ABI override. Check that the symbol is
|
|
// using the expected ABI.
|
|
want := obj.ABIInternal
|
|
if f.lsym.ABI() != want {
|
|
Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.lsym.Name, f.lsym.ABI(), want)
|
|
}
|
|
}
|
|
|
|
isLinknameExported := nam.Sym.Linkname != "" && (hasBody || hasDefABI)
|
|
if abi, ok := symabiRefs[f.lsym.Name]; (ok && abi == obj.ABI0) || isLinknameExported {
|
|
// Either 1) this symbol is definitely
|
|
// referenced as ABI0 from this package; or 2)
|
|
// this symbol is defined in this package but
|
|
// given a linkname, indicating that it may be
|
|
// referenced from another package. Create an
|
|
// ABI0 -> Internal wrapper so it can be
|
|
// called as ABI0. In case 2, it's important
|
|
// that we know it's defined in this package
|
|
// since other packages may "pull" symbols
|
|
// using linkname and we don't want to create
|
|
// duplicate ABI wrappers.
|
|
if f.lsym.ABI() != obj.ABI0 {
|
|
needABIAlias, aliasABI = true, obj.ABI0
|
|
}
|
|
}
|
|
|
|
if needABIAlias {
|
|
// These LSyms have the same name as the
|
|
// native function, so we create them directly
|
|
// rather than looking them up. The uniqueness
|
|
// of f.lsym ensures uniqueness of asym.
|
|
asym := &obj.LSym{
|
|
Name: f.lsym.Name,
|
|
Type: objabi.SABIALIAS,
|
|
R: []obj.Reloc{{Sym: f.lsym}}, // 0 size, so "informational"
|
|
}
|
|
asym.SetABI(aliasABI)
|
|
asym.Set(obj.AttrDuplicateOK, true)
|
|
Ctxt.ABIAliases = append(Ctxt.ABIAliases, asym)
|
|
}
|
|
}
|
|
|
|
if !hasBody {
|
|
// For body-less functions, we only create the LSym.
|
|
return
|
|
}
|
|
|
|
var flag int
|
|
if f.Dupok() {
|
|
flag |= obj.DUPOK
|
|
}
|
|
if f.Wrapper() {
|
|
flag |= obj.WRAPPER
|
|
}
|
|
if f.Needctxt() {
|
|
flag |= obj.NEEDCTXT
|
|
}
|
|
if f.Pragma&Nosplit != 0 {
|
|
flag |= obj.NOSPLIT
|
|
}
|
|
if f.ReflectMethod() {
|
|
flag |= obj.REFLECTMETHOD
|
|
}
|
|
|
|
// Clumsy but important.
|
|
// See test/recover.go for test cases and src/reflect/value.go
|
|
// for the actual functions being considered.
|
|
if myimportpath == "reflect" {
|
|
switch f.Nname.Sym.Name {
|
|
case "callReflect", "callMethod":
|
|
flag |= obj.WRAPPER
|
|
}
|
|
}
|
|
|
|
Ctxt.InitTextSym(f.lsym, flag)
|
|
}
|
|
|
|
func ggloblnod(nam *Node) {
|
|
s := nam.Sym.Linksym()
|
|
s.Gotype = ngotype(nam).Linksym()
|
|
flags := 0
|
|
if nam.Name.Readonly() {
|
|
flags = obj.RODATA
|
|
}
|
|
if nam.Type != nil && !nam.Type.HasPointers() {
|
|
flags |= obj.NOPTR
|
|
}
|
|
Ctxt.Globl(s, nam.Type.Width, flags)
|
|
if nam.Name.LibfuzzerExtraCounter() {
|
|
s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
|
|
}
|
|
}
|
|
|
|
func ggloblsym(s *obj.LSym, width int32, flags int16) {
|
|
if flags&obj.LOCAL != 0 {
|
|
s.Set(obj.AttrLocal, true)
|
|
flags &^= obj.LOCAL
|
|
}
|
|
Ctxt.Globl(s, int64(width), int(flags))
|
|
}
|
|
|
|
func Addrconst(a *obj.Addr, v int64) {
|
|
a.Sym = nil
|
|
a.Type = obj.TYPE_CONST
|
|
a.Offset = v
|
|
}
|
|
|
|
func Patch(p *obj.Prog, to *obj.Prog) {
|
|
if p.To.Type != obj.TYPE_BRANCH {
|
|
Fatalf("patch: not a branch")
|
|
}
|
|
p.To.SetTarget(to)
|
|
p.To.Offset = to.Pc
|
|
}
|