Skip to content

Commit 9d46ee5

Browse files
committed
reflect: handle stack-to-register translation in callMethod
callMethod previously assumed erroneously that between the "value" and "method" ABIs (that is, the ABI the caller is following to call this method value and the actual ABI of the method), it could never happen that an argument passed on the stack in the former could be passed in registers in the latter. The cited reason was that the latter always uses strictly more registers. However, there are situations where the value ABI could pass a value on the stack, but later is passed in a register. For instance, if the receiver pushes a value passed in registers that uses multiple registers to be passed on the stack, later arguments which were passed on the stack may now be passed in registers. This change fixes callMethod to no longer makes this assumption, and handles the stack-to-register translation explicitly. Fixes #46696. Change-Id: I7100a664d97bbe401302cc893b3a98b28cdcdfc0 Reviewed-on: https://go-review.googlesource.com/c/go/+/327089 Trust: Michael Knyszek <[email protected]> Run-TryBot: Michael Knyszek <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Cherry Mui <[email protected]>
1 parent e552a6d commit 9d46ee5

File tree

2 files changed

+74
-11
lines changed

2 files changed

+74
-11
lines changed

src/reflect/abi_test.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,34 @@ func TestMethodValueCallABI(t *testing.T) {
7979
t.Errorf("bad method value call: got %#v, want %#v", r2, a2)
8080
}
8181
if s.Value != 3 {
82-
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 1)
82+
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 3)
83+
}
84+
85+
s, i = makeMethodValue("ValueRegMethodSpillInt")
86+
f3 := i.(func(StructFillRegs, int, MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, int))
87+
r3a, r3b := f3(a2, 42, MagicLastTypeNameForTestingRegisterABI{})
88+
if r3a != a2 {
89+
t.Errorf("bad method value call: got %#v, want %#v", r3a, a2)
90+
}
91+
if r3b != 42 {
92+
t.Errorf("bad method value call: got %#v, want %#v", r3b, 42)
93+
}
94+
if s.Value != 4 {
95+
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 4)
96+
}
97+
98+
s, i = makeMethodValue("ValueRegMethodSpillPtr")
99+
f4 := i.(func(StructFillRegs, *byte, MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, *byte))
100+
vb := byte(10)
101+
r4a, r4b := f4(a2, &vb, MagicLastTypeNameForTestingRegisterABI{})
102+
if r4a != a2 {
103+
t.Errorf("bad method value call: got %#v, want %#v", r4a, a2)
104+
}
105+
if r4b != &vb {
106+
t.Errorf("bad method value call: got %#v, want %#v", r4b, &vb)
107+
}
108+
if s.Value != 5 {
109+
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 5)
83110
}
84111
}
85112

@@ -112,6 +139,20 @@ func (m *StructWithMethods) SpillStructCall(s StructFillRegs, _ MagicLastTypeNam
112139
return s
113140
}
114141

142+
// When called as a method value, i is passed on the stack.
143+
// When called as a method, i is passed in a register.
144+
func (m *StructWithMethods) ValueRegMethodSpillInt(s StructFillRegs, i int, _ MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, int) {
145+
m.Value = 4
146+
return s, i
147+
}
148+
149+
// When called as a method value, i is passed on the stack.
150+
// When called as a method, i is passed in a register.
151+
func (m *StructWithMethods) ValueRegMethodSpillPtr(s StructFillRegs, i *byte, _ MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, *byte) {
152+
m.Value = 5
153+
return s, i
154+
}
155+
115156
func TestReflectCallABI(t *testing.T) {
116157
// Enable register-based reflect.Call and ensure we don't
117158
// use potentially incorrect cached versions by clearing

src/reflect/value.go

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -952,25 +952,47 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *a
952952
continue
953953
}
954954

955-
// There are three cases to handle in translating each
955+
// There are four cases to handle in translating each
956956
// argument:
957957
// 1. Stack -> stack translation.
958-
// 2. Registers -> stack translation.
959-
// 3. Registers -> registers translation.
960-
// The fourth cases can't happen, because a method value
961-
// call uses strictly fewer registers than a method call.
958+
// 2. Stack -> registers translation.
959+
// 3. Registers -> stack translation.
960+
// 4. Registers -> registers translation.
961+
// TODO(mknyszek): Cases 2 and 3 below only work on little endian
962+
// architectures. This is OK for now, but this needs to be fixed
963+
// before supporting the register ABI on big endian architectures.
962964

963965
// If the value ABI passes the value on the stack,
964966
// then the method ABI does too, because it has strictly
965967
// fewer arguments. Simply copy between the two.
966968
if vStep := valueSteps[0]; vStep.kind == abiStepStack {
967969
mStep := methodSteps[0]
968-
if mStep.kind != abiStepStack || vStep.size != mStep.size {
969-
panic("method ABI and value ABI do not align")
970+
// Handle stack -> stack translation.
971+
if mStep.kind == abiStepStack {
972+
if vStep.size != mStep.size {
973+
panic("method ABI and value ABI do not align")
974+
}
975+
typedmemmove(t,
976+
add(methodFrame, mStep.stkOff, "precomputed stack offset"),
977+
add(valueFrame, vStep.stkOff, "precomputed stack offset"))
978+
continue
979+
}
980+
// Handle stack -> register translation.
981+
for _, mStep := range methodSteps {
982+
from := add(valueFrame, vStep.stkOff+mStep.offset, "precomputed stack offset")
983+
switch mStep.kind {
984+
case abiStepPointer:
985+
// Do the pointer copy directly so we get a write barrier.
986+
methodRegs.Ptrs[mStep.ireg] = *(*unsafe.Pointer)(from)
987+
fallthrough // We need to make sure this ends up in Ints, too.
988+
case abiStepIntReg:
989+
memmove(unsafe.Pointer(&methodRegs.Ints[mStep.ireg]), from, mStep.size)
990+
case abiStepFloatReg:
991+
memmove(unsafe.Pointer(&methodRegs.Floats[mStep.freg]), from, mStep.size)
992+
default:
993+
panic("unexpected method step")
994+
}
970995
}
971-
typedmemmove(t,
972-
add(methodFrame, mStep.stkOff, "precomputed stack offset"),
973-
add(valueFrame, vStep.stkOff, "precomputed stack offset"))
974996
continue
975997
}
976998
// Handle register -> stack translation.

0 commit comments

Comments
 (0)