Skip to content

Commit 29f8990

Browse files
bors[bot]ptersilie
andauthored
79: Fix bug in FixStackmapsSpillReloads pass. r=ltratt a=ptersilie Co-authored-by: Lukas Diekmann <[email protected]>
2 parents 1cd3745 + 4294758 commit 29f8990

File tree

2 files changed

+183
-5
lines changed

2 files changed

+183
-5
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// RUN: %clang -flto -Xclang -disable-O0-optnone -fuse-ld=lld -Wl,--mllvm=--yk-stackmap-spillreloads-fix -Wl,--mllvm=--yk-insert-stackmaps -Wl,--mllvm=--yk-optnone-after-ir-passes -Wl,--lto-newpm-passes=instcombine -Wl,--mllvm=--yk-shadow-stack %s
2+
3+
// Test case scenario extracted from CPython source code: license at
4+
// https://github.com/python/cpython/blob/main/LICENSE.
5+
//
6+
// Checks whether this program compiles as it previously led to an error in the
7+
// FixStackmapsSpillReloads pass.
8+
9+
#include <stdio.h>
10+
11+
struct PyObject {
12+
int val;
13+
};
14+
typedef struct PyObject PyObject;
15+
16+
struct PyFrameConstructor {
17+
PyObject *fc_globals;
18+
PyObject *fc_builtins;
19+
PyObject *fc_name;
20+
PyObject *fc_qualname;
21+
PyObject *fc_code;
22+
PyObject *fc_defaults;
23+
PyObject *fc_kwdefaults;
24+
PyObject *fc_closure;
25+
};
26+
typedef struct PyFrameConstructor PyFrameConstructor;
27+
28+
struct PyThreadState {
29+
int val;
30+
};
31+
typedef struct PyThreadState PyThreadState;
32+
33+
PyThreadState PYTS;
34+
PyObject PYO;
35+
PyObject **PYOS;
36+
37+
PyThreadState *_PyThreadState_GET() {
38+
return &PYTS;
39+
}
40+
41+
PyObject *_PyTuple_FromArray(PyObject *const *defs, int defcount) {
42+
PYO.val = defcount;
43+
return &PYO;
44+
}
45+
46+
void PyTuple_SET_ITEM(PyObject *o, int i, PyObject *P) {
47+
}
48+
49+
PyObject *_PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) {
50+
return globals;
51+
}
52+
53+
PyObject *PyTuple_New(int kwcount) {
54+
return &PYO;
55+
}
56+
57+
PyObject **PyMem_Malloc(int kwcount) {
58+
return PYOS;
59+
}
60+
61+
void PyMem_Free(PyObject **PO) {
62+
printf("%p", PO);
63+
}
64+
65+
void Py_DECREF(PyObject *defaults) {
66+
defaults->val++;
67+
}
68+
69+
PyObject *_PyEval_Vector(PyThreadState *PO, PyFrameConstructor *PFC, PyObject *locals, PyObject** allargs, int count, PyObject *kwnames) {
70+
return locals;
71+
}
72+
73+
/* Legacy API */
74+
PyObject *
75+
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
76+
PyObject *const *args, int argcount,
77+
PyObject *const *kws, int kwcount,
78+
PyObject *const *defs, int defcount,
79+
PyObject *kwdefs, PyObject *closure)
80+
{
81+
PyThreadState *tstate = _PyThreadState_GET();
82+
PyObject *res;
83+
PyObject *defaults = _PyTuple_FromArray(defs, defcount);
84+
if (defaults == NULL) {
85+
return NULL;
86+
}
87+
PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref
88+
if (builtins == NULL) {
89+
Py_DECREF(defaults);
90+
return NULL;
91+
}
92+
if (locals == NULL) {
93+
locals = globals;
94+
}
95+
PyObject *kwnames;
96+
PyObject *const *allargs;
97+
PyObject **newargs;
98+
if (kwcount == 0) {
99+
allargs = args;
100+
kwnames = NULL;
101+
}
102+
else {
103+
kwnames = PyTuple_New(kwcount);
104+
if (kwnames == NULL) {
105+
res = NULL;
106+
goto fail;
107+
}
108+
newargs = PyMem_Malloc(sizeof(PyObject *)*(kwcount+argcount));
109+
if (newargs == NULL) {
110+
res = NULL;
111+
Py_DECREF(kwnames);
112+
goto fail;
113+
}
114+
for (int i = 0; i < argcount; i++) {
115+
newargs[i] = args[i];
116+
}
117+
for (int i = 0; i < kwcount; i++) {
118+
Py_DECREF(kws[2*i]);
119+
PyTuple_SET_ITEM(kwnames, i, kws[2*i]);
120+
newargs[argcount+i] = kws[2*i+1];
121+
}
122+
allargs = newargs;
123+
}
124+
PyObject **kwargs = PyMem_Malloc(sizeof(PyObject *)*kwcount);
125+
if (kwargs == NULL) {
126+
res = NULL;
127+
Py_DECREF(kwnames);
128+
goto fail;
129+
}
130+
for (int i = 0; i < kwcount; i++) {
131+
Py_DECREF(kws[2*i]);
132+
PyTuple_SET_ITEM(kwnames, i, kws[2*i]);
133+
kwargs[i] = kws[2*i+1];
134+
}
135+
PyFrameConstructor constr = {
136+
.fc_globals = globals,
137+
.fc_builtins = builtins,
138+
.fc_name = _co,
139+
.fc_qualname = _co,
140+
.fc_code = _co,
141+
.fc_defaults = defaults,
142+
.fc_kwdefaults = kwdefs,
143+
.fc_closure = closure
144+
};
145+
res = _PyEval_Vector(tstate, &constr, locals,
146+
(PyObject **)allargs, argcount,
147+
kwnames);
148+
if (kwcount) {
149+
Py_DECREF(kwnames);
150+
PyMem_Free(newargs);
151+
}
152+
fail:
153+
Py_DECREF(defaults);
154+
return res;
155+
}
156+
157+
int main() {
158+
PyObject _co;
159+
PyObject globals;
160+
PyObject locals;
161+
PyObject args;
162+
PyObject kws;
163+
PyObject defs;
164+
PyObject kwdefs;
165+
PyObject *r = PyEval_EvalCodeEx(&_co, &globals, &locals, (PyObject *const *) &args, 0, (PyObject *const *) &kws, 0, (PyObject *const *) &defs, 5, &kwdefs, 0);
166+
printf("%p\n", r);
167+
}

