2015-02-13 14:40:36 -05:00
|
|
|
// Derived from Inferno utils/6c/txt.c
|
2016-08-28 17:04:46 -07:00
|
|
|
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/txt.c
|
2015-02-13 14:40:36 -05:00
|
|
|
//
|
|
|
|
|
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
|
|
|
|
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
|
|
|
|
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
|
|
|
|
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
|
|
|
|
// Portions Copyright © 2004,2006 Bruce Ellis
|
|
|
|
|
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
|
|
|
|
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
2016-04-10 14:32:26 -07:00
|
|
|
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
2015-02-13 14:40:36 -05:00
|
|
|
//
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
|
//
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
|
//
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
|
// THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
package gc
|
|
|
|
|
|
2015-03-18 17:26:36 -04:00
|
|
|
import (
|
|
|
|
|
"cmd/internal/obj"
|
2016-04-06 12:01:40 -07:00
|
|
|
"cmd/internal/sys"
|
2015-03-18 17:26:36 -04:00
|
|
|
"fmt"
|
|
|
|
|
"runtime"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-03-12 13:40:30 -08:00
|
|
|
var (
|
|
|
|
|
ddumped bool
|
|
|
|
|
dfirst *obj.Prog
|
|
|
|
|
dpc *obj.Prog
|
|
|
|
|
)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-03-07 18:00:08 -08:00
|
|
|
func Prog(as obj.As) *obj.Prog {
|
2015-02-13 14:40:36 -05:00
|
|
|
var p *obj.Prog
|
|
|
|
|
|
2016-03-13 14:24:22 -07:00
|
|
|
if as == obj.AGLOBL {
|
2016-03-12 13:40:30 -08:00
|
|
|
if ddumped {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("already dumped data")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
if dpc == nil {
|
|
|
|
|
dpc = Ctxt.NewProg()
|
|
|
|
|
dfirst = dpc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p = dpc
|
|
|
|
|
dpc = Ctxt.NewProg()
|
|
|
|
|
p.Link = dpc
|
|
|
|
|
} else {
|
|
|
|
|
p = Pc
|
|
|
|
|
Pc = Ctxt.NewProg()
|
|
|
|
|
Clearp(Pc)
|
|
|
|
|
p.Link = Pc
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-12 13:40:30 -08:00
|
|
|
if lineno == 0 && Debug['K'] != 0 {
|
|
|
|
|
Warn("prog: line 0")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-07 18:00:08 -08:00
|
|
|
p.As = as
|
2015-02-13 14:40:36 -05:00
|
|
|
p.Lineno = lineno
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Nodreg(n *Node, t *Type, r int) {
|
|
|
|
|
if t == nil {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("nodreg: t nil")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*n = Node{}
|
|
|
|
|
n.Op = OREGISTER
|
2015-04-02 19:58:37 -07:00
|
|
|
n.Addable = true
|
2015-02-13 14:40:36 -05:00
|
|
|
ullmancalc(n)
|
2015-04-13 10:28:57 -07:00
|
|
|
n.Reg = int16(r)
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Type = t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Afunclit(a *obj.Addr, n *Node) {
|
|
|
|
|
if a.Type == obj.TYPE_ADDR && a.Name == obj.NAME_EXTERN {
|
|
|
|
|
a.Type = obj.TYPE_MEM
|
|
|
|
|
a.Sym = Linksym(n.Sym)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Clearp(p *obj.Prog) {
|
|
|
|
|
obj.Nopout(p)
|
|
|
|
|
p.As = obj.AEND
|
|
|
|
|
p.Pc = int64(pcloc)
|
|
|
|
|
pcloc++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func dumpdata() {
|
2016-03-12 13:40:30 -08:00
|
|
|
ddumped = true
|
2015-02-13 14:40:36 -05:00
|
|
|
if dfirst == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
newplist()
|
|
|
|
|
*Pc = *dfirst
|
|
|
|
|
Pc = dpc
|
|
|
|
|
Clearp(Pc)
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 09:12:51 -08:00
|
|
|
func flushdata() {
|
|
|
|
|
if dfirst == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
newplist()
|
|
|
|
|
*Pc = *dfirst
|
|
|
|
|
Pc = dpc
|
|
|
|
|
Clearp(Pc)
|
|
|
|
|
dfirst = nil
|
|
|
|
|
dpc = nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 13:57:36 -05:00
|
|
|
// Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
|
2015-02-13 14:40:36 -05:00
|
|
|
func fixautoused(p *obj.Prog) {
|
2015-02-23 16:07:24 -05:00
|
|
|
for lp := &p; ; {
|
2015-02-13 14:40:36 -05:00
|
|
|
p = *lp
|
2015-02-17 22:13:49 -05:00
|
|
|
if p == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
}
|
2015-03-06 21:18:41 +11:00
|
|
|
if p.As == obj.ATYPE && p.From.Node != nil && p.From.Name == obj.NAME_AUTO && !((p.From.Node).(*Node)).Used {
|
2015-02-13 14:40:36 -05:00
|
|
|
*lp = p.Link
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
cmd/compile: recognize Syscall-like functions for liveness analysis
Consider this code:
func f(*int)
func g() {
p := new(int)
f(p)
}
where f is an assembly function.
In general liveness analysis assumes that during the call to f, p is dead
in this frame. If f has retained p, p will be found alive in f's frame and keep
the new(int) from being garbage collected. This is all correct and works.
We use the Go func declaration for f to give the assembly function
liveness information (the arguments are assumed live for the entire call).
Now consider this code:
func h1() {
p := new(int)
syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p)))
}
Here syscall.Syscall is taking the place of f, but because its arguments
are uintptr, the liveness analysis and the garbage collector ignore them.
Since p is no longer live in h once the call starts, if the garbage collector
scans the stack while the system call is blocked, it will find no reference
to the new(int) and reclaim it. If the kernel is going to write to *p once
the call finishes, reclaiming the memory is a mistake.
We can't change the arguments or the liveness information for
syscall.Syscall itself, both for compatibility and because sometimes the
arguments really are integers, and the garbage collector will get quite upset
if it finds an integer where it expects a pointer. The problem is that
these arguments are fundamentally untyped.
The solution we have taken in the syscall package's wrappers in past
releases is to insert a call to a dummy function named "use", to make
it look like the argument is live during the call to syscall.Syscall:
func h2() {
p := new(int)
syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p)))
use(unsafe.Pointer(p))
}
Keeping p alive during the call means that if the garbage collector
scans the stack during the system call now, it will find the reference to p.
Unfortunately, this approach is not available to users outside syscall,
because 'use' is unexported, and people also have to realize they need
to use it and do so. There is much existing code using syscall.Syscall
without a 'use'-like function. That code will fail very occasionally in
mysterious ways (see #13372).
This CL fixes all that existing code by making the compiler do the right
thing automatically, without any code modifications. That is, it takes h1
above, which is incorrect code today, and makes it correct code.
Specifically, if the compiler sees a foreign func definition (one
without a body) that has uintptr arguments, it marks those arguments
as "unsafe uintptrs". If it later sees the function being called
with uintptr(unsafe.Pointer(x)) as an argument, it arranges to mark x
as having escaped, and it makes sure to hold x in a live temporary
variable until the call returns, so that the garbage collector cannot
reclaim whatever heap memory x points to.
For now I am leaving the explicit calls to use in package syscall,
but they can be removed early in a future cycle (likely Go 1.7).
The rule has no effect on escape analysis, only on liveness analysis.
Fixes #13372.
Change-Id: I2addb83f70d08db08c64d394f9d06ff0a063c500
Reviewed-on: https://go-review.googlesource.com/18584
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-01-13 00:46:28 -05:00
|
|
|
if (p.As == obj.AVARDEF || p.As == obj.AVARKILL || p.As == obj.AVARLIVE) && p.To.Node != nil && !((p.To.Node).(*Node)).Used {
|
2015-02-13 14:40:36 -05:00
|
|
|
// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
|
|
|
|
|
// VARDEFs are interspersed with other code, and a jump might be using the
|
|
|
|
|
// VARDEF as a target. Replace with a no-op instead. A later pass will remove
|
|
|
|
|
// the no-ops.
|
|
|
|
|
obj.Nopout(p)
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.From.Name == obj.NAME_AUTO && p.From.Node != nil {
|
2015-05-26 21:30:20 -04:00
|
|
|
p.From.Offset += stkdelta[p.From.Node.(*Node)]
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.To.Name == obj.NAME_AUTO && p.To.Node != nil {
|
2015-05-26 21:30:20 -04:00
|
|
|
p.To.Offset += stkdelta[p.To.Node.(*Node)]
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lp = &p.Link
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ggloblnod(nam *Node) {
|
2015-02-23 16:07:24 -05:00
|
|
|
p := Thearch.Gins(obj.AGLOBL, nam, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
p.Lineno = nam.Lineno
|
|
|
|
|
p.From.Sym.Gotype = Linksym(ngotype(nam))
|
|
|
|
|
p.To.Sym = nil
|
|
|
|
|
p.To.Type = obj.TYPE_CONST
|
|
|
|
|
p.To.Offset = nam.Type.Width
|
2015-05-27 15:01:44 -04:00
|
|
|
p.From3 = new(obj.Addr)
|
2015-05-15 10:02:19 -07:00
|
|
|
if nam.Name.Readonly {
|
2015-02-13 14:40:36 -05:00
|
|
|
p.From3.Offset = obj.RODATA
|
|
|
|
|
}
|
|
|
|
|
if nam.Type != nil && !haspointers(nam.Type) {
|
|
|
|
|
p.From3.Offset |= obj.NOPTR
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-18 08:14:08 +12:00
|
|
|
func ggloblsym(s *Sym, width int32, flags int16) {
|
2016-03-16 22:22:58 -07:00
|
|
|
ggloblLSym(Linksym(s), width, flags)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ggloblLSym(s *obj.LSym, width int32, flags int16) {
|
2015-02-23 16:07:24 -05:00
|
|
|
p := Thearch.Gins(obj.AGLOBL, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
p.From.Type = obj.TYPE_MEM
|
|
|
|
|
p.From.Name = obj.NAME_EXTERN
|
2016-03-16 22:22:58 -07:00
|
|
|
p.From.Sym = s
|
2015-04-18 08:14:08 +12:00
|
|
|
if flags&obj.LOCAL != 0 {
|
|
|
|
|
p.From.Sym.Local = true
|
2016-03-29 14:09:22 +02:00
|
|
|
flags &^= obj.LOCAL
|
2015-04-18 08:14:08 +12:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
p.To.Type = obj.TYPE_CONST
|
|
|
|
|
p.To.Offset = int64(width)
|
2015-05-27 15:01:44 -04:00
|
|
|
p.From3 = new(obj.Addr)
|
2015-02-13 14:40:36 -05:00
|
|
|
p.From3.Offset = int64(flags)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func gtrack(s *Sym) {
|
2015-02-23 16:07:24 -05:00
|
|
|
p := Thearch.Gins(obj.AUSEFIELD, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
p.From.Type = obj.TYPE_MEM
|
|
|
|
|
p.From.Name = obj.NAME_EXTERN
|
|
|
|
|
p.From.Sym = Linksym(s)
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 14:34:20 +10:00
|
|
|
func isfat(t *Type) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if t != nil {
|
|
|
|
|
switch t.Etype {
|
2016-04-18 14:02:08 -07:00
|
|
|
case TSTRUCT, TARRAY, TSLICE, TSTRING,
|
2015-02-13 14:40:36 -05:00
|
|
|
TINTER: // maybe remove later
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-03-05 13:57:36 -05:00
|
|
|
// Sweep the prog list to mark any used nodes.
|
2015-02-13 14:40:36 -05:00
|
|
|
func markautoused(p *obj.Prog) {
|
|
|
|
|
for ; p != nil; p = p.Link {
|
|
|
|
|
if p.As == obj.ATYPE || p.As == obj.AVARDEF || p.As == obj.AVARKILL {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.From.Node != nil {
|
2015-03-06 21:18:41 +11:00
|
|
|
((p.From.Node).(*Node)).Used = true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.To.Node != nil {
|
2015-03-06 21:18:41 +11:00
|
|
|
((p.To.Node).(*Node)).Used = true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-16 15:27:19 -04:00
|
|
|
// Naddr rewrites a to refer to n.
|
|
|
|
|
// It assumes that a is zeroed on entry.
|
|
|
|
|
func Naddr(a *obj.Addr, n *Node) {
|
2015-02-13 14:40:36 -05:00
|
|
|
if n == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if n.Type != nil && n.Type.Etype != TIDEAL {
|
|
|
|
|
// TODO(rsc): This is undone by the selective clearing of width below,
|
|
|
|
|
// to match architectures that were not as aggressive in setting width
|
|
|
|
|
// during naddr. Those widths must be cleared to avoid triggering
|
|
|
|
|
// failures in gins when it detects real but heretofore latent (and one
|
|
|
|
|
// hopes innocuous) type mismatches.
|
|
|
|
|
// The type mismatches should be fixed and the clearing below removed.
|
|
|
|
|
dowidth(n.Type)
|
|
|
|
|
|
|
|
|
|
a.Width = n.Type.Width
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
|
default:
|
2015-03-02 20:34:22 -05:00
|
|
|
a := a // copy to let escape into Ctxt.Dconv
|
2015-03-18 17:26:36 -04:00
|
|
|
Debug['h'] = 1
|
|
|
|
|
Dump("naddr", n)
|
2016-04-27 15:10:10 +10:00
|
|
|
Fatalf("naddr: bad %v %v", n.Op, Ctxt.Dconv(a))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case OREGISTER:
|
|
|
|
|
a.Type = obj.TYPE_REG
|
2015-04-13 10:28:57 -07:00
|
|
|
a.Reg = n.Reg
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Sym = nil
|
2016-04-06 12:01:40 -07:00
|
|
|
if Thearch.LinkArch.Family == sys.I386 { // TODO(rsc): Never clear a->width.
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Width = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OINDREG:
|
|
|
|
|
a.Type = obj.TYPE_MEM
|
2015-04-13 10:28:57 -07:00
|
|
|
a.Reg = n.Reg
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Sym = Linksym(n.Sym)
|
|
|
|
|
a.Offset = n.Xoffset
|
|
|
|
|
if a.Offset != int64(int32(a.Offset)) {
|
|
|
|
|
Yyerror("offset %d too large for OINDREG", a.Offset)
|
|
|
|
|
}
|
2016-04-06 12:01:40 -07:00
|
|
|
if Thearch.LinkArch.Family == sys.I386 { // TODO(rsc): Never clear a->width.
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Width = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OCLOSUREVAR:
|
2015-03-25 19:33:01 -07:00
|
|
|
if !Curfn.Func.Needctxt {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("closurevar without needctxt")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
a.Type = obj.TYPE_MEM
|
|
|
|
|
a.Reg = int16(Thearch.REGCTXT)
|
|
|
|
|
a.Sym = nil
|
|
|
|
|
a.Offset = n.Xoffset
|
|
|
|
|
|
|
|
|
|
case OCFUNC:
|
2015-03-16 15:27:19 -04:00
|
|
|
Naddr(a, n.Left)
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Sym = Linksym(n.Left.Sym)
|
|
|
|
|
|
|
|
|
|
case ONAME:
|
|
|
|
|
a.Etype = 0
|
|
|
|
|
if n.Type != nil {
|
2015-09-24 23:21:18 +02:00
|
|
|
a.Etype = uint8(Simtype[n.Type.Etype])
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
a.Offset = n.Xoffset
|
2015-02-23 16:07:24 -05:00
|
|
|
s := n.Sym
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Node = n.Orig
|
|
|
|
|
|
|
|
|
|
//if(a->node >= (Node*)&n)
|
|
|
|
|
// fatal("stack node");
|
|
|
|
|
if s == nil {
|
|
|
|
|
s = Lookup(".noname")
|
|
|
|
|
}
|
2016-03-12 13:40:30 -08:00
|
|
|
if n.Name.Method && n.Type != nil && n.Type.Sym != nil && n.Type.Sym.Pkg != nil {
|
|
|
|
|
s = Pkglookup(s.Name, n.Type.Sym.Pkg)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a.Type = obj.TYPE_MEM
|
|
|
|
|
switch n.Class {
|
|
|
|
|
default:
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("naddr: ONAME class %v %d\n", n.Sym, n.Class)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case PEXTERN:
|
|
|
|
|
a.Name = obj.NAME_EXTERN
|
|
|
|
|
|
|
|
|
|
case PAUTO:
|
|
|
|
|
a.Name = obj.NAME_AUTO
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case PPARAM, PPARAMOUT:
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Name = obj.NAME_PARAM
|
|
|
|
|
|
|
|
|
|
case PFUNC:
|
|
|
|
|
a.Name = obj.NAME_EXTERN
|
|
|
|
|
a.Type = obj.TYPE_ADDR
|
|
|
|
|
a.Width = int64(Widthptr)
|
|
|
|
|
s = funcsym(s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a.Sym = Linksym(s)
|
|
|
|
|
|
2015-11-14 15:42:49 -08:00
|
|
|
case ODOT:
|
|
|
|
|
// A special case to make write barriers more efficient.
|
|
|
|
|
// Taking the address of the first field of a named struct
|
|
|
|
|
// is the same as taking the address of the struct.
|
2016-03-30 14:56:08 -07:00
|
|
|
if !n.Left.Type.IsStruct() || n.Left.Type.Field(0).Sym != n.Sym {
|
2015-11-14 15:42:49 -08:00
|
|
|
Debug['h'] = 1
|
|
|
|
|
Dump("naddr", n)
|
2016-04-27 15:10:10 +10:00
|
|
|
Fatalf("naddr: bad %v %v", n.Op, Ctxt.Dconv(a))
|
2015-11-14 15:42:49 -08:00
|
|
|
}
|
|
|
|
|
Naddr(a, n.Left)
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
case OLITERAL:
|
2016-04-06 12:01:40 -07:00
|
|
|
if Thearch.LinkArch.Family == sys.I386 {
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Width = 0
|
|
|
|
|
}
|
2016-04-22 12:27:29 -07:00
|
|
|
switch u := n.Val().U.(type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
default:
|
2016-09-09 21:08:46 -07:00
|
|
|
Fatalf("naddr: const %L", n.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-22 12:27:29 -07:00
|
|
|
case *Mpflt:
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Type = obj.TYPE_FCONST
|
2016-04-22 12:27:29 -07:00
|
|
|
a.Val = u.Float64()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-22 12:27:29 -07:00
|
|
|
case *Mpint:
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Sym = nil
|
|
|
|
|
a.Type = obj.TYPE_CONST
|
2016-04-22 12:27:29 -07:00
|
|
|
a.Offset = u.Int64()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-22 12:27:29 -07:00
|
|
|
case string:
|
|
|
|
|
datagostring(u, a)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-22 12:27:29 -07:00
|
|
|
case bool:
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Sym = nil
|
|
|
|
|
a.Type = obj.TYPE_CONST
|
2016-04-22 12:27:29 -07:00
|
|
|
a.Offset = int64(obj.Bool2int(u))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-22 12:27:29 -07:00
|
|
|
case *NilVal:
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Sym = nil
|
|
|
|
|
a.Type = obj.TYPE_CONST
|
|
|
|
|
a.Offset = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OADDR:
|
2015-03-16 15:27:19 -04:00
|
|
|
Naddr(a, n.Left)
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Etype = uint8(Tptr)
|
2016-04-12 12:26:17 -04:00
|
|
|
if !Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) { // TODO(rsc): Do this even for these architectures.
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Width = int64(Widthptr)
|
|
|
|
|
}
|
|
|
|
|
if a.Type != obj.TYPE_MEM {
|
2015-03-02 20:34:22 -05:00
|
|
|
a := a // copy to let escape into Ctxt.Dconv
|
2016-04-27 15:10:10 +10:00
|
|
|
Fatalf("naddr: OADDR %v (from %v)", Ctxt.Dconv(a), n.Left.Op)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
a.Type = obj.TYPE_ADDR
|
|
|
|
|
|
|
|
|
|
case OITAB:
|
2016-06-06 12:38:19 -07:00
|
|
|
// itable of interface value
|
2015-03-16 15:27:19 -04:00
|
|
|
Naddr(a, n.Left)
|
2015-02-13 14:40:36 -05:00
|
|
|
if a.Type == obj.TYPE_CONST && a.Offset == 0 {
|
|
|
|
|
break // itab(nil)
|
|
|
|
|
}
|
|
|
|
|
a.Etype = uint8(Tptr)
|
|
|
|
|
a.Width = int64(Widthptr)
|
|
|
|
|
|
2016-06-06 12:38:19 -07:00
|
|
|
case OIDATA:
|
|
|
|
|
// idata of interface value
|
|
|
|
|
Naddr(a, n.Left)
|
|
|
|
|
if a.Type == obj.TYPE_CONST && a.Offset == 0 {
|
|
|
|
|
break // idata(nil)
|
|
|
|
|
}
|
|
|
|
|
if isdirectiface(n.Type) {
|
|
|
|
|
a.Etype = uint8(Simtype[n.Type.Etype])
|
|
|
|
|
} else {
|
|
|
|
|
a.Etype = uint8(Tptr)
|
|
|
|
|
}
|
|
|
|
|
a.Offset += int64(Widthptr)
|
|
|
|
|
a.Width = int64(Widthptr)
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
// pointer in a string or slice
|
|
|
|
|
case OSPTR:
|
2015-03-16 15:27:19 -04:00
|
|
|
Naddr(a, n.Left)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if a.Type == obj.TYPE_CONST && a.Offset == 0 {
|
|
|
|
|
break // ptr(nil)
|
|
|
|
|
}
|
2015-09-24 23:21:18 +02:00
|
|
|
a.Etype = uint8(Simtype[Tptr])
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Offset += int64(Array_array)
|
|
|
|
|
a.Width = int64(Widthptr)
|
|
|
|
|
|
|
|
|
|
// len of string or slice
|
|
|
|
|
case OLEN:
|
2015-03-16 15:27:19 -04:00
|
|
|
Naddr(a, n.Left)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if a.Type == obj.TYPE_CONST && a.Offset == 0 {
|
|
|
|
|
break // len(nil)
|
|
|
|
|
}
|
2015-09-24 23:21:18 +02:00
|
|
|
a.Etype = uint8(Simtype[TUINT])
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Offset += int64(Array_nel)
|
2016-04-06 12:01:40 -07:00
|
|
|
if Thearch.LinkArch.Family != sys.ARM { // TODO(rsc): Do this even on arm.
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Width = int64(Widthint)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cap of string or slice
|
|
|
|
|
case OCAP:
|
2015-03-16 15:27:19 -04:00
|
|
|
Naddr(a, n.Left)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if a.Type == obj.TYPE_CONST && a.Offset == 0 {
|
|
|
|
|
break // cap(nil)
|
|
|
|
|
}
|
2015-09-24 23:21:18 +02:00
|
|
|
a.Etype = uint8(Simtype[TUINT])
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Offset += int64(Array_cap)
|
2016-04-06 12:01:40 -07:00
|
|
|
if Thearch.LinkArch.Family != sys.ARM { // TODO(rsc): Do this even on arm.
|
2015-02-13 14:40:36 -05:00
|
|
|
a.Width = int64(Widthint)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newplist() *obj.Plist {
|
2015-02-23 16:07:24 -05:00
|
|
|
pl := obj.Linknewplist(Ctxt)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
Pc = Ctxt.NewProg()
|
|
|
|
|
Clearp(Pc)
|
|
|
|
|
pl.Firstpc = Pc
|
|
|
|
|
|
|
|
|
|
return pl
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-25 10:01:58 -04:00
|
|
|
// nodarg returns a Node for the function argument denoted by t,
|
|
|
|
|
// which is either the entire function argument or result struct (t is a struct *Type)
|
|
|
|
|
// or a specific argument (t is a *Field within a struct *Type).
|
2015-10-08 12:39:56 -04:00
|
|
|
//
|
2016-05-25 10:01:58 -04:00
|
|
|
// If fp is 0, the node is for use by a caller invoking the given
|
|
|
|
|
// function, preparing the arguments before the call
|
|
|
|
|
// or retrieving the results after the call.
|
|
|
|
|
// In this case, the node will correspond to an outgoing argument
|
|
|
|
|
// slot like 8(SP).
|
|
|
|
|
//
|
|
|
|
|
// If fp is 1, the node is for use by the function itself
|
|
|
|
|
// (the callee), to retrieve its arguments or write its results.
|
|
|
|
|
// In this case the node will be an ONAME with an appropriate
|
|
|
|
|
// type and offset.
|
2016-03-14 01:20:49 -07:00
|
|
|
func nodarg(t interface{}, fp int) *Node {
|
2015-02-13 14:40:36 -05:00
|
|
|
var n *Node
|
|
|
|
|
|
2016-05-25 10:01:58 -04:00
|
|
|
var funarg Funarg
|
2016-03-14 01:20:49 -07:00
|
|
|
switch t := t.(type) {
|
2016-05-25 10:01:58 -04:00
|
|
|
default:
|
|
|
|
|
Fatalf("bad nodarg %T(%v)", t, t)
|
|
|
|
|
|
2016-03-14 01:20:49 -07:00
|
|
|
case *Type:
|
2016-05-25 10:01:58 -04:00
|
|
|
// Entire argument struct, not just one arg
|
2016-04-05 16:44:07 -07:00
|
|
|
if !t.IsFuncArgStruct() {
|
2016-03-14 01:20:49 -07:00
|
|
|
Fatalf("nodarg: bad type %v", t)
|
|
|
|
|
}
|
2016-05-25 10:01:58 -04:00
|
|
|
funarg = t.StructType().Funarg
|
|
|
|
|
|
|
|
|
|
// Build fake variable name for whole arg struct.
|
2015-02-13 14:40:36 -05:00
|
|
|
n = Nod(ONAME, nil, nil)
|
|
|
|
|
n.Sym = Lookup(".args")
|
|
|
|
|
n.Type = t
|
2016-03-09 20:45:18 -08:00
|
|
|
first := t.Field(0)
|
2015-02-13 14:40:36 -05:00
|
|
|
if first == nil {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("nodarg: bad struct")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-03-28 09:40:53 -07:00
|
|
|
if first.Offset == BADWIDTH {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("nodarg: offset not computed for %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-03-28 09:40:53 -07:00
|
|
|
n.Xoffset = first.Offset
|
2015-04-02 19:58:37 -07:00
|
|
|
n.Addable = true
|
2016-05-25 10:01:58 -04:00
|
|
|
|
2016-03-14 01:20:49 -07:00
|
|
|
case *Field:
|
2016-05-25 10:01:58 -04:00
|
|
|
funarg = t.Funarg
|
|
|
|
|
if fp == 1 {
|
|
|
|
|
// NOTE(rsc): This should be using t.Nname directly,
|
|
|
|
|
// except in the case where t.Nname.Sym is the blank symbol and
|
|
|
|
|
// so the assignment would be discarded during code generation.
|
|
|
|
|
// In that case we need to make a new node, and there is no harm
|
|
|
|
|
// in optimization passes to doing so. But otherwise we should
|
|
|
|
|
// definitely be using the actual declaration and not a newly built node.
|
|
|
|
|
// The extra Fatalf checks here are verifying that this is the case,
|
|
|
|
|
// without changing the actual logic (at time of writing, it's getting
|
|
|
|
|
// toward time for the Go 1.7 beta).
|
|
|
|
|
// At some quieter time (assuming we've never seen these Fatalfs happen)
|
|
|
|
|
// we could change this code to use "expect" directly.
|
|
|
|
|
expect := t.Nname
|
|
|
|
|
if expect.isParamHeapCopy() {
|
|
|
|
|
expect = expect.Name.Param.Stackcopy
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-14 01:20:49 -07:00
|
|
|
for _, n := range Curfn.Func.Dcl {
|
|
|
|
|
if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
|
2016-05-25 10:01:58 -04:00
|
|
|
if n != expect {
|
|
|
|
|
Fatalf("nodarg: unexpected node: %v (%p %v) vs %v (%p %v)", n, n, n.Op, t.Nname, t.Nname, t.Nname.Op)
|
|
|
|
|
}
|
2016-03-14 01:20:49 -07:00
|
|
|
return n
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-05-25 10:01:58 -04:00
|
|
|
|
|
|
|
|
if !isblanksym(expect.Sym) {
|
|
|
|
|
Fatalf("nodarg: did not find node in dcl list: %v", expect)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-05-25 10:01:58 -04:00
|
|
|
// Build fake name for individual variable.
|
|
|
|
|
// This is safe because if there was a real declared name
|
|
|
|
|
// we'd have used it above.
|
2016-03-14 01:20:49 -07:00
|
|
|
n = Nod(ONAME, nil, nil)
|
|
|
|
|
n.Type = t.Type
|
|
|
|
|
n.Sym = t.Sym
|
2016-03-28 09:40:53 -07:00
|
|
|
if t.Offset == BADWIDTH {
|
2016-03-14 01:20:49 -07:00
|
|
|
Fatalf("nodarg: offset not computed for %v", t)
|
|
|
|
|
}
|
2016-03-28 09:40:53 -07:00
|
|
|
n.Xoffset = t.Offset
|
2016-03-14 01:20:49 -07:00
|
|
|
n.Addable = true
|
|
|
|
|
n.Orig = t.Nname
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rewrite argument named _ to __,
|
|
|
|
|
// or else the assignment to _ will be
|
|
|
|
|
// discarded during code generation.
|
|
|
|
|
if isblank(n) {
|
|
|
|
|
n.Sym = Lookup("__")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch fp {
|
2016-05-25 10:01:58 -04:00
|
|
|
default:
|
|
|
|
|
Fatalf("bad fp")
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-05-25 10:01:58 -04:00
|
|
|
case 0: // preparing arguments for call
|
|
|
|
|
n.Op = OINDREG
|
2015-04-13 10:28:57 -07:00
|
|
|
n.Reg = int16(Thearch.REGSP)
|
2015-10-08 22:13:44 +13:00
|
|
|
n.Xoffset += Ctxt.FixedFrameSize()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-05-25 10:01:58 -04:00
|
|
|
case 1: // reading arguments inside call
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Class = PPARAM
|
2016-05-25 10:01:58 -04:00
|
|
|
if funarg == FunargResults {
|
|
|
|
|
n.Class = PPARAMOUT
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n.Typecheck = 1
|
2016-05-25 10:01:58 -04:00
|
|
|
n.Addrtaken = true // keep optimizers at bay
|
2015-02-13 14:40:36 -05:00
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Patch(p *obj.Prog, to *obj.Prog) {
|
|
|
|
|
if p.To.Type != obj.TYPE_BRANCH {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("patch: not a branch")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-16 15:54:44 -04:00
|
|
|
p.To.Val = to
|
2015-02-13 14:40:36 -05:00
|
|
|
p.To.Offset = to.Pc
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-18 17:26:36 -04:00
|
|
|
var reg [100]int // count of references to reg
|
|
|
|
|
var regstk [100][]byte // allocation sites, when -v is given
|
|
|
|
|
|
|
|
|
|
func ginit() {
|
|
|
|
|
for r := range reg {
|
|
|
|
|
reg[r] = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
|
|
|
|
|
reg[r-Thearch.REGMIN] = 0
|
|
|
|
|
}
|
|
|
|
|
for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ {
|
|
|
|
|
reg[r-Thearch.REGMIN] = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, r := range Thearch.ReservedRegs {
|
|
|
|
|
reg[r-Thearch.REGMIN] = 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-22 09:51:12 +09:00
|
|
|
// allocate register of type t, leave in n.
|
|
|
|
|
// if o != N, o may be reusable register.
|
|
|
|
|
// caller must Regfree(n).
|
2015-03-18 17:26:36 -04:00
|
|
|
func Regalloc(n *Node, t *Type, o *Node) {
|
|
|
|
|
if t == nil {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("regalloc: t nil")
|
2015-03-18 17:26:36 -04:00
|
|
|
}
|
2015-09-24 23:21:18 +02:00
|
|
|
et := Simtype[t.Etype]
|
2016-04-06 12:01:40 -07:00
|
|
|
if Ctxt.Arch.RegSize == 4 && (et == TINT64 || et == TUINT64) {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("regalloc 64bit")
|
2015-03-18 17:26:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var i int
|
|
|
|
|
Switch:
|
|
|
|
|
switch et {
|
|
|
|
|
default:
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("regalloc: unknown type %v", t)
|
2015-03-18 17:26:36 -04:00
|
|
|
|
|
|
|
|
case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TPTR32, TPTR64, TBOOL:
|
|
|
|
|
if o != nil && o.Op == OREGISTER {
|
2015-04-13 10:28:57 -07:00
|
|
|
i = int(o.Reg)
|
2015-03-18 17:26:36 -04:00
|
|
|
if Thearch.REGMIN <= i && i <= Thearch.REGMAX {
|
|
|
|
|
break Switch
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i = Thearch.REGMIN; i <= Thearch.REGMAX; i++ {
|
|
|
|
|
if reg[i-Thearch.REGMIN] == 0 {
|
|
|
|
|
break Switch
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Flusherrors()
|
2016-09-15 14:34:20 +10:00
|
|
|
regdump()
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("out of fixed registers")
|
2015-03-18 17:26:36 -04:00
|
|
|
|
|
|
|
|
case TFLOAT32, TFLOAT64:
|
2015-03-25 09:17:09 +11:00
|
|
|
if Thearch.Use387 {
|
2015-03-24 22:16:48 +11:00
|
|
|
i = Thearch.FREGMIN // x86.REG_F0
|
|
|
|
|
break Switch
|
|
|
|
|
}
|
2015-03-18 17:26:36 -04:00
|
|
|
if o != nil && o.Op == OREGISTER {
|
2015-04-13 10:28:57 -07:00
|
|
|
i = int(o.Reg)
|
2015-03-18 17:26:36 -04:00
|
|
|
if Thearch.FREGMIN <= i && i <= Thearch.FREGMAX {
|
|
|
|
|
break Switch
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i = Thearch.FREGMIN; i <= Thearch.FREGMAX; i++ {
|
|
|
|
|
if reg[i-Thearch.REGMIN] == 0 { // note: REGMIN, not FREGMIN
|
|
|
|
|
break Switch
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Flusherrors()
|
2016-09-15 14:34:20 +10:00
|
|
|
regdump()
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("out of floating registers")
|
2015-03-18 17:26:36 -04:00
|
|
|
|
|
|
|
|
case TCOMPLEX64, TCOMPLEX128:
|
2016-09-15 14:34:20 +10:00
|
|
|
tempname(n, t)
|
2015-03-18 17:26:36 -04:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ix := i - Thearch.REGMIN
|
|
|
|
|
if reg[ix] == 0 && Debug['v'] > 0 {
|
|
|
|
|
if regstk[ix] == nil {
|
|
|
|
|
regstk[ix] = make([]byte, 4096)
|
|
|
|
|
}
|
|
|
|
|
stk := regstk[ix]
|
|
|
|
|
n := runtime.Stack(stk[:cap(stk)], false)
|
|
|
|
|
regstk[ix] = stk[:n]
|
|
|
|
|
}
|
|
|
|
|
reg[ix]++
|
|
|
|
|
Nodreg(n, t, i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Regfree(n *Node) {
|
|
|
|
|
if n.Op == ONAME {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if n.Op != OREGISTER && n.Op != OINDREG {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("regfree: not a register")
|
2015-03-18 17:26:36 -04:00
|
|
|
}
|
2015-04-13 10:28:57 -07:00
|
|
|
i := int(n.Reg)
|
2015-03-18 17:26:36 -04:00
|
|
|
if i == Thearch.REGSP {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
switch {
|
|
|
|
|
case Thearch.REGMIN <= i && i <= Thearch.REGMAX,
|
|
|
|
|
Thearch.FREGMIN <= i && i <= Thearch.FREGMAX:
|
|
|
|
|
// ok
|
|
|
|
|
default:
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("regfree: reg out of range")
|
2015-03-18 17:26:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i -= Thearch.REGMIN
|
|
|
|
|
if reg[i] <= 0 {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("regfree: reg not allocated")
|
2015-03-18 17:26:36 -04:00
|
|
|
}
|
|
|
|
|
reg[i]--
|
|
|
|
|
if reg[i] == 0 {
|
|
|
|
|
regstk[i] = regstk[i][:0]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 14:34:20 +10:00
|
|
|
func regdump() {
|
2015-03-18 17:26:36 -04:00
|
|
|
if Debug['v'] == 0 {
|
|
|
|
|
fmt.Printf("run compiler with -v for register allocation sites\n")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dump := func(r int) {
|
|
|
|
|
stk := regstk[r-Thearch.REGMIN]
|
|
|
|
|
if len(stk) == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("reg %v allocated at:\n", obj.Rconv(r))
|
|
|
|
|
fmt.Printf("\t%s\n", strings.Replace(strings.TrimSpace(string(stk)), "\n", "\n\t", -1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
|
|
|
|
|
if reg[r-Thearch.REGMIN] != 0 {
|
|
|
|
|
dump(r)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ {
|
|
|
|
|
if reg[r-Thearch.REGMIN] == 0 {
|
|
|
|
|
dump(r)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|