cmd/internal/gc: move cgen, regalloc, et al to portable code
This CL moves the bulk of the code that has been copy-and-pasted
since the initial 386 port back into a shared place, cutting 5 copies to 1.
The motivation here is not cleanup per se but instead to reduce the
cost of introducing changes in shared concepts like regalloc or general
expression evaluation. For example, a change after this one will
implement x.(*T) without a call into the runtime. This CL makes that
followup work 5x easier.
The single copy still has more special cases for architecture details
than I'd like, but having them called out explicitly like this at least
opens the door to generalizing the conditions and smoothing out
the distinctions in the future.
This is a LARGE CL. I started by trying to pull in one function at a time
in a sequence of CLs and it became clear that everything was so
interrelated that it had to be moved as a whole. Apologies for the size.
It is not clear how many more releases this code will matter for;
eventually it will be replaced by Keith's SSA work. But as noted above,
the deduplication was necessary to reduce the cost of working on
the current code while we have it.
Passes tests on amd64, 386, arm, and ppc64le.
Can build arm64 binaries but not tested there.
Being able to build binaries means it is probably very close.
Change-Id: I735977f04c0614f80215fb12966dfe9bbd1f5861
Reviewed-on: https://go-review.googlesource.com/7853
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/cmd/internal/gc/gsubr.go b/src/cmd/internal/gc/gsubr.go
index f5d7621..05642d6 100644
--- a/src/cmd/internal/gc/gsubr.go
+++ b/src/cmd/internal/gc/gsubr.go
@@ -30,7 +30,12 @@
package gc
-import "cmd/internal/obj"
+import (
+ "cmd/internal/obj"
+ "fmt"
+ "runtime"
+ "strings"
+)
var ddumped int
@@ -295,6 +300,8 @@
switch n.Op {
default:
a := a // copy to let escape into Ctxt.Dconv
+ Debug['h'] = 1
+ Dump("naddr", n)
Fatal("naddr: bad %v %v", Oconv(int(n.Op), 0), Ctxt.Dconv(a))
case OREGISTER:
@@ -559,11 +566,8 @@
n.Op = OINDREG
n.Val.U.Reg = int16(Thearch.REGSP)
- if Thearch.Thechar == '5' {
- n.Xoffset += 4
- }
- if Thearch.Thechar == '7' || Thearch.Thechar == '9' {
- n.Xoffset += 8
+ if HasLinkRegister() {
+ n.Xoffset += int64(Ctxt.Arch.Ptrsize)
}
case 1: // input arg
@@ -571,10 +575,6 @@
case 2: // offset output arg
Fatal("shouldn't be used")
-
- n.Op = OINDREG
- n.Val.U.Reg = int16(Thearch.REGSP)
- n.Xoffset += Types[Tptr].Width
}
n.Typecheck = 1
@@ -598,3 +598,228 @@
p.To.Offset = 0
return q
}
+
+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
+ }
+}
+
+func gclean() {
+ for _, r := range Thearch.ReservedRegs {
+ reg[r-Thearch.REGMIN]--
+ }
+
+ for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
+ n := reg[r-Thearch.REGMIN]
+ if n != 0 {
+ Yyerror("reg %v left allocated", obj.Rconv(r))
+ if Debug['v'] != 0 {
+ Regdump()
+ }
+ }
+ }
+
+ for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ {
+ n := reg[r-Thearch.REGMIN]
+ if n != 0 {
+ Yyerror("reg %v left allocated", obj.Rconv(r))
+ if Debug['v'] != 0 {
+ Regdump()
+ }
+ }
+ }
+}
+
+func Anyregalloc() bool {
+ n := 0
+ for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
+ if reg[r-Thearch.REGMIN] == 0 {
+ n++
+ }
+ }
+ return n > len(Thearch.ReservedRegs)
+}
+
+/*
+ * allocate register of type t, leave in n.
+ * if o != N, o may be reusable register.
+ * caller must Regfree(n).
+ */
+func Regalloc(n *Node, t *Type, o *Node) {
+ if t == nil {
+ Fatal("regalloc: t nil")
+ }
+ et := int(Simtype[t.Etype])
+ if Ctxt.Arch.Regsize == 4 && (et == TINT64 || et == TUINT64) {
+ Fatal("regalloc 64bit")
+ }
+
+ var i int
+Switch:
+ switch et {
+ default:
+ Fatal("regalloc: unknown type %v", Tconv(t, 0))
+
+ case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TPTR32, TPTR64, TBOOL:
+ if o != nil && o.Op == OREGISTER {
+ i = int(o.Val.U.Reg)
+ 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()
+ Regdump()
+ Fatal("out of fixed registers")
+
+ case TFLOAT32, TFLOAT64:
+ if o != nil && o.Op == OREGISTER {
+ i = int(o.Val.U.Reg)
+ 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()
+ Regdump()
+ Fatal("out of floating registers")
+
+ case TCOMPLEX64, TCOMPLEX128:
+ Tempname(n, t)
+ 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 {
+ Fatal("regfree: not a register")
+ }
+ i := int(n.Val.U.Reg)
+ if i == Thearch.REGSP {
+ return
+ }
+ switch {
+ case Thearch.REGMIN <= i && i <= Thearch.REGMAX,
+ Thearch.FREGMIN <= i && i <= Thearch.FREGMAX:
+ // ok
+ default:
+ Fatal("regfree: reg out of range")
+ }
+
+ i -= Thearch.REGMIN
+ if reg[i] <= 0 {
+ Fatal("regfree: reg not allocated")
+ }
+ reg[i]--
+ if reg[i] == 0 {
+ regstk[i] = regstk[i][:0]
+ }
+}
+
+// Reginuse reports whether r is in use.
+func Reginuse(r int) bool {
+ switch {
+ case Thearch.REGMIN <= r && r <= Thearch.REGMAX,
+ Thearch.FREGMIN <= r && r <= Thearch.FREGMAX:
+ // ok
+ default:
+ Fatal("reginuse: reg out of range")
+ }
+
+ return reg[r-Thearch.REGMIN] > 0
+}
+
+// Regrealloc(n) undoes the effect of Regfree(n),
+// so that a register can be given up but then reclaimed.
+func Regrealloc(n *Node) {
+ if n.Op != OREGISTER && n.Op != OINDREG {
+ Fatal("regrealloc: not a register")
+ }
+ i := int(n.Val.U.Reg)
+ if i == Thearch.REGSP {
+ return
+ }
+ switch {
+ case Thearch.REGMIN <= i && i <= Thearch.REGMAX,
+ Thearch.FREGMIN <= i && i <= Thearch.FREGMAX:
+ // ok
+ default:
+ Fatal("regrealloc: reg out of range")
+ }
+
+ i -= Thearch.REGMIN
+ if reg[i] == 0 && Debug['v'] > 0 {
+ if regstk[i] == nil {
+ regstk[i] = make([]byte, 4096)
+ }
+ stk := regstk[i]
+ n := runtime.Stack(stk[:cap(stk)], false)
+ regstk[i] = stk[:n]
+ }
+ reg[i]++
+}
+
+func Regdump() {
+ 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)
+ }
+ }
+}