Source file src/cmd/compile/internal/ir/stmt.go

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

View as plain text