mirror of https://github.com/golang/go.git
432 lines
13 KiB
Go
432 lines
13 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/abi"
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/obj"
|
|
"cmd/internal/src"
|
|
"internal/buildcfg"
|
|
)
|
|
|
|
// 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 // block lowering function, first round
|
|
lowerValue valueRewriter // value lowering function, first round
|
|
lateLowerBlock blockRewriter // block lowering function that needs to be run after the first round; only used on some architectures
|
|
lateLowerValue valueRewriter // value lowering function that needs to be run after the first round; only used on some architectures
|
|
splitLoad valueRewriter // function for splitting merged load ops; only used on some architectures
|
|
registers []Register // machine registers
|
|
gpRegMask regMask // general purpose integer register mask
|
|
fpRegMask regMask // floating point register mask
|
|
fp32RegMask regMask // floating point register mask
|
|
fp64RegMask regMask // floating point register mask
|
|
specialRegMask regMask // special register mask
|
|
intParamRegs []int8 // register numbers of integer param (in/out) registers
|
|
floatParamRegs []int8 // register numbers of floating param (in/out) registers
|
|
ABI1 *abi.ABIConfig // "ABIInternal" under development // TODO change comment when this becomes current
|
|
ABI0 *abi.ABIConfig
|
|
GCRegMap []*Register // garbage collector register map, by GC register index
|
|
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
|
|
useAvg bool // Use optimizations that need Avg* operations
|
|
useHmul bool // Use optimizations that need Hmul* operations
|
|
SoftFloat bool //
|
|
Race bool // race detector enabled
|
|
BigEndian bool //
|
|
UseFMA bool // Use hardware FMA operation
|
|
unalignedOK bool // Unaligned loads/stores are ok
|
|
haveBswap64 bool // architecture implements Bswap64
|
|
haveBswap32 bool // architecture implements Bswap32
|
|
haveBswap16 bool // architecture implements Bswap16
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// NewTypes creates and populates a Types.
|
|
func NewTypes() *Types {
|
|
t := new(Types)
|
|
t.SetTypPtrs()
|
|
return t
|
|
}
|
|
|
|
// SetTypPtrs populates t.
|
|
func (t *Types) SetTypPtrs() {
|
|
t.Bool = types.Types[types.TBOOL]
|
|
t.Int8 = types.Types[types.TINT8]
|
|
t.Int16 = types.Types[types.TINT16]
|
|
t.Int32 = types.Types[types.TINT32]
|
|
t.Int64 = types.Types[types.TINT64]
|
|
t.UInt8 = types.Types[types.TUINT8]
|
|
t.UInt16 = types.Types[types.TUINT16]
|
|
t.UInt32 = types.Types[types.TUINT32]
|
|
t.UInt64 = types.Types[types.TUINT64]
|
|
t.Int = types.Types[types.TINT]
|
|
t.Float32 = types.Types[types.TFLOAT32]
|
|
t.Float64 = types.Types[types.TFLOAT64]
|
|
t.UInt = types.Types[types.TUINT]
|
|
t.Uintptr = types.Types[types.TUINTPTR]
|
|
t.String = types.Types[types.TSTRING]
|
|
t.BytePtr = types.NewPtr(types.Types[types.TUINT8])
|
|
t.Int32Ptr = types.NewPtr(types.Types[types.TINT32])
|
|
t.UInt32Ptr = types.NewPtr(types.Types[types.TUINT32])
|
|
t.IntPtr = types.NewPtr(types.Types[types.TINT])
|
|
t.UintptrPtr = types.NewPtr(types.Types[types.TUINTPTR])
|
|
t.Float32Ptr = types.NewPtr(types.Types[types.TFLOAT32])
|
|
t.Float64Ptr = types.NewPtr(types.Types[types.TFLOAT64])
|
|
t.BytePtrPtr = types.NewPtr(types.NewPtr(types.Types[types.TUINT8]))
|
|
}
|
|
|
|
type Logger interface {
|
|
// Logf logs a message from the compiler.
|
|
Logf(string, ...interface{})
|
|
|
|
// Log reports whether 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 {
|
|
Logger
|
|
|
|
// CanSSA reports whether variabbles of type t are SSA-able.
|
|
CanSSA(t *types.Type) bool
|
|
|
|
// StringData returns a symbol pointing to the given string's contents.
|
|
StringData(string) *obj.LSym
|
|
|
|
// 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) *ir.Name
|
|
|
|
// Given the name for a compound type, returns the name we should use
|
|
// for the parts of that compound type.
|
|
SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot
|
|
|
|
// 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 reports whether write barrier is enabled
|
|
UseWriteBarrier() bool
|
|
|
|
// MyImportPath provides the import name (roughly, the package) for the function being compiled.
|
|
MyImportPath() string
|
|
|
|
// Func returns the ir.Func of the function being compiled.
|
|
Func() *ir.Func
|
|
}
|
|
|
|
// NewConfig returns a new configuration object for the given architecture.
|
|
func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat bool) *Config {
|
|
c := &Config{arch: arch, Types: types}
|
|
c.useAvg = true
|
|
c.useHmul = true
|
|
switch arch {
|
|
case "amd64":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockAMD64
|
|
c.lowerValue = rewriteValueAMD64
|
|
c.lateLowerBlock = rewriteBlockAMD64latelower
|
|
c.lateLowerValue = rewriteValueAMD64latelower
|
|
c.splitLoad = rewriteValueAMD64splitload
|
|
c.registers = registersAMD64[:]
|
|
c.gpRegMask = gpRegMaskAMD64
|
|
c.fpRegMask = fpRegMaskAMD64
|
|
c.specialRegMask = specialRegMaskAMD64
|
|
c.intParamRegs = paramIntRegAMD64
|
|
c.floatParamRegs = paramFloatRegAMD64
|
|
c.FPReg = framepointerRegAMD64
|
|
c.LinkReg = linkRegAMD64
|
|
c.hasGReg = true
|
|
c.unalignedOK = true
|
|
c.haveBswap64 = true
|
|
c.haveBswap32 = true
|
|
c.haveBswap16 = true
|
|
case "386":
|
|
c.PtrSize = 4
|
|
c.RegSize = 4
|
|
c.lowerBlock = rewriteBlock386
|
|
c.lowerValue = rewriteValue386
|
|
c.splitLoad = rewriteValue386splitload
|
|
c.registers = registers386[:]
|
|
c.gpRegMask = gpRegMask386
|
|
c.fpRegMask = fpRegMask386
|
|
c.FPReg = framepointerReg386
|
|
c.LinkReg = linkReg386
|
|
c.hasGReg = false
|
|
c.unalignedOK = true
|
|
c.haveBswap32 = true
|
|
c.haveBswap16 = true
|
|
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.lateLowerBlock = rewriteBlockARM64latelower
|
|
c.lateLowerValue = rewriteValueARM64latelower
|
|
c.registers = registersARM64[:]
|
|
c.gpRegMask = gpRegMaskARM64
|
|
c.fpRegMask = fpRegMaskARM64
|
|
c.intParamRegs = paramIntRegARM64
|
|
c.floatParamRegs = paramFloatRegARM64
|
|
c.FPReg = framepointerRegARM64
|
|
c.LinkReg = linkRegARM64
|
|
c.hasGReg = true
|
|
c.unalignedOK = true
|
|
c.haveBswap64 = true
|
|
c.haveBswap32 = true
|
|
c.haveBswap16 = true
|
|
case "ppc64":
|
|
c.BigEndian = true
|
|
fallthrough
|
|
case "ppc64le":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockPPC64
|
|
c.lowerValue = rewriteValuePPC64
|
|
c.lateLowerBlock = rewriteBlockPPC64latelower
|
|
c.lateLowerValue = rewriteValuePPC64latelower
|
|
c.registers = registersPPC64[:]
|
|
c.gpRegMask = gpRegMaskPPC64
|
|
c.fpRegMask = fpRegMaskPPC64
|
|
c.specialRegMask = specialRegMaskPPC64
|
|
c.intParamRegs = paramIntRegPPC64
|
|
c.floatParamRegs = paramFloatRegPPC64
|
|
c.FPReg = framepointerRegPPC64
|
|
c.LinkReg = linkRegPPC64
|
|
c.hasGReg = true
|
|
c.unalignedOK = true
|
|
// Note: ppc64 has register bswap ops only when GOPPC64>=10.
|
|
// But it has bswap+load and bswap+store ops for all ppc64 variants.
|
|
// That is the sense we're using them here - they are only used
|
|
// in contexts where they can be merged with a load or store.
|
|
c.haveBswap64 = true
|
|
c.haveBswap32 = true
|
|
c.haveBswap16 = 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 "loong64":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockLOONG64
|
|
c.lowerValue = rewriteValueLOONG64
|
|
c.registers = registersLOONG64[:]
|
|
c.gpRegMask = gpRegMaskLOONG64
|
|
c.fpRegMask = fpRegMaskLOONG64
|
|
c.FPReg = framepointerRegLOONG64
|
|
c.LinkReg = linkRegLOONG64
|
|
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
|
|
c.unalignedOK = true
|
|
c.haveBswap64 = true
|
|
c.haveBswap32 = true
|
|
c.haveBswap16 = true // only for loads&stores, see ppc64 comment
|
|
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
|
|
case "riscv64":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockRISCV64
|
|
c.lowerValue = rewriteValueRISCV64
|
|
c.lateLowerBlock = rewriteBlockRISCV64latelower
|
|
c.lateLowerValue = rewriteValueRISCV64latelower
|
|
c.registers = registersRISCV64[:]
|
|
c.gpRegMask = gpRegMaskRISCV64
|
|
c.fpRegMask = fpRegMaskRISCV64
|
|
c.intParamRegs = paramIntRegRISCV64
|
|
c.floatParamRegs = paramFloatRegRISCV64
|
|
c.FPReg = framepointerRegRISCV64
|
|
c.hasGReg = true
|
|
case "wasm":
|
|
c.PtrSize = 8
|
|
c.RegSize = 8
|
|
c.lowerBlock = rewriteBlockWasm
|
|
c.lowerValue = rewriteValueWasm
|
|
c.registers = registersWasm[:]
|
|
c.gpRegMask = gpRegMaskWasm
|
|
c.fpRegMask = fpRegMaskWasm
|
|
c.fp32RegMask = fp32RegMaskWasm
|
|
c.fp64RegMask = fp64RegMaskWasm
|
|
c.FPReg = framepointerRegWasm
|
|
c.LinkReg = linkRegWasm
|
|
c.hasGReg = true
|
|
c.noDuffDevice = true
|
|
c.useAvg = false
|
|
c.useHmul = false
|
|
default:
|
|
ctxt.Diag("arch %s not implemented", arch)
|
|
}
|
|
c.ctxt = ctxt
|
|
c.optimize = optimize
|
|
c.useSSE = true
|
|
c.UseFMA = true
|
|
c.SoftFloat = softfloat
|
|
if softfloat {
|
|
c.floatParamRegs = nil // no FP registers in softfloat mode
|
|
}
|
|
|
|
c.ABI0 = abi.NewABIConfig(0, 0, ctxt.Arch.FixedFrameSize)
|
|
c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.Arch.FixedFrameSize)
|
|
|
|
// On Plan 9, floating point operations are not allowed in note handler.
|
|
if buildcfg.GOOS == "plan9" {
|
|
// Don't use FMA on Plan 9
|
|
c.UseFMA = false
|
|
|
|
// Don't use Duff's device and SSE on Plan 9 AMD64.
|
|
if arch == "amd64" {
|
|
c.noDuffDevice = true
|
|
c.useSSE = false
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// Create the GC register map index.
|
|
// TODO: This is only used for debug printing. Maybe export config.registers?
|
|
gcRegMapSize := int16(0)
|
|
for _, r := range c.registers {
|
|
if r.gcNum+1 > gcRegMapSize {
|
|
gcRegMapSize = r.gcNum + 1
|
|
}
|
|
}
|
|
c.GCRegMap = make([]*Register, gcRegMapSize)
|
|
for i, r := range c.registers {
|
|
if r.gcNum != -1 {
|
|
c.GCRegMap[r.gcNum] = &c.registers[i]
|
|
}
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func (c *Config) Ctxt() *obj.Link { return c.ctxt }
|
|
|
|
func (c *Config) haveByteSwap(size int64) bool {
|
|
switch size {
|
|
case 8:
|
|
return c.haveBswap64
|
|
case 4:
|
|
return c.haveBswap32
|
|
case 2:
|
|
return c.haveBswap16
|
|
default:
|
|
base.Fatalf("bad size %d\n", size)
|
|
return false
|
|
}
|
|
}
|