Skip to content

Commit 382851c

Browse files
committed
cmd/compile: fix failure to communicate between ABIinfo producer&consumer
ABI info producer and consumer had different ideas for register order for parameters. Includes a test, includes improvements to debugging output. Updates #44816. Change-Id: I4812976f7a6c08d6fc02aac1ec0544b1f141cca6 Reviewed-on: https://go-review.googlesource.com/c/go/+/299570 Trust: David Chase <drchase@google.com> Reviewed-by: Than McIntosh <thanm@google.com>
1 parent 9f5298c commit 382851c

File tree

6 files changed

+142
-67
lines changed

6 files changed

+142
-67
lines changed

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

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -477,29 +477,35 @@ func (c *RegAmounts) regString(r RegIndex) string {
477477
return fmt.Sprintf("<?>%d", r)
478478
}
479479

480-
// toString method renders an ABIParamAssignment in human-readable
480+
// ToString method renders an ABIParamAssignment in human-readable
481481
// form, suitable for debugging or unit testing.
482-
func (ri *ABIParamAssignment) toString(config *ABIConfig) string {
482+
func (ri *ABIParamAssignment) ToString(config *ABIConfig, extra bool) string {
483483
regs := "R{"
484484
offname := "spilloffset" // offset is for spill for register(s)
485485
if len(ri.Registers) == 0 {
486486
offname = "offset" // offset is for memory arg
487487
}
488488
for _, r := range ri.Registers {
489489
regs += " " + config.regAmounts.regString(r)
490+
if extra {
491+
regs += fmt.Sprintf("(%d)", r)
492+
}
493+
}
494+
if extra {
495+
regs += fmt.Sprintf(" | #I=%d, #F=%d", config.regAmounts.intRegs, config.regAmounts.floatRegs)
490496
}
491497
return fmt.Sprintf("%s } %s: %d typ: %v", regs, offname, ri.offset, ri.Type)
492498
}
493499

494-
// toString method renders an ABIParamResultInfo in human-readable
500+
// String method renders an ABIParamResultInfo in human-readable
495501
// form, suitable for debugging or unit testing.
496502
func (ri *ABIParamResultInfo) String() string {
497503
res := ""
498504
for k, p := range ri.inparams {
499-
res += fmt.Sprintf("IN %d: %s\n", k, p.toString(ri.config))
505+
res += fmt.Sprintf("IN %d: %s\n", k, p.ToString(ri.config, false))
500506
}
501507
for k, r := range ri.outparams {
502-
res += fmt.Sprintf("OUT %d: %s\n", k, r.toString(ri.config))
508+
res += fmt.Sprintf("OUT %d: %s\n", k, r.ToString(ri.config, false))
503509
}
504510
res += fmt.Sprintf("offsetToSpillArea: %d spillAreaSize: %d",
505511
ri.offsetToSpillArea, ri.spillAreaSize)
@@ -537,25 +543,54 @@ func (state *assignState) stackSlot(t *types.Type) int64 {
537543
return rv
538544
}
539545

540-
// allocateRegs returns a set of register indices for a parameter or result
546+
// allocateRegs returns an ordered list of register indices for a parameter or result
541547
// that we've just determined to be register-assignable. The number of registers
542548
// needed is assumed to be stored in state.pUsed.
543-
func (state *assignState) allocateRegs() []RegIndex {
544-
regs := []RegIndex{}
545-
546-
// integer
547-
for r := state.rUsed.intRegs; r < state.rUsed.intRegs+state.pUsed.intRegs; r++ {
548-
regs = append(regs, RegIndex(r))
549+
func (state *assignState) allocateRegs(regs []RegIndex, t *types.Type) []RegIndex {
550+
if t.Width == 0 {
551+
return regs
549552
}
550-
state.rUsed.intRegs += state.pUsed.intRegs
551-
552-
// floating
553-
for r := state.rUsed.floatRegs; r < state.rUsed.floatRegs+state.pUsed.floatRegs; r++ {
554-
regs = append(regs, RegIndex(r+state.rTotal.intRegs))
553+
ri := state.rUsed.intRegs
554+
rf := state.rUsed.floatRegs
555+
if t.IsScalar() || t.IsPtrShaped() {
556+
if t.IsComplex() {
557+
regs = append(regs, RegIndex(rf+state.rTotal.intRegs), RegIndex(rf+1+state.rTotal.intRegs))
558+
rf += 2
559+
} else if t.IsFloat() {
560+
regs = append(regs, RegIndex(rf+state.rTotal.intRegs))
561+
rf += 1
562+
} else {
563+
n := (int(t.Size()) + types.RegSize - 1) / types.RegSize
564+
for i := 0; i < n; i++ { // looking ahead to really big integers
565+
regs = append(regs, RegIndex(ri))
566+
ri += 1
567+
}
568+
}
569+
state.rUsed.intRegs = ri
570+
state.rUsed.floatRegs = rf
571+
return regs
572+
} else {
573+
typ := t.Kind()
574+
switch typ {
575+
case types.TARRAY:
576+
for i := int64(0); i < t.NumElem(); i++ {
577+
regs = state.allocateRegs(regs, t.Elem())
578+
}
579+
return regs
580+
case types.TSTRUCT:
581+
for _, f := range t.FieldSlice() {
582+
regs = state.allocateRegs(regs, f.Type)
583+
}
584+
return regs
585+
case types.TSLICE:
586+
return state.allocateRegs(regs, synthSlice)
587+
case types.TSTRING:
588+
return state.allocateRegs(regs, synthString)
589+
case types.TINTER:
590+
return state.allocateRegs(regs, synthIface)
591+
}
555592
}
556-
state.rUsed.floatRegs += state.pUsed.floatRegs
557-
558-
return regs
593+
panic(fmt.Errorf("Was not expecting type %s", t))
559594
}
560595

561596
// regAllocate creates a register ABIParamAssignment object for a param
@@ -571,7 +606,7 @@ func (state *assignState) regAllocate(t *types.Type, name types.Object, isReturn
571606
return ABIParamAssignment{
572607
Type: t,
573608
Name: name,
574-
Registers: state.allocateRegs(),
609+
Registers: state.allocateRegs([]RegIndex{}, t),
575610
offset: int32(spillLoc),
576611
}
577612
}

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

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
303303
if x.debug {
304304
x.indent(3)
305305
defer x.indent(-3)
306-
x.Printf("rewriteSelect(%s, %s, %d)\n", leaf.LongString(), selector.LongString(), offset)
306+
x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset)
307307
}
308308
var locs []LocalSlot
309309
leafType := leaf.Type
@@ -581,7 +581,13 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t
581581
rts, offs := pa.RegisterTypesAndOffsets()
582582
last := loadRegOffset + x.regWidth(t)
583583
if offs[loadRegOffset] != 0 {
584-
panic(fmt.Errorf("offset %d of requested register %d should be zero", offs[loadRegOffset], loadRegOffset))
584+
// Document the problem before panicking.
585+
for i := 0; i < len(rts); i++ {
586+
rt := rts[i]
587+
off := offs[i]
588+
fmt.Printf("rt=%s, off=%d, rt.Width=%d, rt.Align=%d\n", rt.String(), off, rt.Width, rt.Align)
589+
}
590+
panic(fmt.Errorf("offset %d of requested register %d should be zero, source=%s", offs[loadRegOffset], loadRegOffset, source.LongString()))
585591
}
586592
for i := loadRegOffset; i < last; i++ {
587593
rt := rts[i]
@@ -704,7 +710,7 @@ func storeOneArg(x *expandState, pos src.XPos, b *Block, source, mem *Value, t *
704710
if x.debug {
705711
x.indent(3)
706712
defer x.indent(-3)
707-
fmt.Printf("storeOneArg(%s; %s; %s; aO=%d; sO=%d; lrO=%d; %s)\n", source.LongString(), mem.String(), t.String(), argOffset, storeOffset, loadRegOffset, storeRc.String())
713+
x.Printf("storeOneArg(%s; %s; %s; aO=%d; sO=%d; lrO=%d; %s)\n", source.LongString(), mem.String(), t.String(), argOffset, storeOffset, loadRegOffset, storeRc.String())
708714
}
709715

710716
w := x.commonArgs[selKey{source, argOffset, t.Width, t}]
@@ -1388,14 +1394,8 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value {
13881394
}
13891395
case 1:
13901396
r := pa.Registers[0]
1391-
i := x.f.ABISelf.FloatIndexFor(r)
1392-
// TODO seems like this has implications for debugging. How does this affect the location?
1393-
if i >= 0 { // float PR
1394-
v.Op = OpArgFloatReg
1395-
} else {
1396-
v.Op = OpArgIntReg
1397-
i = int64(r)
1398-
}
1397+
var i int64
1398+
v.Op, i = ArgOpAndRegisterFor(r, x.f.ABISelf)
13991399
v.Aux = &AuxNameOffset{v.Aux.(*ir.Name), 0}
14001400
v.AuxInt = i
14011401

@@ -1409,6 +1409,11 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value {
14091409
// or rewrites it into a copy of the appropriate OpArgXXX. The actual OpArgXXX is determined by combining baseArg (an OpArg)
14101410
// with offset, regOffset, and t to determine which portion of it to reference (either all or a part, in memory or in registers).
14111411
func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, regOffset Abi1RO, t *types.Type, pos src.XPos) *Value {
1412+
if x.debug {
1413+
x.indent(3)
1414+
defer x.indent(-3)
1415+
x.Printf("newArgToMemOrRegs(base=%s; toReplace=%s; t=%s; memOff=%d; regOff=%d)\n", baseArg.String(), toReplace.LongString(), t, offset, regOffset)
1416+
}
14121417
key := selKey{baseArg, offset, t.Width, t}
14131418
w := x.commonArgs[key]
14141419
if w != nil {
@@ -1432,53 +1437,51 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64,
14321437
toReplace.Aux = aux
14331438
toReplace.AuxInt = auxInt
14341439
toReplace.Type = t
1435-
x.commonArgs[key] = toReplace
1436-
return toReplace
1440+
w = toReplace
14371441
} else {
1438-
w := baseArg.Block.NewValue0IA(pos, OpArg, t, auxInt, aux)
1439-
x.commonArgs[key] = w
1440-
if x.debug {
1441-
x.Printf("---new %s\n", w.LongString())
1442-
}
1443-
if toReplace != nil {
1444-
toReplace.copyOf(w)
1445-
}
1446-
return w
1442+
w = baseArg.Block.NewValue0IA(pos, OpArg, t, auxInt, aux)
1443+
}
1444+
x.commonArgs[key] = w
1445+
if toReplace != nil {
1446+
toReplace.copyOf(w)
14471447
}
1448+
if x.debug {
1449+
x.Printf("-->%s\n", w.LongString())
1450+
}
1451+
return w
14481452
}
14491453
// Arg is in registers
14501454
r := pa.Registers[regOffset]
1451-
auxInt := x.f.ABISelf.FloatIndexFor(r)
1452-
op := OpArgFloatReg
1453-
// TODO seems like this has implications for debugging. How does this affect the location?
1454-
if auxInt < 0 { // int (not float) parameter register
1455-
op = OpArgIntReg
1456-
auxInt = int64(r)
1455+
op, auxInt := ArgOpAndRegisterFor(r, x.f.ABISelf)
1456+
if op == OpArgIntReg && t.IsFloat() || op == OpArgFloatReg && t.IsInteger() {
1457+
fmt.Printf("pa=%v\nx.f.OwnAux.abiInfo=%s\n",
1458+
pa.ToString(x.f.ABISelf, true),
1459+
x.f.OwnAux.abiInfo.String())
1460+
panic(fmt.Errorf("Op/Type mismatch, op=%s, type=%s", op.String(), t.String()))
14571461
}
14581462
aux := &AuxNameOffset{baseArg.Aux.(*ir.Name), baseArg.AuxInt + offset}
14591463
if toReplace != nil && toReplace.Block == baseArg.Block {
14601464
toReplace.reset(op)
14611465
toReplace.Aux = aux
14621466
toReplace.AuxInt = auxInt
14631467
toReplace.Type = t
1464-
x.commonArgs[key] = toReplace
1465-
return toReplace
1468+
w = toReplace
14661469
} else {
1467-
w := baseArg.Block.NewValue0IA(pos, op, t, auxInt, aux)
1468-
if x.debug {
1469-
x.Printf("---new %s\n", w.LongString())
1470-
}
1471-
x.commonArgs[key] = w
1472-
if toReplace != nil {
1473-
toReplace.copyOf(w)
1474-
}
1475-
return w
1470+
w = baseArg.Block.NewValue0IA(pos, op, t, auxInt, aux)
1471+
}
1472+
x.commonArgs[key] = w
1473+
if toReplace != nil {
1474+
toReplace.copyOf(w)
14761475
}
1476+
if x.debug {
1477+
x.Printf("-->%s\n", w.LongString())
1478+
}
1479+
return w
1480+
14771481
}
14781482

14791483
// argOpAndRegisterFor converts an abi register index into an ssa Op and corresponding
14801484
// arg register index.
1481-
// TODO could call this in at least two places earlier in this file.
14821485
func ArgOpAndRegisterFor(r abi.RegIndex, abiConfig *abi.ABIConfig) (Op, int64) {
14831486
i := abiConfig.FloatIndexFor(r)
14841487
if i >= 0 { // float PR

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,12 @@ func (v *Value) auxString() string {
198198
if v.Aux != nil {
199199
return fmt.Sprintf(" {%v}", v.Aux)
200200
}
201-
case auxSymOff, auxCallOff, auxTypSize:
201+
case auxSymOff, auxCallOff, auxTypSize, auxNameOffsetInt8:
202202
s := ""
203203
if v.Aux != nil {
204204
s = fmt.Sprintf(" {%v}", v.Aux)
205205
}
206-
if v.AuxInt != 0 {
206+
if v.AuxInt != 0 || opcodeTable[v.Op].auxType == auxNameOffsetInt8 {
207207
s += fmt.Sprintf(" [%v]", v.AuxInt)
208208
}
209209
return s

src/cmd/compile/internal/test/abiutils_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ func TestABIUtilsStruct2(t *testing.T) {
170170
exp := makeExpectedDump(`
171171
IN 0: R{ I0 } spilloffset: 0 typ: struct { int64; struct {} }
172172
IN 1: R{ I1 } spilloffset: 16 typ: struct { int64; struct {} }
173-
IN 2: R{ I2 F0 } spilloffset: 32 typ: struct { float64; struct { int64; struct {} }; struct {} }
174-
OUT 0: R{ I0 F0 } spilloffset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} }
175-
OUT 1: R{ I1 F1 } spilloffset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} }
173+
IN 2: R{ F0 I2 } spilloffset: 32 typ: struct { float64; struct { int64; struct {} }; struct {} }
174+
OUT 0: R{ F0 I0 } spilloffset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} }
175+
OUT 1: R{ F1 I1 } spilloffset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} }
176176
offsetToSpillArea: 0 spillAreaSize: 64
177177
`)
178178

src/cmd/internal/obj/x86/asm6.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5306,7 +5306,7 @@ bad:
53065306
}
53075307
}
53085308

5309-
ctxt.Diag("invalid instruction: %v", p)
5309+
ctxt.Diag("%s: invalid instruction: %v", cursym.Name, p)
53105310
}
53115311

53125312
// byteswapreg returns a byte-addressable register (AX, BX, CX, DX)

test/abi/s_sif_sif.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
// Test ensures that abi information producer and consumer agree about the
13+
// order of registers for inputs. T's registers should be I0, F0, I1, F1.
14+
15+
import "fmt"
16+
17+
type P struct {
18+
a int8
19+
x float64
20+
}
21+
22+
type T struct {
23+
d, e P
24+
}
25+
26+
//go:registerparams
27+
//go:noinline
28+
func G(t T) float64 {
29+
return float64(t.d.a+t.e.a) + t.d.x + t.e.x
30+
}
31+
32+
func main() {
33+
x := G(T{P{10, 20}, P{30, 40}})
34+
if x != 100.0 {
35+
fmt.Printf("FAIL, Expected 100, got %f\n", x)
36+
}
37+
}

0 commit comments

Comments
 (0)