blob: 0801ecdd9e8722f825fd7de0c55b5616114a9a58 [file] [log] [blame] [edit]
// 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/obj"
"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:
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) SetOp(op Op) {
switch op {
default:
panic(n.no("SetOp " + op.String()))
case OBREAK, OCONTINUE, OFALL, OGOTO:
n.op = op
}
}
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
// RTypes is a list of RType expressions, which are copied to the
// corresponding OEQ nodes that are emitted when switch statements
// are desugared. RTypes[i] must be non-nil if the emitted
// comparison for List[i] will be a mixed interface/concrete
// comparison; see reflectdata.CompareRType for details.
//
// Because mixed interface/concrete switch cases are rare, we allow
// len(RTypes) < len(List). Missing entries are implicitly nil.
RTypes Nodes
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 }
type ForStmt struct {
miniStmt
Label *types.Sym
Cond Node
Post Node
Body Nodes
DistinctVars bool
}
func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node, distinctVars bool) *ForStmt {
n := &ForStmt{Cond: cond, Post: post}
n.pos = pos
n.op = OFOR
if init != nil {
n.init = []Node{init}
}
n.Body = body
n.DistinctVars = distinctVars
return n
}
// 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
DeferAt Expr
}
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 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 InterfaceSwitchStmt is used to implement type switches.
// Its semantics are:
//
// if RuntimeType implements Descriptor.Cases[0] {
// Case, Itab = 0, itab<RuntimeType, Descriptor.Cases[0]>
// } else if RuntimeType implements Descriptor.Cases[1] {
// Case, Itab = 1, itab<RuntimeType, Descriptor.Cases[1]>
// ...
// } else if RuntimeType implements Descriptor.Cases[N-1] {
// Case, Itab = N-1, itab<RuntimeType, Descriptor.Cases[N-1]>
// } else {
// Case, Itab = len(cases), nil
// }
//
// RuntimeType must be a non-nil *runtime._type.
// Hash must be the hash field of RuntimeType (or its copy loaded from an itab).
// Descriptor must represent an abi.InterfaceSwitch global variable.
type InterfaceSwitchStmt struct {
miniStmt
Case Node
Itab Node
RuntimeType Node
Hash Node
Descriptor *obj.LSym
}
func NewInterfaceSwitchStmt(pos src.XPos, case_, itab, runtimeType, hash Node, descriptor *obj.LSym) *InterfaceSwitchStmt {
n := &InterfaceSwitchStmt{
Case: case_,
Itab: itab,
RuntimeType: runtimeType,
Hash: hash,
Descriptor: descriptor,
}
n.pos = pos
n.op = OINTERFACESWITCH
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
RType Node `mknode:"-"` // see reflectdata/helpers.go
Key Node
Value Node
Body Nodes
DistinctVars bool
Prealloc *Name
// When desugaring the RangeStmt during walk, the assignments to Key
// and Value may require OCONVIFACE operations. If so, these fields
// will be copied to their respective ConvExpr fields.
KeyTypeWord Node `mknode:"-"`
KeySrcRType Node `mknode:"-"`
ValueTypeWord Node `mknode:"-"`
ValueSrcRType Node `mknode:"-"`
}
func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node, distinctVars bool) *RangeStmt {
n := &RangeStmt{X: x, Key: key, Value: value}
n.pos = pos
n.op = ORANGE
n.Body = body
n.DistinctVars = distinctVars
return n
}
// A ReturnStmt is a return statement.
type ReturnStmt struct {
miniStmt
Results Nodes // return list
}
func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
n := &ReturnStmt{}
n.pos = pos
n.op = ORETURN
n.Results = results
return n
}
// A SelectStmt is a block: { Cases }.
type SelectStmt struct {
miniStmt
Label *types.Sym
Cases []*CommClause
// 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
// 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
}