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