llvm/lib/CodeGen/Yk/FixStackmapsSpillReloads.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,23 @@ bool FixStackmapsSpillReloads::runOnMachineFunction(MachineFunction &MF) {
149149
} else if (TII->isLoadFromStackSlotPostFE(*SMI, FI)) {
150150
// If the reload is a load from the stack, replace the operand
151151
// with multiple operands describing a stack location.
152-
MIB.addImm(StackMaps::IndirectMemRefOp);
153152
std::optional<unsigned> Size = SMI->getRestoreSize(TII);
154-
assert(Size.has_value() && "RestoreSize has no value.");
155-
MIB.addImm(Size.value()); // Size
156-
MIB.add(SMI->getOperand(1)); // Register
157-
MIB.add(SMI->getOperand(4)); // Offset
153+
if(!Size.has_value()) {
154+
// This reload isn't a spill (e.g. this could be loading an
155+
// argument passed via the stack), so we don't need to
156+
// replace it. Since registers in lower frames aren't reset
157+
// during deopt, this is only of consequence to the top stack
158+
// frame. And even there this will simply temporarily put a
159+
// value into the register MOI, only to then immediately
160+
// reload the same value into MOI once the reload instruction
161+
// `SMI` is executed after deopt returns to normal execution.
162+
MIB.add(*MOI);
163+
} else {
164+
MIB.addImm(StackMaps::IndirectMemRefOp);
165+
MIB.addImm(Size.value()); // Size
166+
MIB.add(SMI->getOperand(1)); // Register
167+
MIB.add(SMI->getOperand(4)); // Offset
168+
}
158169
} else {
159170
assert(false && "Unknown instruction found");
160171
}

0 commit comments

Comments
 (0)