mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
We used to have {Arg,Auto,Extern}Symbol structs with which we wrapped
a *gc.Node or *obj.LSym before storing them in the Aux field
of an ssa.Value. This let the SSA part of the compiler distinguish
between autos and args, for example. We no longer need the wrappers
as we can query the underlying objects directly.
There was also some sloppy usage, where VarDef had a *gc.Node
directly in its Aux field, whereas the use of that variable had
that *gc.Node wrapped in an AutoSymbol. Thus the Aux fields didn't
match (using ==) when they probably should.
This sloppy usage cleanup is the only thing in the CL that changes the
generated code - we can get rid of some more unused auto variables if
the matching happens reliably.
Removing this wrapper also lets us get rid of the varsyms cache
(which was used to prevent wrapping the same *gc.Node twice).
Change-Id: I0dedf8f82f84bfee413d310342b777316bd1d478
Reviewed-on: https://go-review.googlesource.com/64452
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
319 lines
9.5 KiB
Go
319 lines
9.5 KiB
Go
// Copyright 2015 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 ssa
|
|
|
|
import (
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/obj"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/src"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
// A Config holds readonly compilation information.
|
|
// It is created once, early during compilation,
|
|
// and shared across all compilations.
|
|
type Config struct {
|
|
arch string // "amd64", etc.
|
|
PtrSize int64 // 4 or 8; copy of cmd/internal/sys.Arch.PtrSize
|
|
RegSize int64 // 4 or 8; copy of cmd/internal/sys.Arch.RegSize
|
|
Types Types
|
|
lowerBlock blockRewriter // lowering function
|
|
lowerValue valueRewriter // lowering function
|
|
registers []Register // machine registers
|
|
gpRegMask regMask // general purpose integer register mask
|
|
fpRegMask regMask // floating point register mask
|
|
specialRegMask regMask // special register mask
|
|
FPReg int8 // register number of frame pointer, -1 if not used
|
|
LinkReg int8 // register number of link register if it is a general purpose register, -1 if not used
|
|
hasGReg bool // has hardware g register
|
|
ctxt *obj.Link // Generic arch information
|
|
optimize bool // Do optimization
|
|
noDuffDevice bool // Don't use Duff's device
|
|
useSSE bool // Use SSE for non-float operations
|
|
nacl bool // GOOS=nacl
|
|
use387 bool // GO386=387
|
|
NeedsFpScratch bool // No direct move between GP and FP register sets
|
|
BigEndian bool //
|
|
sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score
|
|
}
|
|
|
|
type (
|
|
blockRewriter func(*Block) bool
|
|
valueRewriter func(*Value) bool
|
|
)
|
|
|
|
type Types struct {
|
|
Bool *types.Type
|
|
Int8 *types.Type
|
|
Int16 *types.Type
|
|
Int32 *types.Type
|
|
Int64 *types.Type
|
|
UInt8 *types.Type
|
|
UInt16 *types.Type
|
|
UInt32 *types.Type
|
|
UInt64 *types.Type
|
|
Int *types.Type
|
|
Float32 *types.Type
|
|
Float64 *types.Type
|
|
UInt *types.Type
|
|
Uintptr *types.Type
|
|
String *types.Type
|
|
BytePtr *types.Type // TODO: use unsafe.Pointer instead?
|
|
Int32Ptr *types.Type
|
|
UInt32Ptr *types.Type
|
|
IntPtr *types.Type
|
|
UintptrPtr *types.Type
|
|
Float32Ptr *types.Type
|
|
Float64Ptr *types.Type
|
|
BytePtrPtr *types.Type
|
|
}
|
|
|
|
type Logger interface {
|
|
// Logf logs a message from the compiler.
|
|
Logf(string, ...interface{})
|
|
|
|
// Log returns true if logging is not a no-op
|
|
// some logging calls account for more than a few heap allocations.
|
|
Log() bool
|
|
|
|
// Fatal reports a compiler error and exits.
|
|
Fatalf(pos src.XPos, msg string, args ...interface{})
|
|
|
|
// Warnl writes compiler messages in the form expected by "errorcheck" tests
|
|
Warnl(pos src.XPos, fmt_ string, args ...interface{})
|
|
|
|
// Forwards the Debug flags from gc
|
|
Debug_checknil() bool
|
|
Debug_wb() bool
|
|
}
|
|
|
|
type Frontend interface {
|
|
CanSSA(t *types.Type) bool
|
|
|
|
Logger
|
|
|
|
// StringData returns a symbol pointing to the given string's contents.
|
|
StringData(string) interface{} // returns *gc.Sym
|
|
|
|
// Auto returns a Node for an auto variable of the given type.
|
|
// The SSA compiler uses this function to allocate space for spills.
|
|
Auto(src.XPos, *types.Type) GCNode
|
|
|
|
// Given the name for a compound type, returns the name we should use
|
|
// for the parts of that compound type.
|
|
SplitString(LocalSlot) (LocalSlot, LocalSlot)
|
|
SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
|
|
SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
|
|
SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
|
|
SplitStruct(LocalSlot, int) LocalSlot
|
|
SplitArray(LocalSlot) LocalSlot // array must be length 1
|
|
SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
|
|
|
|
// DerefItab dereferences an itab function
|
|
// entry, given the symbol of the itab and
|
|
// the byte offset of the function pointer.
|
|
// It may return nil.
|
|
DerefItab(sym *obj.LSym, offset int64) *obj.LSym
|
|
|
|
// Line returns a string describing the given position.
|
|
Line(src.XPos) string
|
|
|
|
// AllocFrame assigns frame offsets to all live auto variables.
|
|
AllocFrame(f *Func)
|
|
|
|
// Syslook returns a symbol of the runtime function/variable with the
|
|
// given name.
|
|
Syslook(string) *obj.LSym
|
|
|
|
// UseWriteBarrier returns whether write barrier is enabled
|
|
UseWriteBarrier() bool
|
|
}
|
|
|
|
// interface used to hold a *gc.Node (a stack variable).
|
|
// We'd use *gc.Node directly but that would lead to an import cycle.
|
|
type GCNode interface {
|
|
Typ() *types.Type
|
|
String() string
|
|
StorageClass() StorageClass
|
|
}
|
|
|
|
type StorageClass uint8
|
|
|
|
const (
|
|
ClassAuto StorageClass = iota // local stack variable
|
|
ClassParam // argument
|
|
ClassParamOut // return value
|
|
)
|
|
|
|
// NewConfig returns a new configuration object for the given architecture.
|
|
func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config {
|
|
c := &Config{arch: arch, Types: types}
|
|
switch arch {
|
|
case "amd64":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockAMD64
|
|
c.lowerValue = rewriteValueAMD64
|
|
c.registers = registersAMD64[:]
|
|
c.gpRegMask = gpRegMaskAMD64
|
|
c.fpRegMask = fpRegMaskAMD64
|
|
c.FPReg = framepointerRegAMD64
|
|
c.LinkReg = linkRegAMD64
|
|
c.hasGReg = false
|
|
case "amd64p32":
|
|
c.PtrSize = 4
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockAMD64
|
|
c.lowerValue = rewriteValueAMD64
|
|
c.registers = registersAMD64[:]
|
|
c.gpRegMask = gpRegMaskAMD64
|
|
c.fpRegMask = fpRegMaskAMD64
|
|
c.FPReg = framepointerRegAMD64
|
|
c.LinkReg = linkRegAMD64
|
|
c.hasGReg = false
|
|
c.noDuffDevice = true
|
|
case "386":
|
|
c.PtrSize = 4
|
|
c.RegSize = 4
|
|
c.lowerBlock = rewriteBlock386
|
|
c.lowerValue = rewriteValue386
|
|
c.registers = registers386[:]
|
|
c.gpRegMask = gpRegMask386
|
|
c.fpRegMask = fpRegMask386
|
|
c.FPReg = framepointerReg386
|
|
c.LinkReg = linkReg386
|
|
c.hasGReg = false
|
|
case "arm":
|
|
c.PtrSize = 4
|
|
c.RegSize = 4
|
|
c.lowerBlock = rewriteBlockARM
|
|
c.lowerValue = rewriteValueARM
|
|
c.registers = registersARM[:]
|
|
c.gpRegMask = gpRegMaskARM
|
|
c.fpRegMask = fpRegMaskARM
|
|
c.FPReg = framepointerRegARM
|
|
c.LinkReg = linkRegARM
|
|
c.hasGReg = true
|
|
case "arm64":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockARM64
|
|
c.lowerValue = rewriteValueARM64
|
|
c.registers = registersARM64[:]
|
|
c.gpRegMask = gpRegMaskARM64
|
|
c.fpRegMask = fpRegMaskARM64
|
|
c.FPReg = framepointerRegARM64
|
|
c.LinkReg = linkRegARM64
|
|
c.hasGReg = true
|
|
c.noDuffDevice = objabi.GOOS == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend
|
|
case "ppc64":
|
|
c.BigEndian = true
|
|
fallthrough
|
|
case "ppc64le":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockPPC64
|
|
c.lowerValue = rewriteValuePPC64
|
|
c.registers = registersPPC64[:]
|
|
c.gpRegMask = gpRegMaskPPC64
|
|
c.fpRegMask = fpRegMaskPPC64
|
|
c.FPReg = framepointerRegPPC64
|
|
c.LinkReg = linkRegPPC64
|
|
c.noDuffDevice = true // TODO: Resolve PPC64 DuffDevice (has zero, but not copy)
|
|
c.hasGReg = true
|
|
case "mips64":
|
|
c.BigEndian = true
|
|
fallthrough
|
|
case "mips64le":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockMIPS64
|
|
c.lowerValue = rewriteValueMIPS64
|
|
c.registers = registersMIPS64[:]
|
|
c.gpRegMask = gpRegMaskMIPS64
|
|
c.fpRegMask = fpRegMaskMIPS64
|
|
c.specialRegMask = specialRegMaskMIPS64
|
|
c.FPReg = framepointerRegMIPS64
|
|
c.LinkReg = linkRegMIPS64
|
|
c.hasGReg = true
|
|
case "s390x":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockS390X
|
|
c.lowerValue = rewriteValueS390X
|
|
c.registers = registersS390X[:]
|
|
c.gpRegMask = gpRegMaskS390X
|
|
c.fpRegMask = fpRegMaskS390X
|
|
c.FPReg = framepointerRegS390X
|
|
c.LinkReg = linkRegS390X
|
|
c.hasGReg = true
|
|
c.noDuffDevice = true
|
|
c.BigEndian = true
|
|
case "mips":
|
|
c.BigEndian = true
|
|
fallthrough
|
|
case "mipsle":
|
|
c.PtrSize = 4
|
|
c.RegSize = 4
|
|
c.lowerBlock = rewriteBlockMIPS
|
|
c.lowerValue = rewriteValueMIPS
|
|
c.registers = registersMIPS[:]
|
|
c.gpRegMask = gpRegMaskMIPS
|
|
c.fpRegMask = fpRegMaskMIPS
|
|
c.specialRegMask = specialRegMaskMIPS
|
|
c.FPReg = framepointerRegMIPS
|
|
c.LinkReg = linkRegMIPS
|
|
c.hasGReg = true
|
|
c.noDuffDevice = true
|
|
default:
|
|
ctxt.Diag("arch %s not implemented", arch)
|
|
}
|
|
c.ctxt = ctxt
|
|
c.optimize = optimize
|
|
c.nacl = objabi.GOOS == "nacl"
|
|
c.useSSE = true
|
|
|
|
// Don't use Duff's device nor SSE on Plan 9 AMD64, because
|
|
// floating point operations are not allowed in note handler.
|
|
if objabi.GOOS == "plan9" && arch == "amd64" {
|
|
c.noDuffDevice = true
|
|
c.useSSE = false
|
|
}
|
|
|
|
if c.nacl {
|
|
c.noDuffDevice = true // Don't use Duff's device on NaCl
|
|
|
|
// runtime call clobber R12 on nacl
|
|
opcodeTable[OpARMCALLudiv].reg.clobbers |= 1 << 12 // R12
|
|
}
|
|
|
|
// cutoff is compared with product of numblocks and numvalues,
|
|
// if product is smaller than cutoff, use old non-sparse method.
|
|
// cutoff == 0 implies all sparse.
|
|
// cutoff == -1 implies none sparse.
|
|
// Good cutoff values seem to be O(million) depending on constant factor cost of sparse.
|
|
// TODO: get this from a flag, not an environment variable
|
|
c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash
|
|
ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF")
|
|
if ev != "" {
|
|
v, err := strconv.ParseInt(ev, 10, 64)
|
|
if err != nil {
|
|
ctxt.Diag("Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev)
|
|
}
|
|
c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func (c *Config) Set387(b bool) {
|
|
c.NeedsFpScratch = b
|
|
c.use387 = b
|
|
}
|
|
|
|
func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
|
|
func (c *Config) Ctxt() *obj.Link { return c.ctxt }
|