1// Inferno utils/6l/pass.c
2// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/pass.c
3//
4//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
5//	Portions Copyright © 1995-1997 C H Forsyth ([email protected])
6//	Portions Copyright © 1997-1999 Vita Nuova Limited
7//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8//	Portions Copyright © 2004,2006 Bruce Ellis
9//	Portions Copyright © 2005-2007 C H Forsyth ([email protected])
10//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11//	Portions Copyright © 2009 The Go Authors. All rights reserved.
12//
13// Permission is hereby granted, free of charge, to any person obtaining a copy
14// of this software and associated documentation files (the "Software"), to deal
15// in the Software without restriction, including without limitation the rights
16// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17// copies of the Software, and to permit persons to whom the Software is
18// furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included in
21// all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
26// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29// THE SOFTWARE.
30
31package obj
32
33// Code and data passes.
34
35// brloop returns the ultimate destination of the series of unconditional jumps beginning at p.
36// In the case of an infinite loop, brloop returns nil.
37func brloop(p *Prog) *Prog {
38	c := 0
39	for q := p; q != nil; q = q.To.Target() {
40		if q.As != AJMP || q.To.Target() == nil {
41			return q
42		}
43		c++
44		if c >= 5000 {
45			// infinite loop
46			return nil
47		}
48	}
49	panic("unreachable")
50}
51
52// checkaddr checks that a has an expected encoding, especially TYPE_CONST vs TYPE_ADDR.
53func checkaddr(ctxt *Link, p *Prog, a *Addr) {
54	switch a.Type {
55	case TYPE_NONE, TYPE_REGREG2, TYPE_REGLIST:
56		return
57
58	case TYPE_BRANCH, TYPE_TEXTSIZE:
59		if a.Reg != 0 || a.Index != 0 || a.Scale != 0 || a.Name != 0 {
60			break
61		}
62		return
63
64	case TYPE_MEM:
65		return
66
67	case TYPE_CONST:
68		// TODO(rsc): After fixing SHRQ, check a.Index != 0 too.
69		if a.Name != 0 || a.Sym != nil || a.Reg != 0 {
70			ctxt.Diag("argument is TYPE_CONST, should be TYPE_ADDR, in %v", p)
71			return
72		}
73
74		if a.Reg != 0 || a.Scale != 0 || a.Name != 0 || a.Sym != nil || a.Val != nil {
75			break
76		}
77		return
78
79	case TYPE_FCONST, TYPE_SCONST:
80		if a.Reg != 0 || a.Index != 0 || a.Scale != 0 || a.Name != 0 || a.Offset != 0 || a.Sym != nil {
81			break
82		}
83		return
84
85	case TYPE_REG:
86		// TODO(rsc): After fixing PINSRQ, check a.Offset != 0 too.
87		// TODO(rsc): After fixing SHRQ, check a.Index != 0 too.
88		if a.Scale != 0 || a.Name != 0 || a.Sym != nil {
89			break
90		}
91		return
92
93	case TYPE_ADDR:
94		if a.Val != nil {
95			break
96		}
97		if a.Reg == 0 && a.Index == 0 && a.Scale == 0 && a.Name == 0 && a.Sym == nil {
98			ctxt.Diag("argument is TYPE_ADDR, should be TYPE_CONST, in %v", p)
99		}
100		return
101
102	case TYPE_SHIFT, TYPE_REGREG:
103		if a.Index != 0 || a.Scale != 0 || a.Name != 0 || a.Sym != nil || a.Val != nil {
104			break
105		}
106		return
107
108	case TYPE_INDIR:
109		// Expect sym and name to be set, nothing else.
110		// Technically more is allowed, but this is only used for *name(SB).
111		if a.Reg != 0 || a.Index != 0 || a.Scale != 0 || a.Name == 0 || a.Offset != 0 || a.Sym == nil || a.Val != nil {
112			break
113		}
114		return
115	case TYPE_SPECIAL:
116		if a.Reg != 0 || a.Index != 0 || a.Scale != 0 || a.Name != 0 || a.Class != 0 || a.Sym != nil {
117			break
118		}
119		return
120	}
121
122	ctxt.Diag("invalid encoding for argument %v", p)
123}
124
125func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) {
126	for p := sym.Func().Text; p != nil; p = p.Link {
127		checkaddr(ctxt, p, &p.From)
128		for _, v := range p.RestArgs {
129			checkaddr(ctxt, p, &v.Addr)
130		}
131		checkaddr(ctxt, p, &p.To)
132
133		if ctxt.Arch.Progedit != nil {
134			ctxt.Arch.Progedit(ctxt, p, newprog)
135		}
136		if p.To.Type != TYPE_BRANCH {
137			continue
138		}
139		if p.To.Val != nil {
140			continue
141		}
142
143		if p.To.Sym != nil {
144			continue
145		}
146		q := sym.Func().Text
147		for q != nil && p.To.Offset != q.Pc {
148			if q.Forwd != nil && p.To.Offset >= q.Forwd.Pc {
149				q = q.Forwd
150			} else {
151				q = q.Link
152			}
153		}
154
155		if q == nil {
156			name := "<nil>"
157			if p.To.Sym != nil {
158				name = p.To.Sym.Name
159			}
160			ctxt.Diag("branch out of range (%#x)\n%v [%s]", uint32(p.To.Offset), p, name)
161			p.To.Type = TYPE_NONE
162		}
163
164		p.To.SetTarget(q)
165	}
166
167	if !ctxt.Flag_optimize {
168		return
169	}
170
171	// Collapse series of jumps to jumps.
172	for p := sym.Func().Text; p != nil; p = p.Link {
173		if p.To.Target() == nil {
174			continue
175		}
176		p.To.SetTarget(brloop(p.To.Target()))
177		if p.To.Target() != nil && p.To.Type == TYPE_BRANCH {
178			p.To.Offset = p.To.Target().Pc
179		}
180	}
181}
182