mirror of
https://github.com/golang/go
synced 2024-11-11 22:20:22 -07:00
[dev.typeparams] all: merge dev.regabi (ec741b0
) into dev.typeparams
Conflicts: * src/cmd/compile/internal/gc/main.go Merge List: * 2020-12-22ec741b0447
[dev.regabi] all: merge master (c9fb4eb
) into dev.regabi * 2020-12-22acc32ea124
[dev.regabi] codereview.cfg: add config for dev.regabi * 2020-12-22c9fb4eb0a2
cmd/link: handle grouped resource sections * 2020-12-22c40934b33d
[dev.regabi] cmd/compile: adjust one case in walkexpr * 2020-12-22280e7fd1ee
[dev.regabi] cmd/compile: only access Func method on concrete types * 2020-12-2251ba53f5c2
[dev.regabi] cmd/compile: separate misc for gc split * 2020-12-22572f168ed2
[dev.regabi] cmd/compile: separate various from Main * 2020-12-223b12c6dc08
[dev.regabi] cmd/compile: separate typecheck more cleanly * 2020-12-227c8f5356ab
[dev.regabi] cmd/compile: separate dowidth better * 2020-12-22c06a354bcc
test: trigger SIGSEGV instead of SIGTRAP in issue11656.go * 2020-12-220aa9b4709a
cmd/pack: r command create output file if not exist * 2020-12-22cb28c96be8
[dev.regabi] cmd/compile,cmd/link: initial support for ABI wrappers * 2020-12-22c8610e4700
[dev.regabi] cmd/compile: add ir.BasicLit to represent literals * 2020-12-223512cde10a
[dev.regabi] cmd/compile: stop reusing Ntype for OSLICELIT length * 2020-12-222755361e6a
[dev.regabi] cmd/compile: change noder.declNames to returns ir.Names * 2020-12-22301af2cb71
[dev.regabi] runtime/race: adjust test pattern match for ABI wrapper * 2020-12-224d27c4c223
runtime: correct error handling in several FreeBSD syscall wrappers * 2020-12-229b6147120a
cmd/pack: treat compiler's -linkobj output as "compiler object" * 2020-12-22306b2451c8
[dev.regabi] runtime: fix ABI targets in runtime.panic{Index,Slice} shims * 2020-12-21bc7e4d9257
syscall: don't generate ptrace on iOS * 2020-12-2194cfeca0a5
[dev.regabi] cmd/compile: stop using ONONAME with Name * 2020-12-21cb4898a77d
[dev.regabi] cmd/compile: simplify declaration importing * 2020-12-2106915ac14d
[dev.regabi] cmd/compile: move itabname call out of implements * 2020-12-216cff874c47
runtime/metrics: add Read examples * 2020-12-218438a5779b
runtime: use _exit on darwin * 2020-12-21cb95819cf6
runtime: detect netbsd netpoll overrun in sysmon * 2020-12-2153c984d976
runtime: skip wakep call in wakeNetPoller on Plan 9 * 2020-12-219abbe27710
test: skip issue11656.go on mips/mips64/ppc64 Change-Id: Ia12a1892195f5e08bb41465374124c71a1a135f6
This commit is contained in:
commit
91cc51e005
@ -51,6 +51,7 @@ type DebugFlags struct {
|
||||
TypeAssert int `help:"print information about type assertion inlining"`
|
||||
TypecheckInl int `help:"eager typechecking of inline function bodies"`
|
||||
WB int `help:"print information about write barriers"`
|
||||
ABIWrap int `help:"print information about ABI wrapper generation"`
|
||||
|
||||
any bool // set when any of the values have been set
|
||||
}
|
||||
|
@ -82,6 +82,8 @@ type CmdFlags struct {
|
||||
CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
|
||||
|
||||
// Longer names
|
||||
ABIWrap bool "help:\"enable generation of ABI wrappers\""
|
||||
ABIWrapLimit int "help:\"emit at most N ABI wrappers (for debugging)\""
|
||||
AsmHdr string "help:\"write assembly header to `file`\""
|
||||
Bench string "help:\"append benchmark times to `file`\""
|
||||
BlockProfile string "help:\"write block profile to `file`\""
|
||||
@ -141,6 +143,7 @@ func ParseFlags() {
|
||||
Flag.LowerP = &Ctxt.Pkgpath
|
||||
Flag.LowerV = &Ctxt.Debugvlog
|
||||
|
||||
Flag.ABIWrap = objabi.Regabi_enabled != 0
|
||||
Flag.Dwarf = objabi.GOARCH != "wasm"
|
||||
Flag.DwarfBASEntries = &Ctxt.UseBASEntries
|
||||
Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
|
||||
|
@ -29,13 +29,20 @@ func TestMain(m *testing.M) {
|
||||
thearch.LinkArch = &x86.Linkamd64
|
||||
thearch.REGSP = x86.REGSP
|
||||
thearch.MAXWIDTH = 1 << 50
|
||||
MaxWidth = thearch.MAXWIDTH
|
||||
base.Ctxt = obj.Linknew(thearch.LinkArch)
|
||||
base.Ctxt.DiagFunc = base.Errorf
|
||||
base.Ctxt.DiagFlush = base.FlushErrors
|
||||
base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
|
||||
Widthptr = thearch.LinkArch.PtrSize
|
||||
Widthreg = thearch.LinkArch.RegSize
|
||||
initializeTypesPackage()
|
||||
types.TypeLinkSym = func(t *types.Type) *obj.LSym {
|
||||
return typenamesym(t).Linksym()
|
||||
}
|
||||
types.TypeLinkSym = func(t *types.Type) *obj.LSym {
|
||||
return typenamesym(t).Linksym()
|
||||
}
|
||||
TypecheckInit()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
|
@ -816,7 +816,7 @@ func eqstring(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
|
||||
fn := syslook("memequal")
|
||||
fn = substArgTypes(fn, types.Types[types.TUINT8], types.Types[types.TUINT8])
|
||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, []ir.Node{sptr, tptr, ir.Copy(slen)})
|
||||
call = typecheck(call, ctxExpr|ctxMultiOK).(*ir.CallExpr)
|
||||
TypecheckCall(call)
|
||||
|
||||
cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, slen, tlen)
|
||||
cmp = typecheck(cmp, ctxExpr).(*ir.BinaryExpr)
|
||||
@ -853,7 +853,7 @@ func eqinterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
|
||||
tdata.SetTypecheck(1)
|
||||
|
||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, []ir.Node{stab, sdata, tdata})
|
||||
call = typecheck(call, ctxExpr|ctxMultiOK).(*ir.CallExpr)
|
||||
TypecheckCall(call)
|
||||
|
||||
cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, stab, ttab)
|
||||
cmp = typecheck(cmp, ctxExpr).(*ir.BinaryExpr)
|
||||
|
@ -7,12 +7,14 @@ package gc
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/types"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// MaxWidth is the maximum size of a value on the target architecture.
|
||||
var MaxWidth int64
|
||||
|
||||
// sizeCalculationDisabled indicates whether it is safe
|
||||
// to calculate Types' widths and alignments. See dowidth.
|
||||
var sizeCalculationDisabled bool
|
||||
@ -84,7 +86,7 @@ func expandiface(t *types.Type) {
|
||||
|
||||
sort.Sort(methcmp(methods))
|
||||
|
||||
if int64(len(methods)) >= thearch.MAXWIDTH/int64(Widthptr) {
|
||||
if int64(len(methods)) >= MaxWidth/int64(Widthptr) {
|
||||
base.ErrorfAt(typePos(t), "interface too large")
|
||||
}
|
||||
for i, m := range methods {
|
||||
@ -118,8 +120,7 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
|
||||
o = Rnd(o, int64(f.Type.Align))
|
||||
}
|
||||
f.Offset = o
|
||||
if n := ir.AsNode(f.Nname); n != nil {
|
||||
n := n.Name()
|
||||
if f.Nname != nil {
|
||||
// addrescapes has similar code to update these offsets.
|
||||
// Usually addrescapes runs after widstruct,
|
||||
// in which case we could drop this,
|
||||
@ -127,12 +128,7 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
|
||||
// NOTE(rsc): This comment may be stale.
|
||||
// It's possible the ordering has changed and this is
|
||||
// now the common case. I'm not sure.
|
||||
if n.Name().Stackcopy != nil {
|
||||
n.Name().Stackcopy.SetFrameOffset(o)
|
||||
n.SetFrameOffset(0)
|
||||
} else {
|
||||
n.SetFrameOffset(o)
|
||||
}
|
||||
f.Nname.(types.VarObject).RecordFrameOffset(o)
|
||||
}
|
||||
|
||||
w := f.Type.Width
|
||||
@ -143,7 +139,7 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
|
||||
lastzero = o
|
||||
}
|
||||
o += w
|
||||
maxwidth := thearch.MAXWIDTH
|
||||
maxwidth := MaxWidth
|
||||
// On 32-bit systems, reflect tables impose an additional constraint
|
||||
// that each field start offset must fit in 31 bits.
|
||||
if maxwidth < 1<<32 {
|
||||
@ -206,7 +202,7 @@ func findTypeLoop(t *types.Type, path *[]*types.Type) bool {
|
||||
}
|
||||
|
||||
*path = append(*path, t)
|
||||
if findTypeLoop(t.Obj().(*ir.Name).Ntype.Type(), path) {
|
||||
if findTypeLoop(t.Obj().(types.TypeObject).TypeDefn(), path) {
|
||||
return true
|
||||
}
|
||||
*path = (*path)[:len(*path)-1]
|
||||
@ -419,7 +415,7 @@ func dowidth(t *types.Type) {
|
||||
|
||||
dowidth(t.Elem())
|
||||
if t.Elem().Width != 0 {
|
||||
cap := (uint64(thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
|
||||
cap := (uint64(MaxWidth) - 1) / uint64(t.Elem().Width)
|
||||
if uint64(t.NumElem()) > cap {
|
||||
base.ErrorfAt(typePos(t), "type %L larger than address space", t)
|
||||
}
|
||||
@ -479,6 +475,13 @@ func dowidth(t *types.Type) {
|
||||
resumecheckwidth()
|
||||
}
|
||||
|
||||
// CalcStructSize calculates the size of s,
|
||||
// filling in s.Width and s.Align,
|
||||
// even if size calculation is otherwise disabled.
|
||||
func CalcStructSize(s *types.Type) {
|
||||
s.Width = widstruct(s, s, 0, 1) // sets align
|
||||
}
|
||||
|
||||
// when a type's width should be known, we call checkwidth
|
||||
// to compute it. during a declaration like
|
||||
//
|
||||
|
@ -76,7 +76,7 @@ func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
|
||||
// function associated with the closure.
|
||||
// TODO: This creation of the named function should probably really be done in a
|
||||
// separate pass from type-checking.
|
||||
func typecheckclosure(clo ir.Node, top int) {
|
||||
func typecheckclosure(clo *ir.ClosureExpr, top int) {
|
||||
fn := clo.Func()
|
||||
// Set current associated iota value, so iota can be used inside
|
||||
// function in ConstSpec, see issue #22344
|
||||
@ -327,13 +327,13 @@ func transformclosure(fn *ir.Func) {
|
||||
|
||||
// hasemptycvars reports whether closure clo has an
|
||||
// empty list of captured vars.
|
||||
func hasemptycvars(clo ir.Node) bool {
|
||||
func hasemptycvars(clo *ir.ClosureExpr) bool {
|
||||
return len(clo.Func().ClosureVars) == 0
|
||||
}
|
||||
|
||||
// closuredebugruntimecheck applies boilerplate checks for debug flags
|
||||
// and compiling runtime
|
||||
func closuredebugruntimecheck(clo ir.Node) {
|
||||
func closuredebugruntimecheck(clo *ir.ClosureExpr) {
|
||||
if base.Debug.Closure > 0 {
|
||||
if clo.Esc() == EscHeap {
|
||||
base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func().ClosureVars)
|
||||
@ -349,7 +349,7 @@ func closuredebugruntimecheck(clo ir.Node) {
|
||||
// closureType returns the struct type used to hold all the information
|
||||
// needed in the closure for clo (clo must be a OCLOSURE node).
|
||||
// The address of a variable of the returned type can be cast to a func.
|
||||
func closureType(clo ir.Node) *types.Type {
|
||||
func closureType(clo *ir.ClosureExpr) *types.Type {
|
||||
// Create closure in the form of a composite literal.
|
||||
// supposing the closure captures an int i and a string s
|
||||
// and has one float64 argument and no results,
|
||||
|
@ -130,18 +130,16 @@ func declare(n *ir.Name, ctxt ir.Class) {
|
||||
|
||||
// declare variables from grammar
|
||||
// new_name_list (type | [type] = expr_list)
|
||||
func variter(vl []ir.Node, t ir.Ntype, el []ir.Node) []ir.Node {
|
||||
func variter(vl []*ir.Name, t ir.Ntype, el []ir.Node) []ir.Node {
|
||||
var init []ir.Node
|
||||
doexpr := len(el) > 0
|
||||
|
||||
if len(el) == 1 && len(vl) > 1 {
|
||||
e := el[0]
|
||||
as2 := ir.Nod(ir.OAS2, nil, nil)
|
||||
as2.PtrList().Set(vl)
|
||||
as2.PtrRlist().Set1(e)
|
||||
for _, v := range vl {
|
||||
v := v.(*ir.Name)
|
||||
v.SetOp(ir.ONAME)
|
||||
as2.PtrList().Append(v)
|
||||
declare(v, dclcontext)
|
||||
v.Ntype = t
|
||||
v.Defn = as2
|
||||
@ -153,20 +151,16 @@ func variter(vl []ir.Node, t ir.Ntype, el []ir.Node) []ir.Node {
|
||||
return append(init, as2)
|
||||
}
|
||||
|
||||
nel := len(el)
|
||||
for _, v := range vl {
|
||||
v := v.(*ir.Name)
|
||||
for i, v := range vl {
|
||||
var e ir.Node
|
||||
if doexpr {
|
||||
if len(el) == 0 {
|
||||
base.Errorf("assignment mismatch: %d variables but %d values", len(vl), nel)
|
||||
if i >= len(el) {
|
||||
base.Errorf("assignment mismatch: %d variables but %d values", len(vl), len(el))
|
||||
break
|
||||
}
|
||||
e = el[0]
|
||||
el = el[1:]
|
||||
e = el[i]
|
||||
}
|
||||
|
||||
v.SetOp(ir.ONAME)
|
||||
declare(v, dclcontext)
|
||||
v.Ntype = t
|
||||
|
||||
@ -182,8 +176,8 @@ func variter(vl []ir.Node, t ir.Ntype, el []ir.Node) []ir.Node {
|
||||
}
|
||||
}
|
||||
|
||||
if len(el) != 0 {
|
||||
base.Errorf("assignment mismatch: %d variables but %d values", len(vl), nel)
|
||||
if len(el) > len(vl) {
|
||||
base.Errorf("assignment mismatch: %d variables but %d values", len(vl), len(el))
|
||||
}
|
||||
return init
|
||||
}
|
||||
@ -448,6 +442,12 @@ type funcStackEnt struct {
|
||||
dclcontext ir.Class
|
||||
}
|
||||
|
||||
func CheckFuncStack() {
|
||||
if len(funcStack) != 0 {
|
||||
base.Fatalf("funcStack is non-empty: %v", len(funcStack))
|
||||
}
|
||||
}
|
||||
|
||||
// finish the body.
|
||||
// called in auto-declaration context.
|
||||
// returns in extern-declaration context.
|
||||
@ -892,6 +892,7 @@ func (c *nowritebarrierrecChecker) findExtraCalls(nn ir.Node) {
|
||||
case ir.ONAME:
|
||||
callee = arg.Name().Defn.(*ir.Func)
|
||||
case ir.OCLOSURE:
|
||||
arg := arg.(*ir.ClosureExpr)
|
||||
callee = arg.Func()
|
||||
default:
|
||||
base.Fatalf("expected ONAME or OCLOSURE node, got %+v", arg)
|
||||
|
@ -24,7 +24,7 @@ const (
|
||||
embedFiles
|
||||
)
|
||||
|
||||
func varEmbed(p *noder, names []ir.Node, typ ir.Ntype, exprs []ir.Node, embeds []PragmaEmbed) (newExprs []ir.Node) {
|
||||
func varEmbed(p *noder, names []*ir.Name, typ ir.Ntype, exprs []ir.Node, embeds []PragmaEmbed) (newExprs []ir.Node) {
|
||||
haveEmbed := false
|
||||
for _, decl := range p.file.DeclList {
|
||||
imp, ok := decl.(*syntax.ImportDecl)
|
||||
@ -66,7 +66,7 @@ func varEmbed(p *noder, names []ir.Node, typ ir.Ntype, exprs []ir.Node, embeds [
|
||||
return exprs
|
||||
}
|
||||
|
||||
v := names[0].(*ir.Name)
|
||||
v := names[0]
|
||||
Target.Embeds = append(Target.Embeds, v)
|
||||
v.Embed = new([]ir.Embed)
|
||||
for _, e := range embeds {
|
||||
|
@ -143,10 +143,6 @@ type EscEdge struct {
|
||||
notes *EscNote
|
||||
}
|
||||
|
||||
func init() {
|
||||
ir.EscFmt = escFmt
|
||||
}
|
||||
|
||||
// escFmt is called from node printing to print information about escape analysis results.
|
||||
func escFmt(n ir.Node) string {
|
||||
text := ""
|
||||
@ -682,6 +678,7 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
|
||||
}
|
||||
|
||||
case ir.OCLOSURE:
|
||||
n := n.(*ir.ClosureExpr)
|
||||
k = e.spill(k, n)
|
||||
|
||||
// Link addresses of captured variables to closure.
|
||||
@ -883,7 +880,7 @@ func (e *Escape) call(ks []EscHole, call, where ir.Node) {
|
||||
case v.Op() == ir.ONAME && v.(*ir.Name).Class() == ir.PFUNC:
|
||||
fn = v.(*ir.Name)
|
||||
case v.Op() == ir.OCLOSURE:
|
||||
fn = v.Func().Nname
|
||||
fn = v.(*ir.ClosureExpr).Func().Nname
|
||||
}
|
||||
case ir.OCALLMETH:
|
||||
fn = methodExprName(call.Left())
|
||||
@ -1887,7 +1884,7 @@ func heapAllocReason(n ir.Node) string {
|
||||
return "too large for stack"
|
||||
}
|
||||
|
||||
if n.Op() == ir.OCLOSURE && closureType(n).Size() >= maxImplicitStackVarSize {
|
||||
if n.Op() == ir.OCLOSURE && closureType(n.(*ir.ClosureExpr)).Size() >= maxImplicitStackVarSize {
|
||||
return "too large for stack"
|
||||
}
|
||||
if n.Op() == ir.OCALLPART && partialCallType(n.(*ir.CallPartExpr)).Size() >= maxImplicitStackVarSize {
|
||||
|
@ -77,126 +77,69 @@ func dumpexport(bout *bio.Writer) {
|
||||
}
|
||||
}
|
||||
|
||||
func importsym(ipkg *types.Pkg, s *types.Sym, op ir.Op) *ir.Name {
|
||||
n := ir.AsNode(s.PkgDef())
|
||||
if n == nil {
|
||||
// iimport should have created a stub ONONAME
|
||||
// declaration for all imported symbols. The exception
|
||||
// is declarations for Runtimepkg, which are populated
|
||||
// by loadsys instead.
|
||||
if s.Pkg != Runtimepkg {
|
||||
base.Fatalf("missing ONONAME for %v\n", s)
|
||||
}
|
||||
func importsym(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class) *ir.Name {
|
||||
if n := s.PkgDef(); n != nil {
|
||||
base.Fatalf("importsym of symbol that already exists: %v", n)
|
||||
}
|
||||
|
||||
n = ir.NewDeclNameAt(src.NoXPos, s)
|
||||
s.SetPkgDef(n)
|
||||
s.Importdef = ipkg
|
||||
}
|
||||
if n.Op() != ir.ONONAME && n.Op() != op {
|
||||
redeclare(base.Pos, s, fmt.Sprintf("during import %q", ipkg.Path))
|
||||
}
|
||||
return n.(*ir.Name)
|
||||
n := ir.NewDeclNameAt(pos, op, s)
|
||||
n.SetClass(ctxt) // TODO(mdempsky): Move this into NewDeclNameAt too?
|
||||
s.SetPkgDef(n)
|
||||
s.Importdef = ipkg
|
||||
return n
|
||||
}
|
||||
|
||||
// importtype returns the named type declared by symbol s.
|
||||
// If no such type has been declared yet, a forward declaration is returned.
|
||||
// ipkg is the package being imported
|
||||
func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
|
||||
n := importsym(ipkg, s, ir.OTYPE)
|
||||
if n.Op() != ir.OTYPE {
|
||||
t := types.NewNamed(n)
|
||||
n.SetOp(ir.OTYPE)
|
||||
n.SetPos(pos)
|
||||
n.SetType(t)
|
||||
n.SetClass(ir.PEXTERN)
|
||||
}
|
||||
|
||||
t := n.Type()
|
||||
if t == nil {
|
||||
base.Fatalf("importtype %v", s)
|
||||
}
|
||||
return t
|
||||
func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *ir.Name {
|
||||
n := importsym(ipkg, pos, s, ir.OTYPE, ir.PEXTERN)
|
||||
n.SetType(types.NewNamed(n))
|
||||
return n
|
||||
}
|
||||
|
||||
// importobj declares symbol s as an imported object representable by op.
|
||||
// ipkg is the package being imported
|
||||
func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class, t *types.Type) ir.Node {
|
||||
n := importsym(ipkg, s, op)
|
||||
if n.Op() != ir.ONONAME {
|
||||
if n.Op() == op && (op == ir.ONAME && n.Class() != ctxt || !types.Identical(n.Type(), t)) {
|
||||
redeclare(base.Pos, s, fmt.Sprintf("during import %q", ipkg.Path))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
n.SetOp(op)
|
||||
n.SetPos(pos)
|
||||
n.SetClass(ctxt)
|
||||
func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class, t *types.Type) *ir.Name {
|
||||
n := importsym(ipkg, pos, s, op, ctxt)
|
||||
n.SetType(t)
|
||||
if ctxt == ir.PFUNC {
|
||||
n.Sym().SetFunc(true)
|
||||
}
|
||||
n.SetType(t)
|
||||
return n
|
||||
}
|
||||
|
||||
// importconst declares symbol s as an imported constant with type t and value val.
|
||||
// ipkg is the package being imported
|
||||
func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val constant.Value) {
|
||||
func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val constant.Value) *ir.Name {
|
||||
n := importobj(ipkg, pos, s, ir.OLITERAL, ir.PEXTERN, t)
|
||||
if n == nil { // TODO: Check that value matches.
|
||||
return
|
||||
}
|
||||
|
||||
n.SetVal(val)
|
||||
|
||||
if base.Flag.E != 0 {
|
||||
fmt.Printf("import const %v %L = %v\n", s, t, val)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// importfunc declares symbol s as an imported function with type t.
|
||||
// ipkg is the package being imported
|
||||
func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
|
||||
func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
|
||||
n := importobj(ipkg, pos, s, ir.ONAME, ir.PFUNC, t)
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
name := n.(*ir.Name)
|
||||
|
||||
fn := ir.NewFunc(pos)
|
||||
fn.SetType(t)
|
||||
name.SetFunc(fn)
|
||||
fn.Nname = name
|
||||
n.SetFunc(fn)
|
||||
fn.Nname = n
|
||||
|
||||
if base.Flag.E != 0 {
|
||||
fmt.Printf("import func %v%S\n", s, t)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// importvar declares symbol s as an imported variable with type t.
|
||||
// ipkg is the package being imported
|
||||
func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
|
||||
n := importobj(ipkg, pos, s, ir.ONAME, ir.PEXTERN, t)
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if base.Flag.E != 0 {
|
||||
fmt.Printf("import var %v %L\n", s, t)
|
||||
}
|
||||
func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
|
||||
return importobj(ipkg, pos, s, ir.ONAME, ir.PEXTERN, t)
|
||||
}
|
||||
|
||||
// importalias declares symbol s as an imported type alias with type t.
|
||||
// ipkg is the package being imported
|
||||
func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
|
||||
n := importobj(ipkg, pos, s, ir.OTYPE, ir.PEXTERN, t)
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if base.Flag.E != 0 {
|
||||
fmt.Printf("import type %v = %L\n", s, t)
|
||||
}
|
||||
func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
|
||||
return importobj(ipkg, pos, s, ir.OTYPE, ir.PEXTERN, t)
|
||||
}
|
||||
|
||||
func dumpasmhdr() {
|
||||
|
@ -131,8 +131,6 @@ var (
|
||||
iscmp [ir.OEND]bool
|
||||
)
|
||||
|
||||
var importlist []*ir.Func // imported functions and methods with inlinable bodies
|
||||
|
||||
var (
|
||||
funcsymsmu sync.Mutex // protects funcsyms and associated package lookups (see func funcsym)
|
||||
funcsyms []*types.Sym
|
||||
|
@ -34,9 +34,12 @@ import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/ssa"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/src"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var sharedProgArray = new([10000]obj.Prog) // *T instead of T to work around issue 19839
|
||||
@ -187,32 +190,154 @@ func (pp *Progs) settext(fn *ir.Func) {
|
||||
ptxt.From.Sym = fn.LSym
|
||||
}
|
||||
|
||||
// makeABIWrapper creates a new function that wraps a cross-ABI call
|
||||
// to "f". The wrapper is marked as an ABIWRAPPER.
|
||||
func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
|
||||
|
||||
// Q: is this needed?
|
||||
savepos := base.Pos
|
||||
savedclcontext := dclcontext
|
||||
savedcurfn := Curfn
|
||||
|
||||
base.Pos = autogeneratedPos
|
||||
dclcontext = ir.PEXTERN
|
||||
|
||||
// At the moment we don't support wrapping a method, we'd need machinery
|
||||
// below to handle the receiver. Panic if we see this scenario.
|
||||
ft := f.Nname.Ntype.Type()
|
||||
if ft.NumRecvs() != 0 {
|
||||
panic("makeABIWrapper support for wrapping methods not implemented")
|
||||
}
|
||||
|
||||
// Manufacture a new func type to use for the wrapper.
|
||||
var noReceiver *ir.Field
|
||||
tfn := ir.NewFuncType(base.Pos,
|
||||
noReceiver,
|
||||
structargs(ft.Params(), true),
|
||||
structargs(ft.Results(), false))
|
||||
|
||||
// Reuse f's types.Sym to create a new ODCLFUNC/function.
|
||||
fn := dclfunc(f.Nname.Sym(), tfn)
|
||||
fn.SetDupok(true)
|
||||
fn.SetWrapper(true) // ignore frame for panic+recover matching
|
||||
|
||||
// Select LSYM now.
|
||||
asym := base.Ctxt.LookupABI(f.LSym.Name, wrapperABI)
|
||||
asym.Type = objabi.STEXT
|
||||
if fn.LSym != nil {
|
||||
panic("unexpected")
|
||||
}
|
||||
fn.LSym = asym
|
||||
|
||||
// ABI0-to-ABIInternal wrappers will be mainly loading params from
|
||||
// stack into registers (and/or storing stack locations back to
|
||||
// registers after the wrapped call); in most cases they won't
|
||||
// need to allocate stack space, so it should be OK to mark them
|
||||
// as NOSPLIT in these cases. In addition, my assumption is that
|
||||
// functions written in assembly are NOSPLIT in most (but not all)
|
||||
// cases. In the case of an ABIInternal target that has too many
|
||||
// parameters to fit into registers, the wrapper would need to
|
||||
// allocate stack space, but this seems like an unlikely scenario.
|
||||
// Hence: mark these wrappers NOSPLIT.
|
||||
//
|
||||
// ABIInternal-to-ABI0 wrappers on the other hand will be taking
|
||||
// things in registers and pushing them onto the stack prior to
|
||||
// the ABI0 call, meaning that they will always need to allocate
|
||||
// stack space. If the compiler marks them as NOSPLIT this seems
|
||||
// as though it could lead to situations where the the linker's
|
||||
// nosplit-overflow analysis would trigger a link failure. On the
|
||||
// other hand if they not tagged NOSPLIT then this could cause
|
||||
// problems when building the runtime (since there may be calls to
|
||||
// asm routine in cases where it's not safe to grow the stack). In
|
||||
// most cases the wrapper would be (in effect) inlined, but are
|
||||
// there (perhaps) indirect calls from the runtime that could run
|
||||
// into trouble here.
|
||||
// FIXME: at the moment all.bash does not pass when I leave out
|
||||
// NOSPLIT for these wrappers, so all are currently tagged with NOSPLIT.
|
||||
setupTextLSym(fn, obj.NOSPLIT|obj.ABIWRAPPER)
|
||||
|
||||
// Generate call. Use tail call if no params and no returns,
|
||||
// but a regular call otherwise.
|
||||
//
|
||||
// Note: ideally we would be using a tail call in cases where
|
||||
// there are params but no returns for ABI0->ABIInternal wrappers,
|
||||
// provided that all params fit into registers (e.g. we don't have
|
||||
// to allocate any stack space). Doing this will require some
|
||||
// extra work in typecheck/walk/ssa, might want to add a new node
|
||||
// OTAILCALL or something to this effect.
|
||||
var call ir.Node
|
||||
if tfn.Type().NumResults() == 0 && tfn.Type().NumParams() == 0 && tfn.Type().NumRecvs() == 0 {
|
||||
call = nodSym(ir.ORETJMP, nil, f.Nname.Sym())
|
||||
} else {
|
||||
call = ir.Nod(ir.OCALL, f.Nname, nil)
|
||||
call.PtrList().Set(paramNnames(tfn.Type()))
|
||||
call.SetIsDDD(tfn.Type().IsVariadic())
|
||||
if tfn.Type().NumResults() > 0 {
|
||||
n := ir.Nod(ir.ORETURN, nil, nil)
|
||||
n.PtrList().Set1(call)
|
||||
call = n
|
||||
}
|
||||
}
|
||||
fn.PtrBody().Append(call)
|
||||
|
||||
funcbody()
|
||||
if base.Debug.DclStack != 0 {
|
||||
testdclstack()
|
||||
}
|
||||
|
||||
typecheckFunc(fn)
|
||||
Curfn = fn
|
||||
typecheckslice(fn.Body().Slice(), ctxStmt)
|
||||
|
||||
escapeFuncs([]*ir.Func{fn}, false)
|
||||
|
||||
Target.Decls = append(Target.Decls, fn)
|
||||
|
||||
// Restore previous context.
|
||||
base.Pos = savepos
|
||||
dclcontext = savedclcontext
|
||||
Curfn = savedcurfn
|
||||
}
|
||||
|
||||
// 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.
|
||||
// For body-less functions, we only create the LSym; for functions
|
||||
// with bodies call a helper to setup up / populate the LSym.
|
||||
func initLSym(f *ir.Func, hasBody bool) {
|
||||
// FIXME: for new-style ABI wrappers, we set up the lsym at the
|
||||
// point the wrapper is created.
|
||||
if f.LSym != nil && base.Flag.ABIWrap {
|
||||
return
|
||||
}
|
||||
selectLSym(f, hasBody)
|
||||
if hasBody {
|
||||
setupTextLSym(f, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// selectLSym sets up the LSym for a given function, and
|
||||
// makes calls to helpers to create ABI wrappers if needed.
|
||||
func selectLSym(f *ir.Func, hasBody bool) {
|
||||
if f.LSym != nil {
|
||||
base.Fatalf("Func.initLSym called twice")
|
||||
}
|
||||
|
||||
if nam := f.Nname; !ir.IsBlank(nam) {
|
||||
f.LSym = nam.Sym().Linksym()
|
||||
if f.Pragma&ir.Systemstack != 0 {
|
||||
f.LSym.Set(obj.AttrCFunc, true)
|
||||
}
|
||||
|
||||
var aliasABI obj.ABI
|
||||
needABIAlias := false
|
||||
defABI, hasDefABI := symabiDefs[f.LSym.Name]
|
||||
var wrapperABI obj.ABI
|
||||
needABIWrapper := false
|
||||
defABI, hasDefABI := symabiDefs[nam.Sym().LinksymName()]
|
||||
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
|
||||
f.LSym = nam.Sym().LinksymABI0()
|
||||
needABIWrapper, wrapperABI = true, obj.ABIInternal
|
||||
} else {
|
||||
f.LSym = nam.Sym().Linksym()
|
||||
// No ABI override. Check that the symbol is
|
||||
// using the expected ABI.
|
||||
want := obj.ABIInternal
|
||||
@ -220,6 +345,9 @@ func initLSym(f *ir.Func, hasBody bool) {
|
||||
base.Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.LSym.Name, f.LSym.ABI(), want)
|
||||
}
|
||||
}
|
||||
if f.Pragma&ir.Systemstack != 0 {
|
||||
f.LSym.Set(obj.AttrCFunc, true)
|
||||
}
|
||||
|
||||
isLinknameExported := nam.Sym().Linkname != "" && (hasBody || hasDefABI)
|
||||
if abi, ok := symabiRefs[f.LSym.Name]; (ok && abi == obj.ABI0) || isLinknameExported {
|
||||
@ -235,32 +363,39 @@ func initLSym(f *ir.Func, hasBody bool) {
|
||||
// using linkname and we don't want to create
|
||||
// duplicate ABI wrappers.
|
||||
if f.LSym.ABI() != obj.ABI0 {
|
||||
needABIAlias, aliasABI = true, obj.ABI0
|
||||
needABIWrapper, wrapperABI = 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"
|
||||
if needABIWrapper {
|
||||
if !useABIWrapGen(f) {
|
||||
// Fallback: use alias instead. FIXME.
|
||||
|
||||
// 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(wrapperABI)
|
||||
asym.Set(obj.AttrDuplicateOK, true)
|
||||
base.Ctxt.ABIAliases = append(base.Ctxt.ABIAliases, asym)
|
||||
} else {
|
||||
if base.Debug.ABIWrap != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %s.%s\n",
|
||||
wrapperABI, 1-wrapperABI, types.LocalPkg.Path, f.LSym.Name)
|
||||
}
|
||||
makeABIWrapper(f, wrapperABI)
|
||||
}
|
||||
asym.SetABI(aliasABI)
|
||||
asym.Set(obj.AttrDuplicateOK, true)
|
||||
base.Ctxt.ABIAliases = append(base.Ctxt.ABIAliases, asym)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasBody {
|
||||
// For body-less functions, we only create the LSym.
|
||||
return
|
||||
}
|
||||
|
||||
var flag int
|
||||
// setupTextLsym initializes the LSym for a with-body text symbol.
|
||||
func setupTextLSym(f *ir.Func, flag int) {
|
||||
if f.Dupok() {
|
||||
flag |= obj.DUPOK
|
||||
}
|
||||
|
@ -41,18 +41,23 @@ var (
|
||||
inlineImporter = map[*types.Sym]iimporterAndOffset{}
|
||||
)
|
||||
|
||||
func expandDecl(n *ir.Name) {
|
||||
if n.Op() != ir.ONONAME {
|
||||
return
|
||||
func expandDecl(n ir.Node) ir.Node {
|
||||
if n, ok := n.(*ir.Name); ok {
|
||||
return n
|
||||
}
|
||||
|
||||
r := importReaderFor(n, declImporter)
|
||||
id := n.(*ir.Ident)
|
||||
if n := id.Sym().PkgDef(); n != nil {
|
||||
return n.(*ir.Name)
|
||||
}
|
||||
|
||||
r := importReaderFor(id.Sym(), declImporter)
|
||||
if r == nil {
|
||||
// Can happen if user tries to reference an undeclared name.
|
||||
return
|
||||
return n
|
||||
}
|
||||
|
||||
r.doDecl(n)
|
||||
return r.doDecl(n.Sym())
|
||||
}
|
||||
|
||||
func expandInline(fn *ir.Func) {
|
||||
@ -60,7 +65,7 @@ func expandInline(fn *ir.Func) {
|
||||
return
|
||||
}
|
||||
|
||||
r := importReaderFor(fn.Nname, inlineImporter)
|
||||
r := importReaderFor(fn.Nname.Sym(), inlineImporter)
|
||||
if r == nil {
|
||||
base.Fatalf("missing import reader for %v", fn)
|
||||
}
|
||||
@ -68,13 +73,13 @@ func expandInline(fn *ir.Func) {
|
||||
r.doInline(fn)
|
||||
}
|
||||
|
||||
func importReaderFor(n *ir.Name, importers map[*types.Sym]iimporterAndOffset) *importReader {
|
||||
x, ok := importers[n.Sym()]
|
||||
func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset) *importReader {
|
||||
x, ok := importers[sym]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return x.p.newReader(x.off, n.Sym().Pkg)
|
||||
return x.p.newReader(x.off, sym.Pkg)
|
||||
}
|
||||
|
||||
type intReader struct {
|
||||
@ -272,11 +277,7 @@ func (r *importReader) setPkg() {
|
||||
r.currPkg = r.pkg()
|
||||
}
|
||||
|
||||
func (r *importReader) doDecl(n ir.Node) {
|
||||
if n.Op() != ir.ONONAME {
|
||||
base.Fatalf("doDecl: unexpected Op for %v: %v", n.Sym(), n.Op())
|
||||
}
|
||||
|
||||
func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
|
||||
tag := r.byte()
|
||||
pos := r.pos()
|
||||
|
||||
@ -284,24 +285,26 @@ func (r *importReader) doDecl(n ir.Node) {
|
||||
case 'A':
|
||||
typ := r.typ()
|
||||
|
||||
importalias(r.p.ipkg, pos, n.Sym(), typ)
|
||||
return importalias(r.p.ipkg, pos, sym, typ)
|
||||
|
||||
case 'C':
|
||||
typ := r.typ()
|
||||
val := r.value(typ)
|
||||
|
||||
importconst(r.p.ipkg, pos, n.Sym(), typ, val)
|
||||
return importconst(r.p.ipkg, pos, sym, typ, val)
|
||||
|
||||
case 'F':
|
||||
typ := r.signature(nil)
|
||||
|
||||
importfunc(r.p.ipkg, pos, n.Sym(), typ)
|
||||
n := importfunc(r.p.ipkg, pos, sym, typ)
|
||||
r.funcExt(n)
|
||||
return n
|
||||
|
||||
case 'T':
|
||||
// Types can be recursive. We need to setup a stub
|
||||
// declaration before recursing.
|
||||
t := importtype(r.p.ipkg, pos, n.Sym())
|
||||
n := importtype(r.p.ipkg, pos, sym)
|
||||
t := n.Type()
|
||||
|
||||
// We also need to defer width calculations until
|
||||
// after the underlying type has been assigned.
|
||||
@ -312,7 +315,7 @@ func (r *importReader) doDecl(n ir.Node) {
|
||||
|
||||
if underlying.IsInterface() {
|
||||
r.typeExt(t)
|
||||
break
|
||||
return n
|
||||
}
|
||||
|
||||
ms := make([]*types.Field, r.uint64())
|
||||
@ -339,15 +342,18 @@ func (r *importReader) doDecl(n ir.Node) {
|
||||
for _, m := range ms {
|
||||
r.methExt(m)
|
||||
}
|
||||
return n
|
||||
|
||||
case 'V':
|
||||
typ := r.typ()
|
||||
|
||||
importvar(r.p.ipkg, pos, n.Sym(), typ)
|
||||
n := importvar(r.p.ipkg, pos, sym, typ)
|
||||
r.varExt(n)
|
||||
return n
|
||||
|
||||
default:
|
||||
base.Fatalf("unexpected tag: %v", tag)
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
@ -433,16 +439,11 @@ func (r *importReader) ident() *types.Sym {
|
||||
return pkg.Lookup(name)
|
||||
}
|
||||
|
||||
func (r *importReader) qualifiedIdent() *ir.Name {
|
||||
func (r *importReader) qualifiedIdent() *ir.Ident {
|
||||
name := r.string()
|
||||
pkg := r.pkg()
|
||||
sym := pkg.Lookup(name)
|
||||
n := sym.PkgDef()
|
||||
if n == nil {
|
||||
n = ir.NewDeclNameAt(src.NoXPos, sym)
|
||||
sym.SetPkgDef(n)
|
||||
}
|
||||
return n.(*ir.Name)
|
||||
return ir.NewIdent(src.NoXPos, sym)
|
||||
}
|
||||
|
||||
func (r *importReader) pos() src.XPos {
|
||||
@ -498,10 +499,7 @@ func (r *importReader) typ1() *types.Type {
|
||||
// support inlining functions with local defined
|
||||
// types. Therefore, this must be a package-scope
|
||||
// type.
|
||||
n := r.qualifiedIdent()
|
||||
if n.Op() == ir.ONONAME {
|
||||
expandDecl(n)
|
||||
}
|
||||
n := expandDecl(r.qualifiedIdent())
|
||||
if n.Op() != ir.OTYPE {
|
||||
base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n)
|
||||
}
|
||||
@ -632,7 +630,7 @@ func (r *importReader) varExt(n ir.Node) {
|
||||
r.symIdx(n.Sym())
|
||||
}
|
||||
|
||||
func (r *importReader) funcExt(n ir.Node) {
|
||||
func (r *importReader) funcExt(n *ir.Name) {
|
||||
r.linkname(n.Sym())
|
||||
r.symIdx(n.Sym())
|
||||
|
||||
@ -656,7 +654,7 @@ func (r *importReader) methExt(m *types.Field) {
|
||||
if r.bool() {
|
||||
m.SetNointerface(true)
|
||||
}
|
||||
r.funcExt(ir.AsNode(m.Nname))
|
||||
r.funcExt(m.Nname.(*ir.Name))
|
||||
}
|
||||
|
||||
func (r *importReader) linkname(s *types.Sym) {
|
||||
@ -687,6 +685,21 @@ func (r *importReader) typeExt(t *types.Type) {
|
||||
// so we can use index to reference the symbol.
|
||||
var typeSymIdx = make(map[*types.Type][2]int64)
|
||||
|
||||
func BaseTypeIndex(t *types.Type) int64 {
|
||||
tbase := t
|
||||
if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
|
||||
tbase = t.Elem()
|
||||
}
|
||||
i, ok := typeSymIdx[tbase]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
if t != tbase {
|
||||
return i[1]
|
||||
}
|
||||
return i[0]
|
||||
}
|
||||
|
||||
func (r *importReader) doInline(fn *ir.Func) {
|
||||
if len(fn.Inl.Body) != 0 {
|
||||
base.Fatalf("%v already has inline body", fn)
|
||||
@ -973,9 +986,15 @@ func (r *importReader) node() ir.Node {
|
||||
// statements
|
||||
case ir.ODCL:
|
||||
pos := r.pos()
|
||||
lhs := ir.NewDeclNameAt(pos, r.ident())
|
||||
typ := ir.TypeNode(r.typ())
|
||||
return npos(pos, liststmt(variter([]ir.Node{lhs}, typ, nil))) // TODO(gri) avoid list creation
|
||||
lhs := ir.NewDeclNameAt(pos, ir.ONAME, r.ident())
|
||||
lhs.SetType(r.typ())
|
||||
|
||||
declare(lhs, ir.PAUTO)
|
||||
|
||||
var stmts ir.Nodes
|
||||
stmts.Append(ir.Nod(ir.ODCL, lhs, nil))
|
||||
stmts.Append(ir.Nod(ir.OAS, lhs, nil))
|
||||
return npos(pos, liststmt(stmts.Slice()))
|
||||
|
||||
// case OAS, OASWB:
|
||||
// unreachable - mapped to OAS case below by exporter
|
||||
|
@ -296,6 +296,7 @@ func (d *initDeps) visit(n ir.Node) {
|
||||
}
|
||||
|
||||
case ir.OCLOSURE:
|
||||
n := n.(*ir.ClosureExpr)
|
||||
d.inspectList(n.Func().Body())
|
||||
|
||||
case ir.ODOTMETH, ir.OCALLPART:
|
||||
|
@ -55,6 +55,26 @@ const (
|
||||
inlineBigFunctionMaxCost = 20 // Max cost of inlinee when inlining into a "big" function.
|
||||
)
|
||||
|
||||
func InlinePackage() {
|
||||
// Find functions that can be inlined and clone them before walk expands them.
|
||||
visitBottomUp(Target.Decls, func(list []*ir.Func, recursive bool) {
|
||||
numfns := numNonClosures(list)
|
||||
for _, n := range list {
|
||||
if !recursive || numfns > 1 {
|
||||
// We allow inlining if there is no
|
||||
// recursion, or the recursion cycle is
|
||||
// across more than one function.
|
||||
caninl(n)
|
||||
} else {
|
||||
if base.Flag.LowerM > 1 {
|
||||
fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
|
||||
}
|
||||
}
|
||||
inlcalls(n)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
|
||||
// the ->sym can be re-used in the local package, so peel it off the receiver's type.
|
||||
func fnpkg(fn *ir.Name) *types.Pkg {
|
||||
@ -217,7 +237,7 @@ func caninl(fn *ir.Func) {
|
||||
|
||||
n.Func().Inl = &ir.Inline{
|
||||
Cost: inlineMaxBudget - visitor.budget,
|
||||
Dcl: pruneUnusedAutos(n.Defn.Func().Dcl, &visitor),
|
||||
Dcl: pruneUnusedAutos(n.Defn.(*ir.Func).Func().Dcl, &visitor),
|
||||
Body: ir.DeepCopyList(src.NoXPos, fn.Body().Slice()),
|
||||
}
|
||||
|
||||
@ -452,7 +472,7 @@ func (v *hairyVisitor) doNode(n ir.Node) error {
|
||||
// and don't charge for the OBLOCK itself. The ++ undoes the -- below.
|
||||
v.budget++
|
||||
|
||||
case ir.OCALLPART:
|
||||
case ir.OCALLPART, ir.OSLICELIT:
|
||||
v.budget-- // Hack for toolstash -cmp.
|
||||
}
|
||||
|
||||
@ -657,6 +677,7 @@ func inlCallee(fn ir.Node) *ir.Func {
|
||||
return fn.Func()
|
||||
}
|
||||
case ir.OCLOSURE:
|
||||
fn := fn.(*ir.ClosureExpr)
|
||||
c := fn.Func()
|
||||
caninl(c)
|
||||
return c
|
||||
|
@ -54,9 +54,6 @@ func hidePanic() {
|
||||
// Target is the package being compiled.
|
||||
var Target *ir.Package
|
||||
|
||||
// timing data for compiler phases
|
||||
var timings Timings
|
||||
|
||||
// Main parses flags and Go source files specified in the command-line
|
||||
// arguments, type-checks the parsed Go package, compiles functions to machine
|
||||
// code, and finally writes the compiled package definition to disk.
|
||||
@ -189,40 +186,33 @@ func Main(archInit func(*Arch)) {
|
||||
logopt.LogJsonOption(base.Flag.JSON)
|
||||
}
|
||||
|
||||
ir.EscFmt = escFmt
|
||||
IsIntrinsicCall = isIntrinsicCall
|
||||
SSADumpInline = ssaDumpInline
|
||||
|
||||
ssaDump = os.Getenv("GOSSAFUNC")
|
||||
ssaDir = os.Getenv("GOSSADIR")
|
||||
if ssaDump != "" {
|
||||
if strings.HasSuffix(ssaDump, "+") {
|
||||
ssaDump = ssaDump[:len(ssaDump)-1]
|
||||
ssaDumpStdout = true
|
||||
}
|
||||
spl := strings.Split(ssaDump, ":")
|
||||
if len(spl) > 1 {
|
||||
ssaDump = spl[0]
|
||||
ssaDumpCFG = spl[1]
|
||||
}
|
||||
}
|
||||
initSSAEnv()
|
||||
initSSATables()
|
||||
|
||||
Widthptr = thearch.LinkArch.PtrSize
|
||||
Widthreg = thearch.LinkArch.RegSize
|
||||
MaxWidth = thearch.MAXWIDTH
|
||||
types.TypeLinkSym = func(t *types.Type) *obj.LSym {
|
||||
return typenamesym(t).Linksym()
|
||||
}
|
||||
|
||||
Target = new(ir.Package)
|
||||
|
||||
// initialize types package
|
||||
// (we need to do this to break dependencies that otherwise
|
||||
// would lead to import cycles)
|
||||
initializeTypesPackage()
|
||||
|
||||
dclcontext = ir.PEXTERN
|
||||
NeedFuncSym = makefuncsym
|
||||
NeedITab = func(t, iface *types.Type) { itabname(t, iface) }
|
||||
NeedRuntimeType = addsignat // TODO(rsc): typenamesym for lock?
|
||||
|
||||
autogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
|
||||
|
||||
timings.Start("fe", "loadsys")
|
||||
loadsys()
|
||||
types.TypeLinkSym = func(t *types.Type) *obj.LSym {
|
||||
return typenamesym(t).Linksym()
|
||||
}
|
||||
TypecheckInit()
|
||||
|
||||
// Parse input.
|
||||
timings.Start("fe", "parse")
|
||||
lines := parseFiles(flag.Args())
|
||||
cgoSymABIs()
|
||||
@ -234,134 +224,27 @@ func Main(archInit func(*Arch)) {
|
||||
return
|
||||
}
|
||||
|
||||
finishUniverse()
|
||||
|
||||
recordPackageName()
|
||||
|
||||
typecheckok = true
|
||||
// Typecheck.
|
||||
TypecheckPackage()
|
||||
|
||||
// Process top-level declarations in phases.
|
||||
|
||||
// Phase 1: const, type, and names and types of funcs.
|
||||
// This will gather all the information about types
|
||||
// and methods but doesn't depend on any of it.
|
||||
//
|
||||
// We also defer type alias declarations until phase 2
|
||||
// to avoid cycles like #18640.
|
||||
// TODO(gri) Remove this again once we have a fix for #25838.
|
||||
|
||||
// Don't use range--typecheck can add closures to Target.Decls.
|
||||
timings.Start("fe", "typecheck", "top1")
|
||||
for i := 0; i < len(Target.Decls); i++ {
|
||||
n := Target.Decls[i]
|
||||
if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.(*ir.Decl).Left().Name().Alias()) {
|
||||
Target.Decls[i] = typecheck(n, ctxStmt)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Variable assignments.
|
||||
// To check interface assignments, depends on phase 1.
|
||||
|
||||
// Don't use range--typecheck can add closures to Target.Decls.
|
||||
timings.Start("fe", "typecheck", "top2")
|
||||
for i := 0; i < len(Target.Decls); i++ {
|
||||
n := Target.Decls[i]
|
||||
if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).Left().Name().Alias() {
|
||||
Target.Decls[i] = typecheck(n, ctxStmt)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Type check function bodies.
|
||||
// Don't use range--typecheck can add closures to Target.Decls.
|
||||
timings.Start("fe", "typecheck", "func")
|
||||
var fcount int64
|
||||
for i := 0; i < len(Target.Decls); i++ {
|
||||
n := Target.Decls[i]
|
||||
if n.Op() == ir.ODCLFUNC {
|
||||
Curfn = n.(*ir.Func)
|
||||
decldepth = 1
|
||||
errorsBefore := base.Errors()
|
||||
typecheckslice(Curfn.Body().Slice(), ctxStmt)
|
||||
checkreturn(Curfn)
|
||||
if base.Errors() > errorsBefore {
|
||||
Curfn.PtrBody().Set(nil) // type errors; do not compile
|
||||
}
|
||||
// Now that we've checked whether n terminates,
|
||||
// we can eliminate some obviously dead code.
|
||||
deadcode(Curfn)
|
||||
fcount++
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3.11: Check external declarations.
|
||||
// TODO(mdempsky): This should be handled when type checking their
|
||||
// corresponding ODCL nodes.
|
||||
timings.Start("fe", "typecheck", "externdcls")
|
||||
for i, n := range Target.Externs {
|
||||
if n.Op() == ir.ONAME {
|
||||
Target.Externs[i] = typecheck(Target.Externs[i], ctxExpr)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3.14: With all user code type-checked, it's now safe to verify map keys
|
||||
// and unused dot imports.
|
||||
checkMapKeys()
|
||||
// With all user code typechecked, it's now safe to verify unused dot imports.
|
||||
checkDotImports()
|
||||
base.ExitIfErrors()
|
||||
|
||||
timings.AddEvent(fcount, "funcs")
|
||||
|
||||
// Build init task.
|
||||
if initTask := fninit(); initTask != nil {
|
||||
exportsym(initTask)
|
||||
}
|
||||
|
||||
// Phase 4: Decide how to capture closed variables.
|
||||
// This needs to run before escape analysis,
|
||||
// because variables captured by value do not escape.
|
||||
timings.Start("fe", "capturevars")
|
||||
for _, n := range Target.Decls {
|
||||
if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil {
|
||||
Curfn = n.(*ir.Func)
|
||||
capturevars(Curfn)
|
||||
}
|
||||
}
|
||||
capturevarscomplete = true
|
||||
Curfn = nil
|
||||
base.ExitIfErrors()
|
||||
|
||||
// Phase 5: Inlining
|
||||
// Inlining
|
||||
timings.Start("fe", "inlining")
|
||||
if base.Debug.TypecheckInl != 0 {
|
||||
// Typecheck imported function bodies if Debug.l > 1,
|
||||
// otherwise lazily when used or re-exported.
|
||||
for _, n := range importlist {
|
||||
if n.Inl != nil {
|
||||
typecheckinl(n)
|
||||
}
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
}
|
||||
|
||||
if base.Flag.LowerL != 0 {
|
||||
// Find functions that can be inlined and clone them before walk expands them.
|
||||
visitBottomUp(Target.Decls, func(list []*ir.Func, recursive bool) {
|
||||
numfns := numNonClosures(list)
|
||||
for _, n := range list {
|
||||
if !recursive || numfns > 1 {
|
||||
// We allow inlining if there is no
|
||||
// recursion, or the recursion cycle is
|
||||
// across more than one function.
|
||||
caninl(n)
|
||||
} else {
|
||||
if base.Flag.LowerM > 1 {
|
||||
fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
|
||||
}
|
||||
}
|
||||
inlcalls(n)
|
||||
}
|
||||
})
|
||||
InlinePackage()
|
||||
}
|
||||
|
||||
// Devirtualize.
|
||||
for _, n := range Target.Decls {
|
||||
if n.Op() == ir.ODCLFUNC {
|
||||
devirtualize(n.(*ir.Func))
|
||||
@ -369,7 +252,7 @@ func Main(archInit func(*Arch)) {
|
||||
}
|
||||
Curfn = nil
|
||||
|
||||
// Phase 6: Escape analysis.
|
||||
// Escape analysis.
|
||||
// Required for moving heap allocations onto stack,
|
||||
// which in turn is required by the closure implementation,
|
||||
// which stores the addresses of stack variables into the closure.
|
||||
@ -388,14 +271,17 @@ func Main(archInit func(*Arch)) {
|
||||
EnableNoWriteBarrierRecCheck()
|
||||
}
|
||||
|
||||
// Phase 7: Transform closure bodies to properly reference captured variables.
|
||||
// Transform closure bodies to properly reference captured variables.
|
||||
// This needs to happen before walk, because closures must be transformed
|
||||
// before walk reaches a call of a closure.
|
||||
timings.Start("fe", "xclosures")
|
||||
for _, n := range Target.Decls {
|
||||
if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil {
|
||||
Curfn = n.(*ir.Func)
|
||||
transformclosure(Curfn)
|
||||
if n.Op() == ir.ODCLFUNC {
|
||||
n := n.(*ir.Func)
|
||||
if n.Func().OClosure != nil {
|
||||
Curfn = n
|
||||
transformclosure(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -410,10 +296,10 @@ func Main(archInit func(*Arch)) {
|
||||
Curfn = nil
|
||||
peekitabs()
|
||||
|
||||
// Phase 8: Compile top level functions.
|
||||
// Compile top level functions.
|
||||
// Don't use range--walk can add functions to Target.Decls.
|
||||
timings.Start("be", "compilefuncs")
|
||||
fcount = 0
|
||||
fcount := int64(0)
|
||||
for i := 0; i < len(Target.Decls); i++ {
|
||||
n := Target.Decls[i]
|
||||
if n.Op() == ir.ODCLFUNC {
|
||||
@ -448,21 +334,9 @@ func Main(archInit func(*Arch)) {
|
||||
dumpasmhdr()
|
||||
}
|
||||
|
||||
// Check whether any of the functions we have compiled have gigantic stack frames.
|
||||
sort.Slice(largeStackFrames, func(i, j int) bool {
|
||||
return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
|
||||
})
|
||||
for _, large := range largeStackFrames {
|
||||
if large.callee != 0 {
|
||||
base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
|
||||
} else {
|
||||
base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
|
||||
}
|
||||
}
|
||||
CheckLargeStacks()
|
||||
CheckFuncStack()
|
||||
|
||||
if len(funcStack) != 0 {
|
||||
base.Fatalf("funcStack is non-empty: %v", len(funcStack))
|
||||
}
|
||||
if len(compilequeue) != 0 {
|
||||
base.Fatalf("%d uncompiled functions", len(compilequeue))
|
||||
}
|
||||
@ -480,6 +354,20 @@ func Main(archInit func(*Arch)) {
|
||||
}
|
||||
}
|
||||
|
||||
func CheckLargeStacks() {
|
||||
// Check whether any of the functions we have compiled have gigantic stack frames.
|
||||
sort.Slice(largeStackFrames, func(i, j int) bool {
|
||||
return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
|
||||
})
|
||||
for _, large := range largeStackFrames {
|
||||
if large.callee != 0 {
|
||||
base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
|
||||
} else {
|
||||
base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cgoSymABIs() {
|
||||
// The linker expects an ABI0 wrapper for all cgo-exported
|
||||
// functions.
|
||||
@ -1081,9 +969,11 @@ type lang struct {
|
||||
// any language version is supported.
|
||||
var langWant lang
|
||||
|
||||
// langSupported reports whether language version major.minor is
|
||||
// supported in a particular package.
|
||||
func langSupported(major, minor int, pkg *types.Pkg) bool {
|
||||
// AllowsGoVersion reports whether a particular package
|
||||
// is allowed to use Go version major.minor.
|
||||
// We assume the imported packages have all been checked,
|
||||
// so we only have to check the local package against the -lang flag.
|
||||
func AllowsGoVersion(pkg *types.Pkg, major, minor int) bool {
|
||||
if pkg == nil {
|
||||
// TODO(mdempsky): Set Pkg for local types earlier.
|
||||
pkg = types.LocalPkg
|
||||
@ -1092,13 +982,16 @@ func langSupported(major, minor int, pkg *types.Pkg) bool {
|
||||
// Assume imported packages passed type-checking.
|
||||
return true
|
||||
}
|
||||
|
||||
if langWant.major == 0 && langWant.minor == 0 {
|
||||
return true
|
||||
}
|
||||
return langWant.major > major || (langWant.major == major && langWant.minor >= minor)
|
||||
}
|
||||
|
||||
func langSupported(major, minor int, pkg *types.Pkg) bool {
|
||||
return AllowsGoVersion(pkg, major, minor)
|
||||
}
|
||||
|
||||
// checkLang verifies that the -lang flag holds a valid value, and
|
||||
// exits if not. It initializes data used by langSupported.
|
||||
func checkLang() {
|
||||
@ -1140,12 +1033,25 @@ func parseLang(s string) (lang, error) {
|
||||
return lang{major: major, minor: minor}, nil
|
||||
}
|
||||
|
||||
func initializeTypesPackage() {
|
||||
types.Widthptr = Widthptr
|
||||
types.Dowidth = dowidth
|
||||
types.TypeLinkSym = func(t *types.Type) *obj.LSym {
|
||||
return typenamesym(t).Linksym()
|
||||
// useNewABIWrapGen returns TRUE if the compiler should generate an
|
||||
// ABI wrapper for the function 'f'.
|
||||
func useABIWrapGen(f *ir.Func) bool {
|
||||
if !base.Flag.ABIWrap {
|
||||
return false
|
||||
}
|
||||
|
||||
initUniverse()
|
||||
// Support limit option for bisecting.
|
||||
if base.Flag.ABIWrapLimit == 1 {
|
||||
return false
|
||||
}
|
||||
if base.Flag.ABIWrapLimit < 1 {
|
||||
return true
|
||||
}
|
||||
base.Flag.ABIWrapLimit--
|
||||
if base.Debug.ABIWrap != 0 && base.Flag.ABIWrapLimit == 1 {
|
||||
fmt.Fprintf(os.Stderr, "=-= limit reached after new wrapper for %s\n",
|
||||
f.LSym.Name)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -507,7 +507,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
|
||||
}
|
||||
|
||||
func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node {
|
||||
names := p.declNames(decl.NameList)
|
||||
names := p.declNames(ir.ONAME, decl.NameList)
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
var exprs []ir.Node
|
||||
@ -558,7 +558,7 @@ func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
|
||||
p.checkUnused(pragma)
|
||||
}
|
||||
|
||||
names := p.declNames(decl.NameList)
|
||||
names := p.declNames(ir.OLITERAL, decl.NameList)
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
var values []ir.Node
|
||||
@ -574,7 +574,6 @@ func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
|
||||
|
||||
nn := make([]ir.Node, 0, len(names))
|
||||
for i, n := range names {
|
||||
n := n.(*ir.Name)
|
||||
if i >= len(values) {
|
||||
base.Errorf("missing value in const declaration")
|
||||
break
|
||||
@ -583,8 +582,6 @@ func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
|
||||
if decl.Values == nil {
|
||||
v = ir.DeepCopy(n.Pos(), v)
|
||||
}
|
||||
|
||||
n.SetOp(ir.OLITERAL)
|
||||
declare(n, dclcontext)
|
||||
|
||||
n.Ntype = typ
|
||||
@ -604,8 +601,7 @@ func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
|
||||
}
|
||||
|
||||
func (p *noder) typeDecl(decl *syntax.TypeDecl) ir.Node {
|
||||
n := p.declName(decl.Name)
|
||||
n.SetOp(ir.OTYPE)
|
||||
n := p.declName(ir.OTYPE, decl.Name)
|
||||
declare(n, dclcontext)
|
||||
|
||||
// decl.Type may be nil but in that case we got a syntax error during parsing
|
||||
@ -628,16 +624,16 @@ func (p *noder) typeDecl(decl *syntax.TypeDecl) ir.Node {
|
||||
return nod
|
||||
}
|
||||
|
||||
func (p *noder) declNames(names []*syntax.Name) []ir.Node {
|
||||
nodes := make([]ir.Node, 0, len(names))
|
||||
func (p *noder) declNames(op ir.Op, names []*syntax.Name) []*ir.Name {
|
||||
nodes := make([]*ir.Name, 0, len(names))
|
||||
for _, name := range names {
|
||||
nodes = append(nodes, p.declName(name))
|
||||
nodes = append(nodes, p.declName(op, name))
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (p *noder) declName(name *syntax.Name) *ir.Name {
|
||||
return ir.NewDeclNameAt(p.pos(name), p.name(name))
|
||||
func (p *noder) declName(op ir.Op, name *syntax.Name) *ir.Name {
|
||||
return ir.NewDeclNameAt(p.pos(name), op, p.name(name))
|
||||
}
|
||||
|
||||
func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node {
|
||||
|
@ -127,8 +127,7 @@ func dumpdata() {
|
||||
addsignats(Target.Externs)
|
||||
dumpsignats()
|
||||
dumptabs()
|
||||
ptabsLen := len(ptabs)
|
||||
itabsLen := len(itabs)
|
||||
numPTabs, numITabs := CountTabs()
|
||||
dumpimportstrings()
|
||||
dumpbasictypes()
|
||||
dumpembeds()
|
||||
@ -168,10 +167,11 @@ func dumpdata() {
|
||||
if numExports != len(Target.Exports) {
|
||||
base.Fatalf("Target.Exports changed after compile functions loop")
|
||||
}
|
||||
if ptabsLen != len(ptabs) {
|
||||
newNumPTabs, newNumITabs := CountTabs()
|
||||
if newNumPTabs != numPTabs {
|
||||
base.Fatalf("ptabs changed after compile functions loop")
|
||||
}
|
||||
if itabsLen != len(itabs) {
|
||||
if newNumITabs != numITabs {
|
||||
base.Fatalf("itabs changed after compile functions loop")
|
||||
}
|
||||
}
|
||||
|
@ -1281,7 +1281,7 @@ func (o *Order) expr1(n, lhs ir.Node) ir.Node {
|
||||
n := n.(*ir.CompLitExpr)
|
||||
o.exprList(n.List())
|
||||
if n.Transient() {
|
||||
t := types.NewArray(n.Type().Elem(), ir.Int64Val(n.Right()))
|
||||
t := types.NewArray(n.Type().Elem(), n.Len)
|
||||
n.Prealloc = o.newTemp(t, false)
|
||||
}
|
||||
return n
|
||||
|
@ -32,7 +32,6 @@ func emitptrargsmap(fn *ir.Func) {
|
||||
return
|
||||
}
|
||||
lsym := base.Ctxt.Lookup(fn.LSym.Name + ".args_stackmap")
|
||||
|
||||
nptr := int(fn.Type().ArgWidth() / int64(Widthptr))
|
||||
bv := bvalloc(int32(nptr) * 2)
|
||||
nbitmap := 1
|
||||
@ -165,7 +164,7 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
|
||||
|
||||
dowidth(n.Type())
|
||||
w := n.Type().Width
|
||||
if w >= thearch.MAXWIDTH || w < 0 {
|
||||
if w >= MaxWidth || w < 0 {
|
||||
base.Fatalf("bad width")
|
||||
}
|
||||
if w == 0 && lastHasPtr {
|
||||
@ -223,24 +222,16 @@ func funccompile(fn *ir.Func) {
|
||||
}
|
||||
|
||||
func compile(fn *ir.Func) {
|
||||
errorsBefore := base.Errors()
|
||||
order(fn)
|
||||
if base.Errors() > errorsBefore {
|
||||
return
|
||||
}
|
||||
|
||||
// Set up the function's LSym early to avoid data races with the assemblers.
|
||||
// Do this before walk, as walk needs the LSym to set attributes/relocations
|
||||
// (e.g. in markTypeUsedInInterface).
|
||||
initLSym(fn, true)
|
||||
|
||||
errorsBefore := base.Errors()
|
||||
walk(fn)
|
||||
if base.Errors() > errorsBefore {
|
||||
return
|
||||
}
|
||||
if instrumenting {
|
||||
instrument(fn)
|
||||
}
|
||||
|
||||
// From this point, there should be no uses of Curfn. Enforce that.
|
||||
Curfn = nil
|
||||
@ -399,7 +390,11 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S
|
||||
fn := curfn.(*ir.Func)
|
||||
|
||||
if fn.Nname != nil {
|
||||
if expect := fn.Sym().Linksym(); fnsym != expect {
|
||||
expect := fn.Sym().Linksym()
|
||||
if fnsym.ABI() == obj.ABI0 {
|
||||
expect = fn.Sym().LinksymABI0()
|
||||
}
|
||||
if fnsym != expect {
|
||||
base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func ispkgin(pkgs []string) bool {
|
||||
}
|
||||
|
||||
func instrument(fn *ir.Func) {
|
||||
if fn.Pragma&ir.Norace != 0 {
|
||||
if fn.Pragma&ir.Norace != 0 || (fn.Sym().Linksym() != nil && fn.Sym().Linksym().ABIWrapper()) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,10 @@ type ptabEntry struct {
|
||||
t *types.Type
|
||||
}
|
||||
|
||||
func CountTabs() (numPTabs, numITabs int) {
|
||||
return len(ptabs), len(itabs)
|
||||
}
|
||||
|
||||
// runtime interface and reflection data structures
|
||||
var (
|
||||
signatmu sync.Mutex // protects signatset and signatslice
|
||||
@ -331,8 +335,7 @@ func deferstruct(stksize int64) *types.Type {
|
||||
// build struct holding the above fields
|
||||
s := types.NewStruct(types.NoPkg, fields)
|
||||
s.SetNoalg(true)
|
||||
s.Width = widstruct(s, s, 0, 1)
|
||||
s.Align = uint8(Widthptr)
|
||||
CalcStructSize(s)
|
||||
return s
|
||||
}
|
||||
|
||||
@ -1159,13 +1162,9 @@ func dtypesym(t *types.Type) *obj.LSym {
|
||||
if base.Ctxt.Pkgpath != "runtime" || (tbase != types.Types[tbase.Kind()] && tbase != types.ByteType && tbase != types.RuneType && tbase != types.ErrorType) { // int, float, etc
|
||||
// named types from other files are defined only by those files
|
||||
if tbase.Sym() != nil && tbase.Sym().Pkg != types.LocalPkg {
|
||||
if i, ok := typeSymIdx[tbase]; ok {
|
||||
if i := BaseTypeIndex(t); i >= 0 {
|
||||
lsym.Pkg = tbase.Sym().Pkg.Prefix
|
||||
if t != tbase {
|
||||
lsym.SymIdx = int32(i[1])
|
||||
} else {
|
||||
lsym.SymIdx = int32(i[0])
|
||||
}
|
||||
lsym.SymIdx = int32(i)
|
||||
lsym.Set(obj.AttrIndexed, true)
|
||||
}
|
||||
return lsym
|
||||
|
@ -56,8 +56,11 @@ func visitBottomUp(list []ir.Node, analyze func(list []*ir.Func, recursive bool)
|
||||
v.analyze = analyze
|
||||
v.nodeID = make(map[*ir.Func]uint32)
|
||||
for _, n := range list {
|
||||
if n.Op() == ir.ODCLFUNC && !n.Func().IsHiddenClosure() {
|
||||
v.visit(n.(*ir.Func))
|
||||
if n.Op() == ir.ODCLFUNC {
|
||||
n := n.(*ir.Func)
|
||||
if !n.Func().IsHiddenClosure() {
|
||||
v.visit(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -109,6 +112,7 @@ func (v *bottomUpVisitor) visit(n *ir.Func) uint32 {
|
||||
}
|
||||
}
|
||||
case ir.OCLOSURE:
|
||||
n := n.(*ir.ClosureExpr)
|
||||
if m := v.visit(n.Func()); m < min {
|
||||
min = m
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func findScope(marks []ir.Mark, pos src.XPos) ir.ScopeID {
|
||||
return marks[i-1].Scope
|
||||
}
|
||||
|
||||
func assembleScopes(fnsym *obj.LSym, fn ir.Node, dwarfVars []*dwarf.Var, varScopes []ir.ScopeID) []dwarf.Scope {
|
||||
func assembleScopes(fnsym *obj.LSym, fn *ir.Func, dwarfVars []*dwarf.Var, varScopes []ir.ScopeID) []dwarf.Scope {
|
||||
// Initialize the DWARF scope tree based on lexical scopes.
|
||||
dwarfScopes := make([]dwarf.Scope, 1+len(fn.Func().Parents))
|
||||
for i, parent := range fn.Func().Parents {
|
||||
|
@ -142,8 +142,9 @@ func (s *InitSchedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *type
|
||||
}
|
||||
|
||||
case ir.OSLICELIT:
|
||||
r := r.(*ir.CompLitExpr)
|
||||
// copy slice
|
||||
slicesym(l, loff, s.inittemps[r], ir.Int64Val(r.Right()))
|
||||
slicesym(l, loff, s.inittemps[r], r.Len)
|
||||
return true
|
||||
|
||||
case ir.OARRAYLIT, ir.OSTRUCTLIT:
|
||||
@ -232,14 +233,14 @@ func (s *InitSchedule) staticassign(l *ir.Name, loff int64, r ir.Node, typ *type
|
||||
}
|
||||
|
||||
case ir.OSLICELIT:
|
||||
r := r.(*ir.CompLitExpr)
|
||||
s.initplan(r)
|
||||
// Init slice.
|
||||
bound := ir.Int64Val(r.Right())
|
||||
ta := types.NewArray(r.Type().Elem(), bound)
|
||||
ta := types.NewArray(r.Type().Elem(), r.Len)
|
||||
ta.SetNoalg(true)
|
||||
a := staticname(ta)
|
||||
s.inittemps[r] = a
|
||||
slicesym(l, loff, a, bound)
|
||||
slicesym(l, loff, a, r.Len)
|
||||
// Fall through to init underlying array.
|
||||
l = a
|
||||
loff = 0
|
||||
@ -268,6 +269,7 @@ func (s *InitSchedule) staticassign(l *ir.Name, loff int64, r ir.Node, typ *type
|
||||
break
|
||||
|
||||
case ir.OCLOSURE:
|
||||
r := r.(*ir.ClosureExpr)
|
||||
if hasemptycvars(r) {
|
||||
if base.Debug.Closure > 0 {
|
||||
base.WarnfAt(r.Pos(), "closure converted to global")
|
||||
@ -425,10 +427,11 @@ func getdyn(n ir.Node, top bool) initGenType {
|
||||
return initDynamic
|
||||
|
||||
case ir.OSLICELIT:
|
||||
n := n.(*ir.CompLitExpr)
|
||||
if !top {
|
||||
return initDynamic
|
||||
}
|
||||
if ir.Int64Val(n.Right())/4 > int64(n.List().Len()) {
|
||||
if n.Len/4 > int64(n.List().Len()) {
|
||||
// <25% of entries have explicit values.
|
||||
// Very rough estimation, it takes 4 bytes of instructions
|
||||
// to initialize 1 byte of result. So don't use a static
|
||||
@ -603,14 +606,12 @@ func isSmallSliceLit(n *ir.CompLitExpr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
r := n.Right()
|
||||
|
||||
return smallintconst(r) && (n.Type().Elem().Width == 0 || ir.Int64Val(r) <= smallArrayBytes/n.Type().Elem().Width)
|
||||
return n.Type().Elem().Width == 0 || n.Len <= smallArrayBytes/n.Type().Elem().Width
|
||||
}
|
||||
|
||||
func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) {
|
||||
// make an array type corresponding the number of elements we have
|
||||
t := types.NewArray(n.Type().Elem(), ir.Int64Val(n.Right()))
|
||||
t := types.NewArray(n.Type().Elem(), n.Len)
|
||||
dowidth(t)
|
||||
|
||||
if ctxt == inNonInitFunction {
|
||||
@ -1019,7 +1020,7 @@ func stataddr(n ir.Node) (name *ir.Name, offset int64, ok bool) {
|
||||
}
|
||||
|
||||
// Check for overflow.
|
||||
if n.Type().Width != 0 && thearch.MAXWIDTH/n.Type().Width <= int64(l) {
|
||||
if n.Type().Width != 0 && MaxWidth/n.Type().Width <= int64(l) {
|
||||
break
|
||||
}
|
||||
offset += int64(l) * n.Type().Width
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"bufio"
|
||||
"bytes"
|
||||
@ -48,6 +49,22 @@ func ssaDumpInline(fn *ir.Func) {
|
||||
}
|
||||
}
|
||||
|
||||
func initSSAEnv() {
|
||||
ssaDump = os.Getenv("GOSSAFUNC")
|
||||
ssaDir = os.Getenv("GOSSADIR")
|
||||
if ssaDump != "" {
|
||||
if strings.HasSuffix(ssaDump, "+") {
|
||||
ssaDump = ssaDump[:len(ssaDump)-1]
|
||||
ssaDumpStdout = true
|
||||
}
|
||||
spl := strings.Split(ssaDump, ":")
|
||||
if len(spl) > 1 {
|
||||
ssaDump = spl[0]
|
||||
ssaDumpCFG = spl[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initssaconfig() {
|
||||
types_ := ssa.NewTypes()
|
||||
|
||||
@ -1421,7 +1438,7 @@ func (s *state) stmt(n ir.Node) {
|
||||
case ir.ORETJMP:
|
||||
b := s.exit()
|
||||
b.Kind = ssa.BlockRetJmp // override BlockRet
|
||||
b.Aux = n.Sym().Linksym()
|
||||
b.Aux = callTargetLSym(n.Sym(), s.curfn.LSym)
|
||||
|
||||
case ir.OCONTINUE, ir.OBREAK:
|
||||
var to *ssa.Block
|
||||
@ -3357,7 +3374,7 @@ type intrinsicKey struct {
|
||||
fn string
|
||||
}
|
||||
|
||||
func init() {
|
||||
func initSSATables() {
|
||||
intrinsics = map[intrinsicKey]intrinsicBuilder{}
|
||||
|
||||
var all []*sys.Arch
|
||||
@ -4826,11 +4843,11 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||
}
|
||||
case sym != nil:
|
||||
if testLateExpansion {
|
||||
aux := ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults)
|
||||
aux := ssa.StaticAuxCall(callTargetLSym(sym, s.curfn.LSym), ACArgs, ACResults)
|
||||
call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
|
||||
call.AddArgs(callArgs...)
|
||||
} else {
|
||||
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem())
|
||||
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(callTargetLSym(sym, s.curfn.LSym), ACArgs, ACResults), s.mem())
|
||||
}
|
||||
default:
|
||||
s.Fatalf("bad call type %v %v", n.Op(), n)
|
||||
@ -7291,3 +7308,46 @@ func clobberBase(n ir.Node) ir.Node {
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// callTargetLSym determines the correct LSym for 'callee' when called
|
||||
// from function 'caller'. There are a couple of different scenarios
|
||||
// to contend with here:
|
||||
//
|
||||
// 1. if 'caller' is an ABI wrapper, then we always want to use the
|
||||
// LSym from the Func for the callee.
|
||||
//
|
||||
// 2. if 'caller' is not an ABI wrapper, then we looked at the callee
|
||||
// to see if it corresponds to a "known" ABI0 symbol (e.g. assembly
|
||||
// routine defined in the current package); if so, we want the call to
|
||||
// directly target the ABI0 symbol (effectively bypassing the
|
||||
// ABIInternal->ABI0 wrapper for 'callee').
|
||||
//
|
||||
// 3. in all other cases, want the regular ABIInternal linksym
|
||||
//
|
||||
func callTargetLSym(callee *types.Sym, callerLSym *obj.LSym) *obj.LSym {
|
||||
lsym := callee.Linksym()
|
||||
if !base.Flag.ABIWrap {
|
||||
return lsym
|
||||
}
|
||||
if ir.AsNode(callee.Def) == nil {
|
||||
return lsym
|
||||
}
|
||||
ndclfunc := ir.AsNode(callee.Def).Name().Defn
|
||||
if ndclfunc == nil {
|
||||
return lsym
|
||||
}
|
||||
// check for case 1 above
|
||||
if callerLSym.ABIWrapper() {
|
||||
if nlsym := ndclfunc.Func().LSym; nlsym != nil {
|
||||
lsym = nlsym
|
||||
}
|
||||
} else {
|
||||
// check for case 2 above
|
||||
nam := ndclfunc.Func().Nname
|
||||
defABI, hasDefABI := symabiDefs[nam.Sym().LinksymName()]
|
||||
if hasDefABI && defABI == obj.ABI0 {
|
||||
lsym = nam.Sym().LinksymABI0()
|
||||
}
|
||||
}
|
||||
return lsym
|
||||
}
|
||||
|
@ -304,6 +304,14 @@ func assignop(src, dst *types.Type) (ir.Op, string) {
|
||||
var missing, have *types.Field
|
||||
var ptr int
|
||||
if implements(src, dst, &missing, &have, &ptr) {
|
||||
// Call itabname so that (src, dst)
|
||||
// gets added to itabs early, which allows
|
||||
// us to de-virtualize calls through this
|
||||
// type/interface pair later. See peekitabs in reflect.go
|
||||
if isdirectiface(src) && !dst.IsEmptyInterface() {
|
||||
NeedITab(src, dst)
|
||||
}
|
||||
|
||||
return ir.OCONVIFACE, ""
|
||||
}
|
||||
|
||||
@ -1003,6 +1011,7 @@ func adddot(n *ir.SelectorExpr) *ir.SelectorExpr {
|
||||
for c := len(path) - 1; c >= 0; c-- {
|
||||
dot := nodSym(ir.ODOT, n.Left(), path[c].field.Sym)
|
||||
dot.SetImplicit(true)
|
||||
dot.SetType(path[c].field.Type)
|
||||
n.SetLeft(dot)
|
||||
}
|
||||
case ambig:
|
||||
@ -1232,8 +1241,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
|
||||
if !instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !isifacemethod(method.Type) && !(thearch.LinkArch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) {
|
||||
// generate tail call: adjust pointer receiver and jump to embedded method.
|
||||
left := dot.Left() // skip final .M
|
||||
// TODO(mdempsky): Remove dependency on dotlist.
|
||||
if !dotlist[0].field.Type.IsPtr() {
|
||||
if !left.Type().IsPtr() {
|
||||
left = nodAddr(left)
|
||||
}
|
||||
as := ir.Nod(ir.OAS, nthis, convnop(left, rcvr))
|
||||
@ -1404,14 +1412,6 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool
|
||||
}
|
||||
}
|
||||
|
||||
// We're going to emit an OCONVIFACE.
|
||||
// Call itabname so that (t, iface)
|
||||
// gets added to itabs early, which allows
|
||||
// us to de-virtualize calls through this
|
||||
// type/interface pair later. See peekitabs in reflect.go
|
||||
if isdirectiface(t0) && !iface.IsEmptyInterface() {
|
||||
itabname(t0, iface)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var timings Timings
|
||||
|
||||
// Timings collects the execution times of labeled phases
|
||||
// which are added trough a sequence of Start/Stop calls.
|
||||
// Events may be associated with each phase via AddEvent.
|
||||
|
@ -8,13 +8,160 @@ import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
NeedFuncSym = func(*types.Sym) {}
|
||||
NeedITab = func(t, itype *types.Type) {}
|
||||
NeedRuntimeType = func(*types.Type) {}
|
||||
)
|
||||
|
||||
func TypecheckInit() {
|
||||
types.Widthptr = Widthptr
|
||||
types.Dowidth = dowidth
|
||||
initUniverse()
|
||||
dclcontext = ir.PEXTERN
|
||||
timings.Start("fe", "loadsys")
|
||||
loadsys()
|
||||
}
|
||||
|
||||
func TypecheckPackage() {
|
||||
finishUniverse()
|
||||
|
||||
typecheckok = true
|
||||
|
||||
// Process top-level declarations in phases.
|
||||
|
||||
// Phase 1: const, type, and names and types of funcs.
|
||||
// This will gather all the information about types
|
||||
// and methods but doesn't depend on any of it.
|
||||
//
|
||||
// We also defer type alias declarations until phase 2
|
||||
// to avoid cycles like #18640.
|
||||
// TODO(gri) Remove this again once we have a fix for #25838.
|
||||
|
||||
// Don't use range--typecheck can add closures to Target.Decls.
|
||||
timings.Start("fe", "typecheck", "top1")
|
||||
for i := 0; i < len(Target.Decls); i++ {
|
||||
n := Target.Decls[i]
|
||||
if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.(*ir.Decl).Left().Name().Alias()) {
|
||||
Target.Decls[i] = typecheck(n, ctxStmt)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Variable assignments.
|
||||
// To check interface assignments, depends on phase 1.
|
||||
|
||||
// Don't use range--typecheck can add closures to Target.Decls.
|
||||
timings.Start("fe", "typecheck", "top2")
|
||||
for i := 0; i < len(Target.Decls); i++ {
|
||||
n := Target.Decls[i]
|
||||
if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).Left().Name().Alias() {
|
||||
Target.Decls[i] = typecheck(n, ctxStmt)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Type check function bodies.
|
||||
// Don't use range--typecheck can add closures to Target.Decls.
|
||||
timings.Start("fe", "typecheck", "func")
|
||||
var fcount int64
|
||||
for i := 0; i < len(Target.Decls); i++ {
|
||||
n := Target.Decls[i]
|
||||
if n.Op() == ir.ODCLFUNC {
|
||||
TypecheckFuncBody(n.(*ir.Func))
|
||||
fcount++
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4: Check external declarations.
|
||||
// TODO(mdempsky): This should be handled when type checking their
|
||||
// corresponding ODCL nodes.
|
||||
timings.Start("fe", "typecheck", "externdcls")
|
||||
for i, n := range Target.Externs {
|
||||
if n.Op() == ir.ONAME {
|
||||
Target.Externs[i] = typecheck(Target.Externs[i], ctxExpr)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 5: With all user code type-checked, it's now safe to verify map keys.
|
||||
checkMapKeys()
|
||||
|
||||
// Phase 6: Decide how to capture closed variables.
|
||||
// This needs to run before escape analysis,
|
||||
// because variables captured by value do not escape.
|
||||
timings.Start("fe", "capturevars")
|
||||
for _, n := range Target.Decls {
|
||||
if n.Op() == ir.ODCLFUNC {
|
||||
n := n.(*ir.Func)
|
||||
if n.Func().OClosure != nil {
|
||||
Curfn = n
|
||||
capturevars(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
capturevarscomplete = true
|
||||
Curfn = nil
|
||||
|
||||
if base.Debug.TypecheckInl != 0 {
|
||||
// Typecheck imported function bodies if Debug.l > 1,
|
||||
// otherwise lazily when used or re-exported.
|
||||
TypecheckImports()
|
||||
}
|
||||
}
|
||||
|
||||
func TypecheckAssignExpr(n ir.Node) ir.Node { return typecheck(n, ctxExpr|ctxAssign) }
|
||||
func TypecheckExpr(n ir.Node) ir.Node { return typecheck(n, ctxExpr) }
|
||||
func TypecheckStmt(n ir.Node) ir.Node { return typecheck(n, ctxStmt) }
|
||||
|
||||
func TypecheckExprs(exprs []ir.Node) { typecheckslice(exprs, ctxExpr) }
|
||||
func TypecheckStmts(stmts []ir.Node) { typecheckslice(stmts, ctxStmt) }
|
||||
|
||||
func TypecheckCall(call *ir.CallExpr) {
|
||||
t := call.X.Type()
|
||||
if t == nil {
|
||||
panic("misuse of Call")
|
||||
}
|
||||
ctx := ctxStmt
|
||||
if t.NumResults() > 0 {
|
||||
ctx = ctxExpr | ctxMultiOK
|
||||
}
|
||||
if typecheck(call, ctx) != call {
|
||||
panic("bad typecheck")
|
||||
}
|
||||
}
|
||||
|
||||
func TypecheckCallee(n ir.Node) ir.Node {
|
||||
return typecheck(n, ctxExpr|ctxCallee)
|
||||
}
|
||||
|
||||
func TypecheckFuncBody(n *ir.Func) {
|
||||
Curfn = n
|
||||
decldepth = 1
|
||||
errorsBefore := base.Errors()
|
||||
typecheckslice(n.Body(), ctxStmt)
|
||||
checkreturn(n)
|
||||
if base.Errors() > errorsBefore {
|
||||
n.PtrBody().Set(nil) // type errors; do not compile
|
||||
}
|
||||
// Now that we've checked whether n terminates,
|
||||
// we can eliminate some obviously dead code.
|
||||
deadcode(n)
|
||||
}
|
||||
|
||||
var importlist []*ir.Func
|
||||
|
||||
func TypecheckImports() {
|
||||
for _, n := range importlist {
|
||||
if n.Inl != nil {
|
||||
typecheckinl(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To enable tracing support (-t flag), set enableTrace to true.
|
||||
const enableTrace = false
|
||||
|
||||
@ -97,23 +244,13 @@ func resolve(n ir.Node) (res ir.Node) {
|
||||
if pkgName := dotImportRefs[id]; pkgName != nil {
|
||||
pkgName.Used = true
|
||||
}
|
||||
|
||||
if sym.Def == nil {
|
||||
if _, ok := declImporter[sym]; !ok {
|
||||
return n // undeclared name
|
||||
}
|
||||
sym.Def = ir.NewDeclNameAt(src.NoXPos, sym)
|
||||
}
|
||||
n = ir.AsNode(sym.Def)
|
||||
}
|
||||
|
||||
// Stub ir.Name left for us by iimport.
|
||||
n := n.(*ir.Name)
|
||||
if inimport {
|
||||
base.Fatalf("recursive inimport")
|
||||
}
|
||||
inimport = true
|
||||
expandDecl(n)
|
||||
n = expandDecl(n)
|
||||
inimport = false
|
||||
return n
|
||||
}
|
||||
@ -1944,6 +2081,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) {
|
||||
return n
|
||||
|
||||
case ir.OCLOSURE:
|
||||
n := n.(*ir.ClosureExpr)
|
||||
typecheckclosure(n, top)
|
||||
if n.Type() == nil {
|
||||
return n
|
||||
@ -2395,7 +2533,7 @@ func typecheckMethodExpr(n *ir.SelectorExpr) (res ir.Node) {
|
||||
// to make sure to generate wrappers for anonymous
|
||||
// receiver types too.
|
||||
if mt.Sym() == nil {
|
||||
addsignat(t)
|
||||
NeedRuntimeType(t)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2428,7 +2566,7 @@ func typecheckMethodExpr(n *ir.SelectorExpr) (res ir.Node) {
|
||||
|
||||
// Issue 25065. Make sure that we emit the symbol for a local method.
|
||||
if base.Ctxt.Flag_dynlink && !inimport && (t.Sym() == nil || t.Sym().Pkg == types.LocalPkg) {
|
||||
makefuncsym(me.FuncName_.Sym())
|
||||
NeedFuncSym(me.FuncName_.Sym())
|
||||
}
|
||||
|
||||
return me
|
||||
@ -2861,7 +2999,8 @@ func typecheckcomplit(n *ir.CompLitExpr) (res ir.Node) {
|
||||
case types.TSLICE:
|
||||
length := typecheckarraylit(t.Elem(), -1, n.List().Slice(), "slice literal")
|
||||
n.SetOp(ir.OSLICELIT)
|
||||
n.SetRight(nodintconst(length))
|
||||
n.SetRight(nil)
|
||||
n.Len = length
|
||||
|
||||
case types.TMAP:
|
||||
var cs constSet
|
||||
@ -3461,7 +3600,7 @@ func typecheckfunc(n *ir.Func) {
|
||||
}
|
||||
|
||||
if base.Ctxt.Flag_dynlink && !inimport && n.Nname != nil {
|
||||
makefuncsym(n.Sym())
|
||||
NeedFuncSym(n.Sym())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
"go/constant"
|
||||
)
|
||||
|
||||
var basicTypes = [...]struct {
|
||||
@ -97,8 +98,7 @@ func initUniverse() {
|
||||
|
||||
defBasic := func(kind types.Kind, pkg *types.Pkg, name string) *types.Type {
|
||||
sym := pkg.Lookup(name)
|
||||
n := ir.NewDeclNameAt(src.NoXPos, sym)
|
||||
n.SetOp(ir.OTYPE)
|
||||
n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym)
|
||||
t := types.NewBasic(kind, n)
|
||||
n.SetType(t)
|
||||
sym.Def = n
|
||||
@ -134,8 +134,7 @@ func initUniverse() {
|
||||
|
||||
// error type
|
||||
s := types.BuiltinPkg.Lookup("error")
|
||||
n := ir.NewDeclNameAt(src.NoXPos, s)
|
||||
n.SetOp(ir.OTYPE)
|
||||
n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, s)
|
||||
types.ErrorType = types.NewNamed(n)
|
||||
types.ErrorType.SetUnderlying(makeErrorInterface())
|
||||
n.SetType(types.ErrorType)
|
||||
@ -165,14 +164,10 @@ func initUniverse() {
|
||||
}
|
||||
|
||||
s = types.BuiltinPkg.Lookup("true")
|
||||
b := nodbool(true)
|
||||
b.(*ir.Name).SetSym(lookup("true"))
|
||||
s.Def = b
|
||||
s.Def = ir.NewConstAt(src.NoXPos, s, types.UntypedBool, constant.MakeBool(true))
|
||||
|
||||
s = types.BuiltinPkg.Lookup("false")
|
||||
b = nodbool(false)
|
||||
b.(*ir.Name).SetSym(lookup("false"))
|
||||
s.Def = b
|
||||
s.Def = ir.NewConstAt(src.NoXPos, s, types.UntypedBool, constant.MakeBool(false))
|
||||
|
||||
s = lookup("_")
|
||||
types.BlankSym = s
|
||||
|
@ -26,6 +26,10 @@ const zeroValSize = 1024 // must match value of runtime/map.go:maxZero
|
||||
func walk(fn *ir.Func) {
|
||||
Curfn = fn
|
||||
errorsBefore := base.Errors()
|
||||
order(fn)
|
||||
if base.Errors() > errorsBefore {
|
||||
return
|
||||
}
|
||||
|
||||
if base.Flag.W != 0 {
|
||||
s := fmt.Sprintf("\nbefore walk %v", Curfn.Sym())
|
||||
@ -80,6 +84,10 @@ func walk(fn *ir.Func) {
|
||||
s := fmt.Sprintf("enter %v", Curfn.Sym())
|
||||
ir.DumpList(s, Curfn.Enter)
|
||||
}
|
||||
|
||||
if instrumenting {
|
||||
instrument(fn)
|
||||
}
|
||||
}
|
||||
|
||||
func walkstmtlist(s []ir.Node) {
|
||||
@ -641,11 +649,12 @@ func walkexpr1(n ir.Node, init *ir.Nodes) ir.Node {
|
||||
// transformclosure already did all preparation work.
|
||||
|
||||
// Prepend captured variables to argument list.
|
||||
n.PtrList().Prepend(n.Left().Func().ClosureEnter.Slice()...)
|
||||
n.Left().Func().ClosureEnter.Set(nil)
|
||||
clo := n.Left().(*ir.ClosureExpr)
|
||||
n.PtrList().Prepend(clo.Func().ClosureEnter.Slice()...)
|
||||
clo.Func().ClosureEnter.Set(nil)
|
||||
|
||||
// Replace OCLOSURE with ONAME/PFUNC.
|
||||
n.SetLeft(n.Left().Func().Nname)
|
||||
n.SetLeft(clo.Func().Nname)
|
||||
|
||||
// Update type of OCALLFUNC node.
|
||||
// Output arguments had not changed, but their offsets could.
|
||||
@ -693,38 +702,38 @@ func walkexpr1(n ir.Node, init *ir.Nodes) ir.Node {
|
||||
} else {
|
||||
n.(*ir.AssignStmt).SetLeft(left)
|
||||
}
|
||||
n := n.(*ir.AssignStmt)
|
||||
as := n.(*ir.AssignStmt)
|
||||
|
||||
if oaslit(n, init) {
|
||||
return ir.NodAt(n.Pos(), ir.OBLOCK, nil, nil)
|
||||
if oaslit(as, init) {
|
||||
return ir.NodAt(as.Pos(), ir.OBLOCK, nil, nil)
|
||||
}
|
||||
|
||||
if n.Right() == nil {
|
||||
if as.Right() == nil {
|
||||
// TODO(austin): Check all "implicit zeroing"
|
||||
return n
|
||||
return as
|
||||
}
|
||||
|
||||
if !instrumenting && isZero(n.Right()) {
|
||||
return n
|
||||
if !instrumenting && isZero(as.Right()) {
|
||||
return as
|
||||
}
|
||||
|
||||
switch n.Right().Op() {
|
||||
switch as.Right().Op() {
|
||||
default:
|
||||
n.SetRight(walkexpr(n.Right(), init))
|
||||
as.SetRight(walkexpr(as.Right(), init))
|
||||
|
||||
case ir.ORECV:
|
||||
// x = <-c; n.Left is x, n.Right.Left is c.
|
||||
// x = <-c; as.Left is x, as.Right.Left is c.
|
||||
// order.stmt made sure x is addressable.
|
||||
recv := n.Right().(*ir.UnaryExpr)
|
||||
recv := as.Right().(*ir.UnaryExpr)
|
||||
recv.SetLeft(walkexpr(recv.Left(), init))
|
||||
|
||||
n1 := nodAddr(n.Left())
|
||||
n1 := nodAddr(as.Left())
|
||||
r := recv.Left() // the channel
|
||||
return mkcall1(chanfn("chanrecv1", 2, r.Type()), nil, init, r, n1)
|
||||
|
||||
case ir.OAPPEND:
|
||||
// x = append(...)
|
||||
call := n.Right().(*ir.CallExpr)
|
||||
call := as.Right().(*ir.CallExpr)
|
||||
if call.Type().Elem().NotInHeap() {
|
||||
base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", call.Type().Elem())
|
||||
}
|
||||
@ -736,24 +745,24 @@ func walkexpr1(n ir.Node, init *ir.Nodes) ir.Node {
|
||||
case call.IsDDD():
|
||||
r = appendslice(call, init) // also works for append(slice, string).
|
||||
default:
|
||||
r = walkappend(call, init, n)
|
||||
r = walkappend(call, init, as)
|
||||
}
|
||||
n.SetRight(r)
|
||||
as.SetRight(r)
|
||||
if r.Op() == ir.OAPPEND {
|
||||
// Left in place for back end.
|
||||
// Do not add a new write barrier.
|
||||
// Set up address of type for back end.
|
||||
r.(*ir.CallExpr).SetLeft(typename(r.Type().Elem()))
|
||||
return n
|
||||
return as
|
||||
}
|
||||
// Otherwise, lowered for race detector.
|
||||
// Treat as ordinary assignment.
|
||||
}
|
||||
|
||||
if n.Left() != nil && n.Right() != nil {
|
||||
return convas(n, init)
|
||||
if as.Left() != nil && as.Right() != nil {
|
||||
return convas(as, init)
|
||||
}
|
||||
return n
|
||||
return as
|
||||
|
||||
case ir.OAS2:
|
||||
init.AppendNodes(n.PtrInit())
|
||||
@ -2520,15 +2529,10 @@ func vmkcall(fn ir.Node, t *types.Type, init *ir.Nodes, va []ir.Node) *ir.CallEx
|
||||
base.Fatalf("vmkcall %v needs %v args got %v", fn, n, len(va))
|
||||
}
|
||||
|
||||
call := ir.Nod(ir.OCALL, fn, nil)
|
||||
call.PtrList().Set(va)
|
||||
ctx := ctxStmt
|
||||
if fn.Type().NumResults() > 0 {
|
||||
ctx = ctxExpr | ctxMultiOK
|
||||
}
|
||||
r1 := typecheck(call, ctx)
|
||||
r1.SetType(t)
|
||||
return walkexpr(r1, init).(*ir.CallExpr)
|
||||
call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, va)
|
||||
TypecheckCall(call)
|
||||
call.SetType(t)
|
||||
return walkexpr(call, init).(*ir.CallExpr)
|
||||
}
|
||||
|
||||
func mkcall(name string, t *types.Type, init *ir.Nodes, args ...ir.Node) *ir.CallExpr {
|
||||
|
@ -136,6 +136,25 @@ func (n *AddrExpr) SetOp(op Op) {
|
||||
}
|
||||
}
|
||||
|
||||
// A BasicLit is a literal of basic type.
|
||||
type BasicLit struct {
|
||||
miniExpr
|
||||
val constant.Value
|
||||
}
|
||||
|
||||
func NewBasicLit(pos src.XPos, val constant.Value) Node {
|
||||
n := &BasicLit{val: val}
|
||||
n.op = OLITERAL
|
||||
n.pos = pos
|
||||
if k := val.Kind(); k != constant.Unknown {
|
||||
n.SetType(idealType(k))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *BasicLit) Val() constant.Value { return n.val }
|
||||
func (n *BasicLit) SetVal(val constant.Value) { n.val = val }
|
||||
|
||||
// A BinaryExpr is a binary expression X Op Y,
|
||||
// or Op(X, Y) for builtin functions that do not become calls.
|
||||
type BinaryExpr struct {
|
||||
@ -294,6 +313,7 @@ type CompLitExpr struct {
|
||||
Ntype Ntype
|
||||
List_ Nodes // initialized values
|
||||
Prealloc *Name
|
||||
Len int64 // backing array length for OSLICELIT
|
||||
}
|
||||
|
||||
func NewCompLitExpr(pos src.XPos, op Op, typ Ntype, list []Node) *CompLitExpr {
|
||||
|
@ -1189,6 +1189,7 @@ func dumpNode(w io.Writer, n Node, depth int) {
|
||||
case ODCLFUNC:
|
||||
// Func has many fields we don't want to print.
|
||||
// Bypass reflection and just print what we want.
|
||||
n := n.(*Func)
|
||||
fmt.Fprintf(w, "%+v", n.Op())
|
||||
dumpNodeHeader(w, n)
|
||||
fn := n.Func()
|
||||
|
@ -213,10 +213,21 @@ func (f *Func) SetWBPos(pos src.XPos) {
|
||||
|
||||
// funcname returns the name (without the package) of the function n.
|
||||
func FuncName(n Node) string {
|
||||
if n == nil || n.Func() == nil || n.Func().Nname == nil {
|
||||
var f *Func
|
||||
switch n := n.(type) {
|
||||
case *Func:
|
||||
f = n
|
||||
case *Name:
|
||||
f = n.Func()
|
||||
case *CallPartExpr:
|
||||
f = n.Func()
|
||||
case *ClosureExpr:
|
||||
f = n.Func()
|
||||
}
|
||||
if f == nil || f.Nname == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return n.Func().Nname.Sym().Name
|
||||
return f.Nname.Sym().Name
|
||||
}
|
||||
|
||||
// pkgFuncName returns the name of the function referenced by n, with package prepended.
|
||||
@ -231,10 +242,19 @@ func PkgFuncName(n Node) string {
|
||||
if n.Op() == ONAME {
|
||||
s = n.Sym()
|
||||
} else {
|
||||
if n.Func() == nil || n.Func().Nname == nil {
|
||||
var f *Func
|
||||
switch n := n.(type) {
|
||||
case *CallPartExpr:
|
||||
f = n.Func()
|
||||
case *ClosureExpr:
|
||||
f = n.Func()
|
||||
case *Func:
|
||||
f = n
|
||||
}
|
||||
if f == nil || f.Nname == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
s = n.Func().Nname.Sym()
|
||||
s = f.Nname.Sym()
|
||||
}
|
||||
pkg := s.Pkg
|
||||
|
||||
|
@ -147,6 +147,24 @@ func (n *Name) isExpr() {}
|
||||
// Callers must use n.CloneName to make clear they intend to create a separate name.
|
||||
func (n *Name) CloneName() *Name { c := *n; return &c }
|
||||
|
||||
// TypeDefn returns the type definition for a named OTYPE.
|
||||
// That is, given "type T Defn", it returns Defn.
|
||||
// It is used by package types.
|
||||
func (n *Name) TypeDefn() *types.Type {
|
||||
return n.Ntype.Type()
|
||||
}
|
||||
|
||||
// RecordFrameOffset records the frame offset for the name.
|
||||
// It is used by package types when laying out function arguments.
|
||||
func (n *Name) RecordFrameOffset(offset int64) {
|
||||
if n.Stackcopy != nil {
|
||||
n.Stackcopy.SetFrameOffset(offset)
|
||||
n.SetFrameOffset(0)
|
||||
} else {
|
||||
n.SetFrameOffset(offset)
|
||||
}
|
||||
}
|
||||
|
||||
// NewNameAt returns a new ONAME Node associated with symbol s at position pos.
|
||||
// The caller is responsible for setting Curfn.
|
||||
func NewNameAt(pos src.XPos, sym *types.Sym) *Name {
|
||||
@ -164,13 +182,30 @@ func NewIota(pos src.XPos, sym *types.Sym) *Name {
|
||||
return newNameAt(pos, OIOTA, sym)
|
||||
}
|
||||
|
||||
// NewDeclNameAt returns a new ONONAME Node associated with symbol s at position pos.
|
||||
// NewDeclNameAt returns a new Name associated with symbol s at position pos.
|
||||
// The caller is responsible for setting Curfn.
|
||||
func NewDeclNameAt(pos src.XPos, sym *types.Sym) *Name {
|
||||
func NewDeclNameAt(pos src.XPos, op Op, sym *types.Sym) *Name {
|
||||
if sym == nil {
|
||||
base.Fatalf("NewDeclNameAt nil")
|
||||
}
|
||||
return newNameAt(pos, ONONAME, sym)
|
||||
switch op {
|
||||
case ONAME, OTYPE, OLITERAL:
|
||||
// ok
|
||||
default:
|
||||
base.Fatalf("NewDeclNameAt op %v", op)
|
||||
}
|
||||
return newNameAt(pos, op, sym)
|
||||
}
|
||||
|
||||
// NewConstAt returns a new OLITERAL Node associated with symbol s at position pos.
|
||||
func NewConstAt(pos src.XPos, sym *types.Sym, typ *types.Type, val constant.Value) *Name {
|
||||
if sym == nil {
|
||||
base.Fatalf("NewConstAt nil")
|
||||
}
|
||||
n := newNameAt(pos, OLITERAL, sym)
|
||||
n.SetType(typ)
|
||||
n.SetVal(val)
|
||||
return n
|
||||
}
|
||||
|
||||
// newNameAt is like NewNameAt but allows sym == nil.
|
||||
@ -207,18 +242,6 @@ func (*Name) CanBeNtype() {}
|
||||
func (*Name) CanBeAnSSASym() {}
|
||||
func (*Name) CanBeAnSSAAux() {}
|
||||
|
||||
func (n *Name) SetOp(op Op) {
|
||||
if n.op != ONONAME {
|
||||
base.Fatalf("%v already has Op %v", n, n.op)
|
||||
}
|
||||
switch op {
|
||||
default:
|
||||
panic(n.no("SetOp " + op.String()))
|
||||
case OLITERAL, ONAME, OTYPE, OIOTA:
|
||||
n.op = op
|
||||
}
|
||||
}
|
||||
|
||||
// Pragma returns the PragmaFlag for p, which must be for an OTYPE.
|
||||
func (n *Name) Pragma() PragmaFlag { return n.pragma }
|
||||
|
||||
|
@ -116,6 +116,21 @@ func (n *AssignStmt) editChildren(edit func(Node) Node) {
|
||||
n.Y = maybeEdit(n.Y, edit)
|
||||
}
|
||||
|
||||
func (n *BasicLit) Format(s fmt.State, verb rune) { FmtNode(n, s, verb) }
|
||||
func (n *BasicLit) copy() Node {
|
||||
c := *n
|
||||
c.init = c.init.Copy()
|
||||
return &c
|
||||
}
|
||||
func (n *BasicLit) doChildren(do func(Node) error) error {
|
||||
var err error
|
||||
err = maybeDoList(n.init, err, do)
|
||||
return err
|
||||
}
|
||||
func (n *BasicLit) editChildren(edit func(Node) Node) {
|
||||
editList(n.init, edit)
|
||||
}
|
||||
|
||||
func (n *BinaryExpr) Format(s fmt.State, verb rune) { FmtNode(n, s, verb) }
|
||||
func (n *BinaryExpr) copy() Node {
|
||||
c := *n
|
||||
|
@ -5,6 +5,7 @@
|
||||
package ir
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/src"
|
||||
)
|
||||
@ -164,6 +165,12 @@ type BlockStmt struct {
|
||||
func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt {
|
||||
n := &BlockStmt{}
|
||||
n.pos = pos
|
||||
if !pos.IsKnown() {
|
||||
n.pos = base.Pos
|
||||
if len(list) > 0 {
|
||||
n.pos = list[0].Pos()
|
||||
}
|
||||
}
|
||||
n.op = OBLOCK
|
||||
n.List_.Set(list)
|
||||
return n
|
||||
|
@ -92,12 +92,7 @@ func ValidTypeForConst(t *types.Type, v constant.Value) bool {
|
||||
|
||||
// nodlit returns a new untyped constant with value v.
|
||||
func NewLiteral(v constant.Value) Node {
|
||||
n := newNameAt(base.Pos, OLITERAL, nil)
|
||||
if k := v.Kind(); k != constant.Unknown {
|
||||
n.SetType(idealType(k))
|
||||
n.SetVal(v)
|
||||
}
|
||||
return n
|
||||
return NewBasicLit(base.Pos, v)
|
||||
}
|
||||
|
||||
func idealType(ct constant.Kind) *types.Type {
|
||||
|
@ -93,6 +93,23 @@ func (sym *Sym) Linksym() *obj.LSym {
|
||||
return base.Ctxt.LookupInit(sym.LinksymName(), initPkg)
|
||||
}
|
||||
|
||||
// LinksymABI0 looks up or creates an ABI0 linker symbol for "sym",
|
||||
// in cases where we want to specifically select the ABI0 version of
|
||||
// a symbol (typically used only for ABI wrappers).
|
||||
func (sym *Sym) LinksymABI0() *obj.LSym {
|
||||
if sym == nil {
|
||||
return nil
|
||||
}
|
||||
initPkg := func(r *obj.LSym) {
|
||||
if sym.Linkname != "" {
|
||||
r.Pkg = "_"
|
||||
} else {
|
||||
r.Pkg = sym.Pkg.Prefix
|
||||
}
|
||||
}
|
||||
return base.Ctxt.LookupABIInit(sym.LinksymName(), obj.ABI0, initPkg)
|
||||
}
|
||||
|
||||
// Less reports whether symbol a is ordered before symbol b.
|
||||
//
|
||||
// Symbols are ordered exported before non-exported, then by name, and
|
||||
|
@ -20,6 +20,18 @@ type Object interface {
|
||||
Type() *Type
|
||||
}
|
||||
|
||||
// A TypeObject is an Object representing a named type.
|
||||
type TypeObject interface {
|
||||
Object
|
||||
TypeDefn() *Type // for "type T Defn", returns Defn
|
||||
}
|
||||
|
||||
// A VarObject is an Object representing a function argument, variable, or struct field.
|
||||
type VarObject interface {
|
||||
Object
|
||||
RecordFrameOffset(int64) // save frame offset
|
||||
}
|
||||
|
||||
//go:generate stringer -type EType -trimprefix T
|
||||
|
||||
// EType describes a kind of type.
|
||||
|
@ -635,6 +635,10 @@ const (
|
||||
// ContentAddressable indicates this is a content-addressable symbol.
|
||||
AttrContentAddressable
|
||||
|
||||
// ABI wrapper is set for compiler-generated text symbols that
|
||||
// convert between ABI0 and ABIInternal calling conventions.
|
||||
AttrABIWrapper
|
||||
|
||||
// attrABIBase is the value at which the ABI is encoded in
|
||||
// Attribute. This must be last; all bits after this are
|
||||
// assumed to be an ABI value.
|
||||
@ -660,6 +664,7 @@ func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 }
|
||||
func (a Attribute) Indexed() bool { return a&AttrIndexed != 0 }
|
||||
func (a Attribute) UsedInIface() bool { return a&AttrUsedInIface != 0 }
|
||||
func (a Attribute) ContentAddressable() bool { return a&AttrContentAddressable != 0 }
|
||||
func (a Attribute) ABIWrapper() bool { return a&AttrABIWrapper != 0 }
|
||||
|
||||
func (a *Attribute) Set(flag Attribute, value bool) {
|
||||
if value {
|
||||
@ -695,6 +700,7 @@ var textAttrStrings = [...]struct {
|
||||
{bit: AttrTopFrame, s: "TOPFRAME"},
|
||||
{bit: AttrIndexed, s: ""},
|
||||
{bit: AttrContentAddressable, s: ""},
|
||||
{bit: AttrABIWrapper, s: "ABIWRAPPER"},
|
||||
}
|
||||
|
||||
// TextAttrString formats a for printing in as part of a TEXT prog.
|
||||
|
@ -80,6 +80,11 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string
|
||||
if !strings.HasPrefix(s.Name, "\"\".") {
|
||||
continue
|
||||
}
|
||||
if s.ABIWrapper() {
|
||||
// Don't create an args_stackmap symbol reference for an ABI
|
||||
// wrapper function
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for p := s.Func().Text; p != nil; p = p.Link {
|
||||
if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == objabi.FUNCDATA_ArgsPointerMaps {
|
||||
@ -134,6 +139,7 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
|
||||
s.Set(AttrNoSplit, flag&NOSPLIT != 0)
|
||||
s.Set(AttrReflectMethod, flag&REFLECTMETHOD != 0)
|
||||
s.Set(AttrWrapper, flag&WRAPPER != 0)
|
||||
s.Set(AttrABIWrapper, flag&ABIWRAPPER != 0)
|
||||
s.Set(AttrNeedCtxt, flag&NEEDCTXT != 0)
|
||||
s.Set(AttrNoFrame, flag&NOFRAME != 0)
|
||||
s.Set(AttrTopFrame, flag&TOPFRAME != 0)
|
||||
|
@ -51,4 +51,7 @@ const (
|
||||
// Function is the top of the call stack. Call stack unwinders should stop
|
||||
// at this function.
|
||||
TOPFRAME = 2048
|
||||
|
||||
// Function is an ABI wrapper.
|
||||
ABIWRAPPER = 4096
|
||||
)
|
||||
|
@ -637,7 +637,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
}
|
||||
}
|
||||
|
||||
if !p.From.Sym.NoSplit() || p.From.Sym.Wrapper() {
|
||||
if !p.From.Sym.NoSplit() || (p.From.Sym.Wrapper() && !p.From.Sym.ABIWrapper()) {
|
||||
p = obj.Appendp(p, newprog)
|
||||
p = load_g_cx(ctxt, p, newprog) // load g into CX
|
||||
}
|
||||
@ -690,7 +690,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
p.To.Reg = REG_BP
|
||||
}
|
||||
|
||||
if cursym.Func().Text.From.Sym.Wrapper() {
|
||||
if cursym.Func().Text.From.Sym.Wrapper() && !cursym.Func().Text.From.Sym.ABIWrapper() {
|
||||
// if g._panic != nil && g._panic.argp == FP {
|
||||
// g._panic.argp = bottom-of-frame
|
||||
// }
|
||||
|
@ -1820,7 +1820,7 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
|
||||
Errorf(nil, "%v", err)
|
||||
return
|
||||
}
|
||||
if rsrc != 0 {
|
||||
if len(rsrc) != 0 {
|
||||
setpersrc(ctxt, rsrc)
|
||||
}
|
||||
ctxt.Textp = append(ctxt.Textp, textp...)
|
||||
|
@ -92,11 +92,10 @@ var (
|
||||
FlagRound = flag.Int("R", -1, "set address rounding `quantum`")
|
||||
FlagTextAddr = flag.Int64("T", -1, "set text segment `address`")
|
||||
flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
|
||||
|
||||
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
||||
memprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
||||
memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
|
||||
|
||||
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
||||
memprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
||||
memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
|
||||
flagAbiWrap = false
|
||||
benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
|
||||
benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")
|
||||
)
|
||||
@ -135,6 +134,9 @@ func Main(arch *sys.Arch, theArch Arch) {
|
||||
objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
|
||||
objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog)
|
||||
objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg)
|
||||
if objabi.Regabi_enabled != 0 {
|
||||
flag.BoolVar(&flagAbiWrap, "abiwrap", true, "support ABI wrapper functions")
|
||||
}
|
||||
|
||||
objabi.Flagparse(usage)
|
||||
|
||||
|
@ -253,7 +253,7 @@ type Dll struct {
|
||||
}
|
||||
|
||||
var (
|
||||
rsrcsym loader.Sym
|
||||
rsrcsyms []loader.Sym
|
||||
PESECTHEADR int32
|
||||
PEFILEHEADR int32
|
||||
pe64 int
|
||||
@ -1508,46 +1508,56 @@ func (ctxt *Link) dope() {
|
||||
initdynexport(ctxt)
|
||||
}
|
||||
|
||||
func setpersrc(ctxt *Link, sym loader.Sym) {
|
||||
if rsrcsym != 0 {
|
||||
func setpersrc(ctxt *Link, syms []loader.Sym) {
|
||||
if len(rsrcsyms) != 0 {
|
||||
Errorf(nil, "too many .rsrc sections")
|
||||
}
|
||||
|
||||
rsrcsym = sym
|
||||
rsrcsyms = syms
|
||||
}
|
||||
|
||||
func addpersrc(ctxt *Link) {
|
||||
if rsrcsym == 0 {
|
||||
if len(rsrcsyms) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
data := ctxt.loader.Data(rsrcsym)
|
||||
size := len(data)
|
||||
h := pefile.addSection(".rsrc", size, size)
|
||||
var size int64
|
||||
for _, rsrcsym := range rsrcsyms {
|
||||
size += ctxt.loader.SymSize(rsrcsym)
|
||||
}
|
||||
h := pefile.addSection(".rsrc", int(size), int(size))
|
||||
h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA
|
||||
h.checkOffset(ctxt.Out.Offset())
|
||||
|
||||
// relocation
|
||||
relocs := ctxt.loader.Relocs(rsrcsym)
|
||||
for i := 0; i < relocs.Count(); i++ {
|
||||
r := relocs.At(i)
|
||||
p := data[r.Off():]
|
||||
val := uint32(int64(h.virtualAddress) + r.Add())
|
||||
|
||||
// 32-bit little-endian
|
||||
p[0] = byte(val)
|
||||
|
||||
p[1] = byte(val >> 8)
|
||||
p[2] = byte(val >> 16)
|
||||
p[3] = byte(val >> 24)
|
||||
for _, rsrcsym := range rsrcsyms {
|
||||
// A split resource happens when the actual resource data and its relocations are
|
||||
// split across multiple sections, denoted by a $01 or $02 at the end of the .rsrc
|
||||
// section name.
|
||||
splitResources := strings.Contains(ctxt.loader.SymName(rsrcsym), ".rsrc$")
|
||||
relocs := ctxt.loader.Relocs(rsrcsym)
|
||||
data := ctxt.loader.Data(rsrcsym)
|
||||
for ri := 0; ri < relocs.Count(); ri++ {
|
||||
r := relocs.At(ri)
|
||||
p := data[r.Off():]
|
||||
val := uint32(int64(h.virtualAddress) + r.Add())
|
||||
if splitResources {
|
||||
// If we're a split resource section, and that section has relocation
|
||||
// symbols, then the data that it points to doesn't actually begin at
|
||||
// the virtual address listed in this current section, but rather
|
||||
// begins at the section immediately after this one. So, in order to
|
||||
// calculate the proper virtual address of the data it's pointing to,
|
||||
// we have to add the length of this section to the virtual address.
|
||||
// This works because .rsrc sections are divided into two (but not more)
|
||||
// of these sections.
|
||||
val += uint32(len(data))
|
||||
}
|
||||
binary.LittleEndian.PutUint32(p, val)
|
||||
}
|
||||
ctxt.Out.Write(data)
|
||||
}
|
||||
|
||||
ctxt.Out.Write(data)
|
||||
h.pad(ctxt.Out, uint32(size))
|
||||
|
||||
// update data directory
|
||||
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
|
||||
|
||||
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize
|
||||
}
|
||||
|
||||
|
@ -102,6 +102,41 @@ func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
|
||||
elfshnum = xosect.Elfsect.(*ElfShdr).shnum
|
||||
}
|
||||
|
||||
sname := ldr.SymExtname(x)
|
||||
|
||||
// For functions with ABI wrappers, we have to make sure that we
|
||||
// don't wind up with two elf symbol table entries with the same
|
||||
// name (since this will generated an error from the external
|
||||
// linker). In the CgoExportStatic case, we want the ABI0 symbol
|
||||
// to have the primary symbol table entry (since it's going to be
|
||||
// called from C), so we rename the ABIInternal symbol. In all
|
||||
// other cases, we rename the ABI0 symbol, since we want
|
||||
// cross-load-module calls to target ABIInternal.
|
||||
//
|
||||
// TODO: generalize this for non-ELF (put the rename code in the
|
||||
// loader, and store the rename result in SymExtname).
|
||||
//
|
||||
// TODO: avoid the ldr.Lookup calls below by instead using an aux
|
||||
// sym or marker relocation to associate the wrapper with the
|
||||
// wrapped function.
|
||||
//
|
||||
if flagAbiWrap {
|
||||
if !ldr.IsExternal(x) && ldr.SymType(x) == sym.STEXT {
|
||||
// First case
|
||||
if ldr.SymVersion(x) == sym.SymVerABIInternal {
|
||||
if s2 := ldr.Lookup(sname, sym.SymVerABI0); s2 != 0 && ldr.AttrCgoExportStatic(s2) && ldr.SymType(s2) == sym.STEXT {
|
||||
sname = sname + ".abiinternal"
|
||||
}
|
||||
}
|
||||
// Second case
|
||||
if ldr.SymVersion(x) == sym.SymVerABI0 && !ldr.AttrCgoExportStatic(x) {
|
||||
if s2 := ldr.Lookup(sname, sym.SymVerABIInternal); s2 != 0 && ldr.SymType(s2) == sym.STEXT {
|
||||
sname = sname + ".abi0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// One pass for each binding: elf.STB_LOCAL, elf.STB_GLOBAL,
|
||||
// maybe one day elf.STB_WEAK.
|
||||
bind := elf.STB_GLOBAL
|
||||
@ -140,8 +175,6 @@ func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
|
||||
other |= 3 << 5
|
||||
}
|
||||
|
||||
sname := ldr.SymExtname(x)
|
||||
|
||||
// When dynamically linking, we create Symbols by reading the names from
|
||||
// the symbol tables of the shared libraries and so the names need to
|
||||
// match exactly. Tools like DTrace will have to wait for now.
|
||||
|
@ -157,8 +157,9 @@ func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loa
|
||||
|
||||
// Load loads the PE file pn from input.
|
||||
// Symbols are written into syms, and a slice of the text symbols is returned.
|
||||
// If an .rsrc section is found, its symbol is returned as rsrc.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc loader.Sym, err error) {
|
||||
// If an .rsrc section or set of .rsrc$xx sections is found, its symbols are
|
||||
// returned as rsrc.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) {
|
||||
lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) {
|
||||
s := l.LookupOrCreateSym(name, version)
|
||||
sb := l.MakeSymbolUpdater(s)
|
||||
@ -176,7 +177,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
// TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
|
||||
f, err := pe.NewFile(sr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
@ -211,21 +212,21 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
bld.SetType(sym.STEXT)
|
||||
|
||||
default:
|
||||
return nil, 0, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
|
||||
return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
|
||||
}
|
||||
|
||||
if bld.Type() != sym.SNOPTRBSS {
|
||||
data, err := sect.Data()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
sectdata[sect] = data
|
||||
bld.SetData(data)
|
||||
}
|
||||
bld.SetSize(int64(sect.Size))
|
||||
sectsyms[sect] = s
|
||||
if sect.Name == ".rsrc" {
|
||||
rsrc = s
|
||||
if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") {
|
||||
rsrc = append(rsrc, s)
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,22 +247,23 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
continue
|
||||
}
|
||||
|
||||
splitResources := strings.HasPrefix(rsect.Name, ".rsrc$")
|
||||
sb := l.MakeSymbolUpdater(sectsyms[rsect])
|
||||
for j, r := range rsect.Relocs {
|
||||
if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
|
||||
return nil, 0, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
|
||||
return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
|
||||
}
|
||||
pesym := &f.COFFSymbols[r.SymbolTableIndex]
|
||||
_, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if gosym == 0 {
|
||||
name, err := pesym.FullName(f.StringTable)
|
||||
if err != nil {
|
||||
name = string(pesym.Name[:])
|
||||
}
|
||||
return nil, 0, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
|
||||
return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
|
||||
}
|
||||
|
||||
rSym := gosym
|
||||
@ -271,11 +273,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
var rType objabi.RelocType
|
||||
switch arch.Family {
|
||||
default:
|
||||
return nil, 0, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
|
||||
return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
|
||||
case sys.I386, sys.AMD64:
|
||||
switch r.Type {
|
||||
default:
|
||||
return nil, 0, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
|
||||
return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
|
||||
|
||||
case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
|
||||
IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
|
||||
@ -302,7 +304,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
case sys.ARM:
|
||||
switch r.Type {
|
||||
default:
|
||||
return nil, 0, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
|
||||
return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
|
||||
|
||||
case IMAGE_REL_ARM_SECREL:
|
||||
rType = objabi.R_PCREL
|
||||
@ -323,8 +325,9 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
|
||||
// ld -r could generate multiple section symbols for the
|
||||
// same section but with different values, we have to take
|
||||
// that into account
|
||||
if issect(pesym) {
|
||||
// that into account, or in the case of split resources,
|
||||
// the section and its symbols are split into two sections.
|
||||
if issect(pesym) || splitResources {
|
||||
rAdd += int64(pesym.Value)
|
||||
}
|
||||
|
||||
@ -346,7 +349,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
|
||||
name, err := pesym.FullName(f.StringTable)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if name == "" {
|
||||
continue
|
||||
@ -384,7 +387,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
|
||||
bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if pesym.SectionNumber == 0 { // extern
|
||||
@ -402,14 +405,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
} else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
|
||||
sect = f.Sections[pesym.SectionNumber-1]
|
||||
if _, found := sectsyms[sect]; !found {
|
||||
return nil, 0, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
|
||||
return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
|
||||
}
|
||||
} else {
|
||||
return nil, 0, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
|
||||
return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
|
||||
}
|
||||
|
||||
if sect == nil {
|
||||
return nil, 0, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
if l.OuterSym(s) != 0 {
|
||||
@ -418,7 +421,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
}
|
||||
outerName := l.SymName(l.OuterSym(s))
|
||||
sectName := l.SymName(sectsyms[sect])
|
||||
return nil, 0, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
|
||||
return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
|
||||
}
|
||||
|
||||
bld = makeUpdater(l, bld, s)
|
||||
@ -429,7 +432,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
bld.SetSize(4)
|
||||
if l.SymType(sectsym) == sym.STEXT {
|
||||
if bld.External() && !bld.DuplicateOK() {
|
||||
return nil, 0, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
|
||||
return nil, nil, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
|
||||
}
|
||||
bld.SetExternal(true)
|
||||
}
|
||||
@ -446,7 +449,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
if l.SymType(s) == sym.STEXT {
|
||||
for ; s != 0; s = l.SubSym(s) {
|
||||
if l.AttrOnList(s) {
|
||||
return nil, 0, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
|
||||
return nil, nil, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
|
||||
}
|
||||
l.SetAttrOnList(s, true)
|
||||
textp = append(textp, s)
|
||||
|
@ -786,6 +786,25 @@ func TestPErsrc(t *testing.T) {
|
||||
if !bytes.Contains(b, []byte("Hello Gophers!")) {
|
||||
t.Fatalf("binary does not contain expected content")
|
||||
}
|
||||
|
||||
pkgdir = filepath.Join("testdata", "testPErsrc-complex")
|
||||
exe = filepath.Join(tmpdir, "a.exe")
|
||||
cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
|
||||
cmd.Dir = pkgdir
|
||||
// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("building failed: %v, output:\n%s", err, out)
|
||||
}
|
||||
|
||||
// Check that the binary contains the rsrc data
|
||||
b, err = ioutil.ReadFile(exe)
|
||||
if err != nil {
|
||||
t.Fatalf("reading output failed: %v", err)
|
||||
}
|
||||
if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
|
||||
t.Fatalf("binary does not contain expected content")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentAddressableSymbols(t *testing.T) {
|
||||
|
43
src/cmd/link/testdata/testPErsrc-complex/main.go
vendored
Normal file
43
src/cmd/link/testdata/testPErsrc-complex/main.go
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test that a PE rsrc section is handled correctly, when the object files
|
||||
// have been created by llvm-rc or msvc's rc.exe, which means there's the
|
||||
// @feat.00 symbol as well as split .rsrc$00 and .rsrc$01 section to deal with.
|
||||
//
|
||||
// rsrc.syso is created with:
|
||||
// windres -i a.rc -o rsrc.syso -O coff
|
||||
// where this windres calls into llvm-rc and llvm-cvtres. The source file,
|
||||
// a.rc, simply contains a reference to its own bytes:
|
||||
//
|
||||
// resname RCDATA a.rc
|
||||
//
|
||||
// Object dumping the resultant rsrc.syso, we can see the split sections and
|
||||
// the @feat.00 SEH symbol:
|
||||
//
|
||||
// rsrc.syso: file format coff-x86-64
|
||||
//
|
||||
// architecture: x86_64
|
||||
// start address: 0x0000000000000000
|
||||
//
|
||||
// Export Table:
|
||||
// Sections:
|
||||
// Idx Name Size VMA Type
|
||||
// 0 .rsrc$01 00000068 0000000000000000 DATA
|
||||
// 1 .rsrc$02 00000018 0000000000000000 DATA
|
||||
//
|
||||
// SYMBOL TABLE:
|
||||
// [ 0](sec -1)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000011 @feat.00
|
||||
// [ 1](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rsrc$01
|
||||
// AUX scnlen 0x68 nreloc 1 nlnno 0 checksum 0x0 assoc 0 comdat 0
|
||||
// [ 3](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rsrc$02
|
||||
// AUX scnlen 0x18 nreloc 0 nlnno 0 checksum 0x0 assoc 0 comdat 0
|
||||
// [ 5](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 $R000000
|
||||
// RELOCATION RECORDS FOR [.rsrc$01]:
|
||||
// OFFSET TYPE VALUE
|
||||
// 0000000000000048 IMAGE_REL_AMD64_ADDR32NB $R000000
|
||||
|
||||
package main
|
||||
|
||||
func main() {}
|
BIN
src/cmd/link/testdata/testPErsrc-complex/rsrc.syso
vendored
Normal file
BIN
src/cmd/link/testdata/testPErsrc-complex/rsrc.syso
vendored
Normal file
Binary file not shown.
@ -43,7 +43,7 @@ func main() {
|
||||
ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
|
||||
ar.scan(ar.printContents)
|
||||
case 'r':
|
||||
ar = openArchive(os.Args[2], os.O_RDWR, os.Args[3:])
|
||||
ar = openArchive(os.Args[2], os.O_RDWR|os.O_CREATE, os.Args[3:])
|
||||
ar.addFiles()
|
||||
case 'c':
|
||||
ar = openArchive(os.Args[2], os.O_RDWR|os.O_TRUNC|os.O_CREATE, os.Args[3:])
|
||||
@ -124,10 +124,13 @@ func openArchive(name string, mode int, files []string) *Archive {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var a *archive.Archive
|
||||
if mode&os.O_CREATE != 0 { // the c command
|
||||
if mode&os.O_TRUNC != 0 { // the c command
|
||||
a, err = archive.New(f)
|
||||
} else {
|
||||
a, err = archive.Parse(f, verbose)
|
||||
if err != nil && mode&os.O_CREATE != 0 { // the r command
|
||||
a, err = archive.New(f)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -315,20 +318,25 @@ func (ar *Archive) extractContents1(e *archive.Entry, out io.Writer) {
|
||||
}
|
||||
|
||||
// isGoCompilerObjFile reports whether file is an object file created
|
||||
// by the Go compiler, which is an archive file with exactly two entries:
|
||||
// __.PKGDEF and _go_.o.
|
||||
// by the Go compiler, which is an archive file with exactly one entry
|
||||
// of __.PKGDEF, or _go_.o, or both entries.
|
||||
func isGoCompilerObjFile(a *archive.Archive) bool {
|
||||
if len(a.Entries) != 2 {
|
||||
switch len(a.Entries) {
|
||||
case 1:
|
||||
return (a.Entries[0].Type == archive.EntryGoObj && a.Entries[0].Name == "_go_.o") ||
|
||||
(a.Entries[0].Type == archive.EntryPkgDef && a.Entries[0].Name == "__.PKGDEF")
|
||||
case 2:
|
||||
var foundPkgDef, foundGo bool
|
||||
for _, e := range a.Entries {
|
||||
if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" {
|
||||
foundPkgDef = true
|
||||
}
|
||||
if e.Type == archive.EntryGoObj && e.Name == "_go_.o" {
|
||||
foundGo = true
|
||||
}
|
||||
}
|
||||
return foundPkgDef && foundGo
|
||||
default:
|
||||
return false
|
||||
}
|
||||
var foundPkgDef, foundGo bool
|
||||
for _, e := range a.Entries {
|
||||
if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" {
|
||||
foundPkgDef = true
|
||||
}
|
||||
if e.Type == archive.EntryGoObj && e.Name == "_go_.o" {
|
||||
foundGo = true
|
||||
}
|
||||
}
|
||||
return foundPkgDef && foundGo
|
||||
}
|
||||
|
@ -302,6 +302,95 @@ func TestIssue21703(t *testing.T) {
|
||||
run(goBin, "tool", "compile", "-I", ".", "b.go")
|
||||
}
|
||||
|
||||
// Test the "c" command can "see through" the archive generated by the compiler.
|
||||
// This is peculiar. (See issue #43271)
|
||||
func TestCreateWithCompilerObj(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
dir := tmpDir(t)
|
||||
defer os.RemoveAll(dir)
|
||||
src := filepath.Join(dir, "p.go")
|
||||
prog := "package p; var X = 42\n"
|
||||
err := os.WriteFile(src, []byte(prog), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
run := func(args ...string) string {
|
||||
return doRun(t, dir, args...)
|
||||
}
|
||||
|
||||
goBin := testenv.GoToolPath(t)
|
||||
run(goBin, "build", "cmd/pack") // writes pack binary to dir
|
||||
run(goBin, "tool", "compile", "-pack", "-o", "p.a", "p.go")
|
||||
run("./pack", "c", "packed.a", "p.a")
|
||||
fi, err := os.Stat(filepath.Join(dir, "p.a"))
|
||||
if err != nil {
|
||||
t.Fatalf("stat p.a failed: %v", err)
|
||||
}
|
||||
fi2, err := os.Stat(filepath.Join(dir, "packed.a"))
|
||||
if err != nil {
|
||||
t.Fatalf("stat packed.a failed: %v", err)
|
||||
}
|
||||
// For compiler-generated object file, the "c" command is
|
||||
// expected to get (essentially) the same file back, instead
|
||||
// of packing it into a new archive with a single entry.
|
||||
if want, got := fi.Size(), fi2.Size(); want != got {
|
||||
t.Errorf("packed file with different size: want %d, got %d", want, got)
|
||||
}
|
||||
|
||||
// Test -linkobj flag as well.
|
||||
run(goBin, "tool", "compile", "-linkobj", "p2.a", "-o", "p.x", "p.go")
|
||||
run("./pack", "c", "packed2.a", "p2.a")
|
||||
fi, err = os.Stat(filepath.Join(dir, "p2.a"))
|
||||
if err != nil {
|
||||
t.Fatalf("stat p2.a failed: %v", err)
|
||||
}
|
||||
fi2, err = os.Stat(filepath.Join(dir, "packed2.a"))
|
||||
if err != nil {
|
||||
t.Fatalf("stat packed2.a failed: %v", err)
|
||||
}
|
||||
if want, got := fi.Size(), fi2.Size(); want != got {
|
||||
t.Errorf("packed file with different size: want %d, got %d", want, got)
|
||||
}
|
||||
|
||||
run("./pack", "c", "packed3.a", "p.x")
|
||||
fi, err = os.Stat(filepath.Join(dir, "p.x"))
|
||||
if err != nil {
|
||||
t.Fatalf("stat p.x failed: %v", err)
|
||||
}
|
||||
fi2, err = os.Stat(filepath.Join(dir, "packed3.a"))
|
||||
if err != nil {
|
||||
t.Fatalf("stat packed3.a failed: %v", err)
|
||||
}
|
||||
if want, got := fi.Size(), fi2.Size(); want != got {
|
||||
t.Errorf("packed file with different size: want %d, got %d", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
// Test the "r" command creates the output file if it does not exist.
|
||||
func TestRWithNonexistentFile(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
dir := tmpDir(t)
|
||||
defer os.RemoveAll(dir)
|
||||
src := filepath.Join(dir, "p.go")
|
||||
prog := "package p; var X = 42\n"
|
||||
err := os.WriteFile(src, []byte(prog), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
run := func(args ...string) string {
|
||||
return doRun(t, dir, args...)
|
||||
}
|
||||
|
||||
goBin := testenv.GoToolPath(t)
|
||||
run(goBin, "build", "cmd/pack") // writes pack binary to dir
|
||||
run(goBin, "tool", "compile", "-o", "p.o", "p.go")
|
||||
run("./pack", "r", "p.a", "p.o") // should succeed
|
||||
}
|
||||
|
||||
// doRun runs a program in a directory and returns the output.
|
||||
func doRun(t *testing.T, dir string, args ...string) string {
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
|
@ -1728,67 +1728,67 @@ TEXT runtime·debugCallPanicked(SB),NOSPLIT,$16-16
|
||||
TEXT runtime·panicIndex<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ AX, x+0(FP)
|
||||
MOVQ CX, y+8(FP)
|
||||
JMP runtime·goPanicIndex(SB)
|
||||
JMP runtime·goPanicIndex<ABIInternal>(SB)
|
||||
TEXT runtime·panicIndexU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ AX, x+0(FP)
|
||||
MOVQ CX, y+8(FP)
|
||||
JMP runtime·goPanicIndexU(SB)
|
||||
JMP runtime·goPanicIndexU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceAlen<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ CX, x+0(FP)
|
||||
MOVQ DX, y+8(FP)
|
||||
JMP runtime·goPanicSliceAlen(SB)
|
||||
JMP runtime·goPanicSliceAlen<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceAlenU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ CX, x+0(FP)
|
||||
MOVQ DX, y+8(FP)
|
||||
JMP runtime·goPanicSliceAlenU(SB)
|
||||
JMP runtime·goPanicSliceAlenU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceAcap<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ CX, x+0(FP)
|
||||
MOVQ DX, y+8(FP)
|
||||
JMP runtime·goPanicSliceAcap(SB)
|
||||
JMP runtime·goPanicSliceAcap<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceAcapU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ CX, x+0(FP)
|
||||
MOVQ DX, y+8(FP)
|
||||
JMP runtime·goPanicSliceAcapU(SB)
|
||||
JMP runtime·goPanicSliceAcapU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceB<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ AX, x+0(FP)
|
||||
MOVQ CX, y+8(FP)
|
||||
JMP runtime·goPanicSliceB(SB)
|
||||
JMP runtime·goPanicSliceB<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceBU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ AX, x+0(FP)
|
||||
MOVQ CX, y+8(FP)
|
||||
JMP runtime·goPanicSliceBU(SB)
|
||||
JMP runtime·goPanicSliceBU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3Alen<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ DX, x+0(FP)
|
||||
MOVQ BX, y+8(FP)
|
||||
JMP runtime·goPanicSlice3Alen(SB)
|
||||
JMP runtime·goPanicSlice3Alen<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3AlenU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ DX, x+0(FP)
|
||||
MOVQ BX, y+8(FP)
|
||||
JMP runtime·goPanicSlice3AlenU(SB)
|
||||
JMP runtime·goPanicSlice3AlenU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3Acap<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ DX, x+0(FP)
|
||||
MOVQ BX, y+8(FP)
|
||||
JMP runtime·goPanicSlice3Acap(SB)
|
||||
JMP runtime·goPanicSlice3Acap<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3AcapU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ DX, x+0(FP)
|
||||
MOVQ BX, y+8(FP)
|
||||
JMP runtime·goPanicSlice3AcapU(SB)
|
||||
JMP runtime·goPanicSlice3AcapU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3B<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ CX, x+0(FP)
|
||||
MOVQ DX, y+8(FP)
|
||||
JMP runtime·goPanicSlice3B(SB)
|
||||
JMP runtime·goPanicSlice3B<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3BU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ CX, x+0(FP)
|
||||
MOVQ DX, y+8(FP)
|
||||
JMP runtime·goPanicSlice3BU(SB)
|
||||
JMP runtime·goPanicSlice3BU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3C<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ AX, x+0(FP)
|
||||
MOVQ CX, y+8(FP)
|
||||
JMP runtime·goPanicSlice3C(SB)
|
||||
JMP runtime·goPanicSlice3C<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3CU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
MOVQ AX, x+0(FP)
|
||||
MOVQ CX, y+8(FP)
|
||||
JMP runtime·goPanicSlice3CU(SB)
|
||||
JMP runtime·goPanicSlice3CU<ABIInternal>(SB)
|
||||
|
||||
#ifdef GOOS_android
|
||||
// Use the free TLS_SLOT_APP slot #2 on Android Q.
|
||||
|
@ -13,10 +13,11 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
_EINTR = 0x4
|
||||
_EFAULT = 0xe
|
||||
_EAGAIN = 0x23
|
||||
_ENOSYS = 0x4e
|
||||
_EINTR = 0x4
|
||||
_EFAULT = 0xe
|
||||
_EAGAIN = 0x23
|
||||
_ENOSYS = 0x4e
|
||||
_ETIMEDOUT = 0x3c
|
||||
|
||||
_O_NONBLOCK = 0x4
|
||||
_O_CLOEXEC = 0x100000
|
||||
|
@ -13,10 +13,11 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
_EINTR = 0x4
|
||||
_EFAULT = 0xe
|
||||
_EAGAIN = 0x23
|
||||
_ENOSYS = 0x4e
|
||||
_EINTR = 0x4
|
||||
_EFAULT = 0xe
|
||||
_EAGAIN = 0x23
|
||||
_ENOSYS = 0x4e
|
||||
_ETIMEDOUT = 0x3c
|
||||
|
||||
_O_NONBLOCK = 0x4
|
||||
_O_CLOEXEC = 0x100000
|
||||
|
@ -13,10 +13,11 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
_EINTR = 0x4
|
||||
_EFAULT = 0xe
|
||||
_EAGAIN = 0x23
|
||||
_ENOSYS = 0x4e
|
||||
_EINTR = 0x4
|
||||
_EFAULT = 0xe
|
||||
_EAGAIN = 0x23
|
||||
_ENOSYS = 0x4e
|
||||
_ETIMEDOUT = 0x3c
|
||||
|
||||
_O_NONBLOCK = 0x4
|
||||
_O_CLOEXEC = 0x100000
|
||||
|
@ -13,10 +13,11 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
_EINTR = 0x4
|
||||
_EFAULT = 0xe
|
||||
_EAGAIN = 0x23
|
||||
_ENOSYS = 0x4e
|
||||
_EINTR = 0x4
|
||||
_EFAULT = 0xe
|
||||
_EAGAIN = 0x23
|
||||
_ENOSYS = 0x4e
|
||||
_ETIMEDOUT = 0x3c
|
||||
|
||||
_O_NONBLOCK = 0x4
|
||||
_O_CLOEXEC = 0x100000
|
||||
|
96
src/runtime/metrics/example_test.go
Normal file
96
src/runtime/metrics/example_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package metrics_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/metrics"
|
||||
)
|
||||
|
||||
func ExampleRead_readingOneMetric() {
|
||||
// Name of the metric we want to read.
|
||||
const myMetric = "/memory/classes/heap/free:bytes"
|
||||
|
||||
// Create a sample for the metric.
|
||||
sample := make([]metrics.Sample, 1)
|
||||
sample[0].Name = myMetric
|
||||
|
||||
// Sample the metric.
|
||||
metrics.Read(sample)
|
||||
|
||||
// Check if the metric is actually supported.
|
||||
// If it's not, the resulting value will always have
|
||||
// kind KindBad.
|
||||
if sample[0].Value.Kind() == metrics.KindBad {
|
||||
panic(fmt.Sprintf("metric %q no longer supported", myMetric))
|
||||
}
|
||||
|
||||
// Handle the result.
|
||||
//
|
||||
// It's OK to assume a particular Kind for a metric;
|
||||
// they're guaranteed not to change.
|
||||
freeBytes := sample[0].Value.Uint64()
|
||||
|
||||
fmt.Printf("free but not released memory: %d\n", freeBytes)
|
||||
}
|
||||
|
||||
func ExampleRead_readingAllMetrics() {
|
||||
// Get descriptions for all supported metrics.
|
||||
descs := metrics.All()
|
||||
|
||||
// Create a sample for each metric.
|
||||
samples := make([]metrics.Sample, len(descs))
|
||||
for i := range samples {
|
||||
samples[i].Name = descs[i].Name
|
||||
}
|
||||
|
||||
// Sample the metrics. Re-use the samples slice if you can!
|
||||
metrics.Read(samples)
|
||||
|
||||
// Iterate over all results.
|
||||
for _, sample := range samples {
|
||||
// Pull out the name and value.
|
||||
name, value := sample.Name, sample.Value
|
||||
|
||||
// Handle each sample.
|
||||
switch value.Kind() {
|
||||
case metrics.KindUint64:
|
||||
fmt.Printf("%s: %d\n", name, value.Uint64())
|
||||
case metrics.KindFloat64:
|
||||
fmt.Printf("%s: %f\n", name, value.Float64())
|
||||
case metrics.KindFloat64Histogram:
|
||||
// The histogram may be quite large, so let's just pull out
|
||||
// a crude estimate for the median for the sake of this example.
|
||||
fmt.Printf("%s: %f\n", name, medianBucket(value.Float64Histogram()))
|
||||
case metrics.KindBad:
|
||||
// This should never happen because all metrics are supported
|
||||
// by construction.
|
||||
panic("bug in runtime/metrics package!")
|
||||
default:
|
||||
// This may happen as new metrics get added.
|
||||
//
|
||||
// The safest thing to do here is to simply log it somewhere
|
||||
// as something to look into, but ignore it for now.
|
||||
// In the worst case, you might temporarily miss out on a new metric.
|
||||
fmt.Printf("%s: unexpected metric Kind: %v\n", name, value.Kind())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func medianBucket(h *metrics.Float64Histogram) float64 {
|
||||
total := uint64(0)
|
||||
for _, count := range h.Counts {
|
||||
total += count
|
||||
}
|
||||
thresh := total / 2
|
||||
total = 0
|
||||
for i, count := range h.Counts {
|
||||
total += count
|
||||
if total > thresh {
|
||||
return h.Buckets[i]
|
||||
}
|
||||
}
|
||||
panic("should not happen")
|
||||
}
|
@ -166,7 +166,7 @@ func futexsleep1(addr *uint32, val uint32, ns int64) {
|
||||
utp = &ut
|
||||
}
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, unsafe.Sizeof(*utp), utp)
|
||||
if ret >= 0 || ret == -_EINTR {
|
||||
if ret >= 0 || ret == -_EINTR || ret == -_ETIMEDOUT {
|
||||
return
|
||||
}
|
||||
print("umtx_wait addr=", addr, " val=", val, " ret=", ret, "\n")
|
||||
@ -208,7 +208,6 @@ func newosproc(mp *m) {
|
||||
|
||||
var oset sigset
|
||||
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
|
||||
// TODO: Check for error.
|
||||
ret := thr_new(¶m, int32(unsafe.Sizeof(param)))
|
||||
sigprocmask(_SIG_SETMASK, &oset, nil)
|
||||
if ret < 0 {
|
||||
|
@ -2882,7 +2882,9 @@ func wakeNetPoller(when int64) {
|
||||
} else {
|
||||
// There are no threads in the network poller, try to get
|
||||
// one there so it can handle new timers.
|
||||
wakep()
|
||||
if GOOS != "plan9" { // Temporary workaround - see issue #42303.
|
||||
wakep()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5128,6 +5130,26 @@ func sysmon() {
|
||||
}
|
||||
}
|
||||
mDoFixup()
|
||||
if GOOS == "netbsd" {
|
||||
// netpoll is responsible for waiting for timer
|
||||
// expiration, so we typically don't have to worry
|
||||
// about starting an M to service timers. (Note that
|
||||
// sleep for timeSleepUntil above simply ensures sysmon
|
||||
// starts running again when that timer expiration may
|
||||
// cause Go code to run again).
|
||||
//
|
||||
// However, netbsd has a kernel bug that sometimes
|
||||
// misses netpollBreak wake-ups, which can lead to
|
||||
// unbounded delays servicing timers. If we detect this
|
||||
// overrun, then startm to get something to handle the
|
||||
// timer.
|
||||
//
|
||||
// See issue 42515 and
|
||||
// https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50094.
|
||||
if next, _ := timeSleepUntil(); next < now {
|
||||
startm(nil, false)
|
||||
}
|
||||
}
|
||||
if atomic.Load(&scavenge.sysmonWake) != 0 {
|
||||
// Kick the scavenger awake if someone requested it.
|
||||
wakeScavenger()
|
||||
|
@ -7,6 +7,7 @@
|
||||
package race_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -71,9 +72,24 @@ func TestOutput(t *testing.T) {
|
||||
"GORACE="+test.gorace,
|
||||
)
|
||||
got, _ := cmd.CombinedOutput()
|
||||
if !regexp.MustCompile(test.re).MatchString(string(got)) {
|
||||
t.Fatalf("failed test case %v, expect:\n%v\ngot:\n%s",
|
||||
test.name, test.re, got)
|
||||
matched := false
|
||||
for _, re := range test.re {
|
||||
if regexp.MustCompile(re).MatchString(string(got)) {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
exp := fmt.Sprintf("expect:\n%v\n", test.re[0])
|
||||
if len(test.re) > 1 {
|
||||
exp = fmt.Sprintf("expected one of %d patterns:\n",
|
||||
len(test.re))
|
||||
for k, re := range test.re {
|
||||
exp += fmt.Sprintf("pattern %d:\n%v\n", k, re)
|
||||
}
|
||||
}
|
||||
t.Fatalf("failed test case %v, %sgot:\n%s",
|
||||
test.name, exp, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,7 +100,7 @@ var tests = []struct {
|
||||
goos string
|
||||
gorace string
|
||||
source string
|
||||
re string
|
||||
re []string
|
||||
}{
|
||||
{"simple", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
@ -107,7 +123,7 @@ func racer(x *int, done chan bool) {
|
||||
store(x, 42)
|
||||
done <- true
|
||||
}
|
||||
`, `==================
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Write at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.store\(\)
|
||||
@ -129,7 +145,7 @@ Goroutine [0-9] \(running\) created at:
|
||||
==================
|
||||
Found 1 data race\(s\)
|
||||
exit status 66
|
||||
`},
|
||||
`}},
|
||||
|
||||
{"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", `
|
||||
package main
|
||||
@ -143,7 +159,7 @@ func main() {
|
||||
x = 43
|
||||
<-done
|
||||
}
|
||||
`, `exit status 13`},
|
||||
`, []string{`exit status 13`}},
|
||||
|
||||
{"strip_path_prefix", "run", "", "atexit_sleep_ms=0 strip_path_prefix=/main.", `
|
||||
package main
|
||||
@ -157,9 +173,9 @@ func main() {
|
||||
x = 43
|
||||
<-done
|
||||
}
|
||||
`, `
|
||||
`, []string{`
|
||||
go:7 \+0x[0-9,a-f]+
|
||||
`},
|
||||
`}},
|
||||
|
||||
{"halt_on_error", "run", "", "atexit_sleep_ms=0 halt_on_error=1", `
|
||||
package main
|
||||
@ -173,10 +189,10 @@ func main() {
|
||||
x = 43
|
||||
<-done
|
||||
}
|
||||
`, `
|
||||
`, []string{`
|
||||
==================
|
||||
exit status 66
|
||||
`},
|
||||
`}},
|
||||
|
||||
{"test_fails_on_race", "test", "", "atexit_sleep_ms=0", `
|
||||
package main_test
|
||||
@ -193,12 +209,12 @@ func TestFail(t *testing.T) {
|
||||
<-done
|
||||
t.Log(t.Failed())
|
||||
}
|
||||
`, `
|
||||
`, []string{`
|
||||
==================
|
||||
--- FAIL: TestFail \(0...s\)
|
||||
.*main_test.go:14: true
|
||||
.*testing.go:.*: race detected during execution of test
|
||||
FAIL`},
|
||||
FAIL`}},
|
||||
|
||||
{"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
@ -211,11 +227,11 @@ func main() {
|
||||
data[0] = 1
|
||||
<-done
|
||||
}
|
||||
`, `
|
||||
`, []string{`
|
||||
runtime\.slicebytetostring\(\)
|
||||
.*/runtime/string\.go:.*
|
||||
main\.main\.func1\(\)
|
||||
.*/main.go:7`},
|
||||
.*/main.go:7`}},
|
||||
|
||||
// Test for https://golang.org/issue/33309
|
||||
{"midstack_inlining_traceback", "run", "linux", "atexit_sleep_ms=0", `
|
||||
@ -241,7 +257,7 @@ func g(c chan int) {
|
||||
func h(c chan int) {
|
||||
c <- x
|
||||
}
|
||||
`, `==================
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Read at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.h\(\)
|
||||
@ -261,7 +277,7 @@ Goroutine [0-9] \(running\) created at:
|
||||
==================
|
||||
Found 1 data race\(s\)
|
||||
exit status 66
|
||||
`},
|
||||
`}},
|
||||
|
||||
// Test for https://golang.org/issue/17190
|
||||
{"external_cgo_thread", "run", "linux", "atexit_sleep_ms=0", `
|
||||
@ -300,7 +316,25 @@ func main() {
|
||||
racy++
|
||||
<- done
|
||||
}
|
||||
`, `==================
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Read at 0x[0-9,a-f]+ by main goroutine:
|
||||
main\.main\(\)
|
||||
.*/main\.go:34 \+0x[0-9,a-f]+
|
||||
|
||||
Previous write at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.goCallback\(\)
|
||||
.*/main\.go:27 \+0x[0-9,a-f]+
|
||||
_cgoexp_[0-9a-z]+_goCallback\(\)
|
||||
.*_cgo_gotypes\.go:[0-9]+ \+0x[0-9,a-f]+
|
||||
_cgoexp_[0-9a-z]+_goCallback\(\)
|
||||
<autogenerated>:1 \+0x[0-9,a-f]+
|
||||
|
||||
Goroutine [0-9] \(running\) created at:
|
||||
runtime\.newextram\(\)
|
||||
.*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
|
||||
==================`,
|
||||
`==================
|
||||
WARNING: DATA RACE
|
||||
Read at 0x[0-9,a-f]+ by .*:
|
||||
main\..*
|
||||
@ -313,7 +347,7 @@ Previous write at 0x[0-9,a-f]+ by .*:
|
||||
Goroutine [0-9] \(running\) created at:
|
||||
runtime\.newextram\(\)
|
||||
.*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
|
||||
==================`},
|
||||
==================`}},
|
||||
{"second_test_passes", "test", "", "atexit_sleep_ms=0", `
|
||||
package main_test
|
||||
import "testing"
|
||||
@ -331,11 +365,11 @@ func TestFail(t *testing.T) {
|
||||
|
||||
func TestPass(t *testing.T) {
|
||||
}
|
||||
`, `
|
||||
`, []string{`
|
||||
==================
|
||||
--- FAIL: TestFail \(0...s\)
|
||||
.*testing.go:.*: race detected during execution of test
|
||||
FAIL`},
|
||||
FAIL`}},
|
||||
{"mutex", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
import (
|
||||
@ -366,7 +400,7 @@ func main() {
|
||||
}
|
||||
wg.Wait()
|
||||
if (data == iterations*(threads+1)) { fmt.Println("pass") }
|
||||
}`, `pass`},
|
||||
}`, []string{`pass`}},
|
||||
// Test for https://github.com/golang/go/issues/37355
|
||||
{"chanmm", "run", "", "atexit_sleep_ms=0", `
|
||||
package main
|
||||
@ -395,7 +429,7 @@ func main() {
|
||||
wg.Wait()
|
||||
_ = data
|
||||
}
|
||||
`, `==================
|
||||
`, []string{`==================
|
||||
WARNING: DATA RACE
|
||||
Write at 0x[0-9,a-f]+ by goroutine [0-9]:
|
||||
main\.main\.func2\(\)
|
||||
@ -408,5 +442,5 @@ Previous write at 0x[0-9,a-f]+ by main goroutine:
|
||||
Goroutine [0-9] \(running\) created at:
|
||||
main\.main\(\)
|
||||
.*/main.go:[0-9]+ \+0x[0-9,a-f]+
|
||||
==================`},
|
||||
==================`}},
|
||||
}
|
||||
|
@ -467,7 +467,7 @@ func setNonblock(fd int32) {
|
||||
//go:cgo_import_dynamic libc_pthread_create pthread_create "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_pthread_self pthread_self "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_pthread_kill pthread_kill "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_exit _exit "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_raise raise "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
//go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib"
|
||||
|
@ -13,12 +13,16 @@
|
||||
TEXT runtime·sys_umtx_op(SB),NOSPLIT,$-4
|
||||
MOVL $454, AX
|
||||
INT $0x80
|
||||
JAE 2(PC)
|
||||
NEGL AX
|
||||
MOVL AX, ret+20(FP)
|
||||
RET
|
||||
|
||||
TEXT runtime·thr_new(SB),NOSPLIT,$-4
|
||||
MOVL $455, AX
|
||||
INT $0x80
|
||||
JAE 2(PC)
|
||||
NEGL AX
|
||||
MOVL AX, ret+8(FP)
|
||||
RET
|
||||
|
||||
@ -120,6 +124,8 @@ TEXT runtime·pipe2(SB),NOSPLIT,$12-16
|
||||
MOVL flags+0(FP), BX
|
||||
MOVL BX, 8(SP)
|
||||
INT $0x80
|
||||
JAE 2(PC)
|
||||
NEGL AX
|
||||
MOVL AX, errno+12(FP)
|
||||
RET
|
||||
|
||||
|
@ -18,6 +18,8 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
|
||||
MOVQ ut+24(FP), R8
|
||||
MOVL $454, AX
|
||||
SYSCALL
|
||||
JCC 2(PC)
|
||||
NEGQ AX
|
||||
MOVL AX, ret+32(FP)
|
||||
RET
|
||||
|
||||
@ -26,6 +28,8 @@ TEXT runtime·thr_new(SB),NOSPLIT,$0
|
||||
MOVL size+8(FP), SI
|
||||
MOVL $455, AX
|
||||
SYSCALL
|
||||
JCC 2(PC)
|
||||
NEGQ AX
|
||||
MOVL AX, ret+16(FP)
|
||||
RET
|
||||
|
||||
@ -118,6 +122,8 @@ TEXT runtime·pipe2(SB),NOSPLIT,$0-20
|
||||
MOVL flags+0(FP), SI
|
||||
MOVL $542, AX
|
||||
SYSCALL
|
||||
JCC 2(PC)
|
||||
NEGQ AX
|
||||
MOVL AX, errno+16(FP)
|
||||
RET
|
||||
|
||||
|
@ -51,6 +51,7 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
|
||||
ADD $20, R13 // arg 5 is passed on stack
|
||||
MOVW $SYS__umtx_op, R7
|
||||
SWI $0
|
||||
RSB.CS $0, R0
|
||||
SUB $20, R13
|
||||
// BCS error
|
||||
MOVW R0, ret+20(FP)
|
||||
@ -61,6 +62,7 @@ TEXT runtime·thr_new(SB),NOSPLIT,$0
|
||||
MOVW size+4(FP), R1
|
||||
MOVW $SYS_thr_new, R7
|
||||
SWI $0
|
||||
RSB.CS $0, R0
|
||||
MOVW R0, ret+8(FP)
|
||||
RET
|
||||
|
||||
@ -144,6 +146,7 @@ TEXT runtime·pipe2(SB),NOSPLIT,$0-16
|
||||
MOVW flags+0(FP), R1
|
||||
MOVW $SYS_pipe2, R7
|
||||
SWI $0
|
||||
RSB.CS $0, R0
|
||||
MOVW R0, errno+12(FP)
|
||||
RET
|
||||
|
||||
|
@ -60,6 +60,9 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
|
||||
MOVD ut+24(FP), R4
|
||||
MOVD $SYS__umtx_op, R8
|
||||
SVC
|
||||
BCC ok
|
||||
NEG R0, R0
|
||||
ok:
|
||||
MOVW R0, ret+32(FP)
|
||||
RET
|
||||
|
||||
@ -69,6 +72,9 @@ TEXT runtime·thr_new(SB),NOSPLIT,$0
|
||||
MOVW size+8(FP), R1
|
||||
MOVD $SYS_thr_new, R8
|
||||
SVC
|
||||
BCC ok
|
||||
NEG R0, R0
|
||||
ok:
|
||||
MOVW R0, ret+16(FP)
|
||||
RET
|
||||
|
||||
|
@ -35,3 +35,5 @@
|
||||
// Function is the top of the call stack. Call stack unwinders should stop
|
||||
// at this function.
|
||||
#define TOPFRAME 2048
|
||||
// Function is an ABI wrapper.
|
||||
#define ABIWRAPPER 4096
|
||||
|
@ -125,7 +125,7 @@ while(<>) {
|
||||
# without reading the header.
|
||||
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
|
||||
|
||||
if ($darwin && $func eq "ptrace") {
|
||||
if ($darwin && $func eq "ptrace1") {
|
||||
# The ptrace function is called from forkAndExecInChild where stack
|
||||
# growth is forbidden.
|
||||
$text .= "//go:nosplit\n"
|
||||
|
14
src/syscall/ptrace_darwin.go
Normal file
14
src/syscall/ptrace_darwin.go
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !ios
|
||||
|
||||
package syscall
|
||||
|
||||
// Nosplit because it is called from forkAndExecInChild.
|
||||
//
|
||||
//go:nosplit
|
||||
func ptrace(request int, pid int, addr uintptr, data uintptr) error {
|
||||
return ptrace1(request, pid, addr, data)
|
||||
}
|
12
src/syscall/ptrace_ios.go
Normal file
12
src/syscall/ptrace_ios.go
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package syscall
|
||||
|
||||
// Nosplit because it is called from forkAndExecInChild.
|
||||
//
|
||||
//go:nosplit
|
||||
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
|
||||
panic("unimplemented")
|
||||
}
|
@ -21,7 +21,7 @@ func setTimeval(sec, usec int64) Timeval {
|
||||
//sys Stat(path string, stat *Stat_t) (err error) = SYS_stat64
|
||||
//sys Statfs(path string, stat *Statfs_t) (err error) = SYS_statfs64
|
||||
//sys fstatat(fd int, path string, stat *Stat_t, flags int) (err error) = SYS_fstatat64
|
||||
//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
|
||||
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace
|
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) {
|
||||
k.Ident = uint64(fd)
|
||||
|
@ -21,7 +21,7 @@ func setTimeval(sec, usec int64) Timeval {
|
||||
//sys Stat(path string, stat *Stat_t) (err error)
|
||||
//sys Statfs(path string, stat *Statfs_t) (err error)
|
||||
//sys fstatat(fd int, path string, stat *Stat_t, flags int) (err error)
|
||||
//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
|
||||
//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace
|
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) {
|
||||
k.Ident = uint64(fd)
|
||||
|
@ -1971,7 +1971,7 @@ func libc_fstatat64_trampoline()
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
//go:nosplit
|
||||
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
|
||||
func ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) {
|
||||
_, _, e1 := syscall6(funcPC(libc_ptrace_trampoline), uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
|
@ -1971,7 +1971,7 @@ func libc_fstatat_trampoline()
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
//go:nosplit
|
||||
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
|
||||
func ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) {
|
||||
_, _, e1 := syscall6(funcPC(libc_ptrace_trampoline), uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
|
@ -54,27 +54,30 @@ func f(n int) {
|
||||
x uintptr
|
||||
}
|
||||
|
||||
// We want to force an illegal instruction, to get a crash
|
||||
// at a PC value != 0.
|
||||
// We want to force a seg fault, to get a crash at a PC value != 0.
|
||||
// Not all systems make the data section non-executable.
|
||||
ill := make([]byte, 64)
|
||||
switch runtime.GOARCH {
|
||||
case "386", "amd64":
|
||||
binary.LittleEndian.PutUint16(ill, 0x0b0f) // ud2
|
||||
ill = append(ill, 0x89, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00) // MOVL AX, 0
|
||||
case "arm":
|
||||
binary.LittleEndian.PutUint32(ill, 0xe7f000f0) // no name, but permanently undefined
|
||||
binary.LittleEndian.PutUint32(ill, 0xe3a00000) // MOVW $0, R0
|
||||
binary.LittleEndian.PutUint32(ill, 0xe5800000) // MOVW R0, (R0)
|
||||
case "arm64":
|
||||
binary.LittleEndian.PutUint32(ill, 0xd4207d00) // brk #1000
|
||||
binary.LittleEndian.PutUint32(ill, 0xf90003ff) // MOVD ZR, (ZR)
|
||||
case "ppc64":
|
||||
binary.BigEndian.PutUint32(ill, 0x7fe00008) // trap
|
||||
binary.BigEndian.PutUint32(ill, 0xf8000000) // MOVD R0, (R0)
|
||||
case "ppc64le":
|
||||
binary.LittleEndian.PutUint32(ill, 0x7fe00008) // trap
|
||||
binary.LittleEndian.PutUint32(ill, 0xf8000000) // MOVD R0, (R0)
|
||||
case "mips", "mips64":
|
||||
binary.BigEndian.PutUint32(ill, 0x00000034) // trap
|
||||
binary.BigEndian.PutUint32(ill, 0xfc000000) // MOVV R0, (R0)
|
||||
case "mipsle", "mips64le":
|
||||
binary.LittleEndian.PutUint32(ill, 0x00000034) // trap
|
||||
binary.LittleEndian.PutUint32(ill, 0xfc000000) // MOVV R0, (R0)
|
||||
case "s390x":
|
||||
binary.BigEndian.PutUint32(ill, 0) // undefined instruction
|
||||
ill = append(ill, 0xa7, 0x09, 0x00, 0x00) // MOVD $0, R0
|
||||
ill = append(ill, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x24) // MOVD R0, (R0)
|
||||
case "riscv64":
|
||||
binary.LittleEndian.PutUint32(ill, 0x00003023) // MOV X0, (X0)
|
||||
default:
|
||||
// Just leave it as 0 and hope for the best.
|
||||
}
|
||||
|
@ -353,7 +353,14 @@ TestCases:
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build")
|
||||
// Turn off ABI0 wrapper generation for now. The problem here is
|
||||
// that in these test cases main.main is an assembly routine,
|
||||
// thus calls to it will have to go through an ABI wrapper. The
|
||||
// ABI wrapper will consume some stack space, which throws off
|
||||
// the numbers.
|
||||
workaround := "-gcflags=-abiwrap=0"
|
||||
|
||||
cmd := exec.Command("go", "build", workaround)
|
||||
cmd.Dir = dir
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
|
Loading…
Reference in New Issue
Block a user