detached 36acdc2

purego: fix three small fields losing the last field (#211)

There was a bug, that caused the last small field, that directly followed a set of small fields that combined into one full
machine word, in a struct to not be added to the list of arguments. It was caused by incorrectly marking that the field
that is being processed right now is flushed if processing of previous small field resulted in val getting filled enough to
be flushed.

In order to avoid similar errors and to have flushing logic at one place I've moved that out into an anonymous function. It
should impact performance somewhat, I guess I could just inline it. Would like to hear your opinion on that!

Closes #210
commit
36acdc22cffde0ccc9091c76c8c4ad37938804a3
author
Matvei Vdovitsyn · · 2024-02-19 14:45 UTC
committer
Hajime Hoshi · · 2024-02-19 14:46 UTC
parent
32fe07e
signature
unsigned
refs
tag: v0.6.1
3 files · +47 -26
M struct_amd64.go +29 -26
@@ -76,6 +76,17 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
7676 var shift byte // # of bits to shift
7777 var flushed bool
7878 class := _NO_CLASS
79+ flush := func() {
80+ flushed = true
81+ if class == _SSE {
82+ addFloat(uintptr(val))
83+ } else {
84+ addInt(uintptr(val))
85+ }
86+ val = 0
87+ shift = 0
88+ class = _NO_CLASS
89+ }
7990 var place func(v reflect.Value)
8091 place = func(v reflect.Value) {
8192 var numFields int
@@ -93,16 +104,6 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
93104 } else {
94105 f = v.Index(i)
95106 }
96- if shift >= 64 {
97- shift = 0
98- flushed = true
99- if class == _SSE {
100- addFloat(uintptr(val))
101- } else {
102- addInt(uintptr(val))
103- }
104- class = _NO_CLASS
105- }
106107 switch f.Kind() {
107108 case reflect.Struct:
108109 place(f)
@@ -128,10 +129,9 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
128129 shift += 32
129130 class |= _INTEGER
130131 case reflect.Int64:
131- addInt(uintptr(f.Int()))
132- shift = 0
133- class = _NO_CLASS
134- flushed = true
132+ val = uint64(f.Int())
133+ shift = 64
134+ class = _INTEGER
135135 case reflect.Uint8:
136136 val |= f.Uint() << shift
137137 shift += 8
@@ -145,10 +145,9 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
145145 shift += 32
146146 class |= _INTEGER
147147 case reflect.Uint64:
148- addInt(uintptr(f.Uint()))
149- shift = 0
150- class = _NO_CLASS
151- flushed = true
148+ val = f.Uint()
149+ shift = 64
150+ class = _INTEGER
152151 case reflect.Float32:
153152 val |= uint64(math.Float32bits(float32(f.Float()))) << shift
154153 shift += 32
@@ -158,24 +157,28 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
158157 ok = false
159158 return
160159 }
161- addFloat(uintptr(math.Float64bits(f.Float())))
162- class = _NO_CLASS
163- flushed = true
160+ val = uint64(math.Float64bits(f.Float()))
161+ shift = 64
162+ class = _SSE
164163 case reflect.Array:
165164 place(f)
166165 default:
167166 panic("purego: unsupported kind " + f.Kind().String())
168167 }
168+
169+ if shift == 64 {
170+ flush()
171+ } else if shift > 64 {
172+ // Should never happen, but may if we forget to reset shift after flush (or forget to flush),
173+ // better fall apart here, than corrupt arguments.
174+ panic("purego: tryPlaceRegisters shift > 64")
175+ }
169176 }
170177 }
171178
172179 place(v)
173180 if !flushed {
174- if class == _SSE {
175- addFloat(uintptr(val))
176- } else {
177- addInt(uintptr(val))
178- }
181+ flush()
179182 }
180183 return ok
181184 }
@@ -76,6 +76,17 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
76 var shift byte // # of bits to shift76 var shift byte // # of bits to shift
77 var flushed bool77 var flushed bool
78 class := _NO_CLASS78 class := _NO_CLASS
79+ flush := func() {
80+ flushed = true
81+ if class == _SSE {
82+ addFloat(uintptr(val))
83+ } else {
84+ addInt(uintptr(val))
85+ }
86+ val = 0
87+ shift = 0
88+ class = _NO_CLASS
89+ }
79 var place func(v reflect.Value)90 var place func(v reflect.Value)
80 place = func(v reflect.Value) {91 place = func(v reflect.Value) {
81 var numFields int92 var numFields int
@@ -93,16 +104,6 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
93 } else {104 } else {
94 f = v.Index(i)105 f = v.Index(i)
95 }106 }
96- if shift >= 64 {
97- shift = 0
98- flushed = true
99- if class == _SSE {
100- addFloat(uintptr(val))
101- } else {
102- addInt(uintptr(val))
103- }
104- class = _NO_CLASS
105- }
106 switch f.Kind() {107 switch f.Kind() {
107 case reflect.Struct:108 case reflect.Struct:
108 place(f)109 place(f)
@@ -128,10 +129,9 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
128 shift += 32129 shift += 32
129 class |= _INTEGER130 class |= _INTEGER
130 case reflect.Int64:131 case reflect.Int64:
131- addInt(uintptr(f.Int()))132+ val = uint64(f.Int())
132- shift = 0133+ shift = 64
133- class = _NO_CLASS134+ class = _INTEGER
134- flushed = true
135 case reflect.Uint8:135 case reflect.Uint8:
136 val |= f.Uint() << shift136 val |= f.Uint() << shift
137 shift += 8137 shift += 8
@@ -145,10 +145,9 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
145 shift += 32145 shift += 32
146 class |= _INTEGER146 class |= _INTEGER
147 case reflect.Uint64:147 case reflect.Uint64:
148- addInt(uintptr(f.Uint()))148+ val = f.Uint()
149- shift = 0149+ shift = 64
150- class = _NO_CLASS150+ class = _INTEGER
151- flushed = true
152 case reflect.Float32:151 case reflect.Float32:
153 val |= uint64(math.Float32bits(float32(f.Float()))) << shift152 val |= uint64(math.Float32bits(float32(f.Float()))) << shift
154 shift += 32153 shift += 32
@@ -158,24 +157,28 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
158 ok = false157 ok = false
159 return158 return
160 }159 }
161- addFloat(uintptr(math.Float64bits(f.Float())))160+ val = uint64(math.Float64bits(f.Float()))
162- class = _NO_CLASS161+ shift = 64
163- flushed = true162+ class = _SSE
164 case reflect.Array:163 case reflect.Array:
165 place(f)164 place(f)
166 default:165 default:
167 panic("purego: unsupported kind " + f.Kind().String())166 panic("purego: unsupported kind " + f.Kind().String())
168 }167 }
168+
169+ if shift == 64 {
170+ flush()
171+ } else if shift > 64 {
172+ // Should never happen, but may if we forget to reset shift after flush (or forget to flush),
173+ // better fall apart here, than corrupt arguments.
174+ panic("purego: tryPlaceRegisters shift > 64")
175+ }
169 }176 }
170 }177 }
171 178
172 place(v)179 place(v)
173 if !flushed {180 if !flushed {
174- if class == _SSE {181+ flush()
175- addFloat(uintptr(val))
176- } else {
177- addInt(uintptr(val))
178- }
179 }182 }
180 return ok183 return ok
181 }184 }
M struct_test.go +10 -0
@@ -118,6 +118,16 @@ func TestRegisterFunc_structArgs(t *testing.T) {
118118 t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat)
119119 }
120120 }
121+ {
122+ type ThreeSmallFields struct {
123+ x, y, z float32
124+ }
125+ var ThreeSmallFieldsFn func(ThreeSmallFields) float32
126+ purego.RegisterLibFunc(&ThreeSmallFieldsFn, lib, "ThreeSmallFields")
127+ if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat {
128+ t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat)
129+ }
130+ }
121131 {
122132 type FloatAndInt struct {
123133 x float32
@@ -118,6 +118,16 @@ func TestRegisterFunc_structArgs(t *testing.T) {
118 t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat)118 t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat)
119 }119 }
120 }120 }
121+ {
122+ type ThreeSmallFields struct {
123+ x, y, z float32
124+ }
125+ var ThreeSmallFieldsFn func(ThreeSmallFields) float32
126+ purego.RegisterLibFunc(&ThreeSmallFieldsFn, lib, "ThreeSmallFields")
127+ if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat {
128+ t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat)
129+ }
130+ }
121 {131 {
122 type FloatAndInt struct {132 type FloatAndInt struct {
123 x float32133 x float32
M testdata/structtest/struct_test.c +8 -0
@@ -74,6 +74,14 @@ float FloatLessThan16Bytes(struct FloatLessThan16Bytes f) {
7474 return f.x + f.y;
7575 }
7676
77+struct ThreeSmallFields {
78+ float x, y, z;
79+};
80+
81+float ThreeSmallFields(struct ThreeSmallFields f) {
82+ return f.x + f.y + f.z;
83+}
84+
7785 struct FloatAndInt {
7886 float x;
7987 int y;
@@ -74,6 +74,14 @@ float FloatLessThan16Bytes(struct FloatLessThan16Bytes f) {
74 return f.x + f.y;74 return f.x + f.y;
75 }75 }
76 76
77+struct ThreeSmallFields {
78+ float x, y, z;
79+};
80+
81+float ThreeSmallFields(struct ThreeSmallFields f) {
82+ return f.x + f.y + f.z;
83+}
84+
77 struct FloatAndInt {85 struct FloatAndInt {
78 float x;86 float x;
79 int y;87 int y;

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close