...

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

Documentation: cmd/compile/internal/ir

     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  }
    46  
    47  // A miniStmt is a miniNode with extra fields common to statements.
    48  type miniStmt struct {
    49  	miniNode
    50  	init Nodes
    51  }
    52  
    53  func (*miniStmt) isStmt() {}
    54  
    55  func (n *miniStmt) Init() Nodes     { return n.init }
    56  func (n *miniStmt) SetInit(x Nodes) { n.init = x }
    57  func (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 :=.
    62  type AssignListStmt struct {
    63  	miniStmt
    64  	Lhs Nodes
    65  	Def bool
    66  	Rhs Nodes
    67  }
    68  
    69  func 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  
    78  func (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 :=.
    89  type AssignStmt struct {
    90  	miniStmt
    91  	X   Node
    92  	Def bool
    93  	Y   Node
    94  }
    95  
    96  func 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  
   103  func (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.
   113  type AssignOpStmt struct {
   114  	miniStmt
   115  	X      Node
   116  	AsOp   Op // OADD etc
   117  	Y      Node
   118  	IncDec bool // actually ++ or --
   119  }
   120  
   121  func 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 }.
   129  type BlockStmt struct {
   130  	miniStmt
   131  	List Nodes
   132  }
   133  
   134  func 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.
   149  type BranchStmt struct {
   150  	miniStmt
   151  	Label *types.Sym // label if present
   152  }
   153  
   154  func 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  
   167  func (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  
   176  func (n *BranchStmt) Sym() *types.Sym { return n.Label }
   177  
   178  // A CaseClause is a case statement in a switch or select: case List: Body.
   179  type 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  
   197  func 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  
   204  type CommClause struct {
   205  	miniStmt
   206  	Comm Node // communication case
   207  	Body Nodes
   208  }
   209  
   210  func 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 }
   218  type ForStmt struct {
   219  	miniStmt
   220  	Label        *types.Sym
   221  	Cond         Node
   222  	Post         Node
   223  	Body         Nodes
   224  	DistinctVars bool
   225  }
   226  
   227  func 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).
   244  type GoDeferStmt struct {
   245  	miniStmt
   246  	Call    Node
   247  	DeferAt Expr
   248  }
   249  
   250  func 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 }.
   263  type IfStmt struct {
   264  	miniStmt
   265  	Cond   Node
   266  	Body   Nodes
   267  	Else   Nodes
   268  	Likely bool // code layout hint
   269  }
   270  
   271  func 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.
   291  type 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  
   306  func 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.
   330  type InterfaceSwitchStmt struct {
   331  	miniStmt
   332  
   333  	Case        Node
   334  	Itab        Node
   335  	RuntimeType Node
   336  	Hash        Node
   337  	Descriptor  *obj.LSym
   338  }
   339  
   340  func 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.
   354  type InlineMarkStmt struct {
   355  	miniStmt
   356  	Index int64
   357  }
   358  
   359  func 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  
   366  func (n *InlineMarkStmt) Offset() int64     { return n.Index }
   367  func (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).
   370  type LabelStmt struct {
   371  	miniStmt
   372  	Label *types.Sym // "Label:"
   373  }
   374  
   375  func 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  
   382  func (n *LabelStmt) Sym() *types.Sym { return n.Label }
   383  
   384  // A RangeStmt is a range loop: for Key, Value = range X { Body }
   385  type 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  
   406  func 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.
   416  type ReturnStmt struct {
   417  	miniStmt
   418  	Results Nodes // return list
   419  }
   420  
   421  func 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 }.
   430  type 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  
   439  func 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.
   447  type SendStmt struct {
   448  	miniStmt
   449  	Chan  Node
   450  	Value Node
   451  }
   452  
   453  func 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 }.
   461  type 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  
   471  func 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.
   480  type TailCallStmt struct {
   481  	miniStmt
   482  	Call *CallExpr // the underlying call
   483  }
   484  
   485  func 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.
   493  type TypeSwitchGuard struct {
   494  	miniNode
   495  	Tag  *Ident
   496  	X    Node
   497  	Used bool
   498  }
   499  
   500  func 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  

View as plain text