mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Now that the buffered write barrier is implemented for all architectures, we can remove the old eager write barrier implementation. This CL removes the implementation from the runtime, support in the compiler for calling it, and updates some compiler tests that relied on the old eager barrier support. It also makes sure that all of the useful comments from the old write barrier implementation still have a place to live. Fixes #22460. Updates #21640 since this fixes the layering concerns of the write barrier (but not the other things in that issue). Change-Id: I580f93c152e89607e0a72fe43370237ba97bae74 Reviewed-on: https://go-review.googlesource.com/92705 Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Rick Hudson <rlh@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
337 lines
10 KiB
Go
337 lines
10 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
|
|
SoftFloat bool //
|
|
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
|
|
}
|
|
|
|
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
|
|
|
|
// SetWBPos indicates that a write barrier has been inserted
|
|
// in this function at position pos.
|
|
SetWBPos(pos src.XPos)
|
|
}
|
|
|
|
// 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
|
|
|
|
// Returns clobber BP on nacl/386, so the write
|
|
// barrier does.
|
|
opcodeTable[Op386LoweredWB].reg.clobbers |= 1 << 5 // BP
|
|
|
|
// ... and SI on nacl/amd64.
|
|
opcodeTable[OpAMD64LoweredWB].reg.clobbers |= 1 << 6 // SI
|
|
}
|
|
|
|
if ctxt.Flag_shared {
|
|
// LoweredWB is secretly a CALL and CALLs on 386 in
|
|
// shared mode get rewritten by obj6.go to go through
|
|
// the GOT, which clobbers BX.
|
|
opcodeTable[Op386LoweredWB].reg.clobbers |= 1 << 3 // BX
|
|
}
|
|
|
|
// 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 }
|