1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ssa
6
7import (
8	"cmd/compile/internal/types"
9	"cmd/internal/obj"
10	"cmd/internal/objabi"
11	"cmd/internal/src"
12)
13
14// A Config holds readonly compilation information.
15// It is created once, early during compilation,
16// and shared across all compilations.
17type Config struct {
18	arch           string // "amd64", etc.
19	PtrSize        int64  // 4 or 8; copy of cmd/internal/sys.Arch.PtrSize
20	RegSize        int64  // 4 or 8; copy of cmd/internal/sys.Arch.RegSize
21	Types          Types
22	lowerBlock     blockRewriter // lowering function
23	lowerValue     valueRewriter // lowering function
24	splitLoad      valueRewriter // function for splitting merged load ops; only used on some architectures
25	registers      []Register    // machine registers
26	gpRegMask      regMask       // general purpose integer register mask
27	fpRegMask      regMask       // floating point register mask
28	fp32RegMask    regMask       // floating point register mask
29	fp64RegMask    regMask       // floating point register mask
30	specialRegMask regMask       // special register mask
31	GCRegMap       []*Register   // garbage collector register map, by GC register index
32	FPReg          int8          // register number of frame pointer, -1 if not used
33	LinkReg        int8          // register number of link register if it is a general purpose register, -1 if not used
34	hasGReg        bool          // has hardware g register
35	ctxt           *obj.Link     // Generic arch information
36	optimize       bool          // Do optimization
37	noDuffDevice   bool          // Don't use Duff's device
38	useSSE         bool          // Use SSE for non-float operations
39	useAvg         bool          // Use optimizations that need Avg* operations
40	useHmul        bool          // Use optimizations that need Hmul* operations
41	use387         bool          // GO386=387
42	SoftFloat      bool          //
43	Race           bool          // race detector enabled
44	NeedsFpScratch bool          // No direct move between GP and FP register sets
45	BigEndian      bool          //
46	UseFMA         bool          // Use hardware FMA operation
47}
48
49type (
50	blockRewriter func(*Block) bool
51	valueRewriter func(*Value) bool
52)
53
54type Types struct {
55	Bool       *types.Type
56	Int8       *types.Type
57	Int16      *types.Type
58	Int32      *types.Type
59	Int64      *types.Type
60	UInt8      *types.Type
61	UInt16     *types.Type
62	UInt32     *types.Type
63	UInt64     *types.Type
64	Int        *types.Type
65	Float32    *types.Type
66	Float64    *types.Type
67	UInt       *types.Type
68	Uintptr    *types.Type
69	String     *types.Type
70	BytePtr    *types.Type // TODO: use unsafe.Pointer instead?
71	Int32Ptr   *types.Type
72	UInt32Ptr  *types.Type
73	IntPtr     *types.Type
74	UintptrPtr *types.Type
75	Float32Ptr *types.Type
76	Float64Ptr *types.Type
77	BytePtrPtr *types.Type
78}
79
80// NewTypes creates and populates a Types.
81func NewTypes() *Types {
82	t := new(Types)
83	t.SetTypPtrs()
84	return t
85}
86
87// SetTypPtrs populates t.
88func (t *Types) SetTypPtrs() {
89	t.Bool = types.Types[types.TBOOL]
90	t.Int8 = types.Types[types.TINT8]
91	t.Int16 = types.Types[types.TINT16]
92	t.Int32 = types.Types[types.TINT32]
93	t.Int64 = types.Types[types.TINT64]
94	t.UInt8 = types.Types[types.TUINT8]
95	t.UInt16 = types.Types[types.TUINT16]
96	t.UInt32 = types.Types[types.TUINT32]
97	t.UInt64 = types.Types[types.TUINT64]
98	t.Int = types.Types[types.TINT]
99	t.Float32 = types.Types[types.TFLOAT32]
100	t.Float64 = types.Types[types.TFLOAT64]
101	t.UInt = types.Types[types.TUINT]
102	t.Uintptr = types.Types[types.TUINTPTR]
103	t.String = types.Types[types.TSTRING]
104	t.BytePtr = types.NewPtr(types.Types[types.TUINT8])
105	t.Int32Ptr = types.NewPtr(types.Types[types.TINT32])
106	t.UInt32Ptr = types.NewPtr(types.Types[types.TUINT32])
107	t.IntPtr = types.NewPtr(types.Types[types.TINT])
108	t.UintptrPtr = types.NewPtr(types.Types[types.TUINTPTR])
109	t.Float32Ptr = types.NewPtr(types.Types[types.TFLOAT32])
110	t.Float64Ptr = types.NewPtr(types.Types[types.TFLOAT64])
111	t.BytePtrPtr = types.NewPtr(types.NewPtr(types.Types[types.TUINT8]))
112}
113
114type Logger interface {
115	// Logf logs a message from the compiler.
116	Logf(string, ...interface{})
117
118	// Log reports whether logging is not a no-op
119	// some logging calls account for more than a few heap allocations.
120	Log() bool
121
122	// Fatal reports a compiler error and exits.
123	Fatalf(pos src.XPos, msg string, args ...interface{})
124
125	// Warnl writes compiler messages in the form expected by "errorcheck" tests
126	Warnl(pos src.XPos, fmt_ string, args ...interface{})
127
128	// Forwards the Debug flags from gc
129	Debug_checknil() bool
130}
131
132type Frontend interface {
133	CanSSA(t *types.Type) bool
134
135	Logger
136
137	// StringData returns a symbol pointing to the given string's contents.
138	StringData(string) interface{} // returns *gc.Sym
139
140	// Auto returns a Node for an auto variable of the given type.
141	// The SSA compiler uses this function to allocate space for spills.
142	Auto(src.XPos, *types.Type) GCNode
143
144	// Given the name for a compound type, returns the name we should use
145	// for the parts of that compound type.
146	SplitString(LocalSlot) (LocalSlot, LocalSlot)
147	SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
148	SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
149	SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
150	SplitStruct(LocalSlot, int) LocalSlot
151	SplitArray(LocalSlot) LocalSlot              // array must be length 1
152	SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
153
154	// DerefItab dereferences an itab function
155	// entry, given the symbol of the itab and
156	// the byte offset of the function pointer.
157	// It may return nil.
158	DerefItab(sym *obj.LSym, offset int64) *obj.LSym
159
160	// Line returns a string describing the given position.
161	Line(src.XPos) string
162
163	// AllocFrame assigns frame offsets to all live auto variables.
164	AllocFrame(f *Func)
165
166	// Syslook returns a symbol of the runtime function/variable with the
167	// given name.
168	Syslook(string) *obj.LSym
169
170	// UseWriteBarrier reports whether write barrier is enabled
171	UseWriteBarrier() bool
172
173	// SetWBPos indicates that a write barrier has been inserted
174	// in this function at position pos.
175	SetWBPos(pos src.XPos)
176}
177
178// interface used to hold a *gc.Node (a stack variable).
179// We'd use *gc.Node directly but that would lead to an import cycle.
180type GCNode interface {
181	Typ() *types.Type
182	String() string
183	IsSynthetic() bool
184	IsAutoTmp() bool
185	StorageClass() StorageClass
186}
187
188type StorageClass uint8
189
190const (
191	ClassAuto     StorageClass = iota // local stack variable
192	ClassParam                        // argument
193	ClassParamOut                     // return value
194)
195
196// NewConfig returns a new configuration object for the given architecture.
197func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config {
198	c := &Config{arch: arch, Types: types}
199	c.useAvg = true
200	c.useHmul = true
201	switch arch {
202	case "amd64":
203		c.PtrSize = 8
204		c.RegSize = 8
205		c.lowerBlock = rewriteBlockAMD64
206		c.lowerValue = rewriteValueAMD64
207		c.splitLoad = rewriteValueAMD64splitload
208		c.registers = registersAMD64[:]
209		c.gpRegMask = gpRegMaskAMD64
210		c.fpRegMask = fpRegMaskAMD64
211		c.FPReg = framepointerRegAMD64
212		c.LinkReg = linkRegAMD64
213		c.hasGReg = false
214	case "386":
215		c.PtrSize = 4
216		c.RegSize = 4
217		c.lowerBlock = rewriteBlock386
218		c.lowerValue = rewriteValue386
219		c.splitLoad = rewriteValue386splitload
220		c.registers = registers386[:]
221		c.gpRegMask = gpRegMask386
222		c.fpRegMask = fpRegMask386
223		c.FPReg = framepointerReg386
224		c.LinkReg = linkReg386
225		c.hasGReg = false
226	case "arm":
227		c.PtrSize = 4
228		c.RegSize = 4
229		c.lowerBlock = rewriteBlockARM
230		c.lowerValue = rewriteValueARM
231		c.registers = registersARM[:]
232		c.gpRegMask = gpRegMaskARM
233		c.fpRegMask = fpRegMaskARM
234		c.FPReg = framepointerRegARM
235		c.LinkReg = linkRegARM
236		c.hasGReg = true
237	case "arm64":
238		c.PtrSize = 8
239		c.RegSize = 8
240		c.lowerBlock = rewriteBlockARM64
241		c.lowerValue = rewriteValueARM64
242		c.registers = registersARM64[:]
243		c.gpRegMask = gpRegMaskARM64
244		c.fpRegMask = fpRegMaskARM64
245		c.FPReg = framepointerRegARM64
246		c.LinkReg = linkRegARM64
247		c.hasGReg = true
248		c.noDuffDevice = objabi.GOOS == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend
249	case "ppc64":
250		c.BigEndian = true
251		fallthrough
252	case "ppc64le":
253		c.PtrSize = 8
254		c.RegSize = 8
255		c.lowerBlock = rewriteBlockPPC64
256		c.lowerValue = rewriteValuePPC64
257		c.registers = registersPPC64[:]
258		c.gpRegMask = gpRegMaskPPC64
259		c.fpRegMask = fpRegMaskPPC64
260		c.FPReg = framepointerRegPPC64
261		c.LinkReg = linkRegPPC64
262		c.noDuffDevice = true // TODO: Resolve PPC64 DuffDevice (has zero, but not copy)
263		c.hasGReg = true
264	case "mips64":
265		c.BigEndian = true
266		fallthrough
267	case "mips64le":
268		c.PtrSize = 8
269		c.RegSize = 8
270		c.lowerBlock = rewriteBlockMIPS64
271		c.lowerValue = rewriteValueMIPS64
272		c.registers = registersMIPS64[:]
273		c.gpRegMask = gpRegMaskMIPS64
274		c.fpRegMask = fpRegMaskMIPS64
275		c.specialRegMask = specialRegMaskMIPS64
276		c.FPReg = framepointerRegMIPS64
277		c.LinkReg = linkRegMIPS64
278		c.hasGReg = true
279	case "s390x":
280		c.PtrSize = 8
281		c.RegSize = 8
282		c.lowerBlock = rewriteBlockS390X
283		c.lowerValue = rewriteValueS390X
284		c.registers = registersS390X[:]
285		c.gpRegMask = gpRegMaskS390X
286		c.fpRegMask = fpRegMaskS390X
287		c.FPReg = framepointerRegS390X
288		c.LinkReg = linkRegS390X
289		c.hasGReg = true
290		c.noDuffDevice = true
291		c.BigEndian = true
292	case "mips":
293		c.BigEndian = true
294		fallthrough
295	case "mipsle":
296		c.PtrSize = 4
297		c.RegSize = 4
298		c.lowerBlock = rewriteBlockMIPS
299		c.lowerValue = rewriteValueMIPS
300		c.registers = registersMIPS[:]
301		c.gpRegMask = gpRegMaskMIPS
302		c.fpRegMask = fpRegMaskMIPS
303		c.specialRegMask = specialRegMaskMIPS
304		c.FPReg = framepointerRegMIPS
305		c.LinkReg = linkRegMIPS
306		c.hasGReg = true
307		c.noDuffDevice = true
308	case "riscv64":
309		c.PtrSize = 8
310		c.RegSize = 8
311		c.lowerBlock = rewriteBlockRISCV64
312		c.lowerValue = rewriteValueRISCV64
313		c.registers = registersRISCV64[:]
314		c.gpRegMask = gpRegMaskRISCV64
315		c.fpRegMask = fpRegMaskRISCV64
316		c.FPReg = framepointerRegRISCV64
317		c.hasGReg = true
318	case "wasm":
319		c.PtrSize = 8
320		c.RegSize = 8
321		c.lowerBlock = rewriteBlockWasm
322		c.lowerValue = rewriteValueWasm
323		c.registers = registersWasm[:]
324		c.gpRegMask = gpRegMaskWasm
325		c.fpRegMask = fpRegMaskWasm
326		c.fp32RegMask = fp32RegMaskWasm
327		c.fp64RegMask = fp64RegMaskWasm
328		c.FPReg = framepointerRegWasm
329		c.LinkReg = linkRegWasm
330		c.hasGReg = true
331		c.noDuffDevice = true
332		c.useAvg = false
333		c.useHmul = false
334	default:
335		ctxt.Diag("arch %s not implemented", arch)
336	}
337	c.ctxt = ctxt
338	c.optimize = optimize
339	c.useSSE = true
340	c.UseFMA = true
341
342	// On Plan 9, floating point operations are not allowed in note handler.
343	if objabi.GOOS == "plan9" {
344		// Don't use FMA on Plan 9
345		c.UseFMA = false
346
347		// Don't use Duff's device and SSE on Plan 9 AMD64.
348		if arch == "amd64" {
349			c.noDuffDevice = true
350			c.useSSE = false
351		}
352	}
353
354	if ctxt.Flag_shared {
355		// LoweredWB is secretly a CALL and CALLs on 386 in
356		// shared mode get rewritten by obj6.go to go through
357		// the GOT, which clobbers BX.
358		opcodeTable[Op386LoweredWB].reg.clobbers |= 1 << 3 // BX
359	}
360
361	// Create the GC register map index.
362	// TODO: This is only used for debug printing. Maybe export config.registers?
363	gcRegMapSize := int16(0)
364	for _, r := range c.registers {
365		if r.gcNum+1 > gcRegMapSize {
366			gcRegMapSize = r.gcNum + 1
367		}
368	}
369	c.GCRegMap = make([]*Register, gcRegMapSize)
370	for i, r := range c.registers {
371		if r.gcNum != -1 {
372			c.GCRegMap[r.gcNum] = &c.registers[i]
373		}
374	}
375
376	return c
377}
378
379func (c *Config) Set387(b bool) {
380	c.NeedsFpScratch = b
381	c.use387 = b
382}
383
384func (c *Config) Ctxt() *obj.Link { return c.ctxt }
385