1// Copyright 2020 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 ir
6
7import (
8	"cmd/compile/internal/base"
9	"cmd/compile/internal/types"
10	"cmd/internal/obj"
11	"cmd/internal/src"
12	"go/constant"
13)
14
15// A Decl is a declaration of a const, type, or var. (A declared func is a Func.)
16type Decl struct {
17	miniNode
18	X *Name // the thing being declared
19}
20
21func NewDecl(pos src.XPos, op Op, x *Name) *Decl {
22	n := &Decl{X: x}
23	n.pos = pos
24	switch op {
25	default:
26		panic("invalid Decl op " + op.String())
27	case ODCL:
28		n.op = op
29	}
30	return n
31}
32
33func (*Decl) isStmt() {}
34
35// A Stmt is a Node that can appear as a statement.
36// This includes statement-like expressions such as f().
37//
38// (It's possible it should include <-c, but that would require
39// splitting ORECV out of UnaryExpr, which hasn't yet been
40// necessary. Maybe instead we will introduce ExprStmt at
41// some point.)
42type Stmt interface {
43	Node
44	isStmt()
45}
46
47// A miniStmt is a miniNode with extra fields common to statements.
48type miniStmt struct {
49	miniNode
50	init Nodes
51}
52
53func (*miniStmt) isStmt() {}
54
55func (n *miniStmt) Init() Nodes     { return n.init }
56func (n *miniStmt) SetInit(x Nodes) { n.init = x }
57func (n *miniStmt) PtrInit() *Nodes { return &n.init }
58
59// An AssignListStmt is an assignment statement with
60// more than one item on at least one side: Lhs = Rhs.
61// If Def is true, the assignment is a :=.
62type AssignListStmt struct {
63	miniStmt
64	Lhs Nodes
65	Def bool
66	Rhs Nodes
67}
68
69func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt {
70	n := &AssignListStmt{}
71	n.pos = pos
72	n.SetOp(op)
73	n.Lhs = lhs
74	n.Rhs = rhs
75	return n
76}
77
78func (n *AssignListStmt) SetOp(op Op) {
79	switch op {
80	default:
81		panic(n.no("SetOp " + op.String()))
82	case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2:
83		n.op = op
84	}
85}
86
87// An AssignStmt is a simple assignment statement: X = Y.
88// If Def is true, the assignment is a :=.
89type AssignStmt struct {
90	miniStmt
91	X   Node
92	Def bool
93	Y   Node
94}
95
96func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt {
97	n := &AssignStmt{X: x, Y: y}
98	n.pos = pos
99	n.op = OAS
100	return n
101}
102
103func (n *AssignStmt) SetOp(op Op) {
104	switch op {
105	default:
106		panic(n.no("SetOp " + op.String()))
107	case OAS:
108		n.op = op
109	}
110}
111
112// An AssignOpStmt is an AsOp= assignment statement: X AsOp= Y.
113type AssignOpStmt struct {
114	miniStmt
115	X      Node
116	AsOp   Op // OADD etc
117	Y      Node
118	IncDec bool // actually ++ or --
119}
120
121func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt {
122	n := &AssignOpStmt{AsOp: asOp, X: x, Y: y}
123	n.pos = pos
124	n.op = OASOP
125	return n
126}
127
128// A BlockStmt is a block: { List }.
129type BlockStmt struct {
130	miniStmt
131	List Nodes
132}
133
134func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt {
135	n := &BlockStmt{}
136	n.pos = pos
137	if !pos.IsKnown() {
138		n.pos = base.Pos
139		if len(list) > 0 {
140			n.pos = list[0].Pos()
141		}
142	}
143	n.op = OBLOCK
144	n.List = list
145	return n
146}
147
148// A BranchStmt is a break, continue, fallthrough, or goto statement.
149type BranchStmt struct {
150	miniStmt
151	Label *types.Sym // label if present
152}
153
154func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt {
155	switch op {
156	case OBREAK, OCONTINUE, OFALL, OGOTO:
157		// ok
158	default:
159		panic("NewBranch " + op.String())
160	}
161	n := &BranchStmt{Label: label}
162	n.pos = pos
163	n.op = op
164	return n
165}
166
167func (n *BranchStmt) SetOp(op Op) {
168	switch op {
169	default:
170		panic(n.no("SetOp " + op.String()))
171	case OBREAK, OCONTINUE, OFALL, OGOTO:
172		n.op = op
173	}
174}
175
176func (n *BranchStmt) Sym() *types.Sym { return n.Label }
177
178// A CaseClause is a case statement in a switch or select: case List: Body.
179type CaseClause struct {
180	miniStmt
181	Var  *Name // declared variable for this case in type switch
182	List Nodes // list of expressions for switch, early select
183
184	// RTypes is a list of RType expressions, which are copied to the
185	// corresponding OEQ nodes that are emitted when switch statements
186	// are desugared. RTypes[i] must be non-nil if the emitted
187	// comparison for List[i] will be a mixed interface/concrete
188	// comparison; see reflectdata.CompareRType for details.
189	//
190	// Because mixed interface/concrete switch cases are rare, we allow
191	// len(RTypes) < len(List). Missing entries are implicitly nil.
192	RTypes Nodes
193
194	Body Nodes
195}
196
197func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause {
198	n := &CaseClause{List: list, Body: body}
199	n.pos = pos
200	n.op = OCASE
201	return n
202}
203
204type CommClause struct {
205	miniStmt
206	Comm Node // communication case
207	Body Nodes
208}
209
210func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause {
211	n := &CommClause{Comm: comm, Body: body}
212	n.pos = pos
213	n.op = OCASE
214	return n
215}
216
217// A ForStmt is a non-range for loop: for Init; Cond; Post { Body }
218type ForStmt struct {
219	miniStmt
220	Label        *types.Sym
221	Cond         Node
222	Post         Node
223	Body         Nodes
224	DistinctVars bool
225}
226
227func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node, distinctVars bool) *ForStmt {
228	n := &ForStmt{Cond: cond, Post: post}
229	n.pos = pos
230	n.op = OFOR
231	if init != nil {
232		n.init = []Node{init}
233	}
234	n.Body = body
235	n.DistinctVars = distinctVars
236	return n
237}
238
239// A GoDeferStmt is a go or defer statement: go Call / defer Call.
240//
241// The two opcodes use a single syntax because the implementations
242// are very similar: both are concerned with saving Call and running it
243// in a different context (a separate goroutine or a later time).
244type GoDeferStmt struct {
245	miniStmt
246	Call    Node
247	DeferAt Expr
248}
249
250func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
251	n := &GoDeferStmt{Call: call}
252	n.pos = pos
253	switch op {
254	case ODEFER, OGO:
255		n.op = op
256	default:
257		panic("NewGoDeferStmt " + op.String())
258	}
259	return n
260}
261
262// An IfStmt is a return statement: if Init; Cond { Body } else { Else }.
263type IfStmt struct {
264	miniStmt
265	Cond   Node
266	Body   Nodes
267	Else   Nodes
268	Likely bool // code layout hint
269}
270
271func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt {
272	n := &IfStmt{Cond: cond}
273	n.pos = pos
274	n.op = OIF
275	n.Body = body
276	n.Else = els
277	return n
278}
279
280// A JumpTableStmt is used to implement switches. Its semantics are:
281//
282//	tmp := jt.Idx
283//	if tmp == Cases[0] goto Targets[0]
284//	if tmp == Cases[1] goto Targets[1]
285//	...
286//	if tmp == Cases[n] goto Targets[n]
287//
288// Note that a JumpTableStmt is more like a multiway-goto than
289// a multiway-if. In particular, the case bodies are just
290// labels to jump to, not full Nodes lists.
291type JumpTableStmt struct {
292	miniStmt
293
294	// Value used to index the jump table.
295	// We support only integer types that
296	// are at most the size of a uintptr.
297	Idx Node
298
299	// If Idx is equal to Cases[i], jump to Targets[i].
300	// Cases entries must be distinct and in increasing order.
301	// The length of Cases and Targets must be equal.
302	Cases   []constant.Value
303	Targets []*types.Sym
304}
305
306func NewJumpTableStmt(pos src.XPos, idx Node) *JumpTableStmt {
307	n := &JumpTableStmt{Idx: idx}
308	n.pos = pos
309	n.op = OJUMPTABLE
310	return n
311}
312
313// An InterfaceSwitchStmt is used to implement type switches.
314// Its semantics are:
315//
316//	if RuntimeType implements Descriptor.Cases[0] {
317//	    Case, Itab = 0, itab<RuntimeType, Descriptor.Cases[0]>
318//	} else if RuntimeType implements Descriptor.Cases[1] {
319//	    Case, Itab = 1, itab<RuntimeType, Descriptor.Cases[1]>
320//	...
321//	} else if RuntimeType implements Descriptor.Cases[N-1] {
322//	    Case, Itab = N-1, itab<RuntimeType, Descriptor.Cases[N-1]>
323//	} else {
324//	    Case, Itab = len(cases), nil
325//	}
326//
327// RuntimeType must be a non-nil *runtime._type.
328// Hash must be the hash field of RuntimeType (or its copy loaded from an itab).
329// Descriptor must represent an abi.InterfaceSwitch global variable.
330type InterfaceSwitchStmt struct {
331	miniStmt
332
333	Case        Node
334	Itab        Node
335	RuntimeType Node
336	Hash        Node
337	Descriptor  *obj.LSym
338}
339
340func NewInterfaceSwitchStmt(pos src.XPos, case_, itab, runtimeType, hash Node, descriptor *obj.LSym) *InterfaceSwitchStmt {
341	n := &InterfaceSwitchStmt{
342		Case:        case_,
343		Itab:        itab,
344		RuntimeType: runtimeType,
345		Hash:        hash,
346		Descriptor:  descriptor,
347	}
348	n.pos = pos
349	n.op = OINTERFACESWITCH
350	return n
351}
352
353// An InlineMarkStmt is a marker placed just before an inlined body.
354type InlineMarkStmt struct {
355	miniStmt
356	Index int64
357}
358
359func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt {
360	n := &InlineMarkStmt{Index: index}
361	n.pos = pos
362	n.op = OINLMARK
363	return n
364}
365
366func (n *InlineMarkStmt) Offset() int64     { return n.Index }
367func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x }
368
369// A LabelStmt is a label statement (just the label, not including the statement it labels).
370type LabelStmt struct {
371	miniStmt
372	Label *types.Sym // "Label:"
373}
374
375func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt {
376	n := &LabelStmt{Label: label}
377	n.pos = pos
378	n.op = OLABEL
379	return n
380}
381
382func (n *LabelStmt) Sym() *types.Sym { return n.Label }
383
384// A RangeStmt is a range loop: for Key, Value = range X { Body }
385type RangeStmt struct {
386	miniStmt
387	Label        *types.Sym
388	Def          bool
389	X            Node
390	RType        Node `mknode:"-"` // see reflectdata/helpers.go
391	Key          Node
392	Value        Node
393	Body         Nodes
394	DistinctVars bool
395	Prealloc     *Name
396
397	// When desugaring the RangeStmt during walk, the assignments to Key
398	// and Value may require OCONVIFACE operations. If so, these fields
399	// will be copied to their respective ConvExpr fields.
400	KeyTypeWord   Node `mknode:"-"`
401	KeySrcRType   Node `mknode:"-"`
402	ValueTypeWord Node `mknode:"-"`
403	ValueSrcRType Node `mknode:"-"`
404}
405
406func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node, distinctVars bool) *RangeStmt {
407	n := &RangeStmt{X: x, Key: key, Value: value}
408	n.pos = pos
409	n.op = ORANGE
410	n.Body = body
411	n.DistinctVars = distinctVars
412	return n
413}
414
415// A ReturnStmt is a return statement.
416type ReturnStmt struct {
417	miniStmt
418	Results Nodes // return list
419}
420
421func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
422	n := &ReturnStmt{}
423	n.pos = pos
424	n.op = ORETURN
425	n.Results = results
426	return n
427}
428
429// A SelectStmt is a block: { Cases }.
430type SelectStmt struct {
431	miniStmt
432	Label *types.Sym
433	Cases []*CommClause
434
435	// TODO(rsc): Instead of recording here, replace with a block?
436	Compiled Nodes // compiled form, after walkSelect
437}
438
439func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt {
440	n := &SelectStmt{Cases: cases}
441	n.pos = pos
442	n.op = OSELECT
443	return n
444}
445
446// A SendStmt is a send statement: X <- Y.
447type SendStmt struct {
448	miniStmt
449	Chan  Node
450	Value Node
451}
452
453func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt {
454	n := &SendStmt{Chan: ch, Value: value}
455	n.pos = pos
456	n.op = OSEND
457	return n
458}
459
460// A SwitchStmt is a switch statement: switch Init; Tag { Cases }.
461type SwitchStmt struct {
462	miniStmt
463	Tag   Node
464	Cases []*CaseClause
465	Label *types.Sym
466
467	// TODO(rsc): Instead of recording here, replace with a block?
468	Compiled Nodes // compiled form, after walkSwitch
469}
470
471func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
472	n := &SwitchStmt{Tag: tag, Cases: cases}
473	n.pos = pos
474	n.op = OSWITCH
475	return n
476}
477
478// A TailCallStmt is a tail call statement, which is used for back-end
479// code generation to jump directly to another function entirely.
480type TailCallStmt struct {
481	miniStmt
482	Call *CallExpr // the underlying call
483}
484
485func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt {
486	n := &TailCallStmt{Call: call}
487	n.pos = pos
488	n.op = OTAILCALL
489	return n
490}
491
492// A TypeSwitchGuard is the [Name :=] X.(type) in a type switch.
493type TypeSwitchGuard struct {
494	miniNode
495	Tag  *Ident
496	X    Node
497	Used bool
498}
499
500func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard {
501	n := &TypeSwitchGuard{Tag: tag, X: x}
502	n.pos = pos
503	n.op = OTYPESW
504	return n
505}
506