Commit 181d5850 authored by Marius Wachtler's avatar Marius Wachtler

Merge remote-tracking branch 'upstream/master' into intern

parents d7d47656 9030ec59
......@@ -23,6 +23,7 @@ addons:
- clang-3.5
- cmake
- g++-4.8
- gdb
- libgmp3-dev
- liblzma-dev
- libncurses5-dev
......@@ -51,7 +52,7 @@ script:
- ccache -z
- ninja -j4 pyston
- ccache -s
- ninja check-pyston
- PYSTON_RUN_ARGS=G ninja check-pyston
os:
- linux
......
......@@ -112,12 +112,25 @@ ifeq ($(NEED_OLD_JIT),1)
LLVM_LINK_LIBS += jit
endif
LLVM_CONFIG_DBG := $(LLVM_BUILD)/Release+Asserts/bin/llvm-config
ifneq ($(wildcard $(LLVM_CONFIG_DBG)),)
LLVM_CXXFLAGS := $(shell $(LLVM_BUILD)/Release+Asserts/bin/llvm-config --cxxflags)
LLVM_LDFLAGS := $(shell $(LLVM_BUILD)/Release+Asserts/bin/llvm-config --ldflags --system-libs --libs $(LLVM_LINK_LIBS))
LLVM_LIB_DEPS := $(wildcard $(LLVM_BUILD)/Release+Asserts/lib/*)
else
LLVM_CXXFLAGS := DBG_NOT_BUILT
LLVM_LDFLAGS := DBG_NOT_BUILT
LLVM_LIB_DEPS := DBG_NOT_BUILT
endif
LLVM_CONFIG_DEBUG := $(LLVM_BUILD)/Debug+Asserts/bin/llvm-config
ifneq ($(wildcard $(LLVM_CONFIG_DBG)),)
LLVM_DEBUG_LDFLAGS := $(shell $(LLVM_BUILD)/Debug+Asserts/bin/llvm-config --ldflags --system-libs --libs $(LLVM_LINK_LIBS))
LLVM_DEBUG_LIB_DEPS := $(wildcard $(LLVM_BUILD)/Debug+Asserts/lib/*)
else
LLVM_DEBUG_LDFLAGS := DEBUG_NOT_BUILT
LLVM_DEBUG_LIB_DEPS := DEBUG_NOT_BUILT
endif
LLVM_CONFIG_RELEASE := $(LLVM_BUILD)/Release/bin/llvm-config
ifneq ($(wildcard $(LLVM_CONFIG_RELEASE)),)
......
......@@ -27,6 +27,8 @@
#define SIZEOF_INT 4
#define SIZEOF_LONG 8
#define SIZEOF_LONG_LONG 8
#define SIZEOF_DOUBLE 8
#define SIZEOF_FLOAT 4
#define SIZEOF_OFF_T 8
#define SIZEOF_PTHREAD_T 8
#define HAVE_COPYSIGN 1
......@@ -59,6 +61,9 @@
#define HAVE_STRUCT_TM_TM_ZONE 1
#define HAVE_MKTIME 1
#define TIME_WITH_SYS_TIME
#define HAVE_GETTIMEOFDAY 1
#define PY_FORMAT_LONG_LONG "ll"
#define PY_FORMAT_SIZE_T "z"
......
......@@ -55,7 +55,9 @@ typedef struct BLOCK {
struct BLOCK *leftlink;
} block;
#define MAXFREEBLOCKS 10
// Pyston change: disable free block cache
// #define MAXFREEBLOCKS 10
#define MAXFREEBLOCKS 0
static Py_ssize_t numfreeblocks = 0;
static block *freeblocks[MAXFREEBLOCKS];
......
from django.template.base import Origin, Template, Context, TemplateDoesNotExist
from django.conf import settings
from django.apps import apps
import time
try:
import __pyston__
pyston_loaded = True
except:
pyston_loaded = False
template_source = """
{% extends "admin/base_site.html" %}
{% load i18n admin_static %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}" />{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %}{{ block.super }} dashboard{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content %}
<div id="content-main">
{% if app_list %}
{% for app in app_list %}
<div class="app-{{ app.app_label }} module">
<table>
<caption>
<a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
</caption>
{% for model in app.models %}
<tr class="model-{{ model.object_name|lower }}">
{% if model.admin_url %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.add_url %}
<td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
{% if model.admin_url %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endfor %}
{% else %}
<p>{% trans "You don't have permission to edit anything." %}</p>
{% endif %}
</div>
{% endblock %}
{% block sidebar %}
<div id="content-related">
<div class="module" id="recent-actions-module">
<h2>{% trans 'Recent Actions' %}</h2>
<h3>{% trans 'My Actions' %}</h3>
{% load log %}
{% get_admin_log 10 as admin_log for_user user %}
{% if not admin_log %}
<p>{% trans 'None available' %}</p>
{% else %}
<ul class="actionlist">
{% for entry in admin_log %}
<li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
{% if entry.is_deletion or not entry.get_admin_url %}
{{ entry.object_repr }}
{% else %}
<a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
{% endif %}
<br/>
{% if entry.content_type %}
<span class="mini quiet">{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}</span>
{% else %}
<span class="mini quiet">{% trans 'Unknown content' %}</span>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}
"""
settings.configure()
apps.populate((
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
))
elapsed = 0
for i in xrange(500):
#if pyston_loaded:
# __pyston__.clearStats()
start = time.time()
template = Template(template_source, None, "admin/index.html")
elapsed = time.time() - start
print "took %4.1fms for last iteration" % (elapsed * 1000.0,)
......@@ -30,7 +30,6 @@ add_library(PYSTON_OBJECTS OBJECT ${OPTIONAL_SRCS}
capi/codecs.cpp
capi/descrobject.cpp
capi/errors.cpp
capi/float.cpp
capi/modsupport.cpp
capi/object.cpp
capi/typeobject.cpp
......
......@@ -557,8 +557,29 @@ extern "C" int PyObject_CheckReadBuffer(PyObject* obj) noexcept {
}
extern "C" int PyObject_AsReadBuffer(PyObject* obj, const void** buffer, Py_ssize_t* buffer_len) noexcept {
fatalOrError(PyExc_NotImplementedError, "unimplemented");
return -1;
PyBufferProcs* pb;
void* pp;
Py_ssize_t len;
if (obj == NULL || buffer == NULL || buffer_len == NULL) {
null_error();
return -1;
}
pb = obj->cls->tp_as_buffer;
if (pb == NULL || pb->bf_getreadbuffer == NULL || pb->bf_getsegcount == NULL) {
PyErr_SetString(PyExc_TypeError, "expected a readable buffer object");
return -1;
}
if ((*pb->bf_getsegcount)(obj, NULL) != 1) {
PyErr_SetString(PyExc_TypeError, "expected a single-segment buffer object");
return -1;
}
len = (*pb->bf_getreadbuffer)(obj, 0, &pp);
if (len < 0)
return -1;
*buffer = pp;
*buffer_len = len;
return 0;
}
static PyObject* call_function_tail(PyObject* callable, PyObject* args) {
......@@ -1818,6 +1839,21 @@ extern "C" PyObject* PyNumber_Long(PyObject* o) noexcept {
}
extern "C" PyObject* PyNumber_Float(PyObject* o) noexcept {
if (o == NULL)
return null_error();
if (o->cls == float_cls)
return o;
if (PyInt_Check(o))
return boxFloat(((BoxedInt*)o)->n);
else if (PyLong_Check(o)) {
double result = PyLong_AsDouble(o);
if (result == -1.0 && PyErr_Occurred())
return NULL;
return boxFloat(result);
}
fatalOrError(PyExc_NotImplementedError, "unimplemented");
return nullptr;
}
......
......@@ -18,6 +18,7 @@
namespace pyston {
Box* BoxedMethodDescriptor::__call__(BoxedMethodDescriptor* self, Box* obj, BoxedTuple* varargs, Box** _args) {
STAT_TIMER(t0, "us_timer_boxedmethoddescriptor__call__");
BoxedDict* kwargs = static_cast<BoxedDict*>(_args[0]);
assert(self->cls == method_cls);
......
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file is mostly copied from CPython
#include "Python.h"
#include "core/types.h"
#include "runtime/types.h"
namespace pyston {
extern "C" {
typedef enum { unknown_format, ieee_big_endian_format, ieee_little_endian_format } float_format_type;
static float_format_type double_format, float_format;
static float_format_type detected_double_format, detected_float_format;
static PyObject* float_getformat(PyTypeObject* v, PyObject* arg) noexcept {
float_format_type r;
BoxedString* str = static_cast<BoxedString*>(arg);
if (!PyString_Check(arg)) {
PyErr_Format(PyExc_TypeError, "__getformat__() argument must be string, not %.500s", Py_TYPE(arg)->tp_name);
return NULL;
}
if (str->s == "double") {
r = double_format;
} else if (str->s == "float") {
r = float_format;
} else {
PyErr_SetString(PyExc_ValueError, "__getformat__() argument 1 must be "
"'double' or 'float'");
return NULL;
}
switch (r) {
case unknown_format:
return static_cast<PyObject*>(boxStrConstant("unknown"));
case ieee_little_endian_format:
return static_cast<PyObject*>(boxStrConstant("IEEE, little-endian"));
case ieee_big_endian_format:
return static_cast<PyObject*>(boxStrConstant("IEEE, big-endian"));
default:
Py_FatalError("insane float_format or double_format");
return NULL;
}
}
PyDoc_STRVAR(float_getformat_doc, "float.__getformat__(typestr) -> string\n"
"\n"
"You probably don't want to use this function. It exists mainly to be\n"
"used in Python's test suite.\n"
"\n"
"typestr must be 'double' or 'float'. This function returns whichever of\n"
"'unknown', 'IEEE, big-endian' or 'IEEE, little-endian' best describes the\n"
"format of floating point numbers used by the C type named by typestr.");
static PyObject* float_setformat(PyTypeObject* v, PyObject* args) noexcept {
char* typestr;
char* format;
float_format_type f;
float_format_type detected;
float_format_type* p;
if (!PyArg_ParseTuple(args, "ss:__setformat__", &typestr, &format))
return NULL;
if (strcmp(typestr, "double") == 0) {
p = &double_format;
detected = detected_double_format;
} else if (strcmp(typestr, "float") == 0) {
p = &float_format;
detected = detected_float_format;
} else {
PyErr_SetString(PyExc_ValueError, "__setformat__() argument 1 must "
"be 'double' or 'float'");
return NULL;
}
if (strcmp(format, "unknown") == 0) {
f = unknown_format;
} else if (strcmp(format, "IEEE, little-endian") == 0) {
f = ieee_little_endian_format;
} else if (strcmp(format, "IEEE, big-endian") == 0) {
f = ieee_big_endian_format;
} else {
PyErr_SetString(PyExc_ValueError, "__setformat__() argument 2 must be "
"'unknown', 'IEEE, little-endian' or "
"'IEEE, big-endian'");
return NULL;
}
if (f != unknown_format && f != detected) {
PyErr_Format(PyExc_ValueError, "can only set %s format to 'unknown' or the "
"detected platform value",
typestr);
return NULL;
}
*p = f;
Py_RETURN_NONE;
}
/*----------------------------------------------------------------------------
* _PyFloat_{Pack,Unpack}{4,8}. See floatobject.h.
*/
extern "C" int _PyFloat_Pack4(double x, unsigned char* p, int le) noexcept {
if (float_format == unknown_format) {
unsigned char sign;
int e;
double f;
unsigned int fbits;
int incr = 1;
if (le) {
p += 3;
incr = -1;
}
if (x < 0) {
sign = 1;
x = -x;
} else
sign = 0;
f = frexp(x, &e);
/* Normalize f to be in the range [1.0, 2.0) */
if (0.5 <= f && f < 1.0) {
f *= 2.0;
e--;
} else if (f == 0.0)
e = 0;
else {
PyErr_SetString(PyExc_SystemError, "frexp() result out of range");
return -1;
}
if (e >= 128)
goto Overflow;
else if (e < -126) {
/* Gradual underflow */
f = ldexp(f, 126 + e);
e = 0;
} else if (!(e == 0 && f == 0.0)) {
e += 127;
f -= 1.0; /* Get rid of leading 1 */
}
f *= 8388608.0; /* 2**23 */
fbits = (unsigned int)(f + 0.5); /* Round */
assert(fbits <= 8388608);
if (fbits >> 23) {
/* The carry propagated out of a string of 23 1 bits. */
fbits = 0;
++e;
if (e >= 255)
goto Overflow;
}
/* First byte */
*p = (sign << 7) | (e >> 1);
p += incr;
/* Second byte */
*p = (char)(((e & 1) << 7) | (fbits >> 16));
p += incr;
/* Third byte */
*p = (fbits >> 8) & 0xFF;
p += incr;
/* Fourth byte */
*p = fbits & 0xFF;
/* Done */
return 0;
} else {
float y = (float)x;
const char* s = (char*)&y;
int i, incr = 1;
if (Py_IS_INFINITY(y) && !Py_IS_INFINITY(x))
goto Overflow;
if ((float_format == ieee_little_endian_format && !le) || (float_format == ieee_big_endian_format && le)) {
p += 3;
incr = -1;
}
for (i = 0; i < 4; i++) {
*p = *s++;
p += incr;
}
return 0;
}
Overflow:
PyErr_SetString(PyExc_OverflowError, "float too large to pack with f format");
return -1;
}
extern "C" int _PyFloat_Pack8(double x, unsigned char* p, int le) noexcept {
if (double_format == unknown_format) {
unsigned char sign;
int e;
double f;
unsigned int fhi, flo;
int incr = 1;
if (le) {
p += 7;
incr = -1;
}
if (x < 0) {
sign = 1;
x = -x;
} else
sign = 0;
f = frexp(x, &e);
/* Normalize f to be in the range [1.0, 2.0) */
if (0.5 <= f && f < 1.0) {
f *= 2.0;
e--;
} else if (f == 0.0)
e = 0;
else {
PyErr_SetString(PyExc_SystemError, "frexp() result out of range");
return -1;
}
if (e >= 1024)
goto Overflow;
else if (e < -1022) {
/* Gradual underflow */
f = ldexp(f, 1022 + e);
e = 0;
} else if (!(e == 0 && f == 0.0)) {
e += 1023;
f -= 1.0; /* Get rid of leading 1 */
}
/* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */
f *= 268435456.0; /* 2**28 */
fhi = (unsigned int)f; /* Truncate */
assert(fhi < 268435456);
f -= (double)fhi;
f *= 16777216.0; /* 2**24 */
flo = (unsigned int)(f + 0.5); /* Round */
assert(flo <= 16777216);
if (flo >> 24) {
/* The carry propagated out of a string of 24 1 bits. */
flo = 0;
++fhi;
if (fhi >> 28) {
/* And it also progagated out of the next 28 bits. */
fhi = 0;
++e;
if (e >= 2047)
goto Overflow;
}
}
/* First byte */
*p = (sign << 7) | (e >> 4);
p += incr;
/* Second byte */
*p = (unsigned char)(((e & 0xF) << 4) | (fhi >> 24));
p += incr;
/* Third byte */
*p = (fhi >> 16) & 0xFF;
p += incr;
/* Fourth byte */
*p = (fhi >> 8) & 0xFF;
p += incr;
/* Fifth byte */
*p = fhi & 0xFF;
p += incr;
/* Sixth byte */
*p = (flo >> 16) & 0xFF;
p += incr;
/* Seventh byte */
*p = (flo >> 8) & 0xFF;
p += incr;
/* Eighth byte */
*p = flo & 0xFF;
/* p += incr; Unneeded (for now) */
/* Done */
return 0;
Overflow:
PyErr_SetString(PyExc_OverflowError, "float too large to pack with d format");
return -1;
} else {
const char* s = (char*)&x;
int i, incr = 1;
if ((double_format == ieee_little_endian_format && !le) || (double_format == ieee_big_endian_format && le)) {
p += 7;
incr = -1;
}
for (i = 0; i < 8; i++) {
*p = *s++;
p += incr;
}
return 0;
}
}
double _PyFloat_Unpack4(const unsigned char* p, int le) noexcept {
if (float_format == unknown_format) {
unsigned char sign;
int e;
unsigned int f;
double x;
int incr = 1;
if (le) {
p += 3;
incr = -1;
}
/* First byte */
sign = (*p >> 7) & 1;
e = (*p & 0x7F) << 1;
p += incr;
/* Second byte */
e |= (*p >> 7) & 1;
f = (*p & 0x7F) << 16;
p += incr;
if (e == 255) {
PyErr_SetString(PyExc_ValueError, "can't unpack IEEE 754 special value "
"on non-IEEE platform");
return -1;
}
/* Third byte */
f |= *p << 8;
p += incr;
/* Fourth byte */
f |= *p;
x = (double)f / 8388608.0;
/* XXX This sadly ignores Inf/NaN issues */
if (e == 0)
e = -126;
else {
x += 1.0;
e -= 127;
}
x = ldexp(x, e);
if (sign)
x = -x;
return x;
} else {
float x;
if ((float_format == ieee_little_endian_format && !le) || (float_format == ieee_big_endian_format && le)) {
char buf[4];
char* d = &buf[3];
int i;
for (i = 0; i < 4; i++) {
*d-- = *p++;
}
memcpy(&x, buf, 4);
} else {
memcpy(&x, p, 4);
}
return x;
}
}
double _PyFloat_Unpack8(const unsigned char* p, int le) noexcept {
if (double_format == unknown_format) {
unsigned char sign;
int e;
unsigned int fhi, flo;
double x;
int incr = 1;
if (le) {
p += 7;
incr = -1;
}
/* First byte */
sign = (*p >> 7) & 1;
e = (*p & 0x7F) << 4;
p += incr;
/* Second byte */
e |= (*p >> 4) & 0xF;
fhi = (*p & 0xF) << 24;
p += incr;
if (e == 2047) {
PyErr_SetString(PyExc_ValueError, "can't unpack IEEE 754 special value "
"on non-IEEE platform");
return -1.0;
}
/* Third byte */
fhi |= *p << 16;
p += incr;
/* Fourth byte */
fhi |= *p << 8;
p += incr;
/* Fifth byte */
fhi |= *p;
p += incr;
/* Sixth byte */
flo = *p << 16;
p += incr;
/* Seventh byte */
flo |= *p << 8;
p += incr;
/* Eighth byte */
flo |= *p;
x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */
x /= 268435456.0; /* 2**28 */
if (e == 0)
e = -1022;
else {
x += 1.0;
e -= 1023;
}
x = ldexp(x, e);
if (sign)
x = -x;
return x;
} else {
double x;
if ((double_format == ieee_little_endian_format && !le) || (double_format == ieee_big_endian_format && le)) {
char buf[8];
char* d = &buf[7];
int i;
for (i = 0; i < 8; i++) {
*d-- = *p++;
}
memcpy(&x, buf, 8);
} else {
memcpy(&x, p, 8);
}
return x;
}
}
}
}
......@@ -58,6 +58,7 @@ static int hackcheck(PyObject* self, setattrofunc func, const char* what) noexce
}
static PyObject* wrap_setattr(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_setattr");
setattrofunc func = (setattrofunc)wrapped;
int res;
PyObject* name, *value;
......@@ -74,6 +75,7 @@ static PyObject* wrap_setattr(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_delattr(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_delattr");
setattrofunc func = (setattrofunc)wrapped;
int res;
PyObject* name;
......@@ -91,6 +93,7 @@ static PyObject* wrap_delattr(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_hashfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_hashfunc");
hashfunc func = (hashfunc)wrapped;
long res;
......@@ -103,12 +106,14 @@ static PyObject* wrap_hashfunc(PyObject* self, PyObject* args, void* wrapped) no
}
static PyObject* wrap_call(PyObject* self, PyObject* args, void* wrapped, PyObject* kwds) noexcept {
STAT_TIMER(t0, "us_timer_wrap_call");
ternaryfunc func = (ternaryfunc)wrapped;
return (*func)(self, args, kwds);
}
static PyObject* wrap_richcmpfunc(PyObject* self, PyObject* args, void* wrapped, int op) noexcept {
STAT_TIMER(t0, "us_timer_wrap_richcmpfunc");
richcmpfunc func = (richcmpfunc)wrapped;
PyObject* other;
......@@ -132,6 +137,7 @@ RICHCMP_WRAPPER(gt, Py_GT)
RICHCMP_WRAPPER(ge, Py_GE)
static PyObject* wrap_next(PyObject* self, PyObject* args, void* wrapped) {
STAT_TIMER(t0, "us_timer_wrap_next");
unaryfunc func = (unaryfunc)wrapped;
PyObject* res;
......@@ -144,6 +150,7 @@ static PyObject* wrap_next(PyObject* self, PyObject* args, void* wrapped) {
}
static PyObject* wrap_descr_get(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_descr_get");
descrgetfunc func = (descrgetfunc)wrapped;
PyObject* obj;
PyObject* type = NULL;
......@@ -162,6 +169,7 @@ static PyObject* wrap_descr_get(PyObject* self, PyObject* args, void* wrapped) n
}
static PyObject* wrap_coercefunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_coercefunc");
coercion func = (coercion)wrapped;
PyObject* other, *res;
int ok;
......@@ -188,6 +196,7 @@ static PyObject* wrap_coercefunc(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_ternaryfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_ternaryfunc");
ternaryfunc func = (ternaryfunc)wrapped;
PyObject* other;
PyObject* third = Py_None;
......@@ -200,6 +209,7 @@ static PyObject* wrap_ternaryfunc(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_ternaryfunc_r(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_ternaryfunc_r");
ternaryfunc func = (ternaryfunc)wrapped;
PyObject* other;
PyObject* third = Py_None;
......@@ -212,6 +222,7 @@ static PyObject* wrap_ternaryfunc_r(PyObject* self, PyObject* args, void* wrappe
}
static PyObject* wrap_unaryfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_unaryfunc");
unaryfunc func = (unaryfunc)wrapped;
if (!check_num_args(args, 0))
......@@ -220,6 +231,7 @@ static PyObject* wrap_unaryfunc(PyObject* self, PyObject* args, void* wrapped) n
}
static PyObject* wrap_inquirypred(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_inquirypred");
inquiry func = (inquiry)wrapped;
int res;
......@@ -232,6 +244,7 @@ static PyObject* wrap_inquirypred(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrapInquirypred(PyObject* self, PyObject* args, void* wrapped) {
STAT_TIMER(t0, "us_timer_wrapInquirypred");
inquiry func = (inquiry)wrapped;
int res;
......@@ -244,6 +257,7 @@ static PyObject* wrapInquirypred(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_binaryfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_binaryfunc");
binaryfunc func = (binaryfunc)wrapped;
PyObject* other;
......@@ -254,6 +268,7 @@ static PyObject* wrap_binaryfunc(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_binaryfunc_l(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_binaryfunc_l");
binaryfunc func = (binaryfunc)wrapped;
PyObject* other;
......@@ -268,6 +283,7 @@ static PyObject* wrap_binaryfunc_l(PyObject* self, PyObject* args, void* wrapped
}
static PyObject* wrap_binaryfunc_r(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_binaryfunc_r");
binaryfunc func = (binaryfunc)wrapped;
PyObject* other;
......@@ -300,6 +316,7 @@ static Py_ssize_t getindex(PyObject* self, PyObject* arg) noexcept {
}
static PyObject* wrap_lenfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_lenfunc");
lenfunc func = (lenfunc)wrapped;
Py_ssize_t res;
......@@ -312,6 +329,7 @@ static PyObject* wrap_lenfunc(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_indexargfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_indexargfunc");
ssizeargfunc func = (ssizeargfunc)wrapped;
PyObject* o;
Py_ssize_t i;
......@@ -325,6 +343,7 @@ static PyObject* wrap_indexargfunc(PyObject* self, PyObject* args, void* wrapped
}
static PyObject* wrap_sq_item(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_sq_item");
ssizeargfunc func = (ssizeargfunc)wrapped;
PyObject* arg;
Py_ssize_t i;
......@@ -342,6 +361,7 @@ static PyObject* wrap_sq_item(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_ssizessizeargfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_ssizessizeargfunc");
ssizessizeargfunc func = (ssizessizeargfunc)wrapped;
Py_ssize_t i, j;
......@@ -351,6 +371,7 @@ static PyObject* wrap_ssizessizeargfunc(PyObject* self, PyObject* args, void* wr
}
static PyObject* wrap_sq_setitem(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_sq_setitem");
ssizeobjargproc func = (ssizeobjargproc)wrapped;
Py_ssize_t i;
int res;
......@@ -369,6 +390,7 @@ static PyObject* wrap_sq_setitem(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_sq_delitem(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_sq_delitem");
ssizeobjargproc func = (ssizeobjargproc)wrapped;
Py_ssize_t i;
int res;
......@@ -388,6 +410,7 @@ static PyObject* wrap_sq_delitem(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_ssizessizeobjargproc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_ssizessizeobjargproc");
ssizessizeobjargproc func = (ssizessizeobjargproc)wrapped;
Py_ssize_t i, j;
int res;
......@@ -403,6 +426,7 @@ static PyObject* wrap_ssizessizeobjargproc(PyObject* self, PyObject* args, void*
}
static PyObject* wrap_delslice(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_delslice");
ssizessizeobjargproc func = (ssizessizeobjargproc)wrapped;
Py_ssize_t i, j;
int res;
......@@ -418,6 +442,7 @@ static PyObject* wrap_delslice(PyObject* self, PyObject* args, void* wrapped) no
/* XXX objobjproc is a misnomer; should be objargpred */
static PyObject* wrap_objobjproc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_objobjproc");
objobjproc func = (objobjproc)wrapped;
int res;
PyObject* value;
......@@ -433,6 +458,7 @@ static PyObject* wrap_objobjproc(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_objobjargproc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_objobjargproc");
objobjargproc func = (objobjargproc)wrapped;
int res;
PyObject* key, *value;
......@@ -447,6 +473,7 @@ static PyObject* wrap_objobjargproc(PyObject* self, PyObject* args, void* wrappe
}
static PyObject* wrap_delitem(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_delitem");
objobjargproc func = (objobjargproc)wrapped;
int res;
PyObject* key;
......@@ -462,6 +489,7 @@ static PyObject* wrap_delitem(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_cmpfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_cmpfunc");
cmpfunc func = (cmpfunc)wrapped;
int res;
PyObject* other;
......@@ -482,6 +510,7 @@ static PyObject* wrap_cmpfunc(PyObject* self, PyObject* args, void* wrapped) noe
static PyObject* wrap_init(PyObject* self, PyObject* args, void* wrapped, PyObject* kwds) noexcept {
STAT_TIMER(t0, "us_timer_wrap_init");
initproc func = (initproc)wrapped;
if (func(self, args, kwds) < 0)
......
......@@ -54,6 +54,7 @@ public:
}
static Box* __call__(BoxedCApiFunction* self, BoxedTuple* varargs, BoxedDict* kwargs) {
STAT_TIMER(t0, "us_timer_boxedcapifunction__call__");
assert(self->cls == capifunc_cls);
assert(varargs->cls == tuple_cls);
assert(kwargs->cls == dict_cls);
......@@ -111,6 +112,8 @@ public:
DEFAULT_CLASS(wrapperobject_cls);
static Box* __call__(BoxedWrapperObject* self, Box* args, Box* kwds) {
STAT_TIMER(t0, "us_timer_boxedwrapperobject__call__");
assert(self->cls == wrapperobject_cls);
assert(args->cls == tuple_cls);
assert(kwds->cls == dict_cls);
......
......@@ -284,6 +284,8 @@ public:
Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block, AST_stmt* start_at) {
threading::allowGLReadPreemption();
STAT_TIMER(t0, "us_timer_astinterpreter_execute");
void* frame_addr = __builtin_frame_address(0);
RegisterHelper frame_registerer(&interpreter, frame_addr);
......@@ -340,12 +342,7 @@ Value ASTInterpreter::doBinOp(Box* left, Box* right, int op, BinExpType exp_type
void ASTInterpreter::doStore(InternedString name, Value value) {
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(name);
if (vst == ScopeInfo::VarScopeType::GLOBAL) {
if (globals->cls == module_cls) {
setattr(static_cast<BoxedModule*>(globals), name.c_str(), value.o);
} else {
assert(globals->cls == dict_cls);
static_cast<BoxedDict*>(globals)->d[boxString(name.str())] = value.o;
}
setGlobal(globals, name, value.o);
} else if (vst == ScopeInfo::VarScopeType::NAME) {
assert(frame_info.boxedLocals != NULL);
// TODO should probably pre-box the names when it's a scope that usesNameLookup
......@@ -540,6 +537,7 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
arg_array.push_back(it.second);
}
STAT_TIMER(t0, "us_timer_astinterpreter_jump_osrexit");
CompiledFunction* partial_func = compilePartialFuncInternal(&exit);
auto arg_tuple = getTupleFromArgsArray(&arg_array[0], arg_array.size());
Box* r = partial_func->call(std::get<0>(arg_tuple), std::get<1>(arg_tuple), std::get<2>(arg_tuple),
......@@ -876,7 +874,6 @@ Value ASTInterpreter::visit_delete(AST_Delete* node) {
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(target->id);
if (vst == ScopeInfo::VarScopeType::GLOBAL) {
// Can't use delattr since the errors are different:
delGlobal(globals, &target->id.str());
continue;
} else if (vst == ScopeInfo::VarScopeType::NAME) {
......@@ -927,6 +924,8 @@ Value ASTInterpreter::visit_print(AST_Print* node) {
static const std::string newline_str("\n");
static const std::string space_str(" ");
STAT_TIMER(t0, "us_timer_visit_print");
Box* dest = node->dest ? visit_expr(node->dest).o : getSysStdout();
int nvals = node->values.size();
assert(nvals <= 1 && "cfg should have lowered it to 0 or 1 values");
......
......@@ -362,7 +362,7 @@ static void handle_sigint(int signum) {
// For now, just call abort(), so that we get a traceback at least.
fprintf(stderr, "SIGINT!\n");
joinRuntime();
Stats::dump();
Stats::dump(false);
abort();
}
......
......@@ -182,6 +182,7 @@ static void compileIR(CompiledFunction* cf, EffortLevel effort) {
// The codegen_lock needs to be held in W mode before calling this function:
CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, EffortLevel effort,
const OSREntryDescriptor* entry_descriptor) {
STAT_TIMER(t0, "us_timer_compileFunction");
Timer _t("for compileFunction()", 1000);
assert((entry_descriptor != NULL) + (spec != NULL) == 1);
......@@ -315,10 +316,13 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
assert(cf->clfunc->versions.size());
}
if (cf->is_interpreted)
if (cf->is_interpreted) {
STAT_TIMER(t0, "us_timer_interpreted_module_toplevel");
astInterpretFunction(cf, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
else
} else {
STAT_TIMER(t1, "us_timer_jitted_module_toplevel");
((void (*)())cf->code)();
}
}
Box* evalOrExec(CLFunction* cl, Box* globals, Box* boxedLocals) {
......
......@@ -982,6 +982,7 @@ AST_Module* parse_string(const char* code) {
}
AST_Module* parse_file(const char* fn) {
STAT_TIMER(t0, "us_timer_cpyton_parsing");
Timer _t("parsing");
if (ENABLE_PYPA_PARSER) {
......@@ -1071,9 +1072,10 @@ static ParseResult _reparse(const char* fn, const std::string& cache_fn, AST_Mod
// it's not a huge deal right now, but this caching version can significantly cut down
// on the startup time (40ms -> 10ms).
AST_Module* caching_parse_file(const char* fn) {
STAT_TIMER(t0, "us_timer_caching_parse_file");
static StatCounter us_parsing("us_parsing");
Timer _t("parsing");
_t.setExitCallback([](long t) { us_parsing.log(t); });
_t.setExitCallback([](uint64_t t) { us_parsing.log(t); });
int code;
std::string cache_fn = std::string(fn) + "c";
......
......@@ -494,6 +494,7 @@ void unwindPythonStack(std::function<bool(std::unique_ptr<PythonFrameIteratorImp
}
static std::unique_ptr<PythonFrameIteratorImpl> getTopPythonFrame() {
STAT_TIMER(t0, "us_timer_getTopPythonFrame");
std::unique_ptr<PythonFrameIteratorImpl> rtn(nullptr);
unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl> iter) {
rtn = std::move(iter);
......@@ -514,6 +515,7 @@ static const LineInfo* lineInfoForFrame(PythonFrameIteratorImpl& frame_it) {
static StatCounter us_gettraceback("us_gettraceback");
BoxedTraceback* getTraceback() {
STAT_TIMER(t0, "us_timer_gettraceback");
if (!ENABLE_FRAME_INTROSPECTION) {
static bool printed_warning = false;
if (!printed_warning) {
......@@ -597,6 +599,8 @@ CompiledFunction* getTopCompiledFunction() {
Box* getGlobals() {
auto it = getTopPythonFrame();
if (!it)
return NULL;
return it->getGlobals();
}
......
......@@ -22,9 +22,105 @@
namespace pyston {
#if !DISABLE_STATS
std::vector<long>* Stats::counts;
#if STAT_TIMERS
extern "C" const char* getStatTimerNameById(int id) {
return Stats::getStatName(id).c_str();
}
extern "C" int getStatTimerId() {
return StatTimer::getStack()->getId();
}
extern "C" const char* getStatTimerName() {
return getStatTimerNameById(getStatTimerId());
}
__thread StatTimer* StatTimer::stack;
StatTimer::StatTimer(int statid, bool push) {
uint64_t at_time = getCPUTicks();
_start_time = 0;
_statid = statid;
if (!push) {
_prev = NULL;
return;
}
_prev = stack;
stack = this;
if (_prev) {
_prev->pause(at_time);
}
resume(at_time);
}
StatTimer::StatTimer(int statid, uint64_t at_time) {
_start_time = 0;
_statid = statid;
_prev = stack;
stack = this;
if (_prev) {
_prev->pause(at_time);
}
resume(at_time);
}
StatTimer::~StatTimer() {
assert(stack == this);
uint64_t at_time;
if (!isPaused()) {
at_time = getCPUTicks();
pause(at_time);
} else {
// fprintf (stderr, "WARNING: timer was paused.\n");
at_time = _last_pause_time;
}
stack = _prev;
if (stack) {
stack->resume(at_time);
}
}
void StatTimer::pause(uint64_t at_time) {
assert(!isPaused());
assert(at_time > _start_time);
uint64_t _duration = at_time - _start_time;
Stats::log(_statid, _duration);
_start_time = 0;
_last_pause_time = at_time;
// fprintf (stderr, "paused %d at %lu\n", _statid, at_time);
}
void StatTimer::resume(uint64_t at_time) {
assert(isPaused());
_start_time = at_time;
// fprintf (stderr, "resumed %d at %lu\n", _statid, at_time);
}
StatTimer* StatTimer::swapStack(StatTimer* s, uint64_t at_time) {
StatTimer* prev_stack = stack;
if (stack) {
stack->pause(at_time);
}
stack = s;
if (stack) {
stack->resume(at_time);
}
return prev_stack;
}
#endif
std::vector<uint64_t>* Stats::counts;
std::unordered_map<int, std::string>* Stats::names;
bool Stats::enabled;
timespec Stats::start_ts;
uint64_t Stats::start_tick;
StatCounter::StatCounter(const std::string& name) : id(Stats::getStatId(name)) {
}
......@@ -38,7 +134,7 @@ int Stats::getStatId(const std::string& name) {
// hacky but easy way of getting around static constructor ordering issues for now:
static std::unordered_map<int, std::string> names;
Stats::names = &names;
static std::vector<long> counts;
static std::vector<uint64_t> counts;
Stats::counts = &counts;
static std::unordered_map<std::string, int> made;
......@@ -52,11 +148,36 @@ int Stats::getStatId(const std::string& name) {
return rtn;
}
std::string Stats::getStatName(int id) {
return (*names)[id];
}
void Stats::startEstimatingCPUFreq() {
if (!Stats::enabled)
return;
clock_gettime(CLOCK_REALTIME, &Stats::start_ts);
Stats::start_tick = getCPUTicks();
}
// returns our estimate of the MHz of the cpu. MHz is handy because we're mostly interested in microsoecond-resolution
// timing.
double Stats::estimateCPUFreq() {
timespec dump_ts;
clock_gettime(CLOCK_REALTIME, &dump_ts);
uint64_t end_tick = getCPUTicks();
uint64_t wall_clock_ns = (dump_ts.tv_sec - start_ts.tv_sec) * 1000000000 + (dump_ts.tv_nsec - start_ts.tv_nsec);
return (double)(end_tick - Stats::start_tick) * 1000 / wall_clock_ns;
}
void Stats::dump(bool includeZeros) {
if (!Stats::enabled)
return;
double cycles_per_us = Stats::estimateCPUFreq();
fprintf(stderr, "Stats:\n");
fprintf(stderr, "estimated_cpu_mhz: %5.5f\n", cycles_per_us);
gc::dumpHeapStatistics(0);
......@@ -69,11 +190,41 @@ void Stats::dump(bool includeZeros) {
std::sort(pairs.begin(), pairs.end());
uint64_t ticks_in_main = 0;
uint64_t accumulated_stat_timer_ticks = 0;
for (int i = 0; i < pairs.size(); i++) {
if (includeZeros || (*counts)[pairs[i].second] > 0)
fprintf(stderr, "%s: %ld\n", pairs[i].first.c_str(), (*counts)[pairs[i].second]);
if (includeZeros || (*counts)[pairs[i].second] > 0) {
if (startswith(pairs[i].first, "us_") || startswith(pairs[i].first, "_init_us_")) {
fprintf(stderr, "%s: %lu\n", pairs[i].first.c_str(),
(uint64_t)((*counts)[pairs[i].second] / cycles_per_us));
} else
fprintf(stderr, "%s: %lu\n", pairs[i].first.c_str(), (*counts)[pairs[i].second]);
if (startswith(pairs[i].first, "us_timer_"))
accumulated_stat_timer_ticks += (*counts)[pairs[i].second];
if (pairs[i].first == "ticks_in_main")
ticks_in_main = (*counts)[pairs[i].second];
}
}
if (includeZeros || accumulated_stat_timer_ticks > 0)
fprintf(stderr, "ticks_all_timers: %lu\n", accumulated_stat_timer_ticks);
#if 0
// I want to enable this, but am leaving it disabled for the time
// being because it causes test failures due to:
//
// 1) some tests exit from main from inside catch blocks, without
// going through the logic to stop the timers.
// 2) some tests create multiple threads which causes problems
// with our non-per thread stat timers.
if (ticks_in_main && ticks_in_main != accumulated_stat_timer_ticks) {
fprintf(stderr, "WARNING: accumulated stat timer ticks != ticks in main - don't trust timer output.");
}
#endif
fprintf(stderr, "(End of stats)\n");
}
......
......@@ -22,23 +22,48 @@
#include <vector>
#include "core/options.h"
#include "core/util.h"
namespace pyston {
#define DISABLE_STATS 0
#define STAT_ALLOCATIONS 0 && !DISABLE_STATS
#define STAT_TIMERS 0 && !DISABLE_STATS
#if STAT_TIMERS
#define STAT_TIMER(id, name) \
static int _stid##id = Stats::getStatId(name); \
StatTimer _st##id(_stid##id)
#define STAT_TIMER2(id, name, at_time) \
static int _stid##id = Stats::getStatId(name); \
StatTimer _st##id(_stid##id, at_time)
#else
#define STAT_TIMER(id, name) StatTimer _st##id(0);
#define STAT_TIMER2(id, name, at_time) StatTimer _st##id(0);
#endif
#define STAT_TIMER_NAME(id) _st##id
#if !DISABLE_STATS
struct Stats {
private:
static std::vector<long>* counts;
static std::vector<uint64_t>* counts;
static std::unordered_map<int, std::string>* names;
static bool enabled;
static timespec start_ts;
static uint64_t start_tick;
public:
static void startEstimatingCPUFreq();
static double estimateCPUFreq();
static int getStatId(const std::string& name);
static std::string getStatName(int id);
static void setEnabled(bool enabled) { Stats::enabled = enabled; }
static void log(int id, int count = 1) { (*counts)[id] += count; }
static void log(int id, uint64_t count = 1) { (*counts)[id] += count; }
static void clear() { std::fill(counts->begin(), counts->end(), 0); }
static void dump(bool includeZeros = true);
......@@ -52,7 +77,7 @@ private:
public:
StatCounter(const std::string& name);
void log(int count = 1) { Stats::log(id, count); }
void log(uint64_t count = 1) { Stats::log(id, count); }
};
struct StatPerThreadCounter {
......@@ -62,24 +87,74 @@ private:
public:
StatPerThreadCounter(const std::string& name);
void log(int count = 1) { Stats::log(id, count); }
void log(uint64_t count = 1) { Stats::log(id, count); }
};
#else
struct Stats {
static void startEstimatingCPUFreq() {}
static double estimateCPUFreq() { return 0; }
static void setEnabled(bool enabled) {}
static void dump() { printf("(Stats disabled)\n"); }
static void dump(bool includeZeros = true) { printf("(Stats disabled)\n"); }
static void clear() {}
static void log(int id, int count = 1) {}
static int getStatId(const std::string& name) { return 0; }
static void endOfInit() {}
};
struct StatCounter {
StatCounter(const char* name) {}
void log(int count = 1){};
void log(uint64_t count = 1){};
};
struct StatPerThreadCounter {
StatPerThreadCounter(const char* name) {}
void log(int count = 1){};
void log(uint64_t count = 1){};
};
#endif
#if STAT_TIMERS
class StatTimer {
private:
static __thread StatTimer* stack;
// the start time of the current active segment (0 == paused)
uint64_t _start_time;
uint64_t _last_pause_time;
StatTimer* _prev;
int _statid;
public:
StatTimer(int statid, bool push = true);
StatTimer(int statid, uint64_t at_time);
~StatTimer();
void pause(uint64_t at_time);
void resume(uint64_t at_time);
bool isPaused() const { return _start_time == 0; }
int getId() const { return _statid; }
static StatTimer* getStack() { return stack; }
static StatTimer* swapStack(StatTimer* s, uint64_t at_time);
static void assertActive() { RELEASE_ASSERT(stack && !stack->isPaused(), ""); }
};
#else
struct StatTimer {
StatTimer(int statid, bool push = true) {}
StatTimer(int statid, uint64_t at_time) {}
~StatTimer() {}
bool isPaused() const { return false; }
void pause(uint64_t at_time) {}
void resume(uint64_t at_time) {}
static StatTimer* getStack() { return NULL; }
static StatTimer* swapStack(StatTimer* s, uint64_t at_time) { return NULL; }
static void assertActive() {}
};
#endif
}
......
......@@ -80,6 +80,8 @@ public:
return *this->_str < *rhs._str;
}
operator llvm::StringRef() { return llvm::StringRef(*_str); }
friend class InternedStringPool;
friend struct std::hash<InternedString>;
friend struct std::less<InternedString>;
......
......@@ -485,13 +485,26 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
return Box::operator new(size, default_cls); \
}
#if STAT_ALLOCATIONS
#define ALLOC_STATS(cls) \
std::string per_name_alloc_name = "alloc." + std::string(cls->tp_name); \
std::string per_name_allocsize_name = "allocsize." + std::string(cls->tp_name); \
Stats::log(Stats::getStatId(per_name_alloc_name)); \
Stats::log(Stats::getStatId(per_name_allocsize_name), size);
#else
#define ALLOC_STATS(cls)
#endif
// The restrictions on when you can use the SIMPLE (ie fast) variant are encoded as
// asserts in the 1-arg operator new function:
#define DEFAULT_CLASS_SIMPLE(default_cls) \
void* operator new(size_t size, BoxedClass * cls) __attribute__((visibility("default"))) { \
ALLOC_STATS(cls); \
return Box::operator new(size, cls); \
} \
void* operator new(size_t size) __attribute__((visibility("default"))) { \
ALLOC_STATS(default_cls); \
/* In the simple cases, we can inline the following methods and simplify things a lot: \
* - Box::operator new \
* - cls->tp_alloc \
......
......@@ -16,6 +16,9 @@
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
......@@ -26,6 +29,18 @@
namespace pyston {
static inline uint64_t rdtsc() {
unsigned long lo, hi;
asm("rdtsc" : "=a"(lo), "=d"(hi));
return (lo | (hi << 32));
}
uint64_t getCPUTicks() {
// unsigned int _unused;
// return __rdtscp(&_unused);
return rdtsc();
}
int Timer::level = 0;
Timer::Timer(const char* desc, long min_usec) : min_usec(min_usec), ended(true) {
......@@ -36,7 +51,7 @@ void Timer::restart(const char* newdesc) {
assert(ended);
desc = newdesc;
gettimeofday(&start_time, NULL);
start_time = getCPUTicks();
Timer::level++;
ended = false;
}
......@@ -46,14 +61,14 @@ void Timer::restart(const char* newdesc, long new_min_usec) {
restart(newdesc);
}
long Timer::end() {
uint64_t Timer::end(uint64_t* ended_at) {
if (!ended) {
timeval end;
gettimeofday(&end, NULL);
long us = 1000000L * (end.tv_sec - start_time.tv_sec) + (end.tv_usec - start_time.tv_usec);
uint64_t end = getCPUTicks();
uint64_t duration = end - start_time;
Timer::level--;
if (VERBOSITY("time") >= 2 && desc) {
uint64_t us = (uint64_t)(duration / Stats::estimateCPUFreq());
if (us > min_usec) {
for (int i = 0; i < Timer::level; i++) {
putchar(' ');
......@@ -70,15 +85,17 @@ long Timer::end() {
fflush(stdout);
}
}
if (ended_at)
*ended_at = end;
ended = true;
return us;
return duration;
}
return -1;
}
Timer::~Timer() {
if (!ended) {
long t = end();
uint64_t t = end();
if (exit_callback)
exit_callback(t);
}
......
......@@ -24,30 +24,36 @@
namespace pyston {
uint64_t getCPUTicks();
class Timer {
private:
static int level;
timeval start_time;
uint64_t start_time;
const char* desc;
long min_usec;
bool ended;
std::function<void(long)> exit_callback;
std::function<void(uint64_t)> exit_callback;
public:
Timer(const char* desc = NULL, long min_usec = -1);
~Timer();
void setExitCallback(std::function<void(long)> _exit_callback) { exit_callback = _exit_callback; }
void setExitCallback(std::function<void(uint64_t)> _exit_callback) { exit_callback = _exit_callback; }
void restart(const char* newdesc, long new_min_usec);
void restart(const char* newdesc = NULL);
long end();
long split(const char* newdesc = NULL) {
long rtn = end();
// returns the duration. if @ended_at is non-null, it's filled in
// with the tick the timer stopped at.
uint64_t end(uint64_t* ended_at = NULL);
uint64_t split(const char* newdesc = NULL) {
uint64_t rtn = end();
restart(newdesc);
return rtn;
}
uint64_t getStartTime() const { return start_time; }
};
bool startswith(const std::string& s, const std::string& pattern);
......
......@@ -339,6 +339,8 @@ void runCollection() {
static StatCounter sc("gc_collections");
sc.log();
STAT_TIMER(t0, "us_timer_gc_collection");
ncollections++;
if (VERBOSITY("gc") >= 2)
......
......@@ -28,7 +28,9 @@ namespace pyston {
namespace gc {
static StatCounter gc_alloc_bytes("zzz_gc_alloc_bytes");
extern "C" inline void* gc_alloc(size_t bytes, GCKind kind_id) {
STAT_TIMER(t0, "us_timer_gc_alloc");
size_t alloc_bytes = bytes + sizeof(GCAllocation);
#ifndef NVALGRIND
......@@ -89,8 +91,10 @@ extern "C" inline void* gc_alloc(size_t bytes, GCKind kind_id) {
// if (VERBOSITY()) printf("Allocated %ld bytes at [%p, %p)\n", bytes, r, (char*)r + bytes);
#endif
// printf("Allocated %p\n", r);
#if STAT_ALLOCATIONS
gc_alloc_bytes.log(bytes);
#endif
return r;
}
......@@ -128,6 +132,10 @@ extern "C" inline void* gc_realloc(void* ptr, size_t bytes) {
alloc->kind_data = bytes;
}
#if STAT_ALLOCATIONS
gc_alloc_bytes.log(bytes);
#endif
return rtn;
}
......
......@@ -16,10 +16,12 @@
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <readline/history.h>
#include <readline/readline.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include "llvm/Support/FileSystem.h"
......@@ -90,6 +92,86 @@ static bool handle_toplevel_exn(const ExcInfo& e, int* retcode) {
static bool force_repl = false;
static bool unbuffered = false;
static const char* argv0;
static int pipefds[2];
static void handle_sigsegv(int signum) {
assert(signum == SIGSEGV);
// TODO: this should set a flag saying a KeyboardInterrupt is pending.
// For now, just call abort(), so that we get a traceback at least.
fprintf(stderr, "child encountered segfault! signalling parent watcher to backtrace.\n");
char buf[1];
int r = write(pipefds[1], buf, 1);
RELEASE_ASSERT(r == 1, "");
while (true) {
sleep(1);
}
}
static int gdb_child_pid;
static void propagate_sig(int signum) {
// fprintf(stderr, "parent received signal %d, passing to child and then ignoring\n", signum);
assert(gdb_child_pid);
int r = kill(gdb_child_pid, signum);
assert(!r);
}
static void enableGdbSegfaultWatcher() {
int r = pipe2(pipefds, 0);
RELEASE_ASSERT(r == 0, "");
gdb_child_pid = fork();
if (gdb_child_pid) {
// parent watcher process
close(pipefds[1]);
for (int i = 0; i < _NSIG; i++) {
if (i == SIGCHLD)
continue;
signal(i, &propagate_sig);
}
while (true) {
char buf[1];
int r = read(pipefds[0], buf, 1);
if (r == 1) {
fprintf(stderr, "Parent process woken up by child; collecting backtrace and killing child\n");
char pidbuf[20];
snprintf(pidbuf, sizeof(pidbuf), "%d", gdb_child_pid);
close(STDOUT_FILENO);
dup2(STDERR_FILENO, STDOUT_FILENO);
r = execlp("gdb", "gdb", "-p", pidbuf, argv0, "-batch", "-ex", "set pagination 0", "-ex",
"thread apply all bt", "-ex", "kill", "-ex", "quit -11", NULL);
RELEASE_ASSERT(0, "%d %d %s", r, errno, strerror(errno));
}
if (r == 0) {
int status;
r = waitpid(gdb_child_pid, &status, 0);
RELEASE_ASSERT(r == gdb_child_pid, "%d %d %s", r, errno, strerror(errno));
int rtncode = 0;
if (WIFEXITED(status))
rtncode = WEXITSTATUS(status);
else
rtncode = 128 + WTERMSIG(status);
exit(rtncode);
}
RELEASE_ASSERT(0, "%d %d %s", r, errno, strerror(errno));
}
RELEASE_ASSERT(0, "");
}
close(pipefds[0]);
signal(SIGSEGV, &handle_sigsegv);
}
int handleArg(char code) {
if (code == 'O')
FORCE_OPTIMIZE = true;
......@@ -131,6 +213,8 @@ int handleArg(char code) {
CONTINUE_AFTER_FATAL = true;
} else if (code == 'T') {
ENABLE_TRACEBACKS = false;
} else if (code == 'G') {
enableGdbSegfaultWatcher();
} else {
fprintf(stderr, "Unknown option: -%c\n", code);
return 2;
......@@ -139,223 +223,244 @@ int handleArg(char code) {
}
static int main(int argc, char** argv) {
argv0 = argv[0];
Timer _t("for jit startup");
// llvm::sys::PrintStackTraceOnErrorSignal();
// llvm::PrettyStackTraceProgram X(argc, argv);
llvm::llvm_shutdown_obj Y;
int code;
const char* command = NULL;
timespec before_ts, after_ts;
Timer main_time;
int rtncode;
{
STAT_TIMER2(t0, "us_timer_main_toplevel", main_time.getStartTime());
char* env_args = getenv("PYSTON_RUN_ARGS");
int code;
const char* command = NULL;
if (env_args) {
while (*env_args) {
int r = handleArg(*env_args);
if (r)
return r;
env_args++;
char* env_args = getenv("PYSTON_RUN_ARGS");
if (env_args) {
while (*env_args) {
int r = handleArg(*env_args);
if (r)
return r;
env_args++;
}
}
}
// Suppress getopt errors so we can throw them ourselves
opterr = 0;
while ((code = getopt(argc, argv, "+:OqdIibpjtrsSvnxEc:FuPT")) != -1) {
if (code == 'c') {
assert(optarg);
command = optarg;
// no more option parsing; the rest of our arguments go into sys.argv.
break;
} else if (code == ':') {
fprintf(stderr, "Argument expected for the -%c option\n", optopt);
return 2;
} else if (code == '?') {
fprintf(stderr, "Unknown option: -%c\n", optopt);
return 2;
} else {
int r = handleArg(code);
if (r)
return r;
// Suppress getopt errors so we can throw them ourselves
opterr = 0;
while ((code = getopt(argc, argv, "+:OqdIibpjtrsSvnxEc:FuPTG")) != -1) {
if (code == 'c') {
assert(optarg);
command = optarg;
// no more option parsing; the rest of our arguments go into sys.argv.
break;
} else if (code == ':') {
fprintf(stderr, "Argument expected for the -%c option\n", optopt);
return 2;
} else if (code == '?') {
fprintf(stderr, "Unknown option: -%c\n", optopt);
return 2;
} else {
int r = handleArg(code);
if (r)
return r;
}
}
}
const char* fn = NULL;
Stats::startEstimatingCPUFreq();
threading::registerMainThread();
threading::acquireGLRead();
const char* fn = NULL;
Py_SetProgramName(argv[0]);
threading::registerMainThread();
threading::acquireGLRead();
if (unbuffered) {
setvbuf(stdin, (char*)NULL, _IONBF, BUFSIZ);
setvbuf(stdout, (char*)NULL, _IONBF, BUFSIZ);
setvbuf(stderr, (char*)NULL, _IONBF, BUFSIZ);
}
Py_SetProgramName(argv[0]);
{
Timer _t("for initCodegen");
initCodegen();
}
if (unbuffered) {
setvbuf(stdin, (char*)NULL, _IONBF, BUFSIZ);
setvbuf(stdout, (char*)NULL, _IONBF, BUFSIZ);
setvbuf(stderr, (char*)NULL, _IONBF, BUFSIZ);
}
// Arguments left over after option parsing are of the form:
// [ script | - ] [ arguments... ]
// unless we've been already parsed a `-c command` option, in which case only:
// [ arguments...]
// are parsed.
if (command)
addToSysArgv("-c");
else if (optind != argc) {
addToSysArgv(argv[optind]);
if (strcmp("-", argv[optind]) != 0)
fn = argv[optind];
++optind;
} else
addToSysArgv("");
for (int i = optind; i < argc; i++) {
addToSysArgv(argv[i]);
}
{
Timer _t("for initCodegen");
initCodegen();
}
llvm::StringRef module_search_path = Py_GetPath();
while (true) {
std::pair<llvm::StringRef, llvm::StringRef> split_str = module_search_path.split(DELIM);
if (split_str.first == module_search_path)
break; // could not find the delimiter
appendToSysPath(split_str.first);
module_search_path = split_str.second;
}
// Arguments left over after option parsing are of the form:
// [ script | - ] [ arguments... ]
// unless we've been already parsed a `-c command` option, in which case only:
// [ arguments...]
// are parsed.
if (command)
addToSysArgv("-c");
else if (optind != argc) {
addToSysArgv(argv[optind]);
if (strcmp("-", argv[optind]) != 0)
fn = argv[optind];
++optind;
} else
addToSysArgv("");
for (int i = optind; i < argc; i++) {
addToSysArgv(argv[i]);
}
if (!fn) {
// if we are in repl or command mode prepend "" to the path
prependToSysPath("");
}
llvm::StringRef module_search_path = Py_GetPath();
while (true) {
std::pair<llvm::StringRef, llvm::StringRef> split_str = module_search_path.split(DELIM);
if (split_str.first == module_search_path)
break; // could not find the delimiter
appendToSysPath(split_str.first);
module_search_path = split_str.second;
}
if (!Py_NoSiteFlag) {
try {
std::string module_name = "site";
importModuleLevel(module_name, None, None, 0);
} catch (ExcInfo e) {
e.printExcAndTraceback();
return 1;
if (!fn) {
// if we are in repl or command mode prepend "" to the path
prependToSysPath("");
}
}
// Set encoding for standard streams. This needs to be done after
// sys.path is properly set up, so that we can import the
// encodings module.
setEncodingAndErrors();
// end of argument parsing
_t.split("to run");
BoxedModule* main_module = NULL;
// if the user invoked `pyston -c command`
if (command != NULL) {
try {
main_module = createModule("__main__", "<string>");
AST_Module* m = parse_string(command);
compileAndRunModule(m, main_module);
} catch (ExcInfo e) {
int retcode = 1;
(void)handle_toplevel_exn(e, &retcode);
Stats::dump();
return retcode;
if (!Py_NoSiteFlag) {
try {
std::string module_name = "site";
importModuleLevel(module_name, None, None, 0);
} catch (ExcInfo e) {
e.printExcAndTraceback();
return 1;
}
}
}
if (fn != NULL) {
llvm::SmallString<128> path;
// Set encoding for standard streams. This needs to be done after
// sys.path is properly set up, so that we can import the
// encodings module.
setEncodingAndErrors();
if (!llvm::sys::path::is_absolute(fn)) {
char cwd_buf[1026];
char* cwd = getcwd(cwd_buf, sizeof(cwd_buf));
assert(cwd);
path = cwd;
}
// end of argument parsing
llvm::sys::path::append(path, fn);
llvm::sys::path::remove_filename(path);
prependToSysPath(path.str());
main_module = createModule("__main__", fn);
try {
AST_Module* ast = caching_parse_file(fn);
compileAndRunModule(ast, main_module);
} catch (ExcInfo e) {
int retcode = 1;
(void)handle_toplevel_exn(e, &retcode);
if (!force_repl) {
Stats::dump();
_t.split("to run");
BoxedModule* main_module = NULL;
// if the user invoked `pyston -c command`
if (command != NULL) {
try {
main_module = createModule("__main__", "<string>");
AST_Module* m = parse_string(command);
compileAndRunModule(m, main_module);
} catch (ExcInfo e) {
int retcode = 1;
(void)handle_toplevel_exn(e, &retcode);
Stats::dump(false);
return retcode;
}
}
}
if (force_repl || !(command || fn)) {
printf("Pyston v%d.%d (rev " STRINGIFY(GITREV) ")", PYSTON_VERSION_MAJOR, PYSTON_VERSION_MINOR);
printf(", targeting Python %d.%d.%d\n", PYTHON_VERSION_MAJOR, PYTHON_VERSION_MINOR, PYTHON_VERSION_MICRO);
if (!main_module) {
main_module = createModule("__main__", "<stdin>");
} else {
// main_module->fn = "<stdin>";
}
if (fn != NULL) {
llvm::SmallString<128> path;
for (;;) {
char* line = readline(">> ");
if (!line)
break;
if (!llvm::sys::path::is_absolute(fn)) {
char cwd_buf[1026];
char* cwd = getcwd(cwd_buf, sizeof(cwd_buf));
assert(cwd);
path = cwd;
}
add_history(line);
llvm::sys::path::append(path, fn);
llvm::sys::path::remove_filename(path);
char* real_path
= realpath(path.str().str().c_str(), NULL); // inefficient way of null-terminating the string
prependToSysPath(real_path);
free(real_path);
main_module = createModule("__main__", fn);
try {
AST_Module* m = parse_string(line);
Timer _t("repl");
if (m->body.size() > 0 && m->body[0]->type == AST_TYPE::Expr) {
AST_Expr* e = ast_cast<AST_Expr>(m->body[0]);
AST_Call* c = new AST_Call();
AST_Name* r = new AST_Name(m->interned_strings->get("repr"), AST_TYPE::Load, 0);
c->func = r;
c->starargs = NULL;
c->kwargs = NULL;
c->args.push_back(e->value);
c->lineno = 0;
AST_Print* p = new AST_Print();
p->dest = NULL;
p->nl = true;
p->values.push_back(c);
p->lineno = 0;
m->body[0] = p;
}
compileAndRunModule(m, main_module);
AST_Module* ast = caching_parse_file(fn);
compileAndRunModule(ast, main_module);
} catch (ExcInfo e) {
int retcode = 0xdeadbeef; // should never be seen
if (handle_toplevel_exn(e, &retcode)) {
Stats::dump();
int retcode = 1;
(void)handle_toplevel_exn(e, &retcode);
if (!force_repl) {
Stats::dump(false);
return retcode;
}
}
}
}
threading::finishMainThread();
if (force_repl || !(command || fn)) {
printf("Pyston v%d.%d (rev " STRINGIFY(GITREV) ")", PYSTON_VERSION_MAJOR, PYSTON_VERSION_MINOR);
printf(", targeting Python %d.%d.%d\n", PYTHON_VERSION_MAJOR, PYTHON_VERSION_MINOR, PYTHON_VERSION_MICRO);
// Acquire the GIL to make sure we stop the other threads, since we will tear down
// data structures they are potentially running on.
// Note: we will purposefully not release the GIL on exiting.
threading::promoteGL();
if (!main_module) {
main_module = createModule("__main__", "<stdin>");
} else {
// main_module->fn = "<stdin>";
}
for (;;) {
char* line = readline(">> ");
if (!line)
break;
add_history(line);
try {
AST_Module* m = parse_string(line);
Timer _t("repl");
if (m->body.size() > 0 && m->body[0]->type == AST_TYPE::Expr) {
AST_Expr* e = ast_cast<AST_Expr>(m->body[0]);
AST_Call* c = new AST_Call();
AST_Name* r = new AST_Name(m->interned_strings->get("repr"), AST_TYPE::Load, 0);
c->func = r;
c->starargs = NULL;
c->kwargs = NULL;
c->args.push_back(e->value);
c->lineno = 0;
AST_Print* p = new AST_Print();
p->dest = NULL;
p->nl = true;
p->values.push_back(c);
p->lineno = 0;
m->body[0] = p;
}
compileAndRunModule(m, main_module);
} catch (ExcInfo e) {
int retcode = 0xdeadbeef; // should never be seen
if (handle_toplevel_exn(e, &retcode)) {
Stats::dump(false);
return retcode;
}
}
}
}
_t.split("joinRuntime");
threading::finishMainThread();
int rtncode = joinRuntime();
_t.split("finishing up");
// Acquire the GIL to make sure we stop the other threads, since we will tear down
// data structures they are potentially running on.
// Note: we will purposefully not release the GIL on exiting.
threading::promoteGL();
_t.split("joinRuntime");
rtncode = joinRuntime();
_t.split("finishing up");
uint64_t main_time_ended_at;
uint64_t main_time_duration = main_time.end(&main_time_ended_at);
static StatCounter mt("ticks_in_main");
mt.log(main_time_duration);
STAT_TIMER_NAME(t0).pause(main_time_ended_at);
}
Stats::dump(true);
Stats::dump();
return rtncode;
}
......
......@@ -13,6 +13,7 @@
// limitations under the License.
#include <algorithm>
#include <cfloat>
#include <cstddef>
#include <err.h>
......@@ -303,6 +304,7 @@ extern "C" Box* ord(Box* obj) {
}
Box* range(Box* start, Box* stop, Box* step) {
STAT_TIMER(t0, "us_timer_builtin_range");
i64 istart, istop, istep;
if (stop == NULL) {
istart = 0;
......@@ -346,6 +348,7 @@ Box* notimplementedRepr(Box* self) {
}
Box* sorted(Box* obj, Box* cmp, Box* key, Box** args) {
STAT_TIMER(t0, "us_timer_builtin_sorted");
Box* reverse = args[0];
BoxedList* rtn = new BoxedList();
......@@ -358,6 +361,7 @@ Box* sorted(Box* obj, Box* cmp, Box* key, Box** args) {
}
Box* isinstance_func(Box* obj, Box* cls) {
STAT_TIMER(t0, "us_timer_builtin_isinstance");
int rtn = PyObject_IsInstance(obj, cls);
if (rtn < 0)
checkAndThrowCAPIException();
......@@ -365,16 +369,29 @@ Box* isinstance_func(Box* obj, Box* cls) {
}
Box* issubclass_func(Box* child, Box* parent) {
STAT_TIMER(t0, "us_timer_builtin_issubclass");
int rtn = PyObject_IsSubclass(child, parent);
if (rtn < 0)
checkAndThrowCAPIException();
return boxBool(rtn);
}
Box* intern_func(Box* str) {
if (!PyString_CheckExact(str)) // have to use exact check!
raiseExcHelper(TypeError, "can't intern subclass of string");
PyString_InternInPlace(&str);
checkAndThrowCAPIException();
return str;
}
Box* bltinImport(Box* name, Box* globals, Box* locals, Box** args) {
Box* fromlist = args[0];
Box* level = args[1];
// __import__ takes a 'locals' argument, but it doesn't get used in CPython.
// Well, it gets passed to PyImport_ImportModuleLevel() and then import_module_level(),
// which ignores it. So we don't even pass it through.
name = coerceUnicodeToStr(name);
if (name->cls != str_cls) {
......@@ -586,7 +603,6 @@ public:
static Box* __reduce__(Box* self) {
RELEASE_ASSERT(isSubclass(self->cls, BaseException), "");
BoxedException* exc = static_cast<BoxedException*>(self);
return BoxedTuple::create({ self->cls, EmptyTuple, self->getAttrWrapper() });
}
};
......@@ -973,16 +989,38 @@ Box* builtinRound(Box* _number, Box* _ndigits) {
raiseExcHelper(TypeError, "a float is required");
BoxedFloat* number = (BoxedFloat*)_number;
double x = number->d;
if (isSubclass(_ndigits->cls, int_cls)) {
BoxedInt* ndigits = (BoxedInt*)_ndigits;
/* interpret 2nd argument as a Py_ssize_t; clip on overflow */
Py_ssize_t ndigits = PyNumber_AsSsize_t(_ndigits, NULL);
if (ndigits == -1 && PyErr_Occurred())
throwCAPIException();
if (ndigits->n == 0)
return boxFloat(round(number->d));
/* nans, infinities and zeros round to themselves */
if (!std::isfinite(x) || x == 0.0)
return boxFloat(x);
/* Deal with extreme values for ndigits. For ndigits > NDIGITS_MAX, x
always rounds to itself. For ndigits < NDIGITS_MIN, x always
rounds to +-0.0. Here 0.30103 is an upper bound for log10(2). */
#define NDIGITS_MAX ((int)((DBL_MANT_DIG - DBL_MIN_EXP) * 0.30103))
#define NDIGITS_MIN (-(int)((DBL_MAX_EXP + 1) * 0.30103))
if (ndigits > NDIGITS_MAX)
/* return x */
return boxFloat(x);
else if (ndigits < NDIGITS_MIN)
/* return 0.0, but with sign of x */
return boxFloat(0.0 * x);
else {
/* finite x, and ndigits is not unreasonably large */
/* _Py_double_round is defined in floatobject.c */
Box* rtn = _Py_double_round(x, (int)ndigits);
if (!rtn)
throwCAPIException();
return rtn;
}
fatalOrError(PyExc_NotImplementedError, "unimplemented");
throwCAPIException();
#undef NDIGITS_MAX
#undef NDIGITS_MIN
}
Box* builtinCmp(Box* a, Box* b) {
......@@ -1109,6 +1147,9 @@ void setupBuiltins() {
= new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)issubclass_func, BOXED_BOOL, 2), "issubclass");
builtins_module->giveAttr("issubclass", issubclass_obj);
Box* intern_obj = new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)intern_func, UNKNOWN, 1), "intern");
builtins_module->giveAttr("intern", intern_obj);
CLFunction* import_func = boxRTFunction((void*)bltinImport, UNKNOWN, 5, 4, false, false,
ParamNames({ "name", "globals", "locals", "fromlist", "level" }, "", ""));
builtins_module->giveAttr("__import__", new BoxedBuiltinFunctionOrMethod(import_func, "__import__",
......
......@@ -48,6 +48,7 @@ Box* dictRepr(BoxedDict* self) {
}
Box* dictClear(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictClear");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'clear' requires a 'dict' object but received a '%s'", getTypeName(self));
......@@ -56,6 +57,7 @@ Box* dictClear(BoxedDict* self) {
}
Box* dictCopy(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictCopy");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'copy' requires a 'dict' object but received a '%s'", getTypeName(self));
......@@ -65,6 +67,7 @@ Box* dictCopy(BoxedDict* self) {
}
Box* dictItems(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictItems");
BoxedList* rtn = new BoxedList();
rtn->ensure(self->d.size());
......@@ -77,6 +80,7 @@ Box* dictItems(BoxedDict* self) {
}
Box* dictValues(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictValues");
BoxedList* rtn = new BoxedList();
rtn->ensure(self->d.size());
for (const auto& p : self->d) {
......@@ -86,6 +90,7 @@ Box* dictValues(BoxedDict* self) {
}
Box* dictKeys(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictKeys");
RELEASE_ASSERT(isSubclass(self->cls, dict_cls), "");
BoxedList* rtn = new BoxedList();
......@@ -182,6 +187,7 @@ extern "C" int PyDict_Update(PyObject* a, PyObject* b) noexcept {
}
Box* dictGetitem(BoxedDict* self, Box* k) {
STAT_TIMER(t0, "us_timer_dictGetitem");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__getitem__' requires a 'dict' object but received a '%s'",
getTypeName(self));
......@@ -306,6 +312,7 @@ extern "C" PyObject* PyDict_GetItemString(PyObject* dict, const char* key) noexc
}
Box* dictSetitem(BoxedDict* self, Box* k, Box* v) {
STAT_TIMER(t0, "us_timer_dictSetitem");
// printf("Starting setitem\n");
Box*& pos = self->d[k];
// printf("Got the pos\n");
......@@ -320,6 +327,7 @@ Box* dictSetitem(BoxedDict* self, Box* k, Box* v) {
}
Box* dictDelitem(BoxedDict* self, Box* k) {
STAT_TIMER(t0, "us_timer_dictDelitem");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__delitem__' requires a 'dict' object but received a '%s'",
getTypeName(self));
......
......@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cfloat>
#include <cmath>
#include <cstring>
......@@ -833,6 +834,568 @@ const char* floatGetFormatDoc = "float.__getformat__(typestr) -> string\n"
"'unknown', 'IEEE, big-endian' or 'IEEE, little-endian' best describes the\n"
"format of floating point numbers used by the C type named by typestr.";
static PyObject* float_setformat(PyTypeObject* v, PyObject* args) noexcept {
char* typestr;
char* format;
float_format_type f;
float_format_type detected;
float_format_type* p;
if (!PyArg_ParseTuple(args, "ss:__setformat__", &typestr, &format))
return NULL;
if (strcmp(typestr, "double") == 0) {
p = &double_format;
detected = detected_double_format;
} else if (strcmp(typestr, "float") == 0) {
p = &float_format;
detected = detected_float_format;
} else {
PyErr_SetString(PyExc_ValueError, "__setformat__() argument 1 must "
"be 'double' or 'float'");
return NULL;
}
if (strcmp(format, "unknown") == 0) {
f = unknown_format;
} else if (strcmp(format, "IEEE, little-endian") == 0) {
f = ieee_little_endian_format;
} else if (strcmp(format, "IEEE, big-endian") == 0) {
f = ieee_big_endian_format;
} else {
PyErr_SetString(PyExc_ValueError, "__setformat__() argument 2 must be "
"'unknown', 'IEEE, little-endian' or "
"'IEEE, big-endian'");
return NULL;
}
if (f != unknown_format && f != detected) {
PyErr_Format(PyExc_ValueError, "can only set %s format to 'unknown' or the "
"detected platform value",
typestr);
return NULL;
}
*p = f;
Py_RETURN_NONE;
}
/*----------------------------------------------------------------------------
* _PyFloat_{Pack,Unpack}{4,8}. See floatobject.h.
*/
extern "C" int _PyFloat_Pack4(double x, unsigned char* p, int le) noexcept {
if (float_format == unknown_format) {
unsigned char sign;
int e;
double f;
unsigned int fbits;
int incr = 1;
if (le) {
p += 3;
incr = -1;
}
if (x < 0) {
sign = 1;
x = -x;
} else
sign = 0;
f = frexp(x, &e);
/* Normalize f to be in the range [1.0, 2.0) */
if (0.5 <= f && f < 1.0) {
f *= 2.0;
e--;
} else if (f == 0.0)
e = 0;
else {
PyErr_SetString(PyExc_SystemError, "frexp() result out of range");
return -1;
}
if (e >= 128)
goto Overflow;
else if (e < -126) {
/* Gradual underflow */
f = ldexp(f, 126 + e);
e = 0;
} else if (!(e == 0 && f == 0.0)) {
e += 127;
f -= 1.0; /* Get rid of leading 1 */
}
f *= 8388608.0; /* 2**23 */
fbits = (unsigned int)(f + 0.5); /* Round */
assert(fbits <= 8388608);
if (fbits >> 23) {
/* The carry propagated out of a string of 23 1 bits. */
fbits = 0;
++e;
if (e >= 255)
goto Overflow;
}
/* First byte */
*p = (sign << 7) | (e >> 1);
p += incr;
/* Second byte */
*p = (char)(((e & 1) << 7) | (fbits >> 16));
p += incr;
/* Third byte */
*p = (fbits >> 8) & 0xFF;
p += incr;
/* Fourth byte */
*p = fbits & 0xFF;
/* Done */
return 0;
} else {
float y = (float)x;
const char* s = (char*)&y;
int i, incr = 1;
if (Py_IS_INFINITY(y) && !Py_IS_INFINITY(x))
goto Overflow;
if ((float_format == ieee_little_endian_format && !le) || (float_format == ieee_big_endian_format && le)) {
p += 3;
incr = -1;
}
for (i = 0; i < 4; i++) {
*p = *s++;
p += incr;
}
return 0;
}
Overflow:
PyErr_SetString(PyExc_OverflowError, "float too large to pack with f format");
return -1;
}
extern "C" int _PyFloat_Pack8(double x, unsigned char* p, int le) noexcept {
if (double_format == unknown_format) {
unsigned char sign;
int e;
double f;
unsigned int fhi, flo;
int incr = 1;
if (le) {
p += 7;
incr = -1;
}
if (x < 0) {
sign = 1;
x = -x;
} else
sign = 0;
f = frexp(x, &e);
/* Normalize f to be in the range [1.0, 2.0) */
if (0.5 <= f && f < 1.0) {
f *= 2.0;
e--;
} else if (f == 0.0)
e = 0;
else {
PyErr_SetString(PyExc_SystemError, "frexp() result out of range");
return -1;
}
if (e >= 1024)
goto Overflow;
else if (e < -1022) {
/* Gradual underflow */
f = ldexp(f, 1022 + e);
e = 0;
} else if (!(e == 0 && f == 0.0)) {
e += 1023;
f -= 1.0; /* Get rid of leading 1 */
}
/* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */
f *= 268435456.0; /* 2**28 */
fhi = (unsigned int)f; /* Truncate */
assert(fhi < 268435456);
f -= (double)fhi;
f *= 16777216.0; /* 2**24 */
flo = (unsigned int)(f + 0.5); /* Round */
assert(flo <= 16777216);
if (flo >> 24) {
/* The carry propagated out of a string of 24 1 bits. */
flo = 0;
++fhi;
if (fhi >> 28) {
/* And it also progagated out of the next 28 bits. */
fhi = 0;
++e;
if (e >= 2047)
goto Overflow;
}
}
/* First byte */
*p = (sign << 7) | (e >> 4);
p += incr;
/* Second byte */
*p = (unsigned char)(((e & 0xF) << 4) | (fhi >> 24));
p += incr;
/* Third byte */
*p = (fhi >> 16) & 0xFF;
p += incr;
/* Fourth byte */
*p = (fhi >> 8) & 0xFF;
p += incr;
/* Fifth byte */
*p = fhi & 0xFF;
p += incr;
/* Sixth byte */
*p = (flo >> 16) & 0xFF;
p += incr;
/* Seventh byte */
*p = (flo >> 8) & 0xFF;
p += incr;
/* Eighth byte */
*p = flo & 0xFF;
/* p += incr; Unneeded (for now) */
/* Done */
return 0;
Overflow:
PyErr_SetString(PyExc_OverflowError, "float too large to pack with d format");
return -1;
} else {
const char* s = (char*)&x;
int i, incr = 1;
if ((double_format == ieee_little_endian_format && !le) || (double_format == ieee_big_endian_format && le)) {
p += 7;
incr = -1;
}
for (i = 0; i < 8; i++) {
*p = *s++;
p += incr;
}
return 0;
}
}
extern "C" double _PyFloat_Unpack4(const unsigned char* p, int le) noexcept {
if (float_format == unknown_format) {
unsigned char sign;
int e;
unsigned int f;
double x;
int incr = 1;
if (le) {
p += 3;
incr = -1;
}
/* First byte */
sign = (*p >> 7) & 1;
e = (*p & 0x7F) << 1;
p += incr;
/* Second byte */
e |= (*p >> 7) & 1;
f = (*p & 0x7F) << 16;
p += incr;
if (e == 255) {
PyErr_SetString(PyExc_ValueError, "can't unpack IEEE 754 special value "
"on non-IEEE platform");
return -1;
}
/* Third byte */
f |= *p << 8;
p += incr;
/* Fourth byte */
f |= *p;
x = (double)f / 8388608.0;
/* XXX This sadly ignores Inf/NaN issues */
if (e == 0)
e = -126;
else {
x += 1.0;
e -= 127;
}
x = ldexp(x, e);
if (sign)
x = -x;
return x;
} else {
float x;
if ((float_format == ieee_little_endian_format && !le) || (float_format == ieee_big_endian_format && le)) {
char buf[4];
char* d = &buf[3];
int i;
for (i = 0; i < 4; i++) {
*d-- = *p++;
}
memcpy(&x, buf, 4);
} else {
memcpy(&x, p, 4);
}
return x;
}
}
extern "C" double _PyFloat_Unpack8(const unsigned char* p, int le) noexcept {
if (double_format == unknown_format) {
unsigned char sign;
int e;
unsigned int fhi, flo;
double x;
int incr = 1;
if (le) {
p += 7;
incr = -1;
}
/* First byte */
sign = (*p >> 7) & 1;
e = (*p & 0x7F) << 4;
p += incr;
/* Second byte */
e |= (*p >> 4) & 0xF;
fhi = (*p & 0xF) << 24;
p += incr;
if (e == 2047) {
PyErr_SetString(PyExc_ValueError, "can't unpack IEEE 754 special value "
"on non-IEEE platform");
return -1.0;
}
/* Third byte */
fhi |= *p << 16;
p += incr;
/* Fourth byte */
fhi |= *p << 8;
p += incr;
/* Fifth byte */
fhi |= *p;
p += incr;
/* Sixth byte */
flo = *p << 16;
p += incr;
/* Seventh byte */
flo |= *p << 8;
p += incr;
/* Eighth byte */
flo |= *p;
x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */
x /= 268435456.0; /* 2**28 */
if (e == 0)
e = -1022;
else {
x += 1.0;
e -= 1023;
}
x = ldexp(x, e);
if (sign)
x = -x;
return x;
} else {
double x;
if ((double_format == ieee_little_endian_format && !le) || (double_format == ieee_big_endian_format && le)) {
char buf[8];
char* d = &buf[7];
int i;
for (i = 0; i < 8; i++) {
*d-- = *p++;
}
memcpy(&x, buf, 8);
} else {
memcpy(&x, p, 8);
}
return x;
}
}
#if DBL_MANT_DIG == 53
#define FIVE_POW_LIMIT 22
#else
#error "C doubles do not appear to be IEEE 754 binary64 format"
#endif
extern "C" PyObject* _Py_double_round(double x, int ndigits) noexcept {
double rounded, m;
Py_ssize_t buflen, mybuflen = 100;
char* buf, *buf_end, shortbuf[100], * mybuf = shortbuf;
int decpt, sign, val, halfway_case;
PyObject* result = NULL;
_Py_SET_53BIT_PRECISION_HEADER;
/* Easy path for the common case ndigits == 0. */
if (ndigits == 0) {
rounded = round(x);
if (fabs(rounded - x) == 0.5)
/* halfway between two integers; use round-away-from-zero */
rounded = x + (x > 0.0 ? 0.5 : -0.5);
return PyFloat_FromDouble(rounded);
}
/* The basic idea is very simple: convert and round the double to a
decimal string using _Py_dg_dtoa, then convert that decimal string
back to a double with _Py_dg_strtod. There's one minor difficulty:
Python 2.x expects round to do round-half-away-from-zero, while
_Py_dg_dtoa does round-half-to-even. So we need some way to detect
and correct the halfway cases.
Detection: a halfway value has the form k * 0.5 * 10**-ndigits for
some odd integer k. Or in other words, a rational number x is
exactly halfway between two multiples of 10**-ndigits if its
2-valuation is exactly -ndigits-1 and its 5-valuation is at least
-ndigits. For ndigits >= 0 the latter condition is automatically
satisfied for a binary float x, since any such float has
nonnegative 5-valuation. For 0 > ndigits >= -22, x needs to be an
integral multiple of 5**-ndigits; we can check this using fmod.
For -22 > ndigits, there are no halfway cases: 5**23 takes 54 bits
to represent exactly, so any odd multiple of 0.5 * 10**n for n >=
23 takes at least 54 bits of precision to represent exactly.
Correction: a simple strategy for dealing with halfway cases is to
(for the halfway cases only) call _Py_dg_dtoa with an argument of
ndigits+1 instead of ndigits (thus doing an exact conversion to
decimal), round the resulting string manually, and then convert
back using _Py_dg_strtod.
*/
/* nans, infinities and zeros should have already been dealt
with by the caller (in this case, builtin_round) */
assert(std::isfinite(x) && x != 0.0);
/* find 2-valuation val of x */
m = frexp(x, &val);
while (m != floor(m)) {
m *= 2.0;
val--;
}
/* determine whether this is a halfway case */
if (val == -ndigits - 1) {
if (ndigits >= 0)
halfway_case = 1;
else if (ndigits >= -FIVE_POW_LIMIT) {
double five_pow = 1.0;
int i;
for (i = 0; i < -ndigits; i++)
five_pow *= 5.0;
halfway_case = fmod(x, five_pow) == 0.0;
} else
halfway_case = 0;
} else
halfway_case = 0;
/* round to a decimal string; use an extra place for halfway case */
_Py_SET_53BIT_PRECISION_START;
buf = _Py_dg_dtoa(x, 3, ndigits + halfway_case, &decpt, &sign, &buf_end);
_Py_SET_53BIT_PRECISION_END;
if (buf == NULL) {
PyErr_NoMemory();
return NULL;
}
buflen = buf_end - buf;
/* in halfway case, do the round-half-away-from-zero manually */
if (halfway_case) {
int i, carry;
/* sanity check: _Py_dg_dtoa should not have stripped
any zeros from the result: there should be exactly
ndigits+1 places following the decimal point, and
the last digit in the buffer should be a '5'.*/
assert(buflen - decpt == ndigits + 1);
assert(buf[buflen - 1] == '5');
/* increment and shift right at the same time. */
decpt += 1;
carry = 1;
for (i = buflen - 1; i-- > 0;) {
carry += buf[i] - '0';
buf[i + 1] = carry % 10 + '0';
carry /= 10;
}
buf[0] = carry + '0';
}
/* Get new buffer if shortbuf is too small. Space needed <= buf_end -
buf + 8: (1 extra for '0', 1 for sign, 5 for exp, 1 for '\0'). */
if (buflen + 8 > mybuflen) {
mybuflen = buflen + 8;
mybuf = (char*)PyMem_Malloc(mybuflen);
if (mybuf == NULL) {
PyErr_NoMemory();
goto exit;
}
}
/* copy buf to mybuf, adding exponent, sign and leading 0 */
PyOS_snprintf(mybuf, mybuflen, "%s0%se%d", (sign ? "-" : ""), buf, decpt - (int)buflen);
/* and convert the resulting string back to a double */
errno = 0;
_Py_SET_53BIT_PRECISION_START;
rounded = _Py_dg_strtod(mybuf, NULL);
_Py_SET_53BIT_PRECISION_END;
if (errno == ERANGE && fabs(rounded) >= 1.)
PyErr_SetString(PyExc_OverflowError, "rounded value too large to represent");
else
result = PyFloat_FromDouble(rounded);
/* done computing value; now clean up */
if (mybuf != shortbuf)
PyMem_Free(mybuf);
exit:
_Py_dg_freedtoa(buf);
return result;
}
void setupFloat() {
_addFunc("__add__", BOXED_FLOAT, (void*)floatAddFloat, (void*)floatAddInt, (void*)floatAdd);
float_cls->giveAttr("__radd__", float_cls->getattr("__add__"));
......
......@@ -86,28 +86,36 @@ Context* getReturnContextForGeneratorFrame(void* frame_addr) {
}
void generatorEntry(BoxedGenerator* g) {
assert(g->cls == generator_cls);
assert(g->function->cls == function_cls);
{
STAT_TIMER2(t0, "us_timer_generator_toplevel", g->timer_time);
threading::pushGenerator(g, g->stack_begin, g->returnContext);
assert(g->cls == generator_cls);
assert(g->function->cls == function_cls);
try {
RegisterHelper context_registerer(g, __builtin_frame_address(0));
threading::pushGenerator(g, g->stack_begin, g->returnContext);
try {
RegisterHelper context_registerer(g, __builtin_frame_address(0));
// call body of the generator
BoxedFunctionBase* func = g->function;
// call body of the generator
BoxedFunctionBase* func = g->function;
Box** args = g->args ? &g->args->elts[0] : nullptr;
callCLFunc(func->f, nullptr, func->f->numReceivedArgs(), func->closure, g, func->globals, g->arg1, g->arg2,
g->arg3, args);
} catch (ExcInfo e) {
// unhandled exception: propagate the exception to the caller
g->exception = e;
}
Box** args = g->args ? &g->args->elts[0] : nullptr;
callCLFunc(func->f, nullptr, func->f->numReceivedArgs(), func->closure, g, func->globals, g->arg1, g->arg2,
g->arg3, args);
} catch (ExcInfo e) {
// unhandled exception: propagate the exception to the caller
g->exception = e;
}
// we returned from the body of the generator. next/send/throw will notify the caller
g->entryExited = true;
threading::popGenerator();
// we returned from the body of the generator. next/send/throw will notify the caller
g->entryExited = true;
threading::popGenerator();
#if STAT_TIMERS
g->timer_time = getCPUTicks(); // store off the timer that our caller (in g->returnContext) will resume at
STAT_TIMER_NAME(t0).pause(g->timer_time);
#endif
}
swapContext(&g->context, g->returnContext, 0);
}
......@@ -130,7 +138,21 @@ Box* generatorSend(Box* s, Box* v) {
self->returnValue = v;
self->running = true;
#if STAT_TIMERS
// store off the time that the generator will use to initialize its toplevel timer
self->timer_time = getCPUTicks();
StatTimer* current_timers = StatTimer::swapStack(self->statTimers, self->timer_time);
#endif
swapContext(&self->returnContext, self->context, (intptr_t)self);
#if STAT_TIMERS
// if the generator exited we use the time that generatorEntry stored in self->timer_time (the same time it paused
// its timer at).
self->statTimers = StatTimer::swapStack(current_timers, self->entryExited ? self->timer_time : getCPUTicks());
#endif
self->running = false;
// propagate exception to the caller
......
......@@ -21,6 +21,7 @@
#include "codegen/irgen/hooks.h"
#include "codegen/parser.h"
#include "codegen/unwinding.h"
#include "runtime/capi.h"
#include "runtime/objmodel.h"
......@@ -267,7 +268,7 @@ static Box* getParent(Box* globals, int level, std::string& buf) {
if (globals == NULL || globals == None || level == 0)
return None;
BoxedString* pkgname = static_cast<BoxedString*>(getattrInternal(globals, package_str, NULL));
BoxedString* pkgname = static_cast<BoxedString*>(getFromGlobals(globals, package_str));
if (pkgname != NULL && pkgname != None) {
/* __package__ is set, so use it */
if (pkgname->cls != str_cls) {
......@@ -286,17 +287,11 @@ static Box* getParent(Box* globals, int level, std::string& buf) {
buf += pkgname->s;
} else {
/* __package__ not set, so figure it out and set it */
BoxedString* modname = static_cast<BoxedString*>(getattrInternal(globals, name_str, NULL));
BoxedString* modname = static_cast<BoxedString*>(getFromGlobals(globals, name_str));
if (modname == NULL || modname->cls != str_cls)
return None;
Box* modpath = NULL;
try {
modpath = getattrInternal(globals, path_str, NULL);
} catch (ExcInfo e) {
if (!e.matches(AttributeError))
raiseRaw(e);
}
Box* modpath = getFromGlobals(globals, path_str);
if (modpath != NULL) {
/* __path__ is set, so modname is already the package name */
......@@ -304,7 +299,7 @@ static Box* getParent(Box* globals, int level, std::string& buf) {
raiseExcHelper(ValueError, "Module name too long");
}
buf += modname->s;
globals->setattr(package_str, modname, NULL);
setGlobal(globals, package_str, modname);
} else {
/* Normal module, so work out the package name if any */
size_t lastdot = modname->s.rfind('.');
......@@ -312,7 +307,7 @@ static Box* getParent(Box* globals, int level, std::string& buf) {
raiseExcHelper(ValueError, "Attempted relative import in non-package");
}
if (lastdot == std::string::npos) {
globals->setattr(package_str, None, NULL);
setGlobal(globals, package_str, None);
return None;
}
if (lastdot >= PATH_MAX) {
......@@ -320,7 +315,7 @@ static Box* getParent(Box* globals, int level, std::string& buf) {
}
buf = std::string(modname->s, 0, lastdot);
globals->setattr(package_str, boxStringPtr(&buf), NULL);
setGlobal(globals, package_str, boxStringPtr(&buf));
}
}
......@@ -471,7 +466,6 @@ static bool loadNext(Box* mod, Box* altmod, std::string& name, std::string& buf,
static void ensureFromlist(Box* module, Box* fromlist, std::string& buf, bool recursive);
Box* importModuleLevel(const std::string& name, Box* globals, Box* from_imports, int level) {
RELEASE_ASSERT(!globals || globals == None || isSubclass(globals->cls, module_cls), "");
bool return_first = from_imports == None;
static StatCounter slowpath_import("slowpath_import");
......@@ -665,7 +659,7 @@ Box* nullImporterFindModule(Box* self, Box* fullname, Box* path) {
extern "C" Box* import(int level, Box* from_imports, const std::string* module_name) {
std::string _module_name(*module_name);
return importModuleLevel(_module_name, getCurrentModule(), from_imports, level);
return importModuleLevel(_module_name, getGlobals(), from_imports, level);
}
Box* impFindModule(Box* _name, BoxedList* path) {
......
......@@ -200,7 +200,7 @@ extern "C" PyObject* PyInt_FromString(const char* s, char** pend, int base) noex
if (x < 0)
return PyLong_FromString(s, pend, base);
} else
x = strtoul(s, &end, base);
x = strtol(s, &end, base);
if (end == s || !isalnum(Py_CHARMASK(end[-1])))
goto bad;
while (*end && isspace(Py_CHARMASK(*end)))
......@@ -672,8 +672,13 @@ extern "C" Box* intLShiftInt(BoxedInt* lhs, BoxedInt* rhs) {
if (rhs->n < 0)
raiseExcHelper(ValueError, "negative shift count");
// TODO overflow?
return boxInt(lhs->n << rhs->n);
bool undefined = rhs->n >= sizeof(rhs->n) * 8;
if (!undefined) {
int64_t res = lhs->n << rhs->n;
if ((res >> rhs->n) == lhs->n)
return boxInt(lhs->n << rhs->n);
}
return longLshift(boxLong(lhs->n), rhs);
}
extern "C" Box* intLShift(BoxedInt* lhs, Box* rhs) {
......
......@@ -109,7 +109,7 @@ Box* seqiterNext(Box* s) {
}
static void seqiterGCVisit(GCVisitor* v, Box* b) {
assert(b->cls == seqiter_cls);
assert(b->cls == seqiter_cls || b->cls == seqreviter_cls);
boxGCHandler(v, b);
BoxedSeqIter* si = static_cast<BoxedSeqIter*>(b);
......@@ -182,7 +182,8 @@ void setupIter() {
seqiter_cls->freeze();
seqiter_cls->tpp_hasnext = seqiterHasnextUnboxed;
seqreviter_cls = BoxedHeapClass::create(type_cls, object_cls, NULL, 0, 0, sizeof(BoxedSeqIter), false, "reversed");
seqreviter_cls
= BoxedHeapClass::create(type_cls, object_cls, seqiterGCVisit, 0, 0, sizeof(BoxedSeqIter), false, "reversed");
seqreviter_cls->giveAttr("next", new BoxedFunction(boxRTFunction((void*)seqiterNext, UNKNOWN, 1)));
seqreviter_cls->giveAttr("__hasnext__", new BoxedFunction(boxRTFunction((void*)seqreviterHasnext, BOXED_BOOL, 1)));
......
......@@ -25,6 +25,7 @@
#include "core/stats.h"
#include "core/types.h"
#include "gc/collector.h"
#include "gc/roots.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
#include "runtime/util.h"
......@@ -423,23 +424,21 @@ extern "C" Box* listSetitemSlice(BoxedList* self, BoxedSlice* slice, Box* v) {
size_t v_size;
Box** v_elts;
RootedBox v_as_seq((Box*)nullptr);
if (!v) {
v_size = 0;
v_elts = NULL;
} else if (v->cls == list_cls || isSubclass(v->cls, list_cls)) {
BoxedList* lv = static_cast<BoxedList*>(v);
v_size = lv->size;
} else {
v_as_seq = RootedBox(PySequence_Fast(v, "can only assign an iterable"));
if (v_as_seq == NULL)
throwCAPIException();
v_size = PySequence_Fast_GET_SIZE(v_as_seq);
// If lv->size is 0, lv->elts->elts is garbage
if (v_size)
v_elts = lv->elts->elts;
v_elts = PySequence_Fast_ITEMS(v_as_seq);
else
v_elts = NULL;
} else if (v->cls == tuple_cls || isSubclass(v->cls, tuple_cls)) {
BoxedTuple* tv = static_cast<BoxedTuple*>(v);
v_size = tv->size();
v_elts = &tv->elts[0];
} else {
RELEASE_ASSERT(0, "unsupported type for list slice assignment: '%s'", getTypeName(v));
}
// If self->size is 0, self->elts->elts is garbage
......@@ -542,6 +541,8 @@ extern "C" Box* listInsert(BoxedList* self, Box* idx, Box* v) {
}
Box* listMul(BoxedList* self, Box* rhs) {
STAT_TIMER(t0, "us_timer_listMul");
if (rhs->cls != int_cls) {
raiseExcHelper(TypeError, "can't multiply sequence by non-int of type '%s'", getTypeName(rhs));
}
......@@ -961,7 +962,7 @@ Box* listNe(BoxedList* self, Box* rhs) {
}
Box* listLt(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......@@ -971,7 +972,7 @@ Box* listLt(BoxedList* self, Box* rhs) {
}
Box* listLe(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......@@ -981,7 +982,7 @@ Box* listLe(BoxedList* self, Box* rhs) {
}
Box* listGt(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......@@ -991,7 +992,7 @@ Box* listGt(BoxedList* self, Box* rhs) {
}
Box* listGe(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......
......@@ -116,7 +116,48 @@ extern "C" unsigned PY_LONG_LONG PyLong_AsUnsignedLongLongMask(PyObject* vv) noe
}
extern "C" PY_LONG_LONG PyLong_AsLongLong(PyObject* vv) noexcept {
Py_FatalError("unimplemented");
PY_LONG_LONG bytes;
int one = 1;
int res;
if (vv == NULL) {
PyErr_BadInternalCall();
return -1;
}
if (!PyLong_Check(vv)) {
PyNumberMethods* nb;
PyObject* io;
if (PyInt_Check(vv))
return (PY_LONG_LONG)PyInt_AsLong(vv);
if ((nb = vv->cls->tp_as_number) == NULL || nb->nb_int == NULL) {
PyErr_SetString(PyExc_TypeError, "an integer is required");
return -1;
}
io = (*nb->nb_int)(vv);
if (io == NULL)
return -1;
if (PyInt_Check(io)) {
bytes = PyInt_AsLong(io);
Py_DECREF(io);
return bytes;
}
if (PyLong_Check(io)) {
bytes = PyLong_AsLongLong(io);
Py_DECREF(io);
return bytes;
}
Py_DECREF(io);
PyErr_SetString(PyExc_TypeError, "integer conversion failed");
return -1;
}
res = _PyLong_AsByteArray((PyLongObject*)vv, (unsigned char*)&bytes, SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 1);
/* Plan 9 can't handle PY_LONG_LONG in ? : expressions */
if (res < 0)
return (PY_LONG_LONG)-1;
else
return bytes;
}
extern "C" PY_LONG_LONG PyLong_AsLongLongAndOverflow(PyObject* obj, int* overflow) noexcept {
......@@ -442,7 +483,12 @@ extern "C" void* PyLong_AsVoidPtr(PyObject* vv) noexcept {
extern "C" int _PyLong_AsByteArray(PyLongObject* v, unsigned char* bytes, size_t n, int little_endian,
int is_signed) noexcept {
Py_FatalError("unimplemented");
RELEASE_ASSERT(little_endian == 1, "not implemented");
RELEASE_ASSERT(n == 8, "did not yet check if the behaviour is correct for sizes other than 8");
size_t count = 0;
mpz_export(bytes, &count, -1, n, 0, 0, ((BoxedLong*)v)->n);
RELEASE_ASSERT(count <= n, "overflow handling is not yet implemented");
return 0;
}
extern "C" PyObject* _PyLong_FromByteArray(const unsigned char* bytes, size_t n, int little_endian,
......
......@@ -125,6 +125,7 @@ static Box* (*callattrInternal3)(Box*, const std::string*, LookupScope, CallRewr
* (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*))callattrInternal;
size_t PyHasher::operator()(Box* b) const {
STAT_TIMER(t0, "us_timer_PyHasher");
if (b->cls == str_cls) {
StringHash<char> H;
auto s = static_cast<BoxedString*>(b);
......@@ -138,6 +139,8 @@ size_t PyHasher::operator()(Box* b) const {
}
bool PyEq::operator()(Box* lhs, Box* rhs) const {
STAT_TIMER(t0, "us_timer_PyEq");
if (lhs->cls == rhs->cls) {
if (lhs->cls == str_cls) {
return static_cast<BoxedString*>(lhs)->s == static_cast<BoxedString*>(rhs)->s;
......@@ -151,6 +154,8 @@ bool PyEq::operator()(Box* lhs, Box* rhs) const {
}
bool PyLt::operator()(Box* lhs, Box* rhs) const {
STAT_TIMER(t0, "us_timer_PyLt");
// TODO fix this
Box* cmp = compareInternal(lhs, rhs, AST_TYPE::Lt, NULL);
assert(cmp->cls == bool_cls);
......@@ -211,6 +216,7 @@ extern "C" void my_assert(bool b) {
}
extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent) {
STAT_TIMER(t0, "us_timer_isSubclass");
return PyType_IsSubtype(child, parent);
}
......@@ -476,6 +482,7 @@ const char* getNameOfClass(BoxedClass* cls) {
}
HiddenClass* HiddenClass::getOrMakeChild(const std::string& attr) {
STAT_TIMER(t0, "us_timer_hiddenclass_getOrMakeChild");
assert(type == NORMAL);
auto it = children.find(attr);
......@@ -507,6 +514,8 @@ HiddenClass* HiddenClass::getAttrwrapperChild() {
* del attr from current HiddenClass, maintaining the order of the remaining attrs
*/
HiddenClass* HiddenClass::delAttrToMakeHC(const std::string& attr) {
STAT_TIMER(t0, "us_timer_hiddenclass_delAttrToMakeHC");
assert(type == NORMAL);
int idx = getOffset(attr);
assert(idx >= 0);
......@@ -655,8 +664,9 @@ Box* Box::getattr(const std::string& attr, GetattrRewriteArgs* rewrite_args) {
Box* key = boxString(attr);
auto it = d->d.find(key);
if (it == d->d.end())
if (it == d->d.end()) {
return NULL;
}
return it->second;
}
......@@ -1211,6 +1221,8 @@ Box* getattrInternalEx(Box* obj, const std::string& attr, GetattrRewriteArgs* re
if (!cls_only) {
BoxedClass* cls = obj->cls;
if (obj->cls->tp_getattro && obj->cls->tp_getattro != PyObject_GenericGetAttr) {
STAT_TIMER(t0, "us_timer_tp_getattro");
Box* r = obj->cls->tp_getattro(obj, boxString(attr));
if (!r)
throwCAPIException();
......@@ -1218,6 +1230,8 @@ Box* getattrInternalEx(Box* obj, const std::string& attr, GetattrRewriteArgs* re
}
if (obj->cls->tp_getattr) {
STAT_TIMER(t0, "us_timer_tp_getattr");
Box* r = obj->cls->tp_getattr(obj, const_cast<char*>(attr.c_str()));
if (!r)
throwCAPIException();
......@@ -1242,6 +1256,8 @@ inline Box* getclsattrInternal(Box* obj, const std::string& attr, GetattrRewrite
}
extern "C" Box* getclsattr(Box* obj, const char* attr) {
STAT_TIMER(t0, "us_timer_slowpath_getclsattr");
static StatCounter slowpath_getclsattr("slowpath_getclsattr");
slowpath_getclsattr.log();
......@@ -1312,12 +1328,6 @@ Box* processDescriptor(Box* obj, Box* inst, Box* owner) {
}
static Box* (*runtimeCall0)(Box*, ArgPassSpec) = (Box * (*)(Box*, ArgPassSpec))runtimeCall;
static Box* (*runtimeCall1)(Box*, ArgPassSpec, Box*) = (Box * (*)(Box*, ArgPassSpec, Box*))runtimeCall;
static Box* (*runtimeCall2)(Box*, ArgPassSpec, Box*, Box*) = (Box * (*)(Box*, ArgPassSpec, Box*, Box*))runtimeCall;
static Box* (*runtimeCall3)(Box*, ArgPassSpec, Box*, Box*, Box*)
= (Box * (*)(Box*, ArgPassSpec, Box*, Box*, Box*))runtimeCall;
Box* getattrInternalGeneric(Box* obj, const std::string& attr, GetattrRewriteArgs* rewrite_args, bool cls_only,
bool for_call, Box** bind_obj_out, RewriterVar** r_bind_obj_out) {
if (for_call) {
......@@ -1635,6 +1645,8 @@ Box* getattrInternal(Box* obj, const std::string& attr, GetattrRewriteArgs* rewr
}
extern "C" Box* getattr(Box* obj, const char* attr) {
STAT_TIMER(t0, "us_timer_slowpath_getattr");
static StatCounter slowpath_getattr("slowpath_getattr");
slowpath_getattr.log();
......@@ -1845,10 +1857,14 @@ void setattrGeneric(Box* obj, const std::string& attr, Box* val, SetattrRewriteA
}
extern "C" void setattr(Box* obj, const char* attr, Box* attr_val) {
STAT_TIMER(t0, "us_timer_slowpath_setsattr");
static StatCounter slowpath_setattr("slowpath_setattr");
slowpath_setattr.log();
if (obj->cls->tp_setattr) {
STAT_TIMER(t1, "us_timer_tp_setattr");
int rtn = obj->cls->tp_setattr(obj, const_cast<char*>(attr), attr_val);
if (rtn)
throwCAPIException();
......@@ -1923,6 +1939,7 @@ extern "C" void setattr(Box* obj, const char* attr, Box* attr_val) {
setattr = processDescriptor(setattr, obj, obj->cls);
runtimeCallInternal(setattr, NULL, ArgPassSpec(2), boxstr, attr_val, NULL, NULL, NULL);
} else {
STAT_TIMER(t0, "us_timer_tp_setattro");
int r = tp_setattro(obj, boxstr, attr_val);
if (r)
throwCAPIException();
......@@ -1935,6 +1952,8 @@ bool isUserDefined(BoxedClass* cls) {
}
extern "C" bool nonzero(Box* obj) {
STAT_TIMER(t0, "us_timer_slowpath_nonzero");
assert(gc::isValidGCObject(obj));
static StatCounter slowpath_nonzero("slowpath_nonzero");
......@@ -2009,7 +2028,7 @@ extern "C" bool nonzero(Box* obj) {
return true;
}
Box* r = runtimeCall0(func, ArgPassSpec(0));
Box* r = runtimeCallInternal(func, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
// I believe this behavior is handled by the slot wrappers in CPython:
if (r->cls == bool_cls) {
BoxedBool* b = static_cast<BoxedBool*>(r);
......@@ -2025,6 +2044,7 @@ extern "C" bool nonzero(Box* obj) {
}
extern "C" BoxedString* str(Box* obj) {
STAT_TIMER(t0, "us_timer_str");
static StatCounter slowpath_str("slowpath_str");
slowpath_str.log();
......@@ -2046,6 +2066,7 @@ extern "C" BoxedString* str(Box* obj) {
}
extern "C" Box* strOrUnicode(Box* obj) {
STAT_TIMER(t0, "us_timer_strOrUnicode");
// Like str, but returns unicode objects unchanged.
if (obj->cls == unicode_cls) {
return obj;
......@@ -2054,6 +2075,7 @@ extern "C" Box* strOrUnicode(Box* obj) {
}
extern "C" BoxedString* repr(Box* obj) {
STAT_TIMER(t0, "us_timer_repr");
static StatCounter slowpath_repr("slowpath_repr");
slowpath_repr.log();
......@@ -2071,6 +2093,7 @@ extern "C" BoxedString* repr(Box* obj) {
}
extern "C" BoxedString* reprOrNull(Box* obj) {
STAT_TIMER(t0, "us_timer_reprOrNull");
try {
Box* r = repr(obj);
assert(r->cls == str_cls); // this should be checked by repr()
......@@ -2081,6 +2104,7 @@ extern "C" BoxedString* reprOrNull(Box* obj) {
}
extern "C" BoxedString* strOrNull(Box* obj) {
STAT_TIMER(t0, "us_timer_strOrNull");
try {
BoxedString* r = str(obj);
return static_cast<BoxedString*>(r);
......@@ -2090,6 +2114,7 @@ extern "C" BoxedString* strOrNull(Box* obj) {
}
extern "C" bool exceptionMatches(Box* obj, Box* cls) {
STAT_TIMER(t0, "us_timer_exceptionMatches");
int rtn = PyErr_GivenExceptionMatches(obj, cls);
RELEASE_ASSERT(rtn >= 0, "");
return rtn;
......@@ -2114,7 +2139,7 @@ extern "C" BoxedInt* hash(Box* obj) {
raiseExcHelper(TypeError, "unhashable type: '%s'", obj->cls->tp_name);
}
Box* rtn = runtimeCall0(hash, ArgPassSpec(0));
Box* rtn = runtimeCallInternal(hash, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
if (rtn->cls != int_cls) {
raiseExcHelper(TypeError, "an integer is required");
}
......@@ -2167,6 +2192,8 @@ Box* lenCallInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, Arg
}
extern "C" BoxedInt* len(Box* obj) {
STAT_TIMER(t0, "us_timer_slowpath_len");
static StatCounter slowpath_len("slowpath_len");
slowpath_len.log();
......@@ -2174,6 +2201,8 @@ extern "C" BoxedInt* len(Box* obj) {
}
extern "C" i64 unboxedLen(Box* obj) {
STAT_TIMER(t0, "us_timer_slowpath_unboxedLen");
static StatCounter slowpath_unboxedlen("slowpath_unboxedlen");
slowpath_unboxedlen.log();
......@@ -2515,6 +2544,8 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
extern "C" Box* callattr(Box* obj, const std::string* attr, CallattrFlags flags, ArgPassSpec argspec, Box* arg1,
Box* arg2, Box* arg3, Box** args, const std::vector<const std::string*>* keyword_names) {
STAT_TIMER(t0, "us_timer_slowpath_callattr");
ASSERT(gc::isValidGCObject(obj), "%p", obj);
int npassed_args = argspec.totalPassed();
......@@ -2704,6 +2735,9 @@ static StatCounter slowpath_callfunc_slowpath("slowpath_callfunc_slowpath");
Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2,
Box* arg3, Box** args, const std::vector<const std::string*>* keyword_names) {
#if STAT_TIMERS
StatTimer::assertActive();
#endif
/*
* Procedure:
* - First match up positional arguments; any extra go to varargs. error if too many.
......@@ -3050,12 +3084,26 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
return res;
}
static Box* callChosenCF(CompiledFunction* chosen_cf, BoxedClosure* closure, BoxedGenerator* generator, Box* oarg1,
Box* oarg2, Box* oarg3, Box** oargs) {
if (closure && generator)
return chosen_cf->closure_generator_call(closure, generator, oarg1, oarg2, oarg3, oargs);
else if (closure)
return chosen_cf->closure_call(closure, oarg1, oarg2, oarg3, oargs);
else if (generator)
return chosen_cf->generator_call(generator, oarg1, oarg2, oarg3, oargs);
else
return chosen_cf->call(oarg1, oarg2, oarg3, oargs);
}
Box* callCLFunc(CLFunction* f, CallRewriteArgs* rewrite_args, int num_output_args, BoxedClosure* closure,
BoxedGenerator* generator, Box* globals, Box* oarg1, Box* oarg2, Box* oarg3, Box** oargs) {
CompiledFunction* chosen_cf = pickVersion(f, num_output_args, oarg1, oarg2, oarg3, oargs);
assert(chosen_cf->is_interpreted == (chosen_cf->code == NULL));
if (chosen_cf->is_interpreted) {
STAT_TIMER(t0, "us_timer_astInterpretFunction");
return astInterpretFunction(chosen_cf, num_output_args, closure, generator, globals, oarg1, oarg2, oarg3,
oargs);
}
......@@ -3084,18 +3132,21 @@ Box* callCLFunc(CLFunction* f, CallRewriteArgs* rewrite_args, int num_output_arg
}
Box* r;
if (closure && generator)
r = chosen_cf->closure_generator_call(closure, generator, oarg1, oarg2, oarg3, oargs);
else if (closure)
r = chosen_cf->closure_call(closure, oarg1, oarg2, oarg3, oargs);
else if (generator)
r = chosen_cf->generator_call(generator, oarg1, oarg2, oarg3, oargs);
else
r = chosen_cf->call(oarg1, oarg2, oarg3, oargs);
// we duplicate the call to callChosenCf here so we can
// distinguish lexically between calls that target jitted python
// code and calls that target to builtins.
if (f->source) {
STAT_TIMER(t0, "us_timer_chosen_cf_body_jitted");
r = callChosenCF(chosen_cf, closure, generator, oarg1, oarg2, oarg3, oargs);
} else {
STAT_TIMER(t0, "us_timer_chosen_cf_body_builtins");
r = callChosenCF(chosen_cf, closure, generator, oarg1, oarg2, oarg3, oargs);
}
ASSERT(chosen_cf->spec->rtn_type->isFitBy(r->cls), "%s (%p) was supposed to return %s, but gave a %s",
g.func_addr_registry.getFuncNameAtAddress(chosen_cf->code, true, NULL).c_str(), chosen_cf->code,
chosen_cf->spec->rtn_type->debugName().c_str(), r->cls->tp_name);
return r;
}
......@@ -3225,9 +3276,9 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec ar
Box** new_args = (Box**)alloca(sizeof(Box*) * (npassed_args + 1 - 3));
new_args[0] = arg3;
memcpy(new_args + 1, args, (npassed_args - 3) * sizeof(Box*));
Box* rtn = runtimeCall(im->func, ArgPassSpec(argspec.num_args + 1, argspec.num_keywords,
argspec.has_starargs, argspec.has_kwargs),
im->obj, arg1, arg2, new_args, keyword_names);
Box* rtn = runtimeCallInternal(im->func, NULL, ArgPassSpec(argspec.num_args + 1, argspec.num_keywords,
argspec.has_starargs, argspec.has_kwargs),
im->obj, arg1, arg2, new_args, keyword_names);
return rtn;
}
}
......@@ -3237,6 +3288,8 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec ar
extern "C" Box* runtimeCall(Box* obj, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args,
const std::vector<const std::string*>* keyword_names) {
STAT_TIMER(t0, "us_timer_slowpath_runtimecall");
int npassed_args = argspec.totalPassed();
static StatCounter slowpath_runtimecall("slowpath_runtimecall");
......@@ -3394,6 +3447,8 @@ extern "C" Box* binopInternal(Box* lhs, Box* rhs, int op_type, bool inplace, Bin
}
extern "C" Box* binop(Box* lhs, Box* rhs, int op_type) {
STAT_TIMER(t0, "us_timer_slowpath_binop");
static StatCounter slowpath_binop("slowpath_binop");
slowpath_binop.log();
// static StatCounter nopatch_binop("nopatch_binop");
......@@ -3430,11 +3485,13 @@ extern "C" Box* binop(Box* lhs, Box* rhs, int op_type) {
}
extern "C" Box* augbinop(Box* lhs, Box* rhs, int op_type) {
static StatCounter slowpath_binop("slowpath_binop");
slowpath_binop.log();
// static StatCounter nopatch_binop("nopatch_binop");
STAT_TIMER(t0, "us_timer_slowpath_augbinop");
// int id = Stats::getStatId("slowpath_binop_" + *getTypeName(lhs) + op_name + *getTypeName(rhs));
static StatCounter slowpath_augbinop("slowpath_augbinop");
slowpath_augbinop.log();
// static StatCounter nopatch_binop("nopatch_augbinop");
// int id = Stats::getStatId("slowpath_augbinop_" + *getTypeName(lhs) + op_name + *getTypeName(rhs));
// Stats::log(id);
std::unique_ptr<Rewriter> rewriter((Rewriter*)NULL);
......@@ -3597,6 +3654,8 @@ Box* compareInternal(Box* lhs, Box* rhs, int op_type, CompareRewriteArgs* rewrit
}
extern "C" Box* compare(Box* lhs, Box* rhs, int op_type) {
STAT_TIMER(t0, "us_timer_slowpath_compare");
static StatCounter slowpath_compare("slowpath_compare");
slowpath_compare.log();
static StatCounter nopatch_compare("nopatch_compare");
......@@ -3622,6 +3681,8 @@ extern "C" Box* compare(Box* lhs, Box* rhs, int op_type) {
}
extern "C" Box* unaryop(Box* operand, int op_type) {
STAT_TIMER(t0, "us_timer_slowpath_unaryop");
static StatCounter slowpath_unaryop("slowpath_unaryop");
slowpath_unaryop.log();
......@@ -3631,11 +3692,13 @@ extern "C" Box* unaryop(Box* operand, int op_type) {
ASSERT(attr_func, "%s.%s", getTypeName(operand), op_name.c_str());
Box* rtn = runtimeCall0(attr_func, ArgPassSpec(0));
Box* rtn = runtimeCallInternal(attr_func, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
return rtn;
}
extern "C" Box* getitem(Box* value, Box* slice) {
STAT_TIMER(t0, "us_timer_slowpath_getitem");
// This possibly could just be represented as a single callattr; the only tricky part
// are the error messages.
// Ex "(1)[1]" and "(1).__getitem__(1)" give different error messages.
......@@ -3679,6 +3742,8 @@ extern "C" Box* getitem(Box* value, Box* slice) {
// target[slice] = value
extern "C" void setitem(Box* target, Box* slice, Box* value) {
STAT_TIMER(t0, "us_timer_slowpath_setitem");
static StatCounter slowpath_setitem("slowpath_setitem");
slowpath_setitem.log();
......@@ -3711,6 +3776,8 @@ extern "C" void setitem(Box* target, Box* slice, Box* value) {
// del target[start:end:step]
extern "C" void delitem(Box* target, Box* slice) {
STAT_TIMER(t0, "us_timer_slowpath_delitem");
static StatCounter slowpath_delitem("slowpath_delitem");
slowpath_delitem.log();
......@@ -3793,7 +3860,7 @@ extern "C" void delattrGeneric(Box* obj, const std::string& attr, DelattrRewrite
if (delAttr != NULL) {
Box* boxstr = boxString(attr);
Box* rtn = runtimeCall2(delAttr, ArgPassSpec(2), clsAttr, obj);
Box* rtn = runtimeCallInternal(delAttr, NULL, ArgPassSpec(2), clsAttr, obj, NULL, NULL, NULL);
return;
}
}
......@@ -3841,7 +3908,7 @@ extern "C" void delattrInternal(Box* obj, const std::string& attr, DelattrRewrit
Box* delAttr = typeLookup(obj->cls, delattr_str, NULL);
if (delAttr != NULL) {
Box* boxstr = boxString(attr);
Box* rtn = runtimeCall2(delAttr, ArgPassSpec(2), obj, boxstr);
Box* rtn = runtimeCallInternal(delAttr, NULL, ArgPassSpec(2), obj, boxstr, NULL, NULL, NULL);
return;
}
......@@ -3850,6 +3917,8 @@ extern "C" void delattrInternal(Box* obj, const std::string& attr, DelattrRewrit
// del target.attr
extern "C" void delattr(Box* obj, const char* attr) {
STAT_TIMER(t0, "us_timer_slowpath_delattr");
static StatCounter slowpath_delattr("slowpath_delattr");
slowpath_delattr.log();
......@@ -3869,6 +3938,8 @@ extern "C" Box* createBoxedIterWrapper(Box* o) {
}
extern "C" Box* createBoxedIterWrapperIfNeeded(Box* o) {
STAT_TIMER(t0, "us_timer_slowpath_createBoxedIterWrapperIfNeeded");
std::unique_ptr<Rewriter> rewriter(Rewriter::createRewriter(
__builtin_extract_return_addr(__builtin_return_address(0)), 1, "createBoxedIterWrapperIfNeeded"));
......@@ -3901,6 +3972,8 @@ extern "C" Box* createBoxedIterWrapperIfNeeded(Box* o) {
}
extern "C" Box* getPystonIter(Box* o) {
STAT_TIMER(t0, "us_timer_slowpath_getPystonIter");
Box* r = getiter(o);
// assert((typeLookup(r->cls, hasnext_str, NULL) == NULL) == (r->cls->tpp_hasnext == object_cls->tpp_hasnext));
if (r->cls->tpp_hasnext == object_cls->tpp_hasnext)
......@@ -3938,6 +4011,9 @@ static void assertInitNone(Box* obj) {
}
Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
STAT_TIMER(t0, "us_timer_typeNew");
Box* arg3 = _args[0];
if (!isSubclass(_cls->cls, type_cls))
......@@ -4391,6 +4467,8 @@ extern "C" void delGlobal(Box* globals, const std::string* name) {
}
extern "C" Box* getGlobal(Box* globals, const std::string* name) {
STAT_TIMER(t0, "us_timer_slowpath_getglobal");
static StatCounter slowpath_getglobal("slowpath_getglobal");
slowpath_getglobal.log();
static StatCounter nopatch_getglobal("nopatch_getglobal");
......@@ -4484,7 +4562,43 @@ extern "C" Box* getGlobal(Box* globals, const std::string* name) {
raiseExcHelper(NameError, "global name '%s' is not defined", name->c_str());
}
Box* getFromGlobals(Box* globals, llvm::StringRef name) {
if (globals->cls == attrwrapper_cls) {
globals = unwrapAttrWrapper(globals);
RELEASE_ASSERT(globals->cls == module_cls, "%s", globals->cls->tp_name);
}
if (globals->cls == module_cls) {
return globals->getattr(name);
} else if (globals->cls == dict_cls) {
auto d = static_cast<BoxedDict*>(globals)->d;
auto name_str = boxString(name.str());
auto it = d.find(name_str);
if (it != d.end())
return it->second;
return NULL;
} else {
RELEASE_ASSERT(0, "%s", globals->cls->tp_name);
}
}
void setGlobal(Box* globals, llvm::StringRef name, Box* value) {
if (globals->cls == attrwrapper_cls) {
globals = unwrapAttrWrapper(globals);
RELEASE_ASSERT(globals->cls == module_cls, "%s", globals->cls->tp_name);
}
if (globals->cls == module_cls) {
setattr(static_cast<BoxedModule*>(globals), name.data(), value);
} else {
RELEASE_ASSERT(globals->cls == dict_cls, "%s", globals->cls->tp_name);
static_cast<BoxedDict*>(globals)->d[boxString(name)] = value;
}
}
extern "C" Box* importFrom(Box* _m, const std::string* name) {
STAT_TIMER(t0, "us_timer_importFrom");
Box* r = getattrInternal(_m, *name, NULL);
if (r)
return r;
......@@ -4493,6 +4607,8 @@ extern "C" Box* importFrom(Box* _m, const std::string* name) {
}
extern "C" Box* importStar(Box* _from_module, BoxedModule* to_module) {
STAT_TIMER(t0, "us_timer_importStar");
// TODO(kmod): it doesn't seem too bad to update this to take custom globals;
// it looks like mostly a matter of changing the getattr calls to getitem.
RELEASE_ASSERT(getGlobals() == to_module, "importStar doesn't support custom globals yet");
......
......@@ -183,7 +183,13 @@ inline std::tuple<Box*, Box*, Box*, Box**> getTupleFromArgsArray(Box** args, int
// The `globals` argument can be either a BoxedModule or a BoxedDict
// Corresponds to a name lookup with GLOBAL scope. Checks the passed globals object, then the builtins,
// and if not found raises an exception.
extern "C" Box* getGlobal(Box* globals, const std::string* name);
// Checks for the name just in the passed globals object, and returns NULL if it is not found.
// This includes if the globals object defined a custom __getattr__ method that threw an AttributeError.
Box* getFromGlobals(Box* globals, llvm::StringRef name);
void setGlobal(Box* globals, llvm::StringRef name, Box* value);
extern "C" void delGlobal(Box* globals, const std::string* name);
extern "C" void boxedLocalsSet(Box* boxedLocals, const char* attr, Box* val);
......
......@@ -96,6 +96,7 @@ void unwindExc(Box* exc_obj) {
void raiseRaw(const ExcInfo& e) __attribute__((__noreturn__));
void raiseRaw(const ExcInfo& e) {
STAT_TIMER(t0, "us_timer_raiseraw");
// Should set these to None before getting here:
assert(e.type);
assert(e.value);
......@@ -197,6 +198,7 @@ extern "C" void abort() {
__builtin_unreachable();
}
#if 0
extern "C" void exit(int code) {
static void (*libc_exit)(int) = (void (*)(int))dlsym(RTLD_NEXT, "exit");
......@@ -218,6 +220,7 @@ extern "C" void exit(int code) {
libc_exit(code);
__builtin_unreachable();
}
#endif
extern "C" void raise0() {
ExcInfo* exc_info = getFrameExcInfo();
......
......@@ -1112,6 +1112,7 @@ extern "C" Box* strMod(BoxedString* lhs, Box* rhs) {
}
extern "C" Box* strMul(BoxedString* lhs, Box* rhs) {
STAT_TIMER(t0, "us_timer_strMul");
assert(isSubclass(lhs->cls, str_cls));
int n;
......@@ -1514,6 +1515,7 @@ failed:
}
extern "C" size_t unicodeHashUnboxed(PyUnicodeObject* self) {
STAT_TIMER(t0, "us_timer_unicodeHashUnboxed");
if (self->hash != -1)
return self->hash;
......@@ -1524,6 +1526,7 @@ extern "C" size_t unicodeHashUnboxed(PyUnicodeObject* self) {
}
extern "C" Box* strHash(BoxedString* self) {
STAT_TIMER(t0, "us_timer_strHash");
assert(isSubclass(self->cls, str_cls));
StringHash<char> H;
......@@ -2302,8 +2305,17 @@ extern "C" PyObject* PyString_FromStringAndSize(const char* s, ssize_t n) noexce
return boxStrConstantSize(s, n);
}
static /*const*/ char* string_getbuffer(register PyObject* op) noexcept {
char* s;
Py_ssize_t len;
if (PyString_AsStringAndSize(op, &s, &len))
return NULL;
return s;
}
extern "C" char* PyString_AsString(PyObject* o) noexcept {
RELEASE_ASSERT(isSubclass(o->cls, str_cls), "");
if (!PyString_Check(o))
return string_getbuffer(o);
BoxedString* s = static_cast<BoxedString*>(o);
return getWriteableStringContents(s);
......
......@@ -168,6 +168,7 @@ Box* tupleAdd(BoxedTuple* self, Box* rhs) {
}
Box* tupleMul(BoxedTuple* self, Box* rhs) {
STAT_TIMER(t0, "us_timer_tupleMul");
if (rhs->cls != int_cls) {
raiseExcHelper(TypeError, "can't multiply sequence by non-int of type '%s'", getTypeName(rhs));
}
......@@ -337,6 +338,7 @@ Box* tupleIndex(BoxedTuple* self, Box* elt) {
}
Box* tupleHash(BoxedTuple* self) {
STAT_TIMER(t0, "us_timer_tupleHash");
assert(isSubclass(self->cls, tuple_cls));
int64_t rtn = 3527539;
......
......@@ -978,7 +978,39 @@ Box* sliceRepr(BoxedSlice* self) {
extern "C" int PySlice_GetIndices(PySliceObject* r, Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop,
Py_ssize_t* step) noexcept {
Py_FatalError("unimplemented");
/* XXX support long ints */
if (r->step == Py_None) {
*step = 1;
} else {
if (!PyInt_Check(r->step) && !PyLong_Check(r->step))
return -1;
*step = PyInt_AsSsize_t(r->step);
}
if (r->start == Py_None) {
*start = *step < 0 ? length - 1 : 0;
} else {
if (!PyInt_Check(r->start) && !PyLong_Check(r->step))
return -1;
*start = PyInt_AsSsize_t(r->start);
if (*start < 0)
*start += length;
}
if (r->stop == Py_None) {
*stop = *step < 0 ? -1 : length;
} else {
if (!PyInt_Check(r->stop) && !PyLong_Check(r->step))
return -1;
*stop = PyInt_AsSsize_t(r->stop);
if (*stop < 0)
*stop += length;
}
if (*stop > length)
return -1;
if (*start >= length)
return -1;
if (*step == 0)
return -1;
return 0;
}
extern "C" int PySlice_GetIndicesEx(PySliceObject* _r, Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop,
......@@ -1238,6 +1270,8 @@ public:
}
static Box* setitem(Box* _self, Box* _key, Box* value) {
STAT_TIMER(t0, "us_timer_AttrWrapper_setitem");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1250,6 +1284,7 @@ public:
}
static Box* setdefault(Box* _self, Box* _key, Box* value) {
STAT_TIMER(t0, "us_timer_AttrWrapper_setdefault");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1265,6 +1300,7 @@ public:
}
static Box* get(Box* _self, Box* _key, Box* def) {
STAT_TIMER(t0, "us_timer_AttrWrapper_get");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1279,6 +1315,7 @@ public:
}
static Box* getitem(Box* _self, Box* _key) {
STAT_TIMER(t0, "us_timer_AttrWrapper_getitem");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1312,6 +1349,7 @@ public:
}
static Box* delitem(Box* _self, Box* _key) {
STAT_TIMER(t0, "us_timer_AttrWrapper_delitem");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1377,6 +1415,7 @@ public:
}
static Box* values(Box* _self) {
STAT_TIMER(t0, "us_timer_AttrWrapper_values");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1391,6 +1430,7 @@ public:
}
static Box* items(Box* _self) {
STAT_TIMER(t0, "us_timer_AttrWrapper_items");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1429,6 +1469,7 @@ public:
}
static Box* update(Box* _self, Box* _container) {
STAT_TIMER(t0, "us_timer_AttrWrapper_update");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......
......@@ -410,12 +410,37 @@ public:
size_t size() { return s.size(); }
void* operator new(size_t size, size_t ssize) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_str("alloc.str");
static StatCounter alloc_str1("alloc.str(1)");
static StatCounter allocsize_str("allocsize.str");
if (ssize == 1)
alloc_str1.log();
else
alloc_str.log();
allocsize_str.log(str_cls->tp_basicsize + ssize + 1);
#endif
Box* rtn = static_cast<Box*>(gc_alloc(str_cls->tp_basicsize + ssize + 1, gc::GCKind::PYTHON));
rtn->cls = str_cls;
return rtn;
}
void* operator new(size_t size, BoxedClass* cls, size_t ssize) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_str("alloc.str");
static StatCounter alloc_str1("alloc.str(1)");
static StatCounter allocsize_str("allocsize.str");
if (ssize == 1)
alloc_str1.log();
else
alloc_str.log();
allocsize_str.log(cls->tp_basicsize + ssize + 1);
#endif
Box* rtn = static_cast<Box*>(cls->tp_alloc(cls, ssize + 1));
rtn->cls = cls;
return rtn;
......@@ -517,12 +542,43 @@ public:
Box** elts;
void* operator new(size_t size, size_t nelts) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_tuple("alloc.tuple");
static StatCounter alloc_tuple0("alloc.tuple(0)");
static StatCounter allocsize_tuple("allocsize.tuple");
static StatCounter allocsize_tuple0("allocsize.tuple(0)");
if (nelts == 0) {
alloc_tuple0.log();
allocsize_tuple0.log(_PyObject_VAR_SIZE(tuple_cls, nelts + 1));
} else {
alloc_tuple.log();
allocsize_tuple.log(_PyObject_VAR_SIZE(tuple_cls, nelts + 1));
}
#endif
Box* rtn = static_cast<Box*>(gc_alloc(_PyObject_VAR_SIZE(tuple_cls, nelts + 1), gc::GCKind::PYTHON));
rtn->cls = tuple_cls;
return rtn;
}
void* operator new(size_t size, BoxedClass* cls, size_t nelts) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_tuple("alloc.tuple");
static StatCounter alloc_tuple0("alloc.tuple(0)");
static StatCounter allocsize_tuple("allocsize.tuple");
static StatCounter allocsize_tuple0("allocsize.tuple(0)");
if (nelts == 0) {
alloc_tuple0.log();
allocsize_tuple0.log(_PyObject_VAR_SIZE(cls, nelts + 1));
} else {
alloc_tuple.log();
allocsize_tuple.log(_PyObject_VAR_SIZE(cls, nelts + 1));
}
#endif
Box* rtn = static_cast<Box*>(cls->tp_alloc(cls, nelts));
rtn->cls = cls;
return rtn;
......@@ -817,6 +873,11 @@ public:
struct Context* context, *returnContext;
void* stack_begin;
#if STAT_TIMERS
StatTimer* statTimers;
uint64_t timer_time;
#endif
BoxedGenerator(BoxedFunctionBase* function, Box* arg1, Box* arg2, Box* arg3, Box** args);
DEFAULT_CLASS(generator_cls);
......
......@@ -21,9 +21,10 @@ set -e
set -ux
python -c 'import __future__'
python -c 'import sys; print sys.executable'
pip install bcrypt==1.1.0 python-gflags==2.0
pip install bcrypt==1.1.0 python-gflags==2.0 sqlalchemy==1.0.0
python -c 'import bcrypt; assert bcrypt.__version__ == "1.1.0"; assert bcrypt.hashpw("password1", "$2a$12$0123456789012345678901").endswith("I1hdtg4K"); print "bcrypt seems to work"'
python -c 'import gflags; print "gflags imports"'
python -c 'import sqlalchemy; print "sqlalchemy imports"'
""".strip()
# print sh_script
......
......@@ -105,8 +105,8 @@ print callable(lambda: 1)
print range(5L, 7L)
print round(-1.1), round(-1.9)
print round(0.5), round(-0.5)
for n in [0, 1, 2, 3, 4, 5]:
print round(-1.1, n), round(-1.9, n), round(0.5, n), round(-0.5, n), round(-0.123456789, n)
print list(iter(xrange(100).__iter__().next, 20))
......
import test_package.import_target
import import_target
print
s = """
import import_target
print import_target.__file__.replace(".pyc", ".py")
"""
def test(g):
print "Testing with globals:", "None" if g is None else ("globals" if g is globals() else sorted(g.items()))
print __import__("import_target", g).__file__.replace(".pyc", ".py")
exec s in g
print "Resulting globals:", "None" if g is None else ("globals" if g is globals() else sorted(g.keys()))
print
test(None)
test(globals())
test({"__package__":"test_package"})
test({"__name__":"test_package.foo"})
test({"__name__":"test_package"})
test({"__name__":"test_package", "__path__":"foo"})
......@@ -66,6 +66,9 @@ class L(object):
print type(int(L()))
print int(u'123')
print int("9223372036854775808", 0)
print 1 << 63, 1 << 64, -1 << 63, -1 << 64, 2 << 63
print type(1 << 63), type(1 << 64), type(-1 << 63), type(-1 << 64), type(2 << 63)
for b in range(26):
try:
......
......@@ -124,7 +124,13 @@ for i in xrange(3):
l[j:k] = ["added"]
print i, j, k, l
def G():
yield "a"
yield "b"
yield "c"
l = [0, 1, 2, 3, 4, 5]
l[1:] = G()
print l
l = [1, 3, 5, 7, 2, 4]
print l.sort(key=lambda x:x%3)
......
......@@ -13,5 +13,26 @@ print len(MyList.__new__(MyList))
l[:] = l[:]
print l
print [1,2,3] == MyList((1,2,3))
print [1,2,3] != MyList((1,2,3))
print [1,2,3] == MyList((1,2,3,4))
print [1,2,3] != MyList((1,2,3,4))
print [1,2,3,4] > MyList((1,2,3))
print [1,2,3,4] < MyList((1,2,3))
print [1,2,3] > MyList((1,2,3,4))
print [1,2,3] < MyList((1,2,3,4))
print [1,2,3] >= MyList((1,2,3))
print [1,2,3] <= MyList((1,2,3))
print MyList((1,2,3)) == MyList((1,2,3,4))
print MyList((1,2,3)) != MyList((1,2,3,4))
print MyList((1,2,3,4)) > MyList((1,2,3))
print MyList((1,2,3,4)) < MyList((1,2,3))
print MyList((1,2,3)) > MyList((1,2,3,4))
print MyList((1,2,3)) < MyList((1,2,3,4))
print MyList((1,2,3)) >= MyList((1,2,3))
print MyList((1,2,3)) <= MyList((1,2,3))
......@@ -157,6 +157,9 @@ def run_test(fn, check_stats, run_memcheck):
code = p.wait()
elapsed = time.time() - start
if code >= 128:
code -= 256
return determine_test_result(fn, opts, code, out, stderr, elapsed)
def get_test_options(fn, check_stats, run_memcheck):
......
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