Commit a941cdf4 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #1159 from kmod/reftests2

Fix the lost-ref-from-oargs issue
parents 24fc085f 8504ca49
# expected: reffail
# - fails in interpreter due to keeping refcounts a bit too long (does manual sys.getrefcount checking)
"""Test the arraymodule. """Test the arraymodule.
Roger E. Masse Roger E. Masse
""" """
......
# expected: reffail
import unittest, doctest, operator import unittest, doctest, operator
import inspect import inspect
from test import test_support from test import test_support
......
# expected: reffail # expected: reffail
# - takes too long
# Copyright (c) 2004 Python Software Foundation. # Copyright (c) 2004 Python Software Foundation.
# All rights reserved. # All rights reserved.
......
# expected: fail
"""Test script for the dumbdbm module """Test script for the dumbdbm module
Original by Roger E. Masse Original by Roger E. Masse
""" """
......
# expected: reffail
# - negative ref
import sys import sys
import os import os
import unittest import unittest
......
# expected: reffail
# - negative ref
# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved. # Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
# #
# Permission to use, copy, modify, and distribute this software and its # Permission to use, copy, modify, and distribute this software and its
......
# expected: fail
"""Unit tests for the memoryview """Unit tests for the memoryview
XXX We need more tests! Some tests are in test_bytes XXX We need more tests! Some tests are in test_bytes
......
# expected: fail
# As a test suite for the os module, this is woefully inadequate, but this # As a test suite for the os module, this is woefully inadequate, but this
# does add tests for a few functions which have been determined to be more # does add tests for a few functions which have been determined to be more
# portable than they had been thought to be. # portable than they had been thought to be.
......
# expected: reffail
from test.test_support import verbose, run_unittest, import_module from test.test_support import verbose, run_unittest, import_module
from test.test_support import precisionbigmemtest, _2G, cpython_only from test.test_support import precisionbigmemtest, _2G, cpython_only
from test.test_support import captured_stdout from test.test_support import captured_stdout
......
# expected: reffail # expected: reffail
# - generator abandonment # - unknown segfault
import unittest import unittest
from test import test_support from test import test_support
import gc import gc
......
# expected: reffail
# - unknown unwinding issue
import socket import socket
import telnetlib import telnetlib
import time import time
......
# expected: reffail
# - unknown unwinding issue
from test import test_support, seq_tests from test import test_support, seq_tests
import gc import gc
......
...@@ -1229,8 +1229,7 @@ std::vector<Location> Rewriter::getDecrefLocations() { ...@@ -1229,8 +1229,7 @@ std::vector<Location> Rewriter::getDecrefLocations() {
if (l.type == Location::Scratch) { if (l.type == Location::Scratch) {
// convert to stack based location because later on we may not know the offset of the scratch area from // convert to stack based location because later on we may not know the offset of the scratch area from
// the SP. // the SP.
assert(indirectFor(l).offset % 8 == 0); decref_infos.emplace_back(Location::Stack, indirectFor(l).offset);
decref_infos.emplace_back(Location::Stack, indirectFor(l).offset / 8);
} else if (l.type == Location::Register) { } else if (l.type == Location::Register) {
// CSRs shouldn't be getting allocated, and we should only be calling this at a callsite: // CSRs shouldn't be getting allocated, and we should only be calling this at a callsite:
RELEASE_ASSERT(0, "we shouldn't be trying to decref anything in a register"); RELEASE_ASSERT(0, "we shouldn't be trying to decref anything in a register");
...@@ -1238,6 +1237,20 @@ std::vector<Location> Rewriter::getDecrefLocations() { ...@@ -1238,6 +1237,20 @@ std::vector<Location> Rewriter::getDecrefLocations() {
RELEASE_ASSERT(0, "not implemented"); RELEASE_ASSERT(0, "not implemented");
} }
} }
for (auto&& p : owned_attrs) {
RewriterVar* var = p.first;
assert(var->locations.size() == 1);
Location l = *var->locations.begin();
assert(l.type == Location::Scratch);
int offset1 = indirectFor(l).offset;
int offset2 = p.second;
decref_infos.emplace_back(Location::StackIndirect, offset1, offset2);
}
return decref_infos; return decref_infos;
} }
...@@ -1323,6 +1336,24 @@ bool RewriterVar::needsDecref() { ...@@ -1323,6 +1336,24 @@ bool RewriterVar::needsDecref() {
return reftype == RefType::OWNED && !this->refHandedOff(); return reftype == RefType::OWNED && !this->refHandedOff();
} }
void RewriterVar::registerOwnedAttr(int byte_offset) {
rewriter->addAction([=]() {
auto p = std::make_pair(this, byte_offset);
assert(!this->rewriter->owned_attrs.count(p));
this->rewriter->owned_attrs.insert(p);
this->bumpUse();
}, { this }, ActionType::NORMAL);
}
void RewriterVar::deregisterOwnedAttr(int byte_offset) {
rewriter->addAction([=]() {
auto p = std::make_pair(this, byte_offset);
assert(this->rewriter->owned_attrs.count(p));
this->rewriter->owned_attrs.erase(p);
this->bumpUse();
}, { this }, ActionType::NORMAL);
}
void RewriterVar::bumpUse() { void RewriterVar::bumpUse() {
rewriter->assertPhaseEmitting(); rewriter->assertPhaseEmitting();
...@@ -1351,6 +1382,11 @@ void RewriterVar::releaseIfNoUses() { ...@@ -1351,6 +1382,11 @@ void RewriterVar::releaseIfNoUses() {
void Rewriter::commit() { void Rewriter::commit() {
STAT_TIMER(t0, "us_timer_rewriter", 10); STAT_TIMER(t0, "us_timer_rewriter", 10);
// The rewriter could add decrefs here, but for now let's make the user add them explicitly
// and then call deregisterOwnedAttr(). Making people call it explicitly reduces the chances
// of bugs only in the exceptional path, from forgetting to call deregisterOwnedAttr.
assert(!owned_attrs.size() && "missing a call to deregisterOwnedAttr");
assert(!finished); assert(!finished);
initPhaseEmitting(); initPhaseEmitting();
...@@ -1438,7 +1474,7 @@ void Rewriter::commit() { ...@@ -1438,7 +1474,7 @@ void Rewriter::commit() {
// add increfs if required // add increfs if required
for (auto&& var : actions[i].consumed_refs) { for (auto&& var : actions[i].consumed_refs) {
if (var->refHandedOff()) { if (var->refHandedOff()) {
// if this action is the one which the variable gets handed off we don't need todo anything // if this action is the one which the variable gets handed off we don't need to do anything
assert(var->last_refconsumed_numuses > 0 && var->last_refconsumed_numuses <= var->uses.size()); assert(var->last_refconsumed_numuses > 0 && var->last_refconsumed_numuses <= var->uses.size());
int last_used_action_id = var->uses[var->last_refconsumed_numuses - 1]; int last_used_action_id = var->uses[var->last_refconsumed_numuses - 1];
if (last_used_action_id == i) if (last_used_action_id == i)
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallSet.h"
#include "asm_writing/assembler.h" #include "asm_writing/assembler.h"
...@@ -184,6 +185,12 @@ public: ...@@ -184,6 +185,12 @@ public:
void refUsed(); void refUsed();
// registerOwnedAttr tells the refcounter that a certain memory location holds a pointer
// to an owned reference. This must be paired with a call to deregisterOwnedAttr
// Call these right before emitting the store (for register) or decref (for deregister).
void registerOwnedAttr(int byte_offset);
void deregisterOwnedAttr(int byte_offset);
template <typename Src, typename Dst> inline RewriterVar* getAttrCast(int offset, Location loc = Location::any()); template <typename Src, typename Dst> inline RewriterVar* getAttrCast(int offset, Location loc = Location::any());
...@@ -344,11 +351,13 @@ private: ...@@ -344,11 +351,13 @@ private:
} }
}; };
private:
// This needs to be the first member: // This needs to be the first member:
RegionAllocator allocator; RegionAllocator allocator;
// Increfs that we thought of doing during the guarding phase that we were able to put off. // The rewriter has refcounting logic for handling owned RewriterVars. If we own memory inside those RewriterVars,
std::vector<std::pair<RewriterVar*, int>> pending_increfs; // we need to register that via registerOwnedAttr, which ends up here:
llvm::DenseSet<std::pair<RewriterVar*, int /* offset */>> owned_attrs;
protected: protected:
// Allocates `bytes` bytes of data. The allocation will get freed when the rewriter gets freed. // Allocates `bytes` bytes of data. The allocation will get freed when the rewriter gets freed.
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#ifndef PYSTON_ASMWRITING_TYPES_H #ifndef PYSTON_ASMWRITING_TYPES_H
#define PYSTON_ASMWRITING_TYPES_H #define PYSTON_ASMWRITING_TYPES_H
#include <climits>
#include "core/common.h" #include "core/common.h"
namespace pyston { namespace pyston {
...@@ -185,6 +187,8 @@ public: ...@@ -185,6 +187,8 @@ public:
Stack, Stack,
Scratch, // stack location, relative to the scratch start Scratch, // stack location, relative to the scratch start
StackIndirect, // A location like $rsp[offset1][offset2]
// For representing constants that fit in 32-bits, that can be encoded as immediates // For representing constants that fit in 32-bits, that can be encoded as immediates
AnyReg, // special type for use when specifying a location as a destination AnyReg, // special type for use when specifying a location as a destination
None, // special type that represents the lack of a location, ex where a "ret void" gets returned None, // special type that represents the lack of a location, ex where a "ret void" gets returned
...@@ -199,11 +203,19 @@ public: ...@@ -199,11 +203,19 @@ public:
// also valid if type==XMMRegister // also valid if type==XMMRegister
int32_t regnum; int32_t regnum;
// only valid if type==Stack; this is the offset from bottom of the original frame. // only valid if type==Stack; this is the offset from bottom of the original frame.
// ie argument #6 will have a stack_offset of 0, #7 will have a stack offset of 8, etc // ie argument #6 will have a stack_offset of 0, #7 will have a stack offset of 8, etc.
// Measured in bytes
int32_t stack_offset; int32_t stack_offset;
// only valid if type == Scratch; offset from the beginning of the scratch area // only valid if type == Scratch; offset from the beginning of the scratch area.
// Measured in bytes
int32_t scratch_offset; int32_t scratch_offset;
// Only valid if type == StackIndirect:
struct {
int16_t stack_first_offset;
int16_t stack_second_offset;
};
int32_t _data; int32_t _data;
}; };
...@@ -212,6 +224,12 @@ public: ...@@ -212,6 +224,12 @@ public:
Location& operator=(const Location& r) = default; Location& operator=(const Location& r) = default;
constexpr Location(LocationType type, int32_t data) : type(type), _data(data) {} constexpr Location(LocationType type, int32_t data) : type(type), _data(data) {}
Location(LocationType type, int64_t offset1, int64_t offset2)
: type(type), stack_first_offset(offset1), stack_second_offset(offset2) {
assert(type == StackIndirect);
assert(SHRT_MIN <= offset1 && offset1 <= SHRT_MAX);
assert(SHRT_MIN <= offset2 && offset2 <= SHRT_MAX);
}
constexpr Location(assembler::Register reg) : type(Register), regnum(reg.regnum) {} constexpr Location(assembler::Register reg) : type(Register), regnum(reg.regnum) {}
......
...@@ -584,7 +584,14 @@ public: ...@@ -584,7 +584,14 @@ public:
Box* b = NULL; Box* b = NULL;
if (l.type == Location::Stack) { if (l.type == Location::Stack) {
unw_word_t sp = get_cursor_sp(cursor); unw_word_t sp = get_cursor_sp(cursor);
b = ((Box**)sp)[l.stack_offset]; assert(l.stack_offset % 8 == 0);
b = ((Box**)sp)[l.stack_offset / 8];
} else if (l.type == Location::StackIndirect) {
unw_word_t sp = get_cursor_sp(cursor);
assert(l.stack_first_offset % 8 == 0);
Box** b_ptr = ((Box***)sp)[l.stack_first_offset / 8];
assert(l.stack_second_offset % 8 == 0);
b = b_ptr[l.stack_second_offset / 8];
} else if (l.type == Location::Register) { } else if (l.type == Location::Register) {
RELEASE_ASSERT(0, "untested"); RELEASE_ASSERT(0, "untested");
// This branch should never get hit since we shouldn't generate Register locations, // This branch should never get hit since we shouldn't generate Register locations,
......
...@@ -4096,9 +4096,11 @@ public: ...@@ -4096,9 +4096,11 @@ public:
void decrefOargs(RewriterVar* oargs, bool* oargs_owned, int num_oargs) { void decrefOargs(RewriterVar* oargs, bool* oargs_owned, int num_oargs) {
for (int i = 0; i < num_oargs; i++) { for (int i = 0; i < num_oargs; i++) {
if (oargs_owned[i]) if (oargs_owned[i]) {
oargs->deregisterOwnedAttr(i * sizeof(Box*));
oargs->getAttr(i * sizeof(Box*))->setType(RefType::OWNED); oargs->getAttr(i * sizeof(Box*))->setType(RefType::OWNED);
} }
}
} }
template <Rewritable rewritable, typename FuncNameCB> template <Rewritable rewritable, typename FuncNameCB>
...@@ -4352,12 +4354,16 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa ...@@ -4352,12 +4354,16 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
if (varargs_idx == 2) if (varargs_idx == 2)
rewrite_args->arg3 = varargs_val; rewrite_args->arg3 = varargs_val;
if (varargs_idx >= 3) { if (varargs_idx >= 3) {
rewrite_args->args->setAttr(
(varargs_idx - 3) * sizeof(Box*), varargs_val,
(is_owned ? RewriterVar::SetattrType::HANDED_OFF : RewriterVar::SetattrType::UNKNOWN));
if (is_owned) { if (is_owned) {
rewrite_args->args->registerOwnedAttr((varargs_idx - 3) * sizeof(Box*));
rewrite_args->args->setAttr((varargs_idx - 3) * sizeof(Box*), varargs_val,
RewriterVar::SetattrType::HANDED_OFF);
oargs_owned[varargs_idx - 3] = true; oargs_owned[varargs_idx - 3] = true;
varargs_val->refConsumed(); varargs_val->refConsumed();
} else {
rewrite_args->args->setAttr((varargs_idx - 3) * sizeof(Box*), varargs_val);
} }
} }
} }
......
# Regression test: make sure that, if an exception is thrown after
# rearrange arguments, that we still decref any owned oargs.
def f(a, b, c, d, e, *args):
print a, b, c, d, e, args
1/a
for i in xrange(-100, 100):
try:
f(i, 0, 0, 0, 0, 0)
except ZeroDivisionError:
pass
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment