Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
Pyston
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
Pyston
Commits
a68bb9e6
Commit
a68bb9e6
authored
Feb 10, 2016
by
Kevin Modzelewski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Basic auto-refcounter for the rewriter
parent
6d12a868
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
148 additions
and
327 deletions
+148
-327
src/asm_writing/rewriter.cpp
src/asm_writing/rewriter.cpp
+69
-106
src/asm_writing/rewriter.h
src/asm_writing/rewriter.h
+34
-107
src/codegen/ast_interpreter.cpp
src/codegen/ast_interpreter.cpp
+0
-37
src/codegen/baseline_jit.cpp
src/codegen/baseline_jit.cpp
+33
-39
src/core/options.cpp
src/core/options.cpp
+3
-3
src/runtime/objmodel.cpp
src/runtime/objmodel.cpp
+9
-35
No files found.
src/asm_writing/rewriter.cpp
View file @
a68bb9e6
This diff is collapsed.
Click to expand it.
src/asm_writing/rewriter.h
View file @
a68bb9e6
...
@@ -201,7 +201,12 @@ class RewriterVar;
...
@@ -201,7 +201,12 @@ class RewriterVar;
class
RewriterAction
;
class
RewriterAction
;
enum
class
RefType
{
enum
class
RefType
{
UNKNOWN
,
UNKNOWN
#ifndef NDEBUG
// Set this to non-zero to make it possible for the debugger to
=
0
#endif
,
OWNED
,
OWNED
,
BORROWED
,
BORROWED
,
};
};
...
@@ -209,10 +214,13 @@ enum class RefType {
...
@@ -209,10 +214,13 @@ enum class RefType {
// This might make more sense as an inner class of Rewriter, but
// This might make more sense as an inner class of Rewriter, but
// you can't forward-declare that :/
// you can't forward-declare that :/
class
RewriterVar
{
class
RewriterVar
{
#ifndef NDEBUG
// Fields for automatic refcounting:
private:
int
num_refs_consumed
=
0
;
// The number of "refConsumed()" calls on this RewriterVar
int
skip_assert_last_action
=
0
;
int
last_refconsumed_numuses
=
0
;
// The number of uses in the `uses` array when the last refConsumed() call was made.
#endif
RefType
reftype
=
RefType
::
UNKNOWN
;
// Helper function: whether there is a ref that got consumed but came from the consumption of the
// initial (owned) reference.
bool
refHandedOff
();
public:
public:
typedef
llvm
::
SmallVector
<
RewriterVar
*
,
8
>
SmallVector
;
typedef
llvm
::
SmallVector
<
RewriterVar
*
,
8
>
SmallVector
;
...
@@ -228,109 +236,31 @@ public:
...
@@ -228,109 +236,31 @@ public:
RewriterVar
*
cmp
(
AST_TYPE
::
AST_TYPE
cmp_type
,
RewriterVar
*
other
,
Location
loc
=
Location
::
any
());
RewriterVar
*
cmp
(
AST_TYPE
::
AST_TYPE
cmp_type
,
RewriterVar
*
other
,
Location
loc
=
Location
::
any
());
RewriterVar
*
toBool
(
Location
loc
=
Location
::
any
());
RewriterVar
*
toBool
(
Location
loc
=
Location
::
any
());
RewriterVar
*
setType
(
RefType
type
)
{
RewriterVar
*
setType
(
RefType
type
);
assert
(
this
->
reftype
==
RefType
::
UNKNOWN
);
assert
(
type
!=
RefType
::
UNKNOWN
);
this
->
reftype
=
type
;
this
->
vrefcount
=
1
;
return
this
;
}
// Due to optimizations, there are some functions that sometimes return a new RewriterVar and
// XXX convert callers back to setType
// sometimes return an existing one (specifically, loadConst() does this).
// asBorrowed() is meant for that kind of case.
// I think its presence indicates something fishy about the API though.
RewriterVar
*
asBorrowed
()
{
RewriterVar
*
asBorrowed
()
{
if
(
this
->
reftype
==
RefType
::
UNKNOWN
)
return
setType
(
RefType
::
BORROWED
);
return
setType
(
RefType
::
BORROWED
);
assert
(
this
->
reftype
==
RefType
::
BORROWED
);
// Special case: the rewriter can "resurrect" constants, and produce an existing
// RewriterVar even though at the bjit / IC level it's a new variable.
if
(
this
->
vrefcount
==
0
)
{
#ifndef NDEBUG
skip_assert_last_action
++
;
#endif
vrefcount
++
;
return
this
;
}
else
{
return
this
->
incvref
();
}
}
}
RewriterVar
*
incvref
()
{
RewriterVar
*
incvref
()
{
assert
(
reftype
==
RefType
::
OWNED
||
reftype
==
RefType
::
BORROWED
);
assert
(
0
&&
"don't call incvref anymore"
);
//assert(vrefcount > 0 || (reftype == RefType::BORROWED && vrefcount >= 0));
assert
(
vrefcount
>
0
);
vrefcount
++
;
return
this
;
}
}
#ifndef NDEBUG
// Assert that there are exactly num_left actions get added after this call.
// Meant for debugging. It's a way to cross between the two rewriter phases.
void
addAssertLastActionAction
(
int
num_left
=
0
);
#endif
RewriterVar
*
decvref
()
{
RewriterVar
*
decvref
()
{
assert
(
reftype
==
RefType
::
OWNED
||
reftype
==
RefType
::
BORROWED
);
assert
(
0
&&
"don't call decvref anymore"
);
assert
(
vrefcount
>
0
);
vrefcount
--
;
if
(
vrefcount
==
0
)
{
if
(
reftype
==
RefType
::
OWNED
)
decref
();
// Assert that there are no more uses of the this at the machine-code level.
// This is kind of weird to cross this abstraction boundary like this, but
// I think it's a good safety net.
#ifndef NDEBUG
addAssertLastActionAction
();
#endif
}
return
this
;
}
}
// Steal a ref (not a vref) from this object.
// Call this to let the automatic refcount machinery know that a refcount
// If there aren't any owned refs available, one will be created.
// got "consumed", ie passed off. Such as to a function that steals a reference,
// Doesn't change the vrefcount, but the vref that stealRef acts on
// or when stored into a memory location that is an owned reference, etc.
// becomes a borrowed vref. One can not use this RewriterVar after stealRef
// This should get called *after* the ref got consumed, ie something like
// except for the operation that will actually steal the ref.
// r_array->setAttr(0, r_val);
// If that is necessary, an extra vref should be taken out beforehand.
// r_val->refConsumed()
//
void
refConsumed
();
// Typical usage should look like:
// var->stealRef();
// functionThatStealsRef(var);
// var->decvref();
//
// If you need to use var after functionThatStealsRef, the whole snippet
// needs to be surrounded in an incvref/decvref.
//
// TODO: I still think I can find a better API for this. Maybe something like
// var->materializeRefFor([&](){ functionThatStealsRef(var); });
//
RewriterVar
*
stealRef
()
{
assert
(
reftype
==
RefType
::
OWNED
||
reftype
==
RefType
::
BORROWED
);
assert
(
vrefcount
>
0
);
if
(
reftype
==
RefType
::
OWNED
&&
vrefcount
==
1
)
{
// handoff, do nothing;
reftype
=
RefType
::
BORROWED
;
}
else
{
incref
();
}
if
(
vrefcount
==
1
)
{
RewriterVar
*
stealRef
()
{
// See the comment in decvref.
assert
(
0
&&
"don't call stealref anymore"
);
// This is similar, except that we want there to be exactly one more use of this variable:
// whatever consumes the vref. We want that to happen after the materializeVref call.
#ifndef NDEBUG
addAssertLastActionAction
(
1
);
#endif
}
return
this
;
}
}
...
@@ -360,7 +290,12 @@ private:
...
@@ -360,7 +290,12 @@ private:
llvm
::
SmallVector
<
int
,
32
>
uses
;
llvm
::
SmallVector
<
int
,
32
>
uses
;
int
next_use
;
int
next_use
;
void
bumpUse
();
void
bumpUse
();
// Call this on the result at the end of the action in which it's created
// TODO we should have a better way of dealing with variables that have 0 uses
void
releaseIfNoUses
();
void
releaseIfNoUses
();
// Helper funciton to release all the resources that this var is using.
// Don't call it directly: call bumpUse and releaseIfNoUses instead.
void
_release
();
bool
isDoneUsing
()
{
return
next_use
==
uses
.
size
();
}
bool
isDoneUsing
()
{
return
next_use
==
uses
.
size
();
}
bool
hasScratchAllocation
()
const
{
return
scratch_allocation
.
second
>
0
;
}
bool
hasScratchAllocation
()
const
{
return
scratch_allocation
.
second
>
0
;
}
void
resetHasScratchAllocation
()
{
scratch_allocation
=
std
::
make_pair
(
0
,
0
);
}
void
resetHasScratchAllocation
()
{
scratch_allocation
=
std
::
make_pair
(
0
,
0
);
}
...
@@ -369,9 +304,6 @@ private:
...
@@ -369,9 +304,6 @@ private:
bool
is_arg
;
bool
is_arg
;
bool
is_constant
;
bool
is_constant
;
RefType
reftype
=
RefType
::
UNKNOWN
;
int
vrefcount
;
uint64_t
constant_value
;
uint64_t
constant_value
;
Location
arg_loc
;
Location
arg_loc
;
std
::
pair
<
int
/*offset*/
,
int
/*size*/
>
scratch_allocation
;
std
::
pair
<
int
/*offset*/
,
int
/*size*/
>
scratch_allocation
;
...
@@ -397,12 +329,7 @@ private:
...
@@ -397,12 +329,7 @@ private:
RewriterVar
&
operator
=
(
const
RewriterVar
&
)
=
delete
;
RewriterVar
&
operator
=
(
const
RewriterVar
&
)
=
delete
;
public:
public:
RewriterVar
(
Rewriter
*
rewriter
)
RewriterVar
(
Rewriter
*
rewriter
)
:
rewriter
(
rewriter
),
next_use
(
0
),
is_arg
(
false
),
is_constant
(
false
)
{
:
rewriter
(
rewriter
),
next_use
(
0
),
is_arg
(
false
),
is_constant
(
false
),
vrefcount
(
0
)
{
assert
(
rewriter
);
assert
(
rewriter
);
}
}
...
@@ -410,7 +337,7 @@ public:
...
@@ -410,7 +337,7 @@ public:
// XXX: for testing, reset these on deallocation so that we will see the next time they get set.
// XXX: for testing, reset these on deallocation so that we will see the next time they get set.
~
RewriterVar
()
{
~
RewriterVar
()
{
reftype
=
(
RefType
)
-
1
;
reftype
=
(
RefType
)
-
1
;
vrefcount
=
-
11
;
num_refs_consumed
=
-
11
;
}
}
#endif
#endif
...
...
src/codegen/ast_interpreter.cpp
View file @
a68bb9e6
...
@@ -418,8 +418,6 @@ Box* ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_b
...
@@ -418,8 +418,6 @@ Box* ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_b
interpreter
.
jit
->
emitSetCurrentInst
(
s
);
interpreter
.
jit
->
emitSetCurrentInst
(
s
);
if
(
v
.
o
)
{
if
(
v
.
o
)
{
Py_DECREF
(
v
.
o
);
Py_DECREF
(
v
.
o
);
if
(
v
.
var
)
v
.
var
->
decvref
();
}
}
v
=
interpreter
.
visit_stmt
(
s
);
v
=
interpreter
.
visit_stmt
(
s
);
}
}
...
@@ -509,7 +507,6 @@ void ASTInterpreter::doStore(AST_expr* node, STOLEN(Value) value) {
...
@@ -509,7 +507,6 @@ void ASTInterpreter::doStore(AST_expr* node, STOLEN(Value) value) {
Value
o
=
visit_expr
(
attr
->
value
);
Value
o
=
visit_expr
(
attr
->
value
);
if
(
jit
)
{
if
(
jit
)
{
jit
->
emitSetAttr
(
node
,
o
,
attr
->
attr
.
getBox
(),
value
);
jit
->
emitSetAttr
(
node
,
o
,
attr
->
attr
.
getBox
(),
value
);
o
.
var
->
decvref
();
}
}
pyston
::
setattr
(
o
.
o
,
attr
->
attr
.
getBox
(),
value
.
o
);
pyston
::
setattr
(
o
.
o
,
attr
->
attr
.
getBox
(),
value
.
o
);
Py_DECREF
(
o
.
o
);
Py_DECREF
(
o
.
o
);
...
@@ -578,10 +575,6 @@ Value ASTInterpreter::visit_binop(AST_BinOp* node) {
...
@@ -578,10 +575,6 @@ Value ASTInterpreter::visit_binop(AST_BinOp* node) {
AUTO_DECREF
(
left
.
o
);
AUTO_DECREF
(
left
.
o
);
AUTO_DECREF
(
right
.
o
);
AUTO_DECREF
(
right
.
o
);
Value
r
=
doBinOp
(
node
,
left
,
right
,
node
->
op_type
,
BinExpType
::
BinOp
);
Value
r
=
doBinOp
(
node
,
left
,
right
,
node
->
op_type
,
BinExpType
::
BinOp
);
if
(
jit
)
{
left
.
var
->
decvref
();
right
.
var
->
decvref
();
}
return
r
;
return
r
;
}
}
...
@@ -869,10 +862,6 @@ Value ASTInterpreter::visit_augBinOp(AST_AugBinOp* node) {
...
@@ -869,10 +862,6 @@ Value ASTInterpreter::visit_augBinOp(AST_AugBinOp* node) {
Py_DECREF
(
left
.
o
);
Py_DECREF
(
left
.
o
);
Py_DECREF
(
right
.
o
);
Py_DECREF
(
right
.
o
);
if
(
jit
)
{
left
.
var
->
decvref
();
right
.
var
->
decvref
();
}
return
r
;
return
r
;
}
}
...
@@ -883,8 +872,6 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
...
@@ -883,8 +872,6 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
Value
val
=
visit_expr
(
node
->
args
[
0
]);
Value
val
=
visit_expr
(
node
->
args
[
0
]);
v
=
Value
(
getPystonIter
(
val
.
o
),
jit
?
jit
->
emitGetPystonIter
(
val
)
:
NULL
);
v
=
Value
(
getPystonIter
(
val
.
o
),
jit
?
jit
->
emitGetPystonIter
(
val
)
:
NULL
);
Py_DECREF
(
val
.
o
);
Py_DECREF
(
val
.
o
);
if
(
jit
)
val
.
var
->
decvref
();
}
else
if
(
node
->
opcode
==
AST_LangPrimitive
::
IMPORT_FROM
)
{
}
else
if
(
node
->
opcode
==
AST_LangPrimitive
::
IMPORT_FROM
)
{
assert
(
node
->
args
.
size
()
==
2
);
assert
(
node
->
args
.
size
()
==
2
);
assert
(
node
->
args
[
0
]
->
type
==
AST_TYPE
::
Name
);
assert
(
node
->
args
[
0
]
->
type
==
AST_TYPE
::
Name
);
...
@@ -946,8 +933,6 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
...
@@ -946,8 +933,6 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
Value
obj
=
visit_expr
(
node
->
args
[
0
]);
Value
obj
=
visit_expr
(
node
->
args
[
0
]);
v
=
Value
(
boxBool
(
nonzero
(
obj
.
o
)),
jit
?
jit
->
emitNonzero
(
obj
)
:
NULL
);
v
=
Value
(
boxBool
(
nonzero
(
obj
.
o
)),
jit
?
jit
->
emitNonzero
(
obj
)
:
NULL
);
Py_DECREF
(
obj
.
o
);
Py_DECREF
(
obj
.
o
);
if
(
jit
)
obj
.
var
->
decvref
();
}
else
if
(
node
->
opcode
==
AST_LangPrimitive
::
SET_EXC_INFO
)
{
}
else
if
(
node
->
opcode
==
AST_LangPrimitive
::
SET_EXC_INFO
)
{
assert
(
node
->
args
.
size
()
==
3
);
assert
(
node
->
args
.
size
()
==
3
);
...
@@ -976,8 +961,6 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
...
@@ -976,8 +961,6 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
Value
obj
=
visit_expr
(
node
->
args
[
0
]);
Value
obj
=
visit_expr
(
node
->
args
[
0
]);
v
=
Value
(
boxBool
(
hasnext
(
obj
.
o
)),
jit
?
jit
->
emitHasnext
(
obj
)
:
NULL
);
v
=
Value
(
boxBool
(
hasnext
(
obj
.
o
)),
jit
?
jit
->
emitHasnext
(
obj
)
:
NULL
);
Py_DECREF
(
obj
.
o
);
Py_DECREF
(
obj
.
o
);
if
(
jit
)
obj
.
var
->
decvref
();
}
else
if
(
node
->
opcode
==
AST_LangPrimitive
::
PRINT_EXPR
)
{
}
else
if
(
node
->
opcode
==
AST_LangPrimitive
::
PRINT_EXPR
)
{
abortJITing
();
abortJITing
();
Value
obj
=
visit_expr
(
node
->
args
[
0
]);
Value
obj
=
visit_expr
(
node
->
args
[
0
]);
...
@@ -1157,8 +1140,6 @@ Value ASTInterpreter::visit_makeFunction(AST_MakeFunction* mkfn) {
...
@@ -1157,8 +1140,6 @@ Value ASTInterpreter::visit_makeFunction(AST_MakeFunction* mkfn) {
if
(
jit
)
{
if
(
jit
)
{
auto
prev_func_var
=
func
.
var
;
auto
prev_func_var
=
func
.
var
;
func
.
var
=
jit
->
emitRuntimeCall
(
NULL
,
decorators
[
i
],
ArgPassSpec
(
1
),
{
func
},
NULL
);
func
.
var
=
jit
->
emitRuntimeCall
(
NULL
,
decorators
[
i
],
ArgPassSpec
(
1
),
{
func
},
NULL
);
decorators
[
i
].
var
->
decvref
();
prev_func_var
->
decvref
();
}
}
}
}
return
func
;
return
func
;
...
@@ -1333,12 +1314,6 @@ Value ASTInterpreter::visit_print(AST_Print* node) {
...
@@ -1333,12 +1314,6 @@ Value ASTInterpreter::visit_print(AST_Print* node) {
else
else
printHelper
(
getSysStdout
(),
autoXDecref
(
var
.
o
),
node
->
nl
);
printHelper
(
getSysStdout
(),
autoXDecref
(
var
.
o
),
node
->
nl
);
if
(
jit
)
{
if
(
node
->
dest
)
dest
.
var
->
decvref
();
var
.
var
->
decvref
();
}
return
Value
();
return
Value
();
}
}
...
@@ -1362,10 +1337,6 @@ Value ASTInterpreter::visit_compare(AST_Compare* node) {
...
@@ -1362,10 +1337,6 @@ Value ASTInterpreter::visit_compare(AST_Compare* node) {
Value
r
=
doBinOp
(
node
,
left
,
right
,
node
->
ops
[
0
],
BinExpType
::
Compare
);
Value
r
=
doBinOp
(
node
,
left
,
right
,
node
->
ops
[
0
],
BinExpType
::
Compare
);
Py_DECREF
(
left
.
o
);
Py_DECREF
(
left
.
o
);
Py_DECREF
(
right
.
o
);
Py_DECREF
(
right
.
o
);
if
(
jit
)
{
left
.
var
->
decvref
();
right
.
var
->
decvref
();
}
return
r
;
return
r
;
}
}
...
@@ -1499,12 +1470,6 @@ Value ASTInterpreter::visit_call(AST_Call* node) {
...
@@ -1499,12 +1470,6 @@ Value ASTInterpreter::visit_call(AST_Call* node) {
for
(
auto
e
:
args
)
for
(
auto
e
:
args
)
Py_DECREF
(
e
);
Py_DECREF
(
e
);
if
(
jit
)
{
func
.
var
->
decvref
();
for
(
auto
e
:
args_vars
)
e
->
decvref
();
}
return
v
;
return
v
;
}
}
...
@@ -1693,8 +1658,6 @@ Value ASTInterpreter::visit_attribute(AST_Attribute* node) {
...
@@ -1693,8 +1658,6 @@ Value ASTInterpreter::visit_attribute(AST_Attribute* node) {
Value
v
=
visit_expr
(
node
->
value
);
Value
v
=
visit_expr
(
node
->
value
);
Value
r
(
pyston
::
getattr
(
v
.
o
,
node
->
attr
.
getBox
()),
jit
?
jit
->
emitGetAttr
(
v
,
node
->
attr
.
getBox
(),
node
)
:
NULL
);
Value
r
(
pyston
::
getattr
(
v
.
o
,
node
->
attr
.
getBox
()),
jit
?
jit
->
emitGetAttr
(
v
,
node
->
attr
.
getBox
(),
node
)
:
NULL
);
Py_DECREF
(
v
.
o
);
Py_DECREF
(
v
.
o
);
if
(
jit
)
v
.
var
->
decvref
();
return
r
;
return
r
;
}
}
}
}
...
...
src/codegen/baseline_jit.cpp
View file @
a68bb9e6
...
@@ -278,9 +278,6 @@ RewriterVar* JitFragmentWriter::emitCreateTuple(const llvm::ArrayRef<RewriterVar
...
@@ -278,9 +278,6 @@ RewriterVar* JitFragmentWriter::emitCreateTuple(const llvm::ArrayRef<RewriterVar
else
else
r
=
call
(
false
,
(
void
*
)
createTupleHelper
,
imm
(
num
),
allocArgs
(
values
));
r
=
call
(
false
,
(
void
*
)
createTupleHelper
,
imm
(
num
),
allocArgs
(
values
));
for
(
auto
v
:
values
)
v
->
decvref
();
return
r
;
return
r
;
}
}
...
@@ -305,7 +302,6 @@ RewriterVar* JitFragmentWriter::emitGetBlockLocal(InternedString s, int vreg) {
...
@@ -305,7 +302,6 @@ RewriterVar* JitFragmentWriter::emitGetBlockLocal(InternedString s, int vreg) {
auto
it
=
local_syms
.
find
(
s
);
auto
it
=
local_syms
.
find
(
s
);
if
(
it
==
local_syms
.
end
())
if
(
it
==
local_syms
.
end
())
return
emitGetLocal
(
s
,
vreg
);
return
emitGetLocal
(
s
,
vreg
);
it
->
second
->
incvref
();
return
it
->
second
;
return
it
->
second
;
}
}
...
@@ -378,7 +374,7 @@ RewriterVar* JitFragmentWriter::emitLandingpad() {
...
@@ -378,7 +374,7 @@ RewriterVar* JitFragmentWriter::emitLandingpad() {
RewriterVar
*
JitFragmentWriter
::
emitNonzero
(
RewriterVar
*
v
)
{
RewriterVar
*
JitFragmentWriter
::
emitNonzero
(
RewriterVar
*
v
)
{
// nonzeroHelper returns bool
// nonzeroHelper returns bool
return
call
(
false
,
(
void
*
)
nonzeroHelper
,
v
);
return
call
(
false
,
(
void
*
)
nonzeroHelper
,
v
)
->
setType
(
RefType
::
OWNED
)
;
}
}
RewriterVar
*
JitFragmentWriter
::
emitNotNonzero
(
RewriterVar
*
v
)
{
RewriterVar
*
JitFragmentWriter
::
emitNotNonzero
(
RewriterVar
*
v
)
{
...
@@ -498,11 +494,13 @@ void JitFragmentWriter::emitOSRPoint(AST_Jump* node) {
...
@@ -498,11 +494,13 @@ void JitFragmentWriter::emitOSRPoint(AST_Jump* node) {
}
}
void
JitFragmentWriter
::
emitPrint
(
RewriterVar
*
dest
,
RewriterVar
*
var
,
bool
nl
)
{
void
JitFragmentWriter
::
emitPrint
(
RewriterVar
*
dest
,
RewriterVar
*
var
,
bool
nl
)
{
if
(
LOG_BJIT_ASSEMBLY
)
comment
(
"BJIT: emitPrint() start"
);
if
(
!
dest
)
if
(
!
dest
)
dest
=
call
(
false
,
(
void
*
)
getSysStdout
);
dest
=
call
(
false
,
(
void
*
)
getSysStdout
);
if
(
!
var
)
if
(
!
var
)
var
=
imm
(
0ul
);
var
=
imm
(
0ul
);
call
(
false
,
(
void
*
)
printHelper
,
dest
,
var
,
imm
(
nl
));
call
(
false
,
(
void
*
)
printHelper
,
dest
,
var
,
imm
(
nl
));
if
(
LOG_BJIT_ASSEMBLY
)
comment
(
"BJIT: emitPrint() end"
);
}
}
void
JitFragmentWriter
::
emitRaise0
()
{
void
JitFragmentWriter
::
emitRaise0
()
{
...
@@ -514,38 +512,23 @@ void JitFragmentWriter::emitRaise3(RewriterVar* arg0, RewriterVar* arg1, Rewrite
...
@@ -514,38 +512,23 @@ void JitFragmentWriter::emitRaise3(RewriterVar* arg0, RewriterVar* arg1, Rewrite
}
}
void
JitFragmentWriter
::
emitEndBlock
()
{
void
JitFragmentWriter
::
emitEndBlock
()
{
for
(
auto
v
:
local_syms
)
{
// XXX remove
if
(
v
.
second
)
{
if
(
LOG_BJIT_ASSEMBLY
)
{
// XXX silly but we need to keep this alive
std
::
string
s
=
std
::
string
(
"BJIT: decvref("
)
+
v
.
first
.
c_str
()
+
")"
;
comment
(
*
new
std
::
string
(
s
));
}
v
.
second
->
decvref
();
// xdecref?
}
}
}
}
void
JitFragmentWriter
::
emitReturn
(
RewriterVar
*
v
)
{
void
JitFragmentWriter
::
emitReturn
(
RewriterVar
*
v
)
{
v
->
stealRef
();
addAction
([
=
]()
{
_emitReturn
(
v
);
},
{
v
},
ActionType
::
NORMAL
);
addAction
([
=
]()
{
_emitReturn
(
v
);
},
{
v
},
ActionType
::
NORMAL
);
v
->
decvref
();
v
->
refConsumed
();
}
}
void
JitFragmentWriter
::
emitSetAttr
(
AST_expr
*
node
,
RewriterVar
*
obj
,
BoxedString
*
s
,
STOLEN
(
RewriterVar
*
)
attr
)
{
void
JitFragmentWriter
::
emitSetAttr
(
AST_expr
*
node
,
RewriterVar
*
obj
,
BoxedString
*
s
,
STOLEN
(
RewriterVar
*
)
attr
)
{
attr
->
stealRef
();
attr
->
stealRef
();
emitPPCall
((
void
*
)
setattr
,
{
obj
,
imm
(
s
),
attr
},
2
,
512
,
node
);
emitPPCall
((
void
*
)
setattr
,
{
obj
,
imm
(
s
),
attr
},
2
,
512
,
node
);
attr
->
decvref
();
}
}
void
JitFragmentWriter
::
emitSetBlockLocal
(
InternedString
s
,
STOLEN
(
RewriterVar
*
)
v
)
{
void
JitFragmentWriter
::
emitSetBlockLocal
(
InternedString
s
,
STOLEN
(
RewriterVar
*
)
v
)
{
if
(
LOG_BJIT_ASSEMBLY
)
comment
(
"BJIT: emitSetBlockLocal() start"
);
if
(
LOG_BJIT_ASSEMBLY
)
comment
(
"BJIT: emitSetBlockLocal() start"
);
RewriterVar
*
prev
=
local_syms
[
s
];
RewriterVar
*
prev
=
local_syms
[
s
];
local_syms
[
s
]
=
v
;
local_syms
[
s
]
=
v
;
if
(
prev
)
{
// TODO: xdecref?
prev
->
decvref
();
}
if
(
LOG_BJIT_ASSEMBLY
)
comment
(
"BJIT: emitSetBlockLocal() end"
);
if
(
LOG_BJIT_ASSEMBLY
)
comment
(
"BJIT: emitSetBlockLocal() end"
);
}
}
...
@@ -558,9 +541,8 @@ void JitFragmentWriter::emitSetExcInfo(RewriterVar* type, RewriterVar* value, Re
...
@@ -558,9 +541,8 @@ void JitFragmentWriter::emitSetExcInfo(RewriterVar* type, RewriterVar* value, Re
}
}
void
JitFragmentWriter
::
emitSetGlobal
(
Box
*
global
,
BoxedString
*
s
,
STOLEN
(
RewriterVar
*
)
v
)
{
void
JitFragmentWriter
::
emitSetGlobal
(
Box
*
global
,
BoxedString
*
s
,
STOLEN
(
RewriterVar
*
)
v
)
{
v
->
stealRef
();
emitPPCall
((
void
*
)
setGlobal
,
{
imm
(
global
),
imm
(
s
),
v
},
2
,
512
);
emitPPCall
((
void
*
)
setGlobal
,
{
imm
(
global
),
imm
(
s
),
v
},
2
,
512
);
v
->
decvref
();
v
->
refConsumed
();
}
}
void
JitFragmentWriter
::
emitSetItem
(
RewriterVar
*
target
,
RewriterVar
*
slice
,
RewriterVar
*
value
)
{
void
JitFragmentWriter
::
emitSetItem
(
RewriterVar
*
target
,
RewriterVar
*
slice
,
RewriterVar
*
value
)
{
...
@@ -585,9 +567,8 @@ void JitFragmentWriter::emitSetLocal(InternedString s, int vreg, bool set_closur
...
@@ -585,9 +567,8 @@ void JitFragmentWriter::emitSetLocal(InternedString s, int vreg, bool set_closur
v
);
v
);
}
else
{
}
else
{
RewriterVar
*
prev
=
vregs_array
->
getAttr
(
8
*
vreg
);
RewriterVar
*
prev
=
vregs_array
->
getAttr
(
8
*
vreg
);
v
->
stealRef
();
vregs_array
->
setAttr
(
8
*
vreg
,
v
);
vregs_array
->
setAttr
(
8
*
vreg
,
v
);
v
->
decvref
();
v
->
refConsumed
();
// TODO With definedness analysis, we could know whether we can skip this check (definitely defined)
// TODO With definedness analysis, we could know whether we can skip this check (definitely defined)
// or not even load the previous value (definitely undefined).
// or not even load the previous value (definitely undefined).
...
@@ -973,10 +954,6 @@ void JitFragmentWriter::_emitPPCall(RewriterVar* result, void* func_addr, llvm::
...
@@ -973,10 +954,6 @@ void JitFragmentWriter::_emitPPCall(RewriterVar* result, void* func_addr, llvm::
pp_scratch_location
-=
8
;
pp_scratch_location
-=
8
;
}
}
for
(
RewriterVar
*
arg
:
args
)
{
arg
->
bumpUse
();
}
assertConsistent
();
assertConsistent
();
StackInfo
stack_info
(
pp_scratch_size
,
pp_scratch_location
);
StackInfo
stack_info
(
pp_scratch_size
,
pp_scratch_location
);
...
@@ -987,6 +964,17 @@ void JitFragmentWriter::_emitPPCall(RewriterVar* result, void* func_addr, llvm::
...
@@ -987,6 +964,17 @@ void JitFragmentWriter::_emitPPCall(RewriterVar* result, void* func_addr, llvm::
assertConsistent
();
assertConsistent
();
result
->
releaseIfNoUses
();
result
->
releaseIfNoUses
();
// TODO: it would be nice to be able to bumpUse on these earlier so that we could potentially avoid spilling
// the args across the call if we don't need to.
// This had to get moved to the very end of this function due to the fact that bumpUse can cause refcounting
// operations to happen.
// I'm not sure though that just moving this earlier is good enough though -- we also might need to teach setupCall
// that the args might not be needed afterwards?
// Anyway this feels like micro-optimizations at the moment and we can figure it out later.
for
(
RewriterVar
*
arg
:
args
)
{
arg
->
bumpUse
();
}
}
}
void
JitFragmentWriter
::
_emitRecordType
(
RewriterVar
*
type_recorder_var
,
RewriterVar
*
obj_cls_var
)
{
void
JitFragmentWriter
::
_emitRecordType
(
RewriterVar
*
type_recorder_var
,
RewriterVar
*
obj_cls_var
)
{
...
@@ -1025,6 +1013,20 @@ void JitFragmentWriter::_emitSideExit(STOLEN(RewriterVar*) var, RewriterVar* val
...
@@ -1025,6 +1013,20 @@ void JitFragmentWriter::_emitSideExit(STOLEN(RewriterVar*) var, RewriterVar* val
assert
(
next_block_var
->
is_constant
);
assert
(
next_block_var
->
is_constant
);
uint64_t
val
=
val_constant
->
constant_value
;
uint64_t
val
=
val_constant
->
constant_value
;
assert
(
val
==
(
uint64_t
)
True
||
val
==
(
uint64_t
)
False
);
// HAXX ahead:
// Override the automatic refcounting system, to force a decref to happen before the jump.
// Really, we should probably do a decref on either side post-jump.
// But the automatic refcounter doesn't support that, and since the value is either True or False,
// we can get away with doing the decref early.
// TODO: better solution is to just make NONZERO return a borrowed ref, so we don't have to decref at all.
_decref
(
var
);
// Hax: override the automatic refcount system
assert
(
var
->
reftype
==
RefType
::
OWNED
);
assert
(
var
->
num_refs_consumed
==
0
);
var
->
reftype
=
RefType
::
BORROWED
;
assembler
::
Register
var_reg
=
var
->
getInReg
();
assembler
::
Register
var_reg
=
var
->
getInReg
();
if
(
isLargeConstant
(
val
))
{
if
(
isLargeConstant
(
val
))
{
assembler
::
Register
reg
=
val_constant
->
getInReg
(
Location
::
any
(),
true
,
/* otherThan */
var_reg
);
assembler
::
Register
reg
=
val_constant
->
getInReg
(
Location
::
any
(),
true
,
/* otherThan */
var_reg
);
...
@@ -1034,10 +1036,7 @@ void JitFragmentWriter::_emitSideExit(STOLEN(RewriterVar*) var, RewriterVar* val
...
@@ -1034,10 +1036,7 @@ void JitFragmentWriter::_emitSideExit(STOLEN(RewriterVar*) var, RewriterVar* val
}
}
{
{
// TODO: Figure out if we need a large/small forward based on the number of local syms we will have to decref?
assembler
::
ForwardJump
jne
(
*
assembler
,
assembler
::
COND_EQUAL
);
assembler
::
LargeForwardJump
jne
(
*
assembler
,
assembler
::
COND_EQUAL
);
_decref
(
var
);
ExitInfo
exit_info
;
ExitInfo
exit_info
;
_emitJump
(
next_block
,
next_block_var
,
exit_info
);
_emitJump
(
next_block
,
next_block_var
,
exit_info
);
...
@@ -1049,11 +1048,6 @@ void JitFragmentWriter::_emitSideExit(STOLEN(RewriterVar*) var, RewriterVar* val
...
@@ -1049,11 +1048,6 @@ void JitFragmentWriter::_emitSideExit(STOLEN(RewriterVar*) var, RewriterVar* val
}
}
}
}
if
(
LOG_BJIT_ASSEMBLY
)
assembler
->
comment
(
"BJIT: decreffing emitSideExit var"
);
_decref
(
var
);
if
(
LOG_BJIT_ASSEMBLY
)
assembler
->
comment
(
"BJIT: decreffing emitSideExit var end"
);
var
->
bumpUse
();
var
->
bumpUse
();
val_constant
->
bumpUse
();
val_constant
->
bumpUse
();
...
...
src/core/options.cpp
View file @
a68bb9e6
...
@@ -24,8 +24,8 @@ int PYSTON_VERSION_MICRO = 0;
...
@@ -24,8 +24,8 @@ int PYSTON_VERSION_MICRO = 0;
int
MAX_OPT_ITERATIONS
=
1
;
int
MAX_OPT_ITERATIONS
=
1
;
bool
LOG_IC_ASSEMBLY
=
false
;
bool
LOG_IC_ASSEMBLY
=
0
;
bool
LOG_BJIT_ASSEMBLY
=
false
;
bool
LOG_BJIT_ASSEMBLY
=
0
;
bool
CONTINUE_AFTER_FATAL
=
false
;
bool
CONTINUE_AFTER_FATAL
=
false
;
bool
FORCE_INTERPRETER
=
true
;
bool
FORCE_INTERPRETER
=
true
;
...
@@ -49,7 +49,7 @@ bool FORCE_LLVM_CAPI_CALLS = false;
...
@@ -49,7 +49,7 @@ bool FORCE_LLVM_CAPI_CALLS = false;
bool
FORCE_LLVM_CAPI_THROWS
=
false
;
bool
FORCE_LLVM_CAPI_THROWS
=
false
;
int
OSR_THRESHOLD_INTERPRETER
=
5
;
// XXX
int
OSR_THRESHOLD_INTERPRETER
=
5
;
// XXX
int
REOPT_THRESHOLD_INTERPRETER
=
5
;
// XXX
int
REOPT_THRESHOLD_INTERPRETER
=
1
;
// XXX
int
OSR_THRESHOLD_BASELINE
=
2500
;
int
OSR_THRESHOLD_BASELINE
=
2500
;
int
REOPT_THRESHOLD_BASELINE
=
1500
;
int
REOPT_THRESHOLD_BASELINE
=
1500
;
int
OSR_THRESHOLD_T2
=
10000
;
int
OSR_THRESHOLD_T2
=
10000
;
...
...
src/runtime/objmodel.cpp
View file @
a68bb9e6
...
@@ -1023,15 +1023,13 @@ void Box::setattr(BoxedString* attr, Box* val, SetattrRewriteArgs* rewrite_args)
...
@@ -1023,15 +1023,13 @@ void Box::setattr(BoxedString* attr, Box* val, SetattrRewriteArgs* rewrite_args)
RewriterVar
*
r_hattrs
RewriterVar
*
r_hattrs
=
rewrite_args
->
obj
->
getAttr
(
cls
->
attrs_offset
+
offsetof
(
HCAttrs
,
attr_list
),
Location
::
any
());
=
rewrite_args
->
obj
->
getAttr
(
cls
->
attrs_offset
+
offsetof
(
HCAttrs
,
attr_list
),
Location
::
any
());
// Don't need to do anything: just getting it and setting it to OWNED
// will tell the auto-refcount system to decref it.
r_hattrs
->
getAttr
(
offset
*
sizeof
(
Box
*
)
+
offsetof
(
HCAttrs
::
AttrList
,
attrs
))
r_hattrs
->
getAttr
(
offset
*
sizeof
(
Box
*
)
+
offsetof
(
HCAttrs
::
AttrList
,
attrs
))
->
setType
(
RefType
::
OWNED
)
->
setType
(
RefType
::
OWNED
);
->
decvref
();
// TODO make this stealing
// TODO make this stealing
rewrite_args
->
attrval
->
incvref
();
rewrite_args
->
attrval
->
stealRef
();
r_hattrs
->
setAttr
(
offset
*
sizeof
(
Box
*
)
+
offsetof
(
HCAttrs
::
AttrList
,
attrs
),
r_hattrs
->
setAttr
(
offset
*
sizeof
(
Box
*
)
+
offsetof
(
HCAttrs
::
AttrList
,
attrs
),
rewrite_args
->
attrval
);
rewrite_args
->
attrval
);
rewrite_args
->
attrval
->
decvref
();
rewrite_args
->
out_success
=
true
;
rewrite_args
->
out_success
=
true
;
}
}
...
@@ -1389,8 +1387,6 @@ Box* nondataDescriptorInstanceSpecialCases(GetattrRewriteArgs* rewrite_args, Box
...
@@ -1389,8 +1387,6 @@ Box* nondataDescriptorInstanceSpecialCases(GetattrRewriteArgs* rewrite_args, Box
}
else
{
}
else
{
*
bind_obj_out
=
incref
(
im_self
);
*
bind_obj_out
=
incref
(
im_self
);
if
(
rewrite_args
)
{
if
(
rewrite_args
)
{
r_im_func
->
incvref
();
r_im_self
->
incvref
();
rewrite_args
->
setReturn
(
r_im_func
,
ReturnConvention
::
HAS_RETURN
);
rewrite_args
->
setReturn
(
r_im_func
,
ReturnConvention
::
HAS_RETURN
);
*
r_bind_obj_out
=
r_im_self
;
*
r_bind_obj_out
=
r_im_self
;
}
}
...
@@ -1951,7 +1947,7 @@ Box* getattrInternalGeneric(Box* obj, BoxedString* attr, GetattrRewriteArgs* rew
...
@@ -1951,7 +1947,7 @@ Box* getattrInternalGeneric(Box* obj, BoxedString* attr, GetattrRewriteArgs* rew
// Look up the class attribute (called `descr` here because it might
// Look up the class attribute (called `descr` here because it might
// be a descriptor).
// be a descriptor).
Box
*
descr
=
NULL
;
Box
*
descr
=
NULL
;
AutoDecvref
r_descr
=
NULL
;
RewriterVar
*
r_descr
=
NULL
;
if
(
rewrite_args
)
{
if
(
rewrite_args
)
{
RewriterVar
*
r_obj_cls
=
rewrite_args
->
obj
->
getAttr
(
offsetof
(
Box
,
cls
),
Location
::
any
());
RewriterVar
*
r_obj_cls
=
rewrite_args
->
obj
->
getAttr
(
offsetof
(
Box
,
cls
),
Location
::
any
());
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
r_obj_cls
,
rewrite_args
->
destination
);
GetattrRewriteArgs
grewrite_args
(
rewrite_args
->
rewriter
,
r_obj_cls
,
rewrite_args
->
destination
);
...
@@ -2546,13 +2542,11 @@ void setattrGeneric(Box* obj, BoxedString* attr, STOLEN(Box*) val, SetattrRewrit
...
@@ -2546,13 +2542,11 @@ void setattrGeneric(Box* obj, BoxedString* attr, STOLEN(Box*) val, SetattrRewrit
raiseAttributeError
(
obj
,
attr
->
s
());
raiseAttributeError
(
obj
,
attr
->
s
());
}
}
if
(
rewrite_args
)
rewrite_args
->
attrval
->
stealRef
();
obj
->
setattr
(
attr
,
val
,
rewrite_args
);
// TODO: make setattr() stealing
// TODO: make setattr() stealing
Py_DECREF
(
val
);
obj
->
setattr
(
attr
,
val
,
rewrite_args
);
if
(
rewrite_args
)
if
(
rewrite_args
)
rewrite_args
->
attrval
->
decvref
();
rewrite_args
->
attrval
->
refConsumed
();
Py_DECREF
(
val
);
}
}
// TODO this should be in type_setattro
// TODO this should be in type_setattro
...
@@ -2661,12 +2655,8 @@ extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) {
...
@@ -2661,12 +2655,8 @@ extern "C" void setattr(Box* obj, BoxedString* attr, STOLEN(Box*) attr_val) {
// rewriter->trap();
// rewriter->trap();
SetattrRewriteArgs
rewrite_args
(
rewriter
.
get
(),
rewriter
->
getArg
(
0
),
rewriter
->
getArg
(
2
));
SetattrRewriteArgs
rewrite_args
(
rewriter
.
get
(),
rewriter
->
getArg
(
0
),
rewriter
->
getArg
(
2
));
setattrGeneric
<
REWRITABLE
>
(
obj
,
attr
,
attr_val
,
&
rewrite_args
);
setattrGeneric
<
REWRITABLE
>
(
obj
,
attr
,
attr_val
,
&
rewrite_args
);
if
(
rewrite_args
.
out_success
)
{
if
(
rewrite_args
.
out_success
)
rewriter
->
getArg
(
0
)
->
decvref
();
rewriter
->
getArg
(
1
)
->
decvref
();
// arg 2 vref got consumed by setattrGeneric
rewriter
->
commit
();
rewriter
->
commit
();
}
}
else
{
}
else
{
setattrGeneric
<
NOT_REWRITABLE
>
(
obj
,
attr
,
attr_val
,
NULL
);
setattrGeneric
<
NOT_REWRITABLE
>
(
obj
,
attr
,
attr_val
,
NULL
);
}
}
...
@@ -3245,10 +3235,8 @@ Box* callattrInternal(Box* obj, BoxedString* attr, LookupScope scope, CallattrRe
...
@@ -3245,10 +3235,8 @@ Box* callattrInternal(Box* obj, BoxedString* attr, LookupScope scope, CallattrRe
else
else
arg_array
->
setAttr
(
8
,
rewrite_args
->
rewriter
->
loadConst
(
0
));
arg_array
->
setAttr
(
8
,
rewrite_args
->
rewriter
->
loadConst
(
0
));
auto
r_rtn
=
rewrite_args
->
rewriter
->
call
(
true
,
(
void
*
)
Helper
::
call
,
arg_vec
);
auto
r_rtn
=
rewrite_args
->
rewriter
->
call
(
true
,
(
void
*
)
Helper
::
call
,
arg_vec
)
->
setType
(
RefType
::
OWNED
)
;
rewrite_args
->
setReturn
(
r_rtn
,
S
==
CXX
?
ReturnConvention
::
HAS_RETURN
:
ReturnConvention
::
CAPI_RETURN
);
rewrite_args
->
setReturn
(
r_rtn
,
S
==
CXX
?
ReturnConvention
::
HAS_RETURN
:
ReturnConvention
::
CAPI_RETURN
);
if
(
r_bind_obj
)
r_bind_obj
->
decvref
();
void
*
_args
[
2
]
=
{
args
,
const_cast
<
std
::
vector
<
BoxedString
*>*>
(
keyword_names
)
};
void
*
_args
[
2
]
=
{
args
,
const_cast
<
std
::
vector
<
BoxedString
*>*>
(
keyword_names
)
};
Box
*
rtn
=
Helper
::
call
(
val
,
argspec
,
arg1
,
arg2
,
arg3
,
_args
);
Box
*
rtn
=
Helper
::
call
(
val
,
argspec
,
arg1
,
arg2
,
arg3
,
_args
);
...
@@ -3267,11 +3255,6 @@ Box* callattrInternal(Box* obj, BoxedString* attr, LookupScope scope, CallattrRe
...
@@ -3267,11 +3255,6 @@ Box* callattrInternal(Box* obj, BoxedString* attr, LookupScope scope, CallattrRe
}
else
{
}
else
{
r
=
runtimeCallInternal
<
S
,
NOT_REWRITABLE
>
(
val
,
NULL
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
r
=
runtimeCallInternal
<
S
,
NOT_REWRITABLE
>
(
val
,
NULL
,
argspec
,
arg1
,
arg2
,
arg3
,
args
,
keyword_names
);
}
}
if
(
rewrite_args
)
{
if
(
r_bind_obj
)
r_bind_obj
->
decvref
();
r_val
->
decvref
();
}
return
r
;
return
r
;
}
}
...
@@ -3584,12 +3567,6 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
...
@@ -3584,12 +3567,6 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
if
(
rewrite_args
)
{
if
(
rewrite_args
)
{
// TODO should probably rethink the whole vrefcounting thing here.
// TODO should probably rethink the whole vrefcounting thing here.
// which ones actually need new vrefs?
// which ones actually need new vrefs?
if
(
num_output_args
>=
1
)
rewrite_args
->
arg1
->
incvref
();
if
(
num_output_args
>=
2
)
rewrite_args
->
arg2
->
incvref
();
if
(
num_output_args
>=
3
)
rewrite_args
->
arg3
->
incvref
();
if
(
num_output_args
>=
3
)
{
if
(
num_output_args
>=
3
)
{
RELEASE_ASSERT
(
0
,
"not sure what to do here
\n
"
);
RELEASE_ASSERT
(
0
,
"not sure what to do here
\n
"
);
//for (int i = 0; i < num_output_args - 3; i++) {
//for (int i = 0; i < num_output_args - 3; i++) {
...
@@ -4177,9 +4154,6 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
...
@@ -4177,9 +4154,6 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
}
}
if
(
rewrite_args
)
{
if
(
rewrite_args
)
{
RELEASE_ASSERT
(
num_output_args
<=
3
,
"figure out vrefs for arg array"
);
RELEASE_ASSERT
(
num_output_args
<=
3
,
"figure out vrefs for arg array"
);
for
(
int
i
=
0
;
i
<
num_output_args
;
i
++
)
{
getArg
(
i
,
rewrite_args
)
->
decvref
();
}
}
}
return
res
;
return
res
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment