mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
When a function had no body, Yyerror was called with an extra argument, leading to extraneous printouts. Add the missing verb to the Yyerror call and display the name of the bodiless function. Fixes #10030 Change-Id: I76d76c4547fb9cad1782cb11f7a5c63065a6e0c5 Reviewed-on: https://go-review.googlesource.com/6263 Reviewed-by: Rob Pike <r@golang.org>
571 lines
13 KiB
Go
571 lines
13 KiB
Go
// Copyright 2011 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 gc
|
|
|
|
import (
|
|
"cmd/internal/obj"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// "Portable" code generation.
|
|
// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
|
|
// Must code to the intersection of the three back ends.
|
|
|
|
//#include "opt.h"
|
|
|
|
var makefuncdatasym_nsym int32
|
|
|
|
func makefuncdatasym(namefmt string, funcdatakind int64) *Sym {
|
|
var nod Node
|
|
|
|
namebuf = fmt.Sprintf(namefmt, makefuncdatasym_nsym)
|
|
makefuncdatasym_nsym++
|
|
sym := Lookup(namebuf)
|
|
pnod := newname(sym)
|
|
pnod.Class = PEXTERN
|
|
Nodconst(&nod, Types[TINT32], funcdatakind)
|
|
Thearch.Gins(obj.AFUNCDATA, &nod, pnod)
|
|
return sym
|
|
}
|
|
|
|
// gvardef inserts a VARDEF for n into the instruction stream.
|
|
// VARDEF is an annotation for the liveness analysis, marking a place
|
|
// where a complete initialization (definition) of a variable begins.
|
|
// Since the liveness analysis can see initialization of single-word
|
|
// variables quite easy, gvardef is usually only called for multi-word
|
|
// or 'fat' variables, those satisfying isfat(n->type).
|
|
// However, gvardef is also called when a non-fat variable is initialized
|
|
// via a block move; the only time this happens is when you have
|
|
// return f()
|
|
// for a function with multiple return values exactly matching the return
|
|
// types of the current function.
|
|
//
|
|
// A 'VARDEF x' annotation in the instruction stream tells the liveness
|
|
// analysis to behave as though the variable x is being initialized at that
|
|
// point in the instruction stream. The VARDEF must appear before the
|
|
// actual (multi-instruction) initialization, and it must also appear after
|
|
// any uses of the previous value, if any. For example, if compiling:
|
|
//
|
|
// x = x[1:]
|
|
//
|
|
// it is important to generate code like:
|
|
//
|
|
// base, len, cap = pieces of x[1:]
|
|
// VARDEF x
|
|
// x = {base, len, cap}
|
|
//
|
|
// If instead the generated code looked like:
|
|
//
|
|
// VARDEF x
|
|
// base, len, cap = pieces of x[1:]
|
|
// x = {base, len, cap}
|
|
//
|
|
// then the liveness analysis would decide the previous value of x was
|
|
// unnecessary even though it is about to be used by the x[1:] computation.
|
|
// Similarly, if the generated code looked like:
|
|
//
|
|
// base, len, cap = pieces of x[1:]
|
|
// x = {base, len, cap}
|
|
// VARDEF x
|
|
//
|
|
// then the liveness analysis will not preserve the new value of x, because
|
|
// the VARDEF appears to have "overwritten" it.
|
|
//
|
|
// VARDEF is a bit of a kludge to work around the fact that the instruction
|
|
// stream is working on single-word values but the liveness analysis
|
|
// wants to work on individual variables, which might be multi-word
|
|
// aggregates. It might make sense at some point to look into letting
|
|
// the liveness analysis work on single-word values as well, although
|
|
// there are complications around interface values, slices, and strings,
|
|
// all of which cannot be treated as individual words.
|
|
//
|
|
// VARKILL is the opposite of VARDEF: it marks a value as no longer needed,
|
|
// even if its address has been taken. That is, a VARKILL annotation asserts
|
|
// that its argument is certainly dead, for use when the liveness analysis
|
|
// would not otherwise be able to deduce that fact.
|
|
|
|
func gvardefx(n *Node, as int) {
|
|
if n == nil {
|
|
Fatal("gvardef nil")
|
|
}
|
|
if n.Op != ONAME {
|
|
Yyerror("gvardef %v; %v", Oconv(int(n.Op), obj.FmtSharp), Nconv(n, 0))
|
|
return
|
|
}
|
|
|
|
switch n.Class {
|
|
case PAUTO,
|
|
PPARAM,
|
|
PPARAMOUT:
|
|
Thearch.Gins(as, nil, n)
|
|
}
|
|
}
|
|
|
|
func Gvardef(n *Node) {
|
|
gvardefx(n, obj.AVARDEF)
|
|
}
|
|
|
|
func gvarkill(n *Node) {
|
|
gvardefx(n, obj.AVARKILL)
|
|
}
|
|
|
|
func removevardef(firstp *obj.Prog) {
|
|
for p := firstp; p != nil; p = p.Link {
|
|
for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL) {
|
|
p.Link = p.Link.Link
|
|
}
|
|
if p.To.Type == obj.TYPE_BRANCH {
|
|
for p.To.U.Branch != nil && (p.To.U.Branch.As == obj.AVARDEF || p.To.U.Branch.As == obj.AVARKILL) {
|
|
p.To.U.Branch = p.To.U.Branch.Link
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func gcsymdup(s *Sym) {
|
|
ls := Linksym(s)
|
|
if len(ls.R) > 0 {
|
|
Fatal("cannot rosymdup %s with relocations", ls.Name)
|
|
}
|
|
var d MD5
|
|
md5reset(&d)
|
|
md5write(&d, ls.P, len(ls.P))
|
|
var hi uint64
|
|
lo := md5sum(&d, &hi)
|
|
ls.Name = fmt.Sprintf("gclocals·%016x%016x", lo, hi)
|
|
ls.Dupok = 1
|
|
}
|
|
|
|
func emitptrargsmap() {
|
|
sym := Lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Nname.Sym.Name))
|
|
|
|
nptr := int(Curfn.Type.Argwid / int64(Widthptr))
|
|
bv := bvalloc(int32(nptr) * 2)
|
|
nbitmap := 1
|
|
if Curfn.Type.Outtuple > 0 {
|
|
nbitmap = 2
|
|
}
|
|
off := duint32(sym, 0, uint32(nbitmap))
|
|
off = duint32(sym, off, uint32(bv.n))
|
|
var xoffset int64
|
|
if Curfn.Type.Thistuple > 0 {
|
|
xoffset = 0
|
|
twobitwalktype1(getthisx(Curfn.Type), &xoffset, bv)
|
|
}
|
|
|
|
if Curfn.Type.Intuple > 0 {
|
|
xoffset = 0
|
|
twobitwalktype1(getinargx(Curfn.Type), &xoffset, bv)
|
|
}
|
|
|
|
for j := 0; int32(j) < bv.n; j += 32 {
|
|
off = duint32(sym, off, bv.b[j/32])
|
|
}
|
|
if Curfn.Type.Outtuple > 0 {
|
|
xoffset = 0
|
|
twobitwalktype1(getoutargx(Curfn.Type), &xoffset, bv)
|
|
for j := 0; int32(j) < bv.n; j += 32 {
|
|
off = duint32(sym, off, bv.b[j/32])
|
|
}
|
|
}
|
|
|
|
ggloblsym(sym, int32(off), obj.RODATA)
|
|
}
|
|
|
|
// Sort the list of stack variables. Autos after anything else,
|
|
// within autos, unused after used, within used, things with
|
|
// pointers first, zeroed things first, and then decreasing size.
|
|
// Because autos are laid out in decreasing addresses
|
|
// on the stack, pointers first, zeroed things first and decreasing size
|
|
// really means, in memory, things with pointers needing zeroing at
|
|
// the top of the stack and increasing in size.
|
|
// Non-autos sort on offset.
|
|
func cmpstackvar(a *Node, b *Node) int {
|
|
if a.Class != b.Class {
|
|
if a.Class == PAUTO {
|
|
return +1
|
|
}
|
|
return -1
|
|
}
|
|
|
|
if a.Class != PAUTO {
|
|
if a.Xoffset < b.Xoffset {
|
|
return -1
|
|
}
|
|
if a.Xoffset > b.Xoffset {
|
|
return +1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
if (a.Used == 0) != (b.Used == 0) {
|
|
return int(b.Used) - int(a.Used)
|
|
}
|
|
|
|
ap := bool2int(haspointers(a.Type))
|
|
bp := bool2int(haspointers(b.Type))
|
|
if ap != bp {
|
|
return bp - ap
|
|
}
|
|
|
|
ap = int(a.Needzero)
|
|
bp = int(b.Needzero)
|
|
if ap != bp {
|
|
return bp - ap
|
|
}
|
|
|
|
if a.Type.Width < b.Type.Width {
|
|
return +1
|
|
}
|
|
if a.Type.Width > b.Type.Width {
|
|
return -1
|
|
}
|
|
|
|
return stringsCompare(a.Sym.Name, b.Sym.Name)
|
|
}
|
|
|
|
// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
|
|
func allocauto(ptxt *obj.Prog) {
|
|
Stksize = 0
|
|
stkptrsize = 0
|
|
|
|
if Curfn.Dcl == nil {
|
|
return
|
|
}
|
|
|
|
// Mark the PAUTO's unused.
|
|
for ll := Curfn.Dcl; ll != nil; ll = ll.Next {
|
|
if ll.N.Class == PAUTO {
|
|
ll.N.Used = 0
|
|
}
|
|
}
|
|
|
|
markautoused(ptxt)
|
|
|
|
listsort(&Curfn.Dcl, cmpstackvar)
|
|
|
|
// Unused autos are at the end, chop 'em off.
|
|
ll := Curfn.Dcl
|
|
|
|
n := ll.N
|
|
if n.Class == PAUTO && n.Op == ONAME && n.Used == 0 {
|
|
// No locals used at all
|
|
Curfn.Dcl = nil
|
|
|
|
fixautoused(ptxt)
|
|
return
|
|
}
|
|
|
|
for ll := Curfn.Dcl; ll.Next != nil; ll = ll.Next {
|
|
n = ll.Next.N
|
|
if n.Class == PAUTO && n.Op == ONAME && n.Used == 0 {
|
|
ll.Next = nil
|
|
Curfn.Dcl.End = ll
|
|
break
|
|
}
|
|
}
|
|
|
|
// Reassign stack offsets of the locals that are still there.
|
|
var w int64
|
|
for ll := Curfn.Dcl; ll != nil; ll = ll.Next {
|
|
n = ll.N
|
|
if n.Class != PAUTO || n.Op != ONAME {
|
|
continue
|
|
}
|
|
|
|
dowidth(n.Type)
|
|
w = n.Type.Width
|
|
if w >= Thearch.MAXWIDTH || w < 0 {
|
|
Fatal("bad width")
|
|
}
|
|
Stksize += w
|
|
Stksize = Rnd(Stksize, int64(n.Type.Align))
|
|
if haspointers(n.Type) {
|
|
stkptrsize = Stksize
|
|
}
|
|
if Thearch.Thechar == '5' || Thearch.Thechar == '9' {
|
|
Stksize = Rnd(Stksize, int64(Widthptr))
|
|
}
|
|
if Stksize >= 1<<31 {
|
|
setlineno(Curfn)
|
|
Yyerror("stack frame too large (>2GB)")
|
|
}
|
|
|
|
n.Stkdelta = -Stksize - n.Xoffset
|
|
}
|
|
|
|
Stksize = Rnd(Stksize, int64(Widthreg))
|
|
stkptrsize = Rnd(stkptrsize, int64(Widthreg))
|
|
|
|
fixautoused(ptxt)
|
|
|
|
// The debug information needs accurate offsets on the symbols.
|
|
for ll := Curfn.Dcl; ll != nil; ll = ll.Next {
|
|
if ll.N.Class != PAUTO || ll.N.Op != ONAME {
|
|
continue
|
|
}
|
|
ll.N.Xoffset += ll.N.Stkdelta
|
|
ll.N.Stkdelta = 0
|
|
}
|
|
}
|
|
|
|
func movelarge(l *NodeList) {
|
|
for ; l != nil; l = l.Next {
|
|
if l.N.Op == ODCLFUNC {
|
|
movelargefn(l.N)
|
|
}
|
|
}
|
|
}
|
|
|
|
func movelargefn(fn *Node) {
|
|
var n *Node
|
|
|
|
for l := fn.Dcl; l != nil; l = l.Next {
|
|
n = l.N
|
|
if n.Class == PAUTO && n.Type != nil && n.Type.Width > MaxStackVarSize {
|
|
addrescapes(n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Cgen_checknil(n *Node) {
|
|
if Disable_checknil != 0 {
|
|
return
|
|
}
|
|
|
|
// Ideally we wouldn't see any integer types here, but we do.
|
|
if n.Type == nil || (Isptr[n.Type.Etype] == 0 && Isint[n.Type.Etype] == 0 && n.Type.Etype != TUNSAFEPTR) {
|
|
Dump("checknil", n)
|
|
Fatal("bad checknil")
|
|
}
|
|
|
|
if ((Thearch.Thechar == '5' || Thearch.Thechar == '9') && n.Op != OREGISTER) || n.Addable == 0 || n.Op == OLITERAL {
|
|
var reg Node
|
|
Thearch.Regalloc(®, Types[Tptr], n)
|
|
Thearch.Cgen(n, ®)
|
|
Thearch.Gins(obj.ACHECKNIL, ®, nil)
|
|
Thearch.Regfree(®)
|
|
return
|
|
}
|
|
|
|
Thearch.Gins(obj.ACHECKNIL, n, nil)
|
|
}
|
|
|
|
/*
|
|
* ggen.c
|
|
*/
|
|
func compile(fn *Node) {
|
|
if Newproc == nil {
|
|
Newproc = Sysfunc("newproc")
|
|
Deferproc = Sysfunc("deferproc")
|
|
Deferreturn = Sysfunc("deferreturn")
|
|
Panicindex = Sysfunc("panicindex")
|
|
panicslice = Sysfunc("panicslice")
|
|
throwreturn = Sysfunc("throwreturn")
|
|
}
|
|
|
|
lno := setlineno(fn)
|
|
|
|
Curfn = fn
|
|
dowidth(Curfn.Type)
|
|
|
|
var oldstksize int64
|
|
var nod1 Node
|
|
var ptxt *obj.Prog
|
|
var pl *obj.Plist
|
|
var p *obj.Prog
|
|
var n *Node
|
|
var nam *Node
|
|
var gcargs *Sym
|
|
var gclocals *Sym
|
|
if fn.Nbody == nil {
|
|
if pure_go != 0 || strings.HasPrefix(fn.Nname.Sym.Name, "init.") {
|
|
Yyerror("missing function body for %q", fn.Nname.Sym.Name)
|
|
goto ret
|
|
}
|
|
|
|
if Debug['A'] != 0 {
|
|
goto ret
|
|
}
|
|
emitptrargsmap()
|
|
goto ret
|
|
}
|
|
|
|
saveerrors()
|
|
|
|
// set up domain for labels
|
|
clearlabels()
|
|
|
|
if Curfn.Type.Outnamed != 0 {
|
|
// add clearing of the output parameters
|
|
var save Iter
|
|
t := Structfirst(&save, Getoutarg(Curfn.Type))
|
|
|
|
for t != nil {
|
|
if t.Nname != nil {
|
|
n = Nod(OAS, t.Nname, nil)
|
|
typecheck(&n, Etop)
|
|
Curfn.Nbody = concat(list1(n), Curfn.Nbody)
|
|
}
|
|
|
|
t = structnext(&save)
|
|
}
|
|
}
|
|
|
|
order(Curfn)
|
|
if nerrors != 0 {
|
|
goto ret
|
|
}
|
|
|
|
Hasdefer = 0
|
|
walk(Curfn)
|
|
if nerrors != 0 {
|
|
goto ret
|
|
}
|
|
if flag_race != 0 {
|
|
racewalk(Curfn)
|
|
}
|
|
if nerrors != 0 {
|
|
goto ret
|
|
}
|
|
|
|
continpc = nil
|
|
breakpc = nil
|
|
|
|
pl = newplist()
|
|
pl.Name = Linksym(Curfn.Nname.Sym)
|
|
|
|
setlineno(Curfn)
|
|
|
|
Nodconst(&nod1, Types[TINT32], 0)
|
|
nam = Curfn.Nname
|
|
if isblank(nam) {
|
|
nam = nil
|
|
}
|
|
ptxt = Thearch.Gins(obj.ATEXT, nam, &nod1)
|
|
if fn.Dupok != 0 {
|
|
ptxt.From3.Offset |= obj.DUPOK
|
|
}
|
|
if fn.Wrapper != 0 {
|
|
ptxt.From3.Offset |= obj.WRAPPER
|
|
}
|
|
if fn.Needctxt {
|
|
ptxt.From3.Offset |= obj.NEEDCTXT
|
|
}
|
|
if fn.Nosplit {
|
|
ptxt.From3.Offset |= obj.NOSPLIT
|
|
}
|
|
|
|
// Clumsy but important.
|
|
// See test/recover.go for test cases and src/reflect/value.go
|
|
// for the actual functions being considered.
|
|
if myimportpath != "" && myimportpath == "reflect" {
|
|
if Curfn.Nname.Sym.Name == "callReflect" || Curfn.Nname.Sym.Name == "callMethod" {
|
|
ptxt.From3.Offset |= obj.WRAPPER
|
|
}
|
|
}
|
|
|
|
Afunclit(&ptxt.From, Curfn.Nname)
|
|
|
|
Thearch.Ginit()
|
|
|
|
gcargs = makefuncdatasym("gcargs·%d", obj.FUNCDATA_ArgsPointerMaps)
|
|
gclocals = makefuncdatasym("gclocals·%d", obj.FUNCDATA_LocalsPointerMaps)
|
|
|
|
for t := Curfn.Paramfld; t != nil; t = t.Down {
|
|
gtrack(tracksym(t.Type))
|
|
}
|
|
|
|
for l := fn.Dcl; l != nil; l = l.Next {
|
|
n = l.N
|
|
if n.Op != ONAME { // might be OTYPE or OLITERAL
|
|
continue
|
|
}
|
|
switch n.Class {
|
|
case PAUTO,
|
|
PPARAM,
|
|
PPARAMOUT:
|
|
Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width)
|
|
p = Thearch.Gins(obj.ATYPE, l.N, &nod1)
|
|
p.From.Gotype = Linksym(ngotype(l.N))
|
|
}
|
|
}
|
|
|
|
Genlist(Curfn.Enter)
|
|
Genlist(Curfn.Nbody)
|
|
Thearch.Gclean()
|
|
checklabels()
|
|
if nerrors != 0 {
|
|
goto ret
|
|
}
|
|
if Curfn.Endlineno != 0 {
|
|
lineno = Curfn.Endlineno
|
|
}
|
|
|
|
if Curfn.Type.Outtuple != 0 {
|
|
Thearch.Ginscall(throwreturn, 0)
|
|
}
|
|
|
|
Thearch.Ginit()
|
|
|
|
// TODO: Determine when the final cgen_ret can be omitted. Perhaps always?
|
|
Thearch.Cgen_ret(nil)
|
|
|
|
if Hasdefer != 0 {
|
|
// deferreturn pretends to have one uintptr argument.
|
|
// Reserve space for it so stack scanner is happy.
|
|
if Maxarg < int64(Widthptr) {
|
|
Maxarg = int64(Widthptr)
|
|
}
|
|
}
|
|
|
|
Thearch.Gclean()
|
|
if nerrors != 0 {
|
|
goto ret
|
|
}
|
|
|
|
Pc.As = obj.ARET // overwrite AEND
|
|
Pc.Lineno = lineno
|
|
|
|
fixjmp(ptxt)
|
|
if Debug['N'] == 0 || Debug['R'] != 0 || Debug['P'] != 0 {
|
|
regopt(ptxt)
|
|
nilopt(ptxt)
|
|
}
|
|
|
|
Thearch.Expandchecks(ptxt)
|
|
|
|
oldstksize = Stksize
|
|
allocauto(ptxt)
|
|
|
|
if false {
|
|
fmt.Printf("allocauto: %d to %d\n", oldstksize, int64(Stksize))
|
|
}
|
|
|
|
setlineno(Curfn)
|
|
if int64(Stksize)+Maxarg > 1<<31 {
|
|
Yyerror("stack frame too large (>2GB)")
|
|
goto ret
|
|
}
|
|
|
|
// Emit garbage collection symbols.
|
|
liveness(Curfn, ptxt, gcargs, gclocals)
|
|
|
|
gcsymdup(gcargs)
|
|
gcsymdup(gclocals)
|
|
|
|
Thearch.Defframe(ptxt)
|
|
|
|
if Debug['f'] != 0 {
|
|
frame(0)
|
|
}
|
|
|
|
// Remove leftover instrumentation from the instruction stream.
|
|
removevardef(ptxt)
|
|
|
|
ret:
|
|
lineno = lno
|
|
}
|