mirror of https://github.com/golang/go.git
415 lines
8.6 KiB
Go
415 lines
8.6 KiB
Go
// 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"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// A IfStmt is a return statement: if Init; Cond { Then } 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
|
|
}
|
|
|
|
// 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 walkSwitch
|
|
}
|
|
|
|
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; Expr { 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
|
|
Target *Name
|
|
}
|
|
|
|
func NewTailCallStmt(pos src.XPos, target *Name) *TailCallStmt {
|
|
if target.Op() != ONAME || target.Class != PFUNC {
|
|
base.FatalfAt(pos, "tail call to non-func %v", target)
|
|
}
|
|
n := &TailCallStmt{Target: target}
|
|
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
|
|
}
|