// 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/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 // 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 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 } 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) *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 // 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) // MyImportPath provides the import name (roughly, the package) for the function being compiled. MyImportPath() string } // 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.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 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.intParamRegs = paramIntRegARM64 c.floatParamRegs = paramFloatRegARM64 c.FPReg = framepointerRegARM64 c.LinkReg = linkRegARM64 c.hasGReg = true c.noDuffDevice = buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" // 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 c.SoftFloat = softfloat if softfloat { c.floatParamRegs = nil // no FP registers in softfloat mode } c.ABI0 = abi.NewABIConfig(0, 0, ctxt.FixedFrameSize()) c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.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 }