| // 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" |
| ) |
| |
| // 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 |
| 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 |
| 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 |
| use387 bool // GO386=387 |
| SoftFloat bool // |
| Race bool // race detector enabled |
| NeedsFpScratch bool // No direct move between GP and FP register sets |
| BigEndian bool // |
| UseFMA bool // Use hardware FMA operation |
| } |
| |
| 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 { |
| CanSSA(t *types.Type) bool |
| |
| Logger |
| |
| // 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) 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 reports 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 |
| IsSynthetic() bool |
| IsAutoTmp() bool |
| 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} |
| c.useAvg = true |
| c.useHmul = true |
| switch arch { |
| case "amd64": |
| c.PtrSize = 8 |
| c.RegSize = 8 |
| c.lowerBlock = rewriteBlockAMD64 |
| c.lowerValue = rewriteValueAMD64 |
| c.splitLoad = rewriteValueAMD64splitload |
| c.registers = registersAMD64[:] |
| c.gpRegMask = gpRegMaskAMD64 |
| c.fpRegMask = fpRegMaskAMD64 |
| c.FPReg = framepointerRegAMD64 |
| c.LinkReg = linkRegAMD64 |
| c.hasGReg = false |
| 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 |
| 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 |
| case "riscv64": |
| c.PtrSize = 8 |
| c.RegSize = 8 |
| c.lowerBlock = rewriteBlockRISCV64 |
| c.lowerValue = rewriteValueRISCV64 |
| c.registers = registersRISCV64[:] |
| c.gpRegMask = gpRegMaskRISCV64 |
| c.fpRegMask = fpRegMaskRISCV64 |
| 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 |
| |
| // On Plan 9, floating point operations are not allowed in note handler. |
| if objabi.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) Set387(b bool) { |
| c.NeedsFpScratch = b |
| c.use387 = b |
| } |
| |
| func (c *Config) Ctxt() *obj.Link { return c.ctxt } |