| |
| |
| |
|
|
| package abi |
|
|
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/types" |
| "cmd/internal/obj" |
| "cmd/internal/src" |
| "fmt" |
| "math" |
| "sync" |
| ) |
|
|
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| type ABIParamResultInfo struct { |
| inparams []ABIParamAssignment |
| outparams []ABIParamAssignment |
| offsetToSpillArea int64 |
| spillAreaSize int64 |
| inRegistersUsed int |
| outRegistersUsed int |
| config *ABIConfig |
| } |
|
|
| func (a *ABIParamResultInfo) Config() *ABIConfig { |
| return a.config |
| } |
|
|
| func (a *ABIParamResultInfo) InParams() []ABIParamAssignment { |
| return a.inparams |
| } |
|
|
| func (a *ABIParamResultInfo) OutParams() []ABIParamAssignment { |
| return a.outparams |
| } |
|
|
| func (a *ABIParamResultInfo) InRegistersUsed() int { |
| return a.inRegistersUsed |
| } |
|
|
| func (a *ABIParamResultInfo) OutRegistersUsed() int { |
| return a.outRegistersUsed |
| } |
|
|
| func (a *ABIParamResultInfo) InParam(i int) *ABIParamAssignment { |
| return &a.inparams[i] |
| } |
|
|
| func (a *ABIParamResultInfo) OutParam(i int) *ABIParamAssignment { |
| return &a.outparams[i] |
| } |
|
|
| func (a *ABIParamResultInfo) SpillAreaOffset() int64 { |
| return a.offsetToSpillArea |
| } |
|
|
| func (a *ABIParamResultInfo) SpillAreaSize() int64 { |
| return a.spillAreaSize |
| } |
|
|
| |
| |
| |
| |
| func (a *ABIParamResultInfo) ArgWidth() int64 { |
| return a.spillAreaSize + a.offsetToSpillArea - a.config.LocalsOffset() |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| type RegIndex uint8 |
|
|
| |
| |
| |
| |
| |
| type ABIParamAssignment struct { |
| Type *types.Type |
| Name *ir.Name |
| Registers []RegIndex |
| offset int32 |
| } |
|
|
| |
| |
| func (a *ABIParamAssignment) Offset() int32 { |
| if len(a.Registers) > 0 { |
| base.Fatalf("register allocated parameters have no offset") |
| } |
| return a.offset |
| } |
|
|
| |
| |
| |
| func RegisterTypes(apa []ABIParamAssignment) []*types.Type { |
| rcount := 0 |
| for _, pa := range apa { |
| rcount += len(pa.Registers) |
| } |
| if rcount == 0 { |
| |
| return make([]*types.Type, 0, 1) |
| } |
| rts := make([]*types.Type, 0, rcount+1) |
| for _, pa := range apa { |
| if len(pa.Registers) == 0 { |
| continue |
| } |
| rts = appendParamTypes(rts, pa.Type) |
| } |
| return rts |
| } |
|
|
| func (pa *ABIParamAssignment) RegisterTypesAndOffsets() ([]*types.Type, []int64) { |
| l := len(pa.Registers) |
| if l == 0 { |
| return nil, nil |
| } |
| typs := make([]*types.Type, 0, l) |
| offs := make([]int64, 0, l) |
| offs, _ = appendParamOffsets(offs, 0, pa.Type) |
| return appendParamTypes(typs, pa.Type), offs |
| } |
|
|
| func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type { |
| w := t.Size() |
| if w == 0 { |
| return rts |
| } |
| if t.IsScalar() || t.IsPtrShaped() || t.IsSIMD() { |
| if t.IsComplex() { |
| c := types.FloatForComplex(t) |
| return append(rts, c, c) |
| } else { |
| if int(t.Size()) <= types.RegSize || t.IsSIMD() { |
| return append(rts, t) |
| } |
| |
| |
| if t.IsSigned() { |
| rts = append(rts, types.Types[types.TINT32]) |
| } else { |
| rts = append(rts, types.Types[types.TUINT32]) |
| } |
| return append(rts, types.Types[types.TUINT32]) |
| } |
| } else { |
| typ := t.Kind() |
| switch typ { |
| case types.TARRAY: |
| for i := int64(0); i < t.NumElem(); i++ { |
| rts = appendParamTypes(rts, t.Elem()) |
| } |
| case types.TSTRUCT: |
| for _, f := range t.Fields() { |
| if f.Type.Size() > 0 { |
| rts = appendParamTypes(rts, f.Type) |
| } |
| } |
| case types.TSLICE: |
| return appendParamTypes(rts, synthSlice) |
| case types.TSTRING: |
| return appendParamTypes(rts, synthString) |
| case types.TINTER: |
| return appendParamTypes(rts, synthIface) |
| } |
| } |
| return rts |
| } |
|
|
| |
| |
| |
| func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int64) { |
| w := t.Size() |
| if w == 0 { |
| return offsets, at |
| } |
| if t.IsSIMD() { |
| return append(offsets, at), at + w |
| } |
| if t.IsScalar() || t.IsPtrShaped() { |
| if t.IsComplex() || int(t.Size()) > types.RegSize { |
| s := w / 2 |
| return append(offsets, at, at+s), at + w |
| } else { |
| return append(offsets, at), at + w |
| } |
| } else { |
| typ := t.Kind() |
| switch typ { |
| case types.TARRAY: |
| te := t.Elem() |
| for i := int64(0); i < t.NumElem(); i++ { |
| at = align(at, te) |
| offsets, at = appendParamOffsets(offsets, at, te) |
| } |
| case types.TSTRUCT: |
| at0 := at |
| for i, f := range t.Fields() { |
| at = at0 + f.Offset |
| offsets, at = appendParamOffsets(offsets, at, f.Type) |
| if f.Type.Size() == 0 && i == t.NumFields()-1 { |
| at++ |
| } |
| } |
| at = align(at, t) |
| case types.TSLICE: |
| return appendParamOffsets(offsets, at, synthSlice) |
| case types.TSTRING: |
| return appendParamOffsets(offsets, at, synthString) |
| case types.TINTER: |
| return appendParamOffsets(offsets, at, synthIface) |
| } |
| } |
| return offsets, at |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| func (a *ABIParamAssignment) FrameOffset(i *ABIParamResultInfo) int64 { |
| if a.offset == -1 { |
| base.Fatalf("function parameter has no ABI-defined frame-pointer offset") |
| } |
| if len(a.Registers) == 0 { |
| return int64(a.offset) - i.config.LocalsOffset() |
| } |
| |
| return int64(a.offset) + i.SpillAreaOffset() - i.config.LocalsOffset() |
| } |
|
|
| |
| type RegAmounts struct { |
| intRegs int |
| floatRegs int |
| } |
|
|
| |
| |
| type ABIConfig struct { |
| |
| offsetForLocals int64 |
| regAmounts RegAmounts |
| which obj.ABI |
| } |
|
|
| |
| |
| func NewABIConfig(iRegsCount, fRegsCount int, offsetForLocals int64, which uint8) *ABIConfig { |
| return &ABIConfig{offsetForLocals: offsetForLocals, regAmounts: RegAmounts{iRegsCount, fRegsCount}, which: obj.ABI(which)} |
| } |
|
|
| |
| |
| |
| func (config *ABIConfig) Copy() *ABIConfig { |
| return config |
| } |
|
|
| |
| func (config *ABIConfig) Which() obj.ABI { |
| return config.which |
| } |
|
|
| |
| |
| |
| func (config *ABIConfig) LocalsOffset() int64 { |
| return config.offsetForLocals |
| } |
|
|
| |
| |
| |
| func (config *ABIConfig) FloatIndexFor(r RegIndex) int64 { |
| return int64(r) - int64(config.regAmounts.intRegs) |
| } |
|
|
| |
| |
| |
| func (config *ABIConfig) NumParamRegs(typ *types.Type) int { |
| intRegs, floatRegs := typ.Registers() |
| if intRegs == math.MaxUint8 && floatRegs == math.MaxUint8 { |
| base.Fatalf("cannot represent parameters of type %v in registers", typ) |
| } |
| return int(intRegs) + int(floatRegs) |
| } |
|
|
| |
| |
| |
| |
| func (config *ABIConfig) ABIAnalyzeTypes(params, results []*types.Type) *ABIParamResultInfo { |
| setup() |
| s := assignState{ |
| stackOffset: config.offsetForLocals, |
| rTotal: config.regAmounts, |
| } |
|
|
| assignParams := func(params []*types.Type, isResult bool) []ABIParamAssignment { |
| res := make([]ABIParamAssignment, len(params)) |
| for i, param := range params { |
| res[i] = s.assignParam(param, nil, isResult) |
| } |
| return res |
| } |
|
|
| info := &ABIParamResultInfo{config: config} |
|
|
| |
| info.inparams = assignParams(params, false) |
| s.stackOffset = types.RoundUp(s.stackOffset, int64(types.RegSize)) |
| info.inRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs |
|
|
| |
| s.rUsed = RegAmounts{} |
| info.outparams = assignParams(results, true) |
| |
| |
| info.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize) |
| info.spillAreaSize = alignTo(s.spillOffset, types.RegSize) |
| info.outRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs |
|
|
| return info |
| } |
|
|
| |
| |
| |
| |
| func (config *ABIConfig) ABIAnalyzeFuncType(ft *types.Type) *ABIParamResultInfo { |
| setup() |
| s := assignState{ |
| stackOffset: config.offsetForLocals, |
| rTotal: config.regAmounts, |
| } |
|
|
| assignParams := func(params []*types.Field, isResult bool) []ABIParamAssignment { |
| res := make([]ABIParamAssignment, len(params)) |
| for i, param := range params { |
| var name *ir.Name |
| if param.Nname != nil { |
| name = param.Nname.(*ir.Name) |
| } |
| res[i] = s.assignParam(param.Type, name, isResult) |
| } |
| return res |
| } |
|
|
| info := &ABIParamResultInfo{config: config} |
|
|
| |
| info.inparams = assignParams(ft.RecvParams(), false) |
| s.stackOffset = types.RoundUp(s.stackOffset, int64(types.RegSize)) |
| info.inRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs |
|
|
| |
| s.rUsed = RegAmounts{} |
| info.outparams = assignParams(ft.Results(), true) |
| |
| |
| info.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize) |
| info.spillAreaSize = alignTo(s.spillOffset, types.RegSize) |
| info.outRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs |
| return info |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| func (config *ABIConfig) ABIAnalyze(t *types.Type, setNname bool) *ABIParamResultInfo { |
| result := config.ABIAnalyzeFuncType(t) |
|
|
| |
| for i, f := range t.RecvParams() { |
| config.updateOffset(result, f, result.inparams[i], false, setNname) |
| } |
| for i, f := range t.Results() { |
| config.updateOffset(result, f, result.outparams[i], true, setNname) |
| } |
| return result |
| } |
|
|
| func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field, a ABIParamAssignment, isResult, setNname bool) { |
| if f.Offset != types.BADWIDTH { |
| base.Fatalf("field offset for %s at %s has been set to %d", f.Sym, base.FmtPos(f.Pos), f.Offset) |
| } |
|
|
| |
| if !isResult || len(a.Registers) == 0 { |
| |
| |
| off := a.FrameOffset(result) |
| if setNname && f.Nname != nil { |
| f.Nname.(*ir.Name).SetFrameOffset(off) |
| f.Nname.(*ir.Name).SetIsOutputParamInRegisters(false) |
| } |
| } else { |
| if setNname && f.Nname != nil { |
| fname := f.Nname.(*ir.Name) |
| fname.SetIsOutputParamInRegisters(true) |
| fname.SetFrameOffset(0) |
| } |
| } |
| } |
|
|
| |
| |
| |
|
|
| |
| func (c *RegAmounts) regString(r RegIndex) string { |
| if int(r) < c.intRegs { |
| return fmt.Sprintf("I%d", int(r)) |
| } else if int(r) < c.intRegs+c.floatRegs { |
| return fmt.Sprintf("F%d", int(r)-c.intRegs) |
| } |
| return fmt.Sprintf("<?>%d", r) |
| } |
|
|
| |
| |
| func (ri *ABIParamAssignment) ToString(config *ABIConfig, extra bool) string { |
| regs := "R{" |
| offname := "spilloffset" |
| if len(ri.Registers) == 0 { |
| offname = "offset" |
| } |
| for _, r := range ri.Registers { |
| regs += " " + config.regAmounts.regString(r) |
| if extra { |
| regs += fmt.Sprintf("(%d)", r) |
| } |
| } |
| if extra { |
| regs += fmt.Sprintf(" | #I=%d, #F=%d", config.regAmounts.intRegs, config.regAmounts.floatRegs) |
| } |
| return fmt.Sprintf("%s } %s: %d typ: %v", regs, offname, ri.offset, ri.Type) |
| } |
|
|
| |
| |
| func (ri *ABIParamResultInfo) String() string { |
| res := "" |
| for k, p := range ri.inparams { |
| res += fmt.Sprintf("IN %d: %s\n", k, p.ToString(ri.config, false)) |
| } |
| for k, r := range ri.outparams { |
| res += fmt.Sprintf("OUT %d: %s\n", k, r.ToString(ri.config, false)) |
| } |
| res += fmt.Sprintf("offsetToSpillArea: %d spillAreaSize: %d", |
| ri.offsetToSpillArea, ri.spillAreaSize) |
| return res |
| } |
|
|
| |
| |
| type assignState struct { |
| rTotal RegAmounts |
| rUsed RegAmounts |
| stackOffset int64 |
| spillOffset int64 |
| } |
|
|
| |
| func align(a int64, t *types.Type) int64 { |
| return alignTo(a, int(uint8(t.Alignment()))) |
| } |
|
|
| |
| func alignTo(a int64, t int) int64 { |
| if t == 0 { |
| return a |
| } |
| return types.RoundUp(a, int64(t)) |
| } |
|
|
| |
| func nextSlot(offsetp *int64, typ *types.Type) int64 { |
| offset := align(*offsetp, typ) |
| *offsetp = offset + typ.Size() |
| return offset |
| } |
|
|
| |
| |
| |
| func (state *assignState) allocateRegs(regs []RegIndex, t *types.Type) []RegIndex { |
| if t.Size() == 0 { |
| return regs |
| } |
| ri := state.rUsed.intRegs |
| rf := state.rUsed.floatRegs |
| if t.IsScalar() || t.IsPtrShaped() || t.IsSIMD() { |
| if t.IsComplex() { |
| regs = append(regs, RegIndex(rf+state.rTotal.intRegs), RegIndex(rf+1+state.rTotal.intRegs)) |
| rf += 2 |
| } else if t.IsFloat() || t.IsSIMD() { |
| regs = append(regs, RegIndex(rf+state.rTotal.intRegs)) |
| rf += 1 |
| } else { |
| n := (int(t.Size()) + types.RegSize - 1) / types.RegSize |
| for i := 0; i < n; i++ { |
| regs = append(regs, RegIndex(ri)) |
| ri += 1 |
| } |
| } |
| state.rUsed.intRegs = ri |
| state.rUsed.floatRegs = rf |
| return regs |
| } else { |
| typ := t.Kind() |
| switch typ { |
| case types.TARRAY: |
| for i := int64(0); i < t.NumElem(); i++ { |
| regs = state.allocateRegs(regs, t.Elem()) |
| } |
| return regs |
| case types.TSTRUCT: |
| for _, f := range t.Fields() { |
| regs = state.allocateRegs(regs, f.Type) |
| } |
| return regs |
| case types.TSLICE: |
| return state.allocateRegs(regs, synthSlice) |
| case types.TSTRING: |
| return state.allocateRegs(regs, synthString) |
| case types.TINTER: |
| return state.allocateRegs(regs, synthIface) |
| } |
| } |
| base.Fatalf("was not expecting type %s", t) |
| panic("unreachable") |
| } |
|
|
| |
| var synthOnce sync.Once |
|
|
| |
| |
| var synthSlice *types.Type |
| var synthString *types.Type |
| var synthIface *types.Type |
|
|
| |
| |
| func setup() { |
| synthOnce.Do(func() { |
| fname := types.BuiltinPkg.Lookup |
| nxp := src.NoXPos |
| bp := types.NewPtr(types.Types[types.TUINT8]) |
| it := types.Types[types.TINT] |
| synthSlice = types.NewStruct([]*types.Field{ |
| types.NewField(nxp, fname("ptr"), bp), |
| types.NewField(nxp, fname("len"), it), |
| types.NewField(nxp, fname("cap"), it), |
| }) |
| types.CalcStructSize(synthSlice) |
| synthString = types.NewStruct([]*types.Field{ |
| types.NewField(nxp, fname("data"), bp), |
| types.NewField(nxp, fname("len"), it), |
| }) |
| types.CalcStructSize(synthString) |
| unsp := types.Types[types.TUNSAFEPTR] |
| synthIface = types.NewStruct([]*types.Field{ |
| types.NewField(nxp, fname("f1"), unsp), |
| types.NewField(nxp, fname("f2"), unsp), |
| }) |
| types.CalcStructSize(synthIface) |
| }) |
| } |
|
|
| |
| |
| |
| |
| func (state *assignState) assignParam(typ *types.Type, name *ir.Name, isResult bool) ABIParamAssignment { |
| registers := state.tryAllocRegs(typ) |
|
|
| var offset int64 = -1 |
| if registers == nil { |
| offset = nextSlot(&state.stackOffset, typ) |
| } else if !isResult { |
| offset = nextSlot(&state.spillOffset, typ) |
| } |
|
|
| return ABIParamAssignment{ |
| Type: typ, |
| Name: name, |
| Registers: registers, |
| offset: int32(offset), |
| } |
| } |
|
|
| |
| |
| func (state *assignState) tryAllocRegs(typ *types.Type) []RegIndex { |
| if typ.Size() == 0 { |
| return nil |
| } |
|
|
| intRegs, floatRegs := typ.Registers() |
| if int(intRegs) > state.rTotal.intRegs-state.rUsed.intRegs || int(floatRegs) > state.rTotal.floatRegs-state.rUsed.floatRegs { |
| return nil |
| } |
|
|
| regs := make([]RegIndex, 0, int(intRegs)+int(floatRegs)) |
| return state.allocateRegs(regs, typ) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 { |
| nr := len(pa.Registers) |
| padding := storage[:nr] |
| clear(padding) |
| if pa.Type.Kind() != types.TSTRUCT || nr == 0 { |
| return padding |
| } |
| types := make([]*types.Type, 0, nr) |
| types = appendParamTypes(types, pa.Type) |
| if len(types) != nr { |
| panic("internal error") |
| } |
| offsets, _ := appendParamOffsets([]int64{}, 0, pa.Type) |
| for idx, t := range types { |
| ts := t.Size() |
| off := offsets[idx] + ts |
| if idx < len(types)-1 { |
| noff := offsets[idx+1] |
| if noff != off { |
| padding[idx] = uint64(noff - off) |
| } |
| } |
| } |
| return padding |
| } |
|
|