Skip to content

Commit

Permalink
First version, at least it runs :)
Browse files Browse the repository at this point in the history
Change-Id: Id214fff7c9a8656fc64fb59e8040d09170a90ba1
  • Loading branch information
rasky committed Jul 31, 2019
1 parent 9195948 commit 50db447
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 48 deletions.
7 changes: 6 additions & 1 deletion src/cmd/compile/internal/gc/gsubr.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,12 @@ func ggloblnod(nam *Node) {
if nam.Type != nil && !types.Haspointers(nam.Type) {
flags |= obj.NOPTR
}
Ctxt.Globl(s, nam.Type.Width, flags)
ot := int(nam.Type.Width)
if nam.HasInitFunc() {
initfun := lookup("init.var." + nam.Sym.Name)
ot = dsymptr(s, ot, initfun.Linksym(), 0)
}
Ctxt.Globl(s, int64(ot), flags)
}

func ggloblsym(s *obj.LSym, width int32, flags int16) {
Expand Down
152 changes: 127 additions & 25 deletions src/cmd/compile/internal/gc/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package gc
import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"strings"
)

// A function named init is a special case.
Expand All @@ -24,45 +25,109 @@ func renameinit() *types.Sym {
return s
}

func buildinitfunc(name string, body []*Node) *types.Sym {
lineno = body[0].Pos
initvarfn := lookup(name)
disableExport(initvarfn)
fn := dclfunc(initvarfn, nod(OTFUNC, nil, nil))
for _, dcl := range dummyInitFn.Func.Dcl {
dcl.Name.Curfn = fn
}
fn.Func.Dcl = append(fn.Func.Dcl, dummyInitFn.Func.Dcl...)
dummyInitFn.Func.Dcl = nil

fn.Nbody.Set(body)
funcbody()

fn = typecheck(fn, ctxStmt)
Curfn = fn
typecheckslice(body, ctxStmt)
Curfn = nil
funccompile(fn)
return initvarfn
}

