| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ir |
| |
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/types" |
| "cmd/internal/src" |
| "go/constant" |
| ) |
| |
| // A Decl is a declaration of a const, type, or var. (A declared func is a Func.) |
| type Decl struct { |
| miniNode |
| X *Name // the thing being declared |
| } |
| |
| func NewDecl(pos src.XPos, op Op, x *Name) *Decl { |
| n := &Decl{X: x} |
| n.pos = pos |
| switch op { |
| default: |
| panic("invalid Decl op " + op.String()) |
| case ODCL, ODCLCONST, ODCLTYPE: |
| n.op = op |
| } |
| return n |
| } |
| |
| func (*Decl) isStmt() {} |
| |
| // A Stmt is a Node that can appear as a statement. |
| // This includes statement-like expressions such as f(). |
| // |
| // (It's possible it should include <-c, but that would require |
| // splitting ORECV out of UnaryExpr, which hasn't yet been |
| // necessary. Maybe instead we will introduce ExprStmt at |
| // some point.) |
| type Stmt interface { |
| Node |
| isStmt() |
| } |
| |
| // A miniStmt is a miniNode with extra fields common to statements. |
| type miniStmt struct { |
| miniNode |
| init Nodes |
| } |
| |
| func (*miniStmt) isStmt() {} |
| |
| func (n *miniStmt) Init() Nodes { return n.init } |
| func (n *miniStmt) SetInit(x Nodes) { n.init = x } |
| func (n *miniStmt) PtrInit() *Nodes { return &n.init } |
| |
| // An AssignListStmt is an assignment statement with |
| // more than one item on at least one side: Lhs = Rhs. |
| // If Def is true, the assignment is a :=. |
| type AssignListStmt struct { |
| miniStmt |
| Lhs Nodes |
| Def bool |
| Rhs Nodes |
| } |
| |
| func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt { |
| n := &AssignListStmt{} |
| n.pos = pos |
| n.SetOp(op) |
| n.Lhs = lhs |
| n.Rhs = rhs |
| return n |
| } |
| |
| func (n *AssignListStmt) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2: |
| n.op = op |
| } |
| } |
| |
| // An AssignStmt is a simple assignment statement: X = Y. |
| // If Def is true, the assignment is a :=. |
| type AssignStmt struct { |
| miniStmt |
| X Node |
| Def bool |
| Y Node |
| } |
| |
| func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt { |
| n := &AssignStmt{X: x, Y: y} |
| n.pos = pos |
| n.op = OAS |
| return n |
| } |
| |
| func (n *AssignStmt) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OAS: |
| n.op = op |
| } |
| } |
| |
| // An AssignOpStmt is an AsOp= assignment statement: X AsOp= Y. |
| type AssignOpStmt struct { |
| miniStmt |
| X Node |
| AsOp Op // OADD etc |
| Y Node |
| IncDec bool // actually ++ or -- |
| } |
| |
| func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt { |
| n := &AssignOpStmt{AsOp: asOp, X: x, Y: y} |
| n.pos = pos |
| n.op = OASOP |
| return n |
| } |
| |
| // A BlockStmt is a block: { List }. |
| type BlockStmt struct { |
| miniStmt |
| List Nodes |
| } |
| |
| func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt { |
| n := &BlockStmt{} |
| n.pos = pos |
| if !pos.IsKnown() { |
| n.pos = base.Pos |
| if len(list) > 0 { |
| n.pos = list[0].Pos() |
| } |
| } |
| n.op = OBLOCK |
| n.List = list |
| return n |
| } |
| |
| // A BranchStmt is a break, continue, fallthrough, or goto statement. |
| type BranchStmt struct { |
| miniStmt |
| Label *types.Sym // label if present |
| } |
| |
| func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt { |
| switch op { |
| case OBREAK, OCONTINUE, OFALL, OGOTO: |
| // ok |
| default: |
| panic("NewBranch " + op.String()) |
| } |
| n := &BranchStmt{Label: label} |
| n.pos = pos |
| n.op = op |
| return n |
| } |
| |
| func (n *BranchStmt) Sym() *types.Sym { return n.Label } |
| |
| // A CaseClause is a case statement in a switch or select: case List: Body. |
| type CaseClause struct { |
| miniStmt |
| Var *Name // declared variable for this case in type switch |
| List Nodes // list of expressions for switch, early select |
| Body Nodes |
| } |
| |
| func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause { |
| n := &CaseClause{List: list, Body: body} |
| n.pos = pos |
| n.op = OCASE |
| return n |
| } |
| |
| type CommClause struct { |
| miniStmt |
| Comm Node // communication case |
| Body Nodes |
| } |
| |
| func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause { |
| n := &CommClause{Comm: comm, Body: body} |
| n.pos = pos |
| n.op = OCASE |
| return n |
| } |
| |
| // A ForStmt is a non-range for loop: for Init; Cond; Post { Body } |
| // Op can be OFOR or OFORUNTIL (!Cond). |
| type ForStmt struct { |
| miniStmt |
| Label *types.Sym |
| Cond Node |
| Late Nodes |
| Post Node |
| Body Nodes |
| HasBreak bool |
| } |
| |
| func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt { |
| n := &ForStmt{Cond: cond, Post: post} |
| n.pos = pos |
| n.op = OFOR |
| if init != nil { |
| n.init = []Node{init} |
| } |
| n.Body = body |
| return n |
| } |
| |
| func (n *ForStmt) SetOp(op Op) { |
| if op != OFOR && op != OFORUNTIL { |
| panic(n.no("SetOp " + op.String())) |
| } |
| n.op = op |
| } |
| |
| // A GoDeferStmt is a go or defer statement: go Call / defer Call. |
| // |
| // The two opcodes use a single syntax because the implementations |
| // are very similar: both are concerned with saving Call and running it |
| // in a different context (a separate goroutine or a later time). |
| type GoDeferStmt struct { |
| miniStmt |
| Call Node |
| } |
| |
| func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt { |
| n := &GoDeferStmt{Call: call} |
| n.pos = pos |
| switch op { |
| case ODEFER, OGO: |
| n.op = op |
| default: |
| panic("NewGoDeferStmt " + op.String()) |
| } |
| return n |
| } |
| |
| // An IfStmt is a return statement: if Init; Cond { Body } else { Else }. |
| type IfStmt struct { |
| miniStmt |
| Cond Node |
| Body Nodes |
| Else Nodes |
| Likely bool // code layout hint |
| } |
| |
| func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt { |
| n := &IfStmt{Cond: cond} |
| n.pos = pos |
| n.op = OIF |
| n.Body = body |
| n.Else = els |
| return n |
| } |
| |
| // A JumpTableStmt is used to implement switches. Its semantics are: |
| // tmp := jt.Idx |
| // if tmp == Cases[0] goto Targets[0] |
| // if tmp == Cases[1] goto Targets[1] |
| // ... |
| // if tmp == Cases[n] goto Targets[n] |
| // Note that a JumpTableStmt is more like a multiway-goto than |
| // a multiway-if. In particular, the case bodies are just |
| // labels to jump to, not not full Nodes lists. |
| type JumpTableStmt struct { |
| miniStmt |
| |
| // Value used to index the jump table. |
| // We support only integer types that |
| // are at most the size of a uintptr. |
| Idx Node |
| |
| // If Idx is equal to Cases[i], jump to Targets[i]. |
| // Cases entries must be distinct and in increasing order. |
| // The length of Cases and Targets must be equal. |
| Cases []constant.Value |
| Targets []*types.Sym |
| } |
| |
| func NewJumpTableStmt(pos src.XPos, idx Node) *JumpTableStmt { |
| n := &JumpTableStmt{Idx: idx} |
| n.pos = pos |
| n.op = OJUMPTABLE |
| return n |
| } |
| |
| // An InlineMarkStmt is a marker placed just before an inlined body. |
| type InlineMarkStmt struct { |
| miniStmt |
| Index int64 |
| } |
| |
| func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt { |
| n := &InlineMarkStmt{Index: index} |
| n.pos = pos |
| n.op = OINLMARK |
| return n |
| } |
| |
| func (n *InlineMarkStmt) Offset() int64 { return n.Index } |
| func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x } |
| |
| // A LabelStmt is a label statement (just the label, not including the statement it labels). |
| type LabelStmt struct { |
| miniStmt |
| Label *types.Sym // "Label:" |
| } |
| |
| func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt { |
| n := &LabelStmt{Label: label} |
| n.pos = pos |
| n.op = OLABEL |
| return n |
| } |
| |
| func (n *LabelStmt) Sym() *types.Sym { return n.Label } |
| |
| // A RangeStmt is a range loop: for Key, Value = range X { Body } |
| type RangeStmt struct { |
| miniStmt |
| Label *types.Sym |
| Def bool |
| X Node |
| Key Node |
| Value Node |
| Body Nodes |
| HasBreak bool |
| Prealloc *Name |
| } |
| |
| func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt { |
| n := &RangeStmt{X: x, Key: key, Value: value} |
| n.pos = pos |
| n.op = ORANGE |
| n.Body = body |
| return n |
| } |
| |
| // A ReturnStmt is a return statement. |
| type ReturnStmt struct { |
| miniStmt |
| origNode // for typecheckargs rewrite |
| Results Nodes // return list |
| } |
| |
| func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt { |
| n := &ReturnStmt{} |
| n.pos = pos |
| n.op = ORETURN |
| n.orig = n |
| n.Results = results |
| return n |
| } |
| |
| // A SelectStmt is a block: { Cases }. |
| type SelectStmt struct { |
| miniStmt |
| Label *types.Sym |
| Cases []*CommClause |
| HasBreak bool |
| |
| // TODO(rsc): Instead of recording here, replace with a block? |
| Compiled Nodes // compiled form, after walkSelect |
| } |
| |
| func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt { |
| n := &SelectStmt{Cases: cases} |
| n.pos = pos |
| n.op = OSELECT |
| return n |
| } |
| |
| // A SendStmt is a send statement: X <- Y. |
| type SendStmt struct { |
| miniStmt |
| Chan Node |
| Value Node |
| } |
| |
| func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt { |
| n := &SendStmt{Chan: ch, Value: value} |
| n.pos = pos |
| n.op = OSEND |
| return n |
| } |
| |
| // A SwitchStmt is a switch statement: switch Init; Tag { Cases }. |
| type SwitchStmt struct { |
| miniStmt |
| Tag Node |
| Cases []*CaseClause |
| Label *types.Sym |
| HasBreak bool |
| |
| // TODO(rsc): Instead of recording here, replace with a block? |
| Compiled Nodes // compiled form, after walkSwitch |
| } |
| |
| func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt { |
| n := &SwitchStmt{Tag: tag, Cases: cases} |
| n.pos = pos |
| n.op = OSWITCH |
| return n |
| } |
| |
| // A TailCallStmt is a tail call statement, which is used for back-end |
| // code generation to jump directly to another function entirely. |
| type TailCallStmt struct { |
| miniStmt |
| Call *CallExpr // the underlying call |
| } |
| |
| func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt { |
| n := &TailCallStmt{Call: call} |
| n.pos = pos |
| n.op = OTAILCALL |
| return n |
| } |
| |
| // A TypeSwitchGuard is the [Name :=] X.(type) in a type switch. |
| type TypeSwitchGuard struct { |
| miniNode |
| Tag *Ident |
| X Node |
| Used bool |
| } |
| |
| func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard { |
| n := &TypeSwitchGuard{Tag: tag, X: x} |
| n.pos = pos |
| n.op = OTYPESW |
| return n |
| } |