github.com/bir3/gocompiler@v0.9.2202/src/cmd/compile/internal/ir/stmt.go (about) 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 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 9 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 10 "github.com/bir3/gocompiler/src/cmd/internal/obj" 11 "github.com/bir3/gocompiler/src/cmd/internal/src" 12 "github.com/bir3/gocompiler/src/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 }