// fninit makes an initialization record for the package.
// See runtime/proc.go:initTask for its layout.
// The 3 tasks for initialization are:
// 1) Initialize all of the packages the current package depends on.
// 2) Initialize all the variables that have initializers.
// 3) Run any init functions.
// 3) Run any user-defined init functions.
func fninit(n []*Node) {

// Find all the statements that initialize global variables and need to
// be executed at startup; we will be creating init functions for each of
// these.
nf := initOrder(n)

var deps []*obj.LSym // initTask records for packages the current package depends on
var fns []*obj.LSym // functions to call for package initialization
var deps []*obj.LSym // initTask records for packages the current package depends on
var fns []*obj.LSym // functions to call for package initialization
var varfns []*obj.LSym // functions to call to initialize variables (generated)

// Find imported packages with init tasks.
for _, s := range types.InitSyms {
deps = append(deps, s.Linksym())
}

// Make a function that contains all the initialization statements.
if len(nf) > 0 {
lineno = nf[0].Pos // prolog/epilog gets line number of first init stmt
initializers := lookup("init")
disableExport(initializers)
fn := dclfunc(initializers, nod(OTFUNC, nil, nil))
for _, dcl := range dummyInitFn.Func.Dcl {
dcl.Name.Curfn = fn
var initvarNotPure []*Node

// For each statement that initializes a global variable, create a init function
// called init.var.NN. We create separate functions (rather than a single one)
// because the linker might be able to remove them, if the corresponding
// variable ends up being unused.
for i := 0; i < len(nf); i++ {
var body []*Node

// Record that the initialized variable(s) (LHS of the assign statement)
// have an initialization function associated to them. This will allow
// ggloblnod() later to emit relocations for them.
var varname string
switch nf[i].Op {
case OAS:
varname = nf[i].Left.Sym.Name

// Slice literals might have the backing array allocated statically,
// and assignments are generated to temporary slots (before going
// into the backing store). Treat these as non-pure: we do not
// have a good way of handling them as we don't know which
// variable they refer to.
if strings.HasPrefix(varname, ".stmp_") {
initvarNotPure = append(initvarNotPure, nf[i])
continue
}
nf[i].Left.SetHasInitFunc(true)

// Struct literals might generate init functions, in case of large
// structures where only some fields are initialized. In this case,
// we need to generate a single symbol for all the assignments that
// refer to the same variable.
var j int
for j = i + 1; j < len(nf); j++ {
if !(nf[j].Op == OAS && nf[j].Left.Sym.Name == nf[i].Left.Sym.Name) {
break
}
}
body = nf[i:j]
i = j - 1
case OAS2FUNC, OAS2DOTTYPE, OAS2MAPR, OAS2RECV:
nf[i].List.First().SetHasInitFunc(true)
//FIXME: must refer to same init function, or create wrapper
//nf[i].List.Second().SetHasInitFunc(true)
varname = nf[i].List.First().Sym.Name
body = nf[i : i+1]
default:
Fatalf("unknown init statement: %v", n)
}
fn.Func.Dcl = append(fn.Func.Dcl, dummyInitFn.Func.Dcl...)
dummyInitFn.Func.Dcl = nil

fn.Nbody.Set(nf)
funcbody()

fn = typecheck(fn, ctxStmt)
Curfn = fn
typecheckslice(nf, ctxStmt)
Curfn = nil
funccompile(fn)
fns = append(fns, initializers.Linksym())

initvarfn := buildinitfunc("init.var."+varname, body)
varfns = append(varfns, initvarfn.Linksym())
}

if len(initvarNotPure) != 0 {
initotherfn := buildinitfunc("init.other", initvarNotPure)
fns = append(fns, initotherfn.Linksym())
}

if dummyInitFn.Func.Dcl != nil {
// We only generate temps using dummyInitFn if there
// are package-scope initialization statements, so
Expand All @@ -76,11 +141,11 @@ func fninit(n []*Node) {
fns = append(fns, s.Linksym())
}

if len(deps) == 0 && len(fns) == 0 && localpkg.Name != "main" && localpkg.Name != "runtime" {
if len(deps) == 0 && len(fns) == 0 && len(varfns) == 0 && localpkg.Name != "main" && localpkg.Name != "runtime" {
return // nothing to initialize
}

// Make an .inittask structure.
// Make an .inittask structure, that will be processed by the runtime
sym := lookup(".inittask")
nn := newname(sym)
nn.Type = types.Types[TUINT8] // dummy type
Expand All @@ -92,15 +157,52 @@ func fninit(n []*Node) {
ot = duintptr(lsym, ot, 0) // state: not initialized yet
ot = duintptr(lsym, ot, uint64(len(deps)))
ot = duintptr(lsym, ot, uint64(len(fns)))
ot = duintptr(lsym, ot, uint64(len(varfns)))
for _, d := range deps {
ot = dsymptr(lsym, ot, d, 0)
}
for _, f := range fns {
ot = dsymptr(lsym, ot, f, 0)
}
for _, f := range varfns {
ot = dsymptr(lsym, ot, f, 0)
}
// An initTask has pointers, but none into the Go heap.
// It's not quite read only, the state field must be modifiable.
ggloblsym(lsym, int32(ot), obj.NOPTR)

/*
// For each init.var function, save the corresponding variable name into
// a table. This will be used by the linker to remove init.var functions
// that initialise unused functions. Rather than storing these names within
// .inittask, we use a different symbol so that this table itself will
// be removed by the final binary (as it's only useful to the linker).
sym = lookup(".initvarnames")
nn = newname(sym)
nn.Type = types.Types[TUINT8] // dummy type
nn.SetClass(PEXTERN)
sym.Def = asTypesNode(nn)
exportsym(nn)
lsym = sym.Linksym()
ot = 0
ot = duintptr(lsym, ot, uint64(len(nf)))
for _, f := range nf {
switch f.Op {
case OAS:
ot = duint8(lsym, ot, 0xcd) // 1 => single variable reference
ot = dsymptr(lsym, ot, f.Left.Sym.Linksym(), 0)
case OAS2FUNC, OAS2DOTTYPE, OAS2MAPR, OAS2RECV:
ot = duint8(lsym, ot, 0xcc) // 2 => double variable reference
ot = dsymptr(lsym, ot, f.List.First().Sym.Linksym(), 0)
ot = dsymptr(lsym, ot, f.List.Second().Sym.Linksym(), 0)
default:
Fatalf("unknown init statement: %v", n)
}
}
// FIXME(rasky): check if obj.NOPTR is required. Probably flags don't
// matter as this symbol is going away during linking.
ggloblsym(lsym, int32(ot), obj.NOPTR)
*/
}

func (n *Node) checkInitFuncSignature() {
Expand Down
31 changes: 17 additions & 14 deletions src/cmd/compile/internal/gc/syntax.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,21 @@ const (
_, nodeAssigned // is the variable ever assigned to
_, nodeAddrtaken // address taken, even if not moved to heap
_, nodeImplicit
_, nodeIsDDD // is the argument variadic
_, nodeDiag // already printed error about this
_, nodeColas // OAS resulting from :=
_, nodeNonNil // guaranteed to be non-nil
_, nodeNoescape // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
_, nodeBounded // bounds check unnecessary
_, nodeAddable // addressable
_, nodeHasCall // expression contains a function call
_, nodeLikely // if statement condition likely
_, nodeHasVal // node.E contains a Val
_, nodeHasOpt // node.E contains an Opt
_, nodeEmbedded // ODCLFIELD embedded type
_, nodeInlFormal // OPAUTO created by inliner, derived from callee formal
_, nodeInlLocal // OPAUTO created by inliner, derived from callee local
_, nodeIsDDD // is the argument variadic
_, nodeDiag // already printed error about this
_, nodeColas // OAS resulting from :=
_, nodeNonNil // guaranteed to be non-nil
_, nodeNoescape // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
_, nodeBounded // bounds check unnecessary
_, nodeAddable // addressable
_, nodeHasCall // expression contains a function call
_, nodeLikely // if statement condition likely
_, nodeHasVal // node.E contains a Val
_, nodeHasOpt // node.E contains an Opt
_, nodeEmbedded // ODCLFIELD embedded type
_, nodeInlFormal // OPAUTO created by inliner, derived from callee formal
_, nodeInlLocal // OPAUTO created by inliner, derived from callee local
_, nodeHasInitFunc // an init function must be generated for this node
)

func (n *Node) Class() Class { return Class(n.flags.get3(nodeClass)) }
Expand Down Expand Up @@ -188,6 +189,7 @@ func (n *Node) HasOpt() bool { return n.flags&nodeHasOpt != 0 }
func (n *Node) Embedded() bool { return n.flags&nodeEmbedded != 0 }
func (n *Node) InlFormal() bool { return n.flags&nodeInlFormal != 0 }
func (n *Node) InlLocal() bool { return n.flags&nodeInlLocal != 0 }
func (n *Node) HasInitFunc() bool { return n.flags&nodeHasInitFunc != 0 }

func (n *Node) SetClass(b Class) { n.flags.set3(nodeClass, uint8(b)) }
func (n *Node) SetWalkdef(b uint8) { n.flags.set2(nodeWalkdef, b) }
Expand Down Expand Up @@ -215,6 +217,7 @@ func (n *Node) SetHasOpt(b bool) { n.flags.set(nodeHasOpt, b) }
func (n *Node) SetEmbedded(b bool) { n.flags.set(nodeEmbedded, b) }
func (n *Node) SetInlFormal(b bool) { n.flags.set(nodeInlFormal, b) }
func (n *Node) SetInlLocal(b bool) { n.flags.set(nodeInlLocal, b) }
func (n *Node) SetHasInitFunc(b bool) { n.flags.set(nodeHasInitFunc, b) }

// Val returns the Val for the node.
func (n *Node) Val() Val {
Expand Down
75 changes: 74 additions & 1 deletion src/cmd/link/internal/ld/deadcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"fmt"
"strings"
"unicode"
"unsafe"
)

// deadcode marks all reachable symbols.
Expand Down Expand Up @@ -108,6 +109,9 @@ func deadcode(ctxt *Link) {
}
}

// Remove unreachable init functions
d.cleanupInitTask()

if ctxt.BuildMode != BuildModeShared {
// Keep a itablink if the symbol it points at is being kept.
// (When BuildModeShared, always keep itablinks.)
Expand Down Expand Up @@ -160,6 +164,7 @@ type deadcodepass struct {
ifaceMethod map[methodsig]bool // methods declared in reached interfaces
markableMethods []methodref // methods of reached types
reflectMethod bool
initTasks []string // reachable inittasks
}

func (d *deadcodepass) cleanupReloc(r *sym.Reloc) {
Expand All @@ -174,6 +179,33 @@ func (d *deadcodepass) cleanupReloc(r *sym.Reloc) {
}
}

func (d *deadcodepass) cleanupInitTask() {
for _, it := range d.initTasks {
s := d.ctxt.Syms.ROLookup(it, 0)

var R []sym.Reloc
for i := range s.R {
r := &s.R[i]
if r.Sym.Attr.Reachable() {
R = append(R, *r)
} else {
// If the symbol is not reachable, we need to nil its pointer
// within inittask and remove the relocation
if s.Attr.ReadOnly() {
// The symbol's content is backed by read-only memory.
// Copy it to writable memory.
s.P = append([]byte(nil), s.P...)
s.Attr.Set(sym.AttrReadOnly, false)
}
for i := r.Off; i < r.Off+int32(r.Siz); i++ {
s.P[i] = 0
}
}
}
s.R = R
}
}

// mark appends a symbol to the mark queue for flood filling.
func (d *deadcodepass) mark(s, parent *sym.Symbol) {
if s == nil || s.Attr.Reachable() {
Expand All @@ -193,6 +225,9 @@ func (d *deadcodepass) mark(s, parent *sym.Symbol) {
if d.ctxt.Reachparent != nil {
d.ctxt.Reachparent[s] = parent
}
if strings.HasSuffix(s.Name, ".inittask") {
d.initTasks = append(d.initTasks, s.Name)
}
d.markQueue = append(d.markQueue, s)
}

Expand Down Expand Up @@ -293,13 +328,51 @@ func (d *deadcodepass) flood() {
}
}

numrelocs := len(s.R)
if strings.HasSuffix(s.Name, ".inittask") {
// When flooding through inittask symbols, ignore variabile initialization
// functions: those need to stay alive only if they're reached
// through the variables they initialize (which contain a relocation
// to them), not just because they're init functions.
type initTask struct {
state uintptr
ndeps uintptr
nfns uintptr
nvarfns uintptr
}
var it = (*initTask)(unsafe.Pointer(&s.P[0]))
numrelocs -= int(it.nvarfns)
println("skipping relocations", s.Name, it.nvarfns)
}

/*
if strings.HasPrefix(s.Name, "main") {
println("symbol:", s.Name, s.Type.String())
}
var skipreloc int = -1
if strings.Contains(s.Name, ".init.var.") {
println("inspecting", s.Name)
for i := len(s.R)-1; i >= 0; i-- {
if s.R[i].Type == objabi.R_PCREL {
println("found skippable reloc", s.Name, i)
skipreloc = i
break
}
}
}
*/
mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype
var methods []methodref
for i := range s.R {
for i := 0; i < numrelocs; i++ {
r := &s.R[i]
if r.Sym == nil {
continue
}
// if i == skipreloc {
// println("removed::", r.Sym.Name)
// continue
// }
if r.Type == objabi.R_WEAKADDROFF {
// An R_WEAKADDROFF relocation is not reason
// enough to mark the pointed-to symbol as
Expand Down
Loading

0 comments on commit 50db447

Please sign in to comment.