Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 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/src" |
| 11 | ) |
| 12 | |
| 13 | // A Decl is a declaration of a const, type, or var. (A declared func is a Func.) |
| 14 | type Decl struct { |
| 15 | miniNode |
| 16 | X *Name // the thing being declared |
| 17 | } |
| 18 | |
| 19 | func NewDecl(pos src.XPos, op Op, x *Name) *Decl { |
| 20 | n := &Decl{X: x} |
| 21 | n.pos = pos |
| 22 | switch op { |
| 23 | default: |
| 24 | panic("invalid Decl op " + op.String()) |
| 25 | case ODCL, ODCLCONST, ODCLTYPE: |
| 26 | n.op = op |
| 27 | } |
| 28 | return n |
| 29 | } |
| 30 | |
| 31 | func (*Decl) isStmt() {} |
| 32 | |
| 33 | // A Stmt is a Node that can appear as a statement. |
| 34 | // This includes statement-like expressions such as f(). |
| 35 | // |
| 36 | // (It's possible it should include <-c, but that would require |
| 37 | // splitting ORECV out of UnaryExpr, which hasn't yet been |
| 38 | // necessary. Maybe instead we will introduce ExprStmt at |
| 39 | // some point.) |
| 40 | type Stmt interface { |
| 41 | Node |
| 42 | isStmt() |
| 43 | } |
| 44 | |
| 45 | // A miniStmt is a miniNode with extra fields common to statements. |
| 46 | type miniStmt struct { |
| 47 | miniNode |
| 48 | init Nodes |
| 49 | } |
| 50 | |
| 51 | func (*miniStmt) isStmt() {} |
| 52 | |
| 53 | func (n *miniStmt) Init() Nodes { return n.init } |
| 54 | func (n *miniStmt) SetInit(x Nodes) { n.init = x } |
| 55 | func (n *miniStmt) PtrInit() *Nodes { return &n.init } |
| 56 | |
| 57 | // An AssignListStmt is an assignment statement with |
| 58 | // more than one item on at least one side: Lhs = Rhs. |
| 59 | // If Def is true, the assignment is a :=. |
| 60 | type AssignListStmt struct { |
| 61 | miniStmt |
| 62 | Lhs Nodes |
| 63 | Def bool |
| 64 | Rhs Nodes |
| 65 | } |
| 66 | |
| 67 | func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt { |
| 68 | n := &AssignListStmt{} |
| 69 | n.pos = pos |
| 70 | n.SetOp(op) |
| 71 | n.Lhs = lhs |
| 72 | n.Rhs = rhs |
| 73 | return n |
| 74 | } |
| 75 | |
| 76 | func (n *AssignListStmt) SetOp(op Op) { |
| 77 | switch op { |
| 78 | default: |
| 79 | panic(n.no("SetOp " + op.String())) |
| 80 | case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2: |
| 81 | n.op = op |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | // An AssignStmt is a simple assignment statement: X = Y. |
| 86 | // If Def is true, the assignment is a :=. |
| 87 | type AssignStmt struct { |
| 88 | miniStmt |
| 89 | X Node |
| 90 | Def bool |
| 91 | Y Node |
| 92 | } |
| 93 | |
| 94 | func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt { |
| 95 | n := &AssignStmt{X: x, Y: y} |
| 96 | n.pos = pos |
| 97 | n.op = OAS |
| 98 | return n |
| 99 | } |
| 100 | |
| 101 | func (n *AssignStmt) SetOp(op Op) { |
| 102 | switch op { |
| 103 | default: |
| 104 | panic(n.no("SetOp " + op.String())) |
| 105 | case OAS: |
| 106 | n.op = op |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | // An AssignOpStmt is an AsOp= assignment statement: X AsOp= Y. |
| 111 | type AssignOpStmt struct { |
| 112 | miniStmt |
| 113 | X Node |
| 114 | AsOp Op // OADD etc |
| 115 | Y Node |
| 116 | IncDec bool // actually ++ or -- |
| 117 | } |
| 118 | |
| 119 | func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt { |
| 120 | n := &AssignOpStmt{AsOp: asOp, X: x, Y: y} |
| 121 | n.pos = pos |
| 122 | n.op = OASOP |
| 123 | return n |
| 124 | } |
| 125 | |
| 126 | // A BlockStmt is a block: { List }. |
| 127 | type BlockStmt struct { |
| 128 | miniStmt |
| 129 | List Nodes |
| 130 | } |
| 131 | |
| 132 | func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt { |
| 133 | n := &BlockStmt{} |
| 134 | n.pos = pos |
| 135 | if !pos.IsKnown() { |
| 136 | n.pos = base.Pos |
| 137 | if len(list) > 0 { |
| 138 | n.pos = list[0].Pos() |
| 139 | } |
| 140 | } |
| 141 | n.op = OBLOCK |
| 142 | n.List = list |
| 143 | return n |
| 144 | } |
| 145 | |
| 146 | // A BranchStmt is a break, continue, fallthrough, or goto statement. |
| 147 | type BranchStmt struct { |
| 148 | miniStmt |
| 149 | Label *types.Sym // label if present |
| 150 | } |
| 151 | |
| 152 | func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt { |
| 153 | switch op { |
| 154 | case OBREAK, OCONTINUE, OFALL, OGOTO: |
| 155 | // ok |
| 156 | default: |
| 157 | panic("NewBranch " + op.String()) |
| 158 | } |
| 159 | n := &BranchStmt{Label: label} |
| 160 | n.pos = pos |
| 161 | n.op = op |
| 162 | return n |
| 163 | } |
| 164 | |
| 165 | func (n *BranchStmt) Sym() *types.Sym { return n.Label } |
| 166 | |
| 167 | // A CaseClause is a case statement in a switch or select: case List: Body. |
| 168 | type CaseClause struct { |
| 169 | miniStmt |
| 170 | Var *Name // declared variable for this case in type switch |
| 171 | List Nodes // list of expressions for switch, early select |
| 172 | Body Nodes |
| 173 | } |
| 174 | |
| 175 | func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause { |
| 176 | n := &CaseClause{List: list, Body: body} |
| 177 | n.pos = pos |
| 178 | n.op = OCASE |
| 179 | return n |
| 180 | } |
| 181 | |
| 182 | type CommClause struct { |
| 183 | miniStmt |
| 184 | Comm Node // communication case |
| 185 | Body Nodes |
| 186 | } |
| 187 | |
| 188 | func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause { |
| 189 | n := &CommClause{Comm: comm, Body: body} |
| 190 | n.pos = pos |
| 191 | n.op = OCASE |
| 192 | return n |
| 193 | } |
| 194 | |
| 195 | // A ForStmt is a non-range for loop: for Init; Cond; Post { Body } |
| 196 | // Op can be OFOR or OFORUNTIL (!Cond). |
| 197 | type ForStmt struct { |
| 198 | miniStmt |
| 199 | Label *types.Sym |
| 200 | Cond Node |
| 201 | Late Nodes |
| 202 | Post Node |
| 203 | Body Nodes |
| 204 | HasBreak bool |
| 205 | } |
| 206 | |
| 207 | func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt { |
| 208 | n := &ForStmt{Cond: cond, Post: post} |
| 209 | n.pos = pos |
| 210 | n.op = OFOR |
| 211 | if init != nil { |
| 212 | n.init = []Node{init} |
| 213 | } |
| 214 | n.Body = body |
| 215 | return n |
| 216 | } |
| 217 | |
| 218 | func (n *ForStmt) SetOp(op Op) { |
| 219 | if op != OFOR && op != OFORUNTIL { |
| 220 | panic(n.no("SetOp " + op.String())) |
| 221 | } |
| 222 | n.op = op |
| 223 | } |
| 224 | |
| 225 | // A GoDeferStmt is a go or defer statement: go Call / defer Call. |
| 226 | // |
| 227 | // The two opcodes use a single syntax because the implementations |
| 228 | // are very similar: both are concerned with saving Call and running it |
| 229 | // in a different context (a separate goroutine or a later time). |
| 230 | type GoDeferStmt struct { |
| 231 | miniStmt |
| 232 | Call Node |
| 233 | } |
| 234 | |
| 235 | func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt { |
| 236 | n := &GoDeferStmt{Call: call} |
| 237 | n.pos = pos |
| 238 | switch op { |
| 239 | case ODEFER, OGO: |
| 240 | n.op = op |
| 241 | default: |
| 242 | panic("NewGoDeferStmt " + op.String()) |
| 243 | } |
| 244 | return n |
| 245 | } |
| 246 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 247 | // An IfStmt is a return statement: if Init; Cond { Body } else { Else }. |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 248 | type IfStmt struct { |
| 249 | miniStmt |
| 250 | Cond Node |
| 251 | Body Nodes |
| 252 | Else Nodes |
| 253 | Likely bool // code layout hint |
| 254 | } |
| 255 | |
| 256 | func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt { |
| 257 | n := &IfStmt{Cond: cond} |
| 258 | n.pos = pos |
| 259 | n.op = OIF |
| 260 | n.Body = body |
| 261 | n.Else = els |
| 262 | return n |
| 263 | } |
| 264 | |
| 265 | // An InlineMarkStmt is a marker placed just before an inlined body. |
| 266 | type InlineMarkStmt struct { |
| 267 | miniStmt |
| 268 | Index int64 |
| 269 | } |
| 270 | |
| 271 | func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt { |
| 272 | n := &InlineMarkStmt{Index: index} |
| 273 | n.pos = pos |
| 274 | n.op = OINLMARK |
| 275 | return n |
| 276 | } |
| 277 | |
| 278 | func (n *InlineMarkStmt) Offset() int64 { return n.Index } |
| 279 | func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x } |
| 280 | |
| 281 | // A LabelStmt is a label statement (just the label, not including the statement it labels). |
| 282 | type LabelStmt struct { |
| 283 | miniStmt |
| 284 | Label *types.Sym // "Label:" |
| 285 | } |
| 286 | |
| 287 | func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt { |
| 288 | n := &LabelStmt{Label: label} |
| 289 | n.pos = pos |
| 290 | n.op = OLABEL |
| 291 | return n |
| 292 | } |
| 293 | |
| 294 | func (n *LabelStmt) Sym() *types.Sym { return n.Label } |
| 295 | |
| 296 | // A RangeStmt is a range loop: for Key, Value = range X { Body } |
| 297 | type RangeStmt struct { |
| 298 | miniStmt |
| 299 | Label *types.Sym |
| 300 | Def bool |
| 301 | X Node |
| 302 | Key Node |
| 303 | Value Node |
| 304 | Body Nodes |
| 305 | HasBreak bool |
| 306 | Prealloc *Name |
| 307 | } |
| 308 | |
| 309 | func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt { |
| 310 | n := &RangeStmt{X: x, Key: key, Value: value} |
| 311 | n.pos = pos |
| 312 | n.op = ORANGE |
| 313 | n.Body = body |
| 314 | return n |
| 315 | } |
| 316 | |
| 317 | // A ReturnStmt is a return statement. |
| 318 | type ReturnStmt struct { |
| 319 | miniStmt |
| 320 | origNode // for typecheckargs rewrite |
| 321 | Results Nodes // return list |
| 322 | } |
| 323 | |
| 324 | func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt { |
| 325 | n := &ReturnStmt{} |
| 326 | n.pos = pos |
| 327 | n.op = ORETURN |
| 328 | n.orig = n |
| 329 | n.Results = results |
| 330 | return n |
| 331 | } |
| 332 | |
| 333 | // A SelectStmt is a block: { Cases }. |
| 334 | type SelectStmt struct { |
| 335 | miniStmt |
| 336 | Label *types.Sym |
| 337 | Cases []*CommClause |
| 338 | HasBreak bool |
| 339 | |
| 340 | // TODO(rsc): Instead of recording here, replace with a block? |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 341 | Compiled Nodes // compiled form, after walkSelect |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 342 | } |
| 343 | |
| 344 | func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt { |
| 345 | n := &SelectStmt{Cases: cases} |
| 346 | n.pos = pos |
| 347 | n.op = OSELECT |
| 348 | return n |
| 349 | } |
| 350 | |
| 351 | // A SendStmt is a send statement: X <- Y. |
| 352 | type SendStmt struct { |
| 353 | miniStmt |
| 354 | Chan Node |
| 355 | Value Node |
| 356 | } |
| 357 | |
| 358 | func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt { |
| 359 | n := &SendStmt{Chan: ch, Value: value} |
| 360 | n.pos = pos |
| 361 | n.op = OSEND |
| 362 | return n |
| 363 | } |
| 364 | |
| 365 | // A SwitchStmt is a switch statement: switch Init; Expr { Cases }. |
| 366 | type SwitchStmt struct { |
| 367 | miniStmt |
| 368 | Tag Node |
| 369 | Cases []*CaseClause |
| 370 | Label *types.Sym |
| 371 | HasBreak bool |
| 372 | |
| 373 | // TODO(rsc): Instead of recording here, replace with a block? |
| 374 | Compiled Nodes // compiled form, after walkSwitch |
| 375 | } |
| 376 | |
| 377 | func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt { |
| 378 | n := &SwitchStmt{Tag: tag, Cases: cases} |
| 379 | n.pos = pos |
| 380 | n.op = OSWITCH |
| 381 | return n |
| 382 | } |
| 383 | |
| 384 | // A TailCallStmt is a tail call statement, which is used for back-end |
| 385 | // code generation to jump directly to another function entirely. |
| 386 | type TailCallStmt struct { |
| 387 | miniStmt |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 388 | Call *CallExpr // the underlying call |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 389 | } |
| 390 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 391 | func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt { |
| 392 | n := &TailCallStmt{Call: call} |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 393 | n.pos = pos |
| 394 | n.op = OTAILCALL |
| 395 | return n |
| 396 | } |
| 397 | |
| 398 | // A TypeSwitchGuard is the [Name :=] X.(type) in a type switch. |
| 399 | type TypeSwitchGuard struct { |
| 400 | miniNode |
| 401 | Tag *Ident |
| 402 | X Node |
| 403 | Used bool |
| 404 | } |
| 405 | |
| 406 | func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard { |
| 407 | n := &TypeSwitchGuard{Tag: tag, X: x} |
| 408 | n.pos = pos |
| 409 | n.op = OTYPESW |
| 410 | return n |
| 411 | } |