Skip to content

Commit 9d88a9e

Browse files
committed
cmd/compile: implement simple register results
at least for ints and strings includes simple test For #40724. Change-Id: Ib8484e5b957b08f961574a67cfd93d3d26551558 Reviewed-on: https://go-review.googlesource.com/c/go/+/295309 Trust: David Chase <[email protected]> Run-TryBot: David Chase <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent 2d30c94 commit 9d88a9e

File tree

7 files changed

+164
-23
lines changed

7 files changed

+164
-23
lines changed

src/cmd/compile/internal/abi/abiutils.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,70 @@ func (a *ABIParamAssignment) Offset() int32 {
101101
return a.offset
102102
}
103103

104+
// RegisterTypes returns a slice of the types of the registers
105+
// corresponding to a slice of parameters. The returned slice
106+
// has capacity for one more, likely a memory type.
107+
func RegisterTypes(apa []ABIParamAssignment) []*types.Type {
108+
rcount := 0
109+
for _, pa := range apa {
110+
rcount += len(pa.Registers)
111+
}
112+
if rcount == 0 {
113+
// Note that this catches top-level struct{} and [0]Foo, which are stack allocated.
114+
return make([]*types.Type, 0, 1)
115+
}
116+
rts := make([]*types.Type, 0, rcount+1)
117+
for _, pa := range apa {
118+
if len(pa.Registers) == 0 {
119+
continue
120+
}
121+
rts = appendParamRegs(rts, pa.Type)
122+
}
123+
return rts
124+
}
125+
126+
func appendParamRegs(rts []*types.Type, t *types.Type) []*types.Type {
127+
if t.IsScalar() || t.IsPtrShaped() {
128+
if t.IsComplex() {
129+
c := types.FloatForComplex(t)
130+
return append(rts, c, c)
131+
} else {
132+
if int(t.Size()) <= types.RegSize {
133+
return append(rts, t)
134+
}
135+
// assume 64bit int on 32-bit machine
136+
// TODO endianness? Should high-order (sign bits) word come first?
137+
if t.IsSigned() {
138+
rts = append(rts, types.Types[types.TINT32])
139+
} else {
140+
rts = append(rts, types.Types[types.TUINT32])
141+
}
142+
return append(rts, types.Types[types.TUINT32])
143+
}
144+
} else {
145+
typ := t.Kind()
146+
switch typ {
147+
case types.TARRAY:
148+
for i := int64(0); i < t.Size(); i++ { // 0 gets no registers, plus future-proofing.
149+
rts = appendParamRegs(rts, t.Elem())
150+
}
151+
case types.TSTRUCT:
152+
for _, f := range t.FieldSlice() {
153+
if f.Type.Size() > 0 { // embedded zero-width types receive no registers
154+
rts = appendParamRegs(rts, f.Type)
155+
}
156+
}
157+
case types.TSLICE:
158+
return appendParamRegs(rts, synthSlice)
159+
case types.TSTRING:
160+
return appendParamRegs(rts, synthString)
161+
case types.TINTER:
162+
return appendParamRegs(rts, synthIface)
163+
}
164+
}
165+
return rts
166+
}
167+
104168
// SpillOffset returns the offset *within the spill area* for the parameter that "a" describes.
105169
// Registers will be spilled here; if a memory home is needed (for a pointer method e.g.)
106170
// then that will be the address.

src/cmd/compile/internal/ssa/expand_calls.go

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ import (
1414
)
1515

1616
type selKey struct {
17-
from *Value
18-
offset int64
19-
size int64
20-
typ *types.Type
17+
from *Value // what is selected from
18+
offsetOrIndex int64 // whatever is appropriate for the selector
19+
size int64
20+
typ *types.Type
2121
}
2222

2323
type offsetKey struct {
@@ -372,6 +372,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
372372
// if applied to Op-mumble-call, the Aux tells us which result, regOffset specifies offset within result. If a register, should rewrite to OpSelectN for new call.
373373
// TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there.
374374
call := selector.Args[0]
375+
call0 := call
375376
aux := call.Aux.(*AuxCall)
376377
which := selector.AuxInt
377378
if which == aux.NResults() { // mem is after the results.
@@ -398,7 +399,6 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
398399
leafType := removeTrivialWrapperTypes(leaf.Type)
399400
if x.canSSAType(leafType) {
400401
pt := types.NewPtr(leafType)
401-
off := x.offsetFrom(x.sp, offset+aux.OffsetOfResult(which), pt)
402402
// Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
403403
if call.Op == OpStaticLECall { // TODO this is temporary until all calls are register-able
404404
// Create a "mem" for any loads that need to occur.
@@ -413,15 +413,30 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
413413
call = mem
414414
}
415415
}
416-
if leaf.Block == call.Block {
417-
leaf.reset(OpLoad)
418-
leaf.SetArgs2(off, call)
419-
leaf.Type = leafType
416+
outParam := aux.abiInfo.OutParam(int(which))
417+
if len(outParam.Registers) > 0 {
418+
reg := int64(outParam.Registers[regOffset])
419+
if leaf.Block == call.Block {
420+
leaf.reset(OpSelectN)
421+
leaf.SetArgs1(call0)
422+
leaf.Type = leafType
423+
leaf.AuxInt = reg
424+
} else {
425+
w := call.Block.NewValue1I(leaf.Pos, OpSelectN, leafType, reg, call0)
426+
leaf.copyOf(w)
427+
}
420428
} else {
421-
w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
422-
leaf.copyOf(w)
423-
if x.debug {
424-
fmt.Printf("\tnew %s\n", w.LongString())
429+
off := x.offsetFrom(x.sp, offset+aux.OffsetOfResult(which), pt)
430+
if leaf.Block == call.Block {
431+
leaf.reset(OpLoad)
432+
leaf.SetArgs2(off, call)
433+
leaf.Type = leafType
434+
} else {
435+
w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
436+
leaf.copyOf(w)
437+
if x.debug {
438+
fmt.Printf("\tnew %s\n", w.LongString())
439+
}
425440
}
426441
}
427442
for _, s := range x.namedSelects[selector] {
@@ -812,7 +827,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
812827
s = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem)
813828
}
814829
if x.debug {
815-
fmt.Printf("\t\tstoreArg returns %s\n", s.LongString())
830+
fmt.Printf("\t\tstoreArg returns %s, storeRc=%s\n", s.LongString(), storeRc.String())
816831
}
817832
return s
818833
}
@@ -983,9 +998,11 @@ func expandCalls(f *Func) {
983998
mem = x.storeArgOrLoad(v.Pos, b, a, mem, aux.TypeOfResult(i), auxOffset, 0, rc)
984999
}
9851000
}
986-
// TODO REGISTER -- keep the Result for block control, splice in contents of AllResults
987-
b.SetControl(mem)
988-
v.reset(OpInvalid) // otherwise it can have a mem operand which will fail check(), even though it is dead.
1001+
v.resetArgs()
1002+
v.AddArgs(allResults...)
1003+
v.AddArg(mem)
1004+
v.Type = types.NewResults(append(abi.RegisterTypes(aux.abiInfo.OutParams()), types.TypeMem))
1005+
b.SetControl(v)
9891006
}
9901007
}
9911008

@@ -1170,7 +1187,7 @@ func expandCalls(f *Func) {
11701187
case OpArraySelect:
11711188
offset = size * v.AuxInt
11721189
case OpSelectN:
1173-
offset = w.Aux.(*AuxCall).OffsetOfResult(v.AuxInt)
1190+
offset = v.AuxInt // offset is just a key, really.
11741191
case OpInt64Hi:
11751192
offset = x.hiOffset
11761193
case OpInt64Lo:
@@ -1182,7 +1199,7 @@ func expandCalls(f *Func) {
11821199
case OpComplexImag:
11831200
offset = size
11841201
}
1185-
sk := selKey{from: w, size: size, offset: offset, typ: typ}
1202+
sk := selKey{from: w, size: size, offsetOrIndex: offset, typ: typ}
11861203
dupe := x.commonSelectors[sk]
11871204
if dupe == nil {
11881205
x.commonSelectors[sk] = v
@@ -1240,8 +1257,9 @@ func expandCalls(f *Func) {
12401257
x.rewriteArgToMemOrRegs(v)
12411258
case OpStaticLECall:
12421259
v.Op = OpStaticCall
1260+
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
12431261
// TODO need to insert all the register types.
1244-
v.Type = types.NewResults([]*types.Type{types.TypeMem})
1262+
v.Type = types.NewResults(append(rts, types.TypeMem))
12451263
case OpClosureLECall:
12461264
v.Op = OpClosureCall
12471265
v.Type = types.TypeMem

src/cmd/compile/internal/ssa/lower.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func checkLower(f *Func) {
2424
case OpSP, OpSB, OpInitMem, OpArg, OpArgIntReg, OpArgFloatReg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive, OpSelect0, OpSelect1, OpSelectN, OpConvert, OpInlMark:
2525
continue // ok not to lower
2626
case OpMakeResult:
27-
if len(b.Controls) == 1 && b.Controls[0] == v {
27+
if b.Controls[0] == v {
2828
continue
2929
}
3030
case OpGetG:
@@ -34,6 +34,7 @@ func checkLower(f *Func) {
3434
}
3535
}
3636
s := "not lowered: " + v.String() + ", " + v.Op.String() + " " + v.Type.SimpleString()
37+
3738
for _, a := range v.Args {
3839
s += " " + a.Type.SimpleString()
3940
}

src/cmd/compile/internal/ssa/op.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,24 @@ func (a *AuxCall) Reg(i *regInfo, c *Config) *regInfo {
134134
return a.reg
135135
}
136136

137+
func (a *AuxCall) ResultReg(c *Config) *regInfo {
138+
if a.abiInfo.OutRegistersUsed() == 0 {
139+
return a.reg
140+
}
141+
if len(a.reg.inputs) > 0 {
142+
return a.reg
143+
}
144+
k := 0
145+
for _, p := range a.abiInfo.OutParams() {
146+
for _, r := range p.Registers {
147+
m := archRegForAbiReg(r, c)
148+
a.reg.inputs = append(a.reg.inputs, inputInfo{idx: k, regs: (1 << m)})
149+
k++
150+
}
151+
}
152+
return a.reg
153+
}
154+
137155
func archRegForAbiReg(r abi.RegIndex, c *Config) uint8 {
138156
var m int8
139157
if int(r) < len(c.intParamRegs) {
@@ -285,10 +303,13 @@ func ClosureAuxCall(args []Param, results []Param, paramResultInfo *abi.ABIParam
285303
func (*AuxCall) CanBeAnSSAAux() {}
286304

287305
// OwnAuxCall returns a function's own AuxCall
288-
289306
func OwnAuxCall(fn *obj.LSym, args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
290307
// TODO if this remains identical to ClosureAuxCall above after new ABI is done, should deduplicate.
291-
return &AuxCall{Fn: fn, args: args, results: results, abiInfo: paramResultInfo}
308+
var reg *regInfo
309+
if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
310+
reg = &regInfo{}
311+
}
312+
return &AuxCall{Fn: fn, args: args, results: results, abiInfo: paramResultInfo, reg: reg}
292313
}
293314

294315
const (

src/cmd/compile/internal/ssa/regalloc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,9 @@ func (s *regAllocState) regspec(v *Value) regInfo {
830830
return *ac.Reg(&opcodeTable[op].reg, s.f.Config)
831831
}
832832
}
833+
if op == OpMakeResult && s.f.OwnAux.reg != nil {
834+
return *s.f.OwnAux.ResultReg(s.f.Config)
835+
}
833836
return opcodeTable[op].reg
834837
}
835838

test/abi/fibish.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// run
2+
3+
//go:build !wasm
4+
// +build !wasm
5+
6+
// Copyright 2021 The Go Authors. All rights reserved.
7+
// Use of this source code is governed by a BSD-style
8+
// license that can be found in the LICENSE file.
9+
10+
package main
11+
12+
import "fmt"
13+
14+
// Test that register results are correctly returned (and passed)
15+
16+
//go:registerparams
17+
//go:noinline
18+
func f(x int) (int, int) {
19+
20+
if x < 3 {
21+
return 0, x
22+
}
23+
24+
a, b := f(x - 2)
25+
c, d := f(x - 1)
26+
return a + d, b + c
27+
}
28+
29+
func main() {
30+
x := 40
31+
a, b := f(x)
32+
fmt.Printf("f(%d)=%d,%d\n", x, a, b)
33+
}

test/abi/fibish.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
f(40)=39088169,126491972

0 commit comments

Comments
 (0)