Commit 3a811c26 authored by Kevin Modzelewski's avatar Kevin Modzelewski Committed by Kevin Modzelewski

Add documentation about some structs/classes

parent 8e2a0949
...@@ -772,11 +772,11 @@ CompiledFunction::CompiledFunction(llvm::Function* func, FunctionSpecialization* ...@@ -772,11 +772,11 @@ CompiledFunction::CompiledFunction(llvm::Function* func, FunctionSpecialization*
ExceptionStyle exception_style, const OSREntryDescriptor* entry_descriptor) ExceptionStyle exception_style, const OSREntryDescriptor* entry_descriptor)
: clfunc(NULL), : clfunc(NULL),
func(func), func(func),
effort(effort),
exception_style(exception_style),
spec(spec), spec(spec),
entry_descriptor(entry_descriptor), entry_descriptor(entry_descriptor),
code(code), code(code),
effort(effort),
exception_style(exception_style),
times_called(0), times_called(0),
times_speculation_failed(0), times_speculation_failed(0),
location_map(nullptr) { location_map(nullptr) {
......
...@@ -26,8 +26,10 @@ ...@@ -26,8 +26,10 @@
namespace pyston { namespace pyston {
// Set this to 1 to disable all stats-related operations. Shouldn't usually be necessary.
#define DISABLE_STATS 0 #define DISABLE_STATS 0
// Enable certain expensive stat collections:
#define STAT_ALLOCATIONS (0 && !DISABLE_STATS) #define STAT_ALLOCATIONS (0 && !DISABLE_STATS)
#define STAT_ALLOCATION_TYPES (0 && !DISABLE_STATS) #define STAT_ALLOCATION_TYPES (0 && !DISABLE_STATS)
#define STAT_CALLATTR_DESCR_ABORTS (0 && !DISABLE_STATS) #define STAT_CALLATTR_DESCR_ABORTS (0 && !DISABLE_STATS)
...@@ -53,6 +55,8 @@ namespace pyston { ...@@ -53,6 +55,8 @@ namespace pyston {
#define STAT_TIMER_NAME(id) _st##id #define STAT_TIMER_NAME(id) _st##id
#if !DISABLE_STATS #if !DISABLE_STATS
// The class that stores and manages stats collection. For normal stats collections purposes,
// you shouldn't have to use this class, and will usually want to use StatCounter instead.
struct Stats { struct Stats {
private: private:
static std::unordered_map<uint64_t*, std::string>* names; static std::unordered_map<uint64_t*, std::string>* names;
...@@ -75,6 +79,22 @@ public: ...@@ -75,6 +79,22 @@ public:
static void endOfInit(); static void endOfInit();
}; };
// A helper class for efficient stats collections. Typical usage:
//
// static StatCounter my_stat_counter("my_informative_stat_name");
// void myFunction() {
// my_stat_counter.log();
// }
//
// The current convention for stat names is underscore_case, such as `num_cxa_throw`,
// though at some point we'd like to move to a period-delimited convention.
// (Run `./pyston -s` to see the list of current stats we log.)
// For single stats, usually `num_foo` is a decent name. If there are many stats in a
// single category, you can drop the `num_`.
// If a stat name is a prefix of another, the event it is counting should be a superset.
// For instance, `ic_rewrites` counts a superset of the events that `ic_rewrites_aborted`
// counts, which itself is a superset of the events that `ic_rewrites_aborted_assemblyfail`
// counts.
struct StatCounter { struct StatCounter {
private: private:
uint64_t* counter; uint64_t* counter;
...@@ -85,6 +105,11 @@ public: ...@@ -85,6 +105,11 @@ public:
void log(uint64_t count = 1) { *counter += count; } void log(uint64_t count = 1) { *counter += count; }
}; };
// Similar to StatCounter, but should be allocated as:
//
// static thread_local StatPerThreadCounter my_stat_counter("cool_stat_name");
//
// This will automatically add the thread id to the stat name.
struct StatPerThreadCounter { struct StatPerThreadCounter {
private: private:
uint64_t* counter = 0; uint64_t* counter = 0;
...@@ -117,6 +142,9 @@ struct StatPerThreadCounter { ...@@ -117,6 +142,9 @@ struct StatPerThreadCounter {
#endif #endif
#if STAT_TIMERS #if STAT_TIMERS
// StatTimers are for a specific type of profiling investigation. Until we make this more usable,
// there probably shouldn't be more changes or uses of this class.
class StatTimer { class StatTimer {
private: private:
static __thread StatTimer* stack; static __thread StatTimer* stack;
...@@ -226,6 +254,8 @@ public: ...@@ -226,6 +254,8 @@ public:
static void assertActive() { ASSERT(stack && !stack->isPaused(), ""); } static void assertActive() { ASSERT(stack && !stack->isPaused(), ""); }
}; };
// Helper class around a StatTimer
class ScopedStatTimer { class ScopedStatTimer {
private: private:
StatTimer timer; StatTimer timer;
...@@ -237,6 +267,7 @@ public: ...@@ -237,6 +267,7 @@ public:
} }
~ScopedStatTimer() { timer.popNonTopLevel(); } ~ScopedStatTimer() { timer.popNonTopLevel(); }
}; };
#else #else
struct StatTimer { struct StatTimer {
StatTimer(uint64_t*) {} StatTimer(uint64_t*) {}
......
...@@ -138,6 +138,7 @@ void demoteGL(); ...@@ -138,6 +138,7 @@ void demoteGL();
// Helper macro for creating a RAII wrapper around two functions.
#define MAKE_REGION(name, start, end) \ #define MAKE_REGION(name, start, end) \
class name { \ class name { \
public: \ public: \
......
...@@ -41,16 +41,35 @@ namespace pyston { ...@@ -41,16 +41,35 @@ namespace pyston {
using gc::GCVisitor; using gc::GCVisitor;
// The "effort" which we will put into compiling a Python function. This applies to the LLVM tier,
// where it can affect things such as the amount of type analysis we do, whether or not to run expensive
// LLVM optimization passes, etc.
// Currently (Nov '15) these are mostly unused, and we only use MAXIMAL. There used to be two other levels
// as well but we stopped using them too.
enum class EffortLevel { enum class EffortLevel {
MODERATE = 2, MODERATE = 2,
MAXIMAL = 3, MAXIMAL = 3,
}; };
// Pyston supports two ways of implementing Python exceptions: by using return-code-based exceptions ("CAPI"
// style since this is what the CPython C API uses), or our custom C++-based exceptions ("CXX" style). CAPI
// is faster when an exception is thrown, and CXX is faster when an exception is not thrown, so depending on
// the situation it can be beneficial to use one or the other. The JIT will use some light profiling to
// determine when to emit code in one style or the other.
// Many runtime functions support being called in either style, and can get passed one of these enum values
// as a template parameter to switch between them.
enum ExceptionStyle { enum ExceptionStyle {
CAPI, CAPI,
CXX, CXX,
}; };
// Much of our runtime supports "rewriting" aka tracing itself. Our tracing JIT requires support from the
// functions to be traced, so our runtime functions will contain checks to see if the tracer is currently
// activated, and then will do additional work.
// When the tracer isn't active, these extra "is the tracer active" checks can become expensive. So when
// the caller knows that the tracer is not active, they can call a special version of the function where
// all of the "is the tracer active" checks are hardcoded to false. This is possible by passing "NOT_REWRITEABLE"
// as a template argument to the called function.
enum Rewritable { enum Rewritable {
NOT_REWRITABLE, NOT_REWRITABLE,
REWRITABLE, REWRITABLE,
...@@ -88,6 +107,8 @@ public: ...@@ -88,6 +107,8 @@ public:
template <ExceptionStyle S> R call(Args... args) noexcept(S == CAPI) { return this->template get<S>()(args...); } template <ExceptionStyle S> R call(Args... args) noexcept(S == CAPI) { return this->template get<S>()(args...); }
}; };
// CompilerType (and a specific kind of CompilerType, the ConcreteCompilerType) are the way that the LLVM JIT represents
// type information. See src/codegen/compvars.h for more information.
class CompilerType; class CompilerType;
template <class V> class ValuedCompilerType; template <class V> class ValuedCompilerType;
typedef ValuedCompilerType<llvm::Value*> ConcreteCompilerType; typedef ValuedCompilerType<llvm::Value*> ConcreteCompilerType;
...@@ -98,6 +119,8 @@ extern ConcreteCompilerType* UNBOXED_INT, *BOXED_INT, *LONG, *UNBOXED_FLOAT, *BO ...@@ -98,6 +119,8 @@ extern ConcreteCompilerType* UNBOXED_INT, *BOXED_INT, *LONG, *UNBOXED_FLOAT, *BO
*BOXED_COMPLEX, *FRAME_INFO; *BOXED_COMPLEX, *FRAME_INFO;
extern CompilerType* UNDEF, *INT, *FLOAT, *UNBOXED_SLICE; extern CompilerType* UNDEF, *INT, *FLOAT, *UNBOXED_SLICE;
// CompilerVariables are the way that the LLVM JIT tracks variables, which are a CompilerType combined with some sort
// of value (the type of value depends on the type of CompilerType).
class CompilerVariable; class CompilerVariable;
template <class V> class ValuedCompilerVariable; template <class V> class ValuedCompilerVariable;
typedef ValuedCompilerVariable<llvm::Value*> ConcreteCompilerVariable; typedef ValuedCompilerVariable<llvm::Value*> ConcreteCompilerVariable;
...@@ -125,6 +148,10 @@ class ScopingAnalysis; ...@@ -125,6 +148,10 @@ class ScopingAnalysis;
class CLFunction; class CLFunction;
class OSREntryDescriptor; class OSREntryDescriptor;
// Pyston's internal calling convention is to pass around arguments in as unprocessed a form as possible,
// which lets the callee decide how they would like to receive their arguments. In addition to the actual
// argument parameters, functions will often receive an ArgPassSpec struct which specifies the meaning of
// the raw pointer values, such as whether they were positional arguments or keyword arguments, etc.
struct ArgPassSpec { struct ArgPassSpec {
bool has_starargs : 1; bool has_starargs : 1;
bool has_kwargs : 1; bool has_kwargs : 1;
...@@ -196,7 +223,8 @@ private: ...@@ -196,7 +223,8 @@ private:
ParamNames() : takes_param_names(false), vararg_name(NULL), kwarg_name(NULL) {} ParamNames() : takes_param_names(false), vararg_name(NULL), kwarg_name(NULL) {}
}; };
// Probably overkill to copy this from ArgPassSpec // Similar to ArgPassSpec, this struct is how functions specify what their parameter signature is.
// (Probably overkill to copy this from ArgPassSpec)
struct ParamReceiveSpec { struct ParamReceiveSpec {
bool takes_varargs : 1; bool takes_varargs : 1;
bool takes_kwargs : 1; bool takes_kwargs : 1;
...@@ -228,6 +256,14 @@ struct ParamReceiveSpec { ...@@ -228,6 +256,14 @@ struct ParamReceiveSpec {
int kwargsIndex() { return num_args + (takes_varargs ? 1 : 0); } int kwargsIndex() { return num_args + (takes_varargs ? 1 : 0); }
}; };
// Inline-caches contain fastpath code, and need to know that their fastpath is valid for a particular set
// of arguments. This is usually done with guards: conditional checks that will avoid the fastpath if the
// assumptions failed. This can also be done using invalidation: no checks will be emitted into the generated
// assembly, but instead if the assumption is invalidated, the IC will get erased.
// This is useful for cases where we expect the assumption to overwhelmingly be true, or cases where it
// is not possible to use guards. It is more difficult to use invalidation because it is much easier to
// get it wrong by forgetting to invalidate in all places that are necessary (whereas it is easier to be
// conservative about guarding).
class ICInvalidator { class ICInvalidator {
private: private:
int64_t cur_version; int64_t cur_version;
...@@ -259,17 +295,31 @@ class ICInfo; ...@@ -259,17 +295,31 @@ class ICInfo;
class LocationMap; class LocationMap;
class JitCodeBlock; class JitCodeBlock;
// A specific compilation of a CLFunction. Usually these will be created by the LLVM JIT, which will take a CLFunction
// and some compilation settings, and produce a CompiledFunction
// CompiledFunctions can also be created from raw function pointers, using boxRTFunction.
// A single CLFunction can have multiple CompiledFunctions associated with it, if they have different settings.
// Typically, this will happen due to specialization on the argument types (ie we will generate a separate versions
// of a function that are faster but only apply to specific argument types).
struct CompiledFunction { struct CompiledFunction {
private: private:
public: public:
CLFunction* clfunc; CLFunction* clfunc;
llvm::Function* func; // the llvm IR object llvm::Function* func; // the llvm IR object
// Some compilation settings:
EffortLevel effort;
ExceptionStyle exception_style;
FunctionSpecialization* spec; FunctionSpecialization* spec;
// If this compilation was due to an OSR, `entry_descriptor` contains metadata about the OSR.
// Otherwise this field is NULL.
const OSREntryDescriptor* entry_descriptor; const OSREntryDescriptor* entry_descriptor;
// Pointers that were written directly into the code, which the GC should be aware of. // Pointers that were written directly into the code, which the GC should be aware of.
std::vector<const void*> pointers_in_code; std::vector<const void*> pointers_in_code;
// The function pointer to the generated code. For convenience, it can be accessed
// as one of many different types.
union { union {
Box* (*call)(Box*, Box*, Box*, Box**); Box* (*call)(Box*, Box*, Box*, Box**);
Box* (*closure_call)(BoxedClosure*, Box*, Box*, Box*, Box**); Box* (*closure_call)(BoxedClosure*, Box*, Box*, Box*, Box**);
...@@ -283,14 +333,20 @@ public: ...@@ -283,14 +333,20 @@ public:
}; };
int code_size; int code_size;
EffortLevel effort; // Some simple profiling stats:
ExceptionStyle exception_style;
int64_t times_called, times_speculation_failed; int64_t times_called, times_speculation_failed;
// A list of ICs that depend on various properties of this CompiledFunction.
// These will get invalidated in situations such as: we compiled a higher-effort version of
// this function so we want to get old callsites to use the newer and better version, or
// we noticed that we compiled the function with speculations that kept on failing and
// we want to generate a more conservative version.
ICInvalidator dependent_callsites; ICInvalidator dependent_callsites;
// Metadata that lets us find local variables from the C stack fram.
LocationMap* location_map; LocationMap* location_map;
// List of metadata objects for ICs inside this compilation
std::vector<ICInfo*> ics; std::vector<ICInfo*> ics;
CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, void* code, EffortLevel effort, CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, void* code, EffortLevel effort,
...@@ -316,9 +372,12 @@ class BoxedModule; ...@@ -316,9 +372,12 @@ class BoxedModule;
class ScopeInfo; class ScopeInfo;
class InternedStringPool; class InternedStringPool;
class LivenessAnalysis; class LivenessAnalysis;
// Data about a single textual function definition.
class SourceInfo { class SourceInfo {
private: private:
BoxedString* fn; // equivalent of code.co_filename BoxedString* fn; // equivalent of code.co_filename
public: public:
BoxedModule* parent_module; BoxedModule* parent_module;
ScopingAnalysis* scoping; ScopingAnalysis* scoping;
...@@ -354,16 +413,24 @@ private: ...@@ -354,16 +413,24 @@ private:
typedef std::vector<CompiledFunction*> FunctionList; typedef std::vector<CompiledFunction*> FunctionList;
struct CallRewriteArgs; struct CallRewriteArgs;
// A BoxedCode is our implementation of the Python "code" object (such as function.func_code).
// It is implemented as a wrapper around a CLFunction.
class BoxedCode; class BoxedCode;
class CLFunction { class CLFunction {
private:
// The Python-level "code" object corresponding to this CLFunction. We store it in the CLFunction
// so that multiple attempts to translate from CLFunction->BoxedCode will always return the same
// BoxedCode object.
// Callers should use getCode()
BoxedCode* code_obj; BoxedCode* code_obj;
public: public:
int num_args; int num_args;
bool takes_varargs, takes_kwargs; bool takes_varargs, takes_kwargs;
//ParamReceiveSpec paramspec;
std::unique_ptr<SourceInfo> source; std::unique_ptr<SourceInfo> source; // source can be NULL for functions defined in the C/C++ runtime
ParamNames param_names; ParamNames param_names;
FunctionList FunctionList
...@@ -371,6 +438,7 @@ public: ...@@ -371,6 +438,7 @@ public:
CompiledFunction* always_use_version; // if this version is set, always use it (for unboxed cases) CompiledFunction* always_use_version; // if this version is set, always use it (for unboxed cases)
std::unordered_map<const OSREntryDescriptor*, CompiledFunction*> osr_versions; std::unordered_map<const OSREntryDescriptor*, CompiledFunction*> osr_versions;
// Profiling counter:
int propagated_cxx_exceptions = 0; int propagated_cxx_exceptions = 0;
// For use by the interpreter/baseline jit: // For use by the interpreter/baseline jit:
...@@ -426,6 +494,9 @@ typedef int64_t i64; ...@@ -426,6 +494,9 @@ typedef int64_t i64;
const char* getNameOfClass(BoxedClass* cls); const char* getNameOfClass(BoxedClass* cls);
std::string getFullNameOfClass(BoxedClass* cls); std::string getFullNameOfClass(BoxedClass* cls);
std::string getFullTypeName(Box* o);
const char* getTypeName(Box* b);
class Rewriter; class Rewriter;
class RewriterVar; class RewriterVar;
...@@ -491,6 +562,8 @@ inline void internStringMortalInplace(BoxedString*& s) noexcept { ...@@ -491,6 +562,8 @@ inline void internStringMortalInplace(BoxedString*& s) noexcept {
PyString_InternInPlace((PyObject**)&s); PyString_InternInPlace((PyObject**)&s);
} }
// The data structure definition for hidden-class-based attributes. Consists of a
// pointer to the hidden class object, and a pointer to a variable-size attributes array.
struct HCAttrs { struct HCAttrs {
public: public:
struct AttrList { struct AttrList {
...@@ -507,12 +580,13 @@ static_assert(sizeof(HCAttrs) == sizeof(struct _hcattrs), ""); ...@@ -507,12 +580,13 @@ static_assert(sizeof(HCAttrs) == sizeof(struct _hcattrs), "");
class BoxedDict; class BoxedDict;
class BoxedString; class BoxedString;
// In Pyston, this is the same type as CPython's PyObject (they are interchangeable, but we // "Box" is the base class of any C++ type that implements a Python type. For example,
// use Box in Pyston wherever possible as a convention). // BoxedString is the data structure that implements Python's str type, and BoxedString
// inherits from Box.
// //
// Other types on Pyston inherit from Box (e.g. BoxedString is a Box). Why is this class not // This is the same as CPython's PyObject (and they are interchangeable), with the difference
// polymorphic? Because of C extension support -- having virtual methods would change the layout // since we are in C++ (whereas CPython is in C) we can use C++ inheritance to implement
// of the object. // Python inheritance, and avoid the raw pointer casts that CPython needs everywhere.
class Box { class Box {
private: private:
BoxedDict** getDictPtr(); BoxedDict** getDictPtr();
...@@ -573,6 +647,12 @@ static_assert(offsetof(Box, cls) == offsetof(struct _object, ob_type), ""); ...@@ -573,6 +647,12 @@ static_assert(offsetof(Box, cls) == offsetof(struct _object, ob_type), "");
// Our default for tp_alloc: // Our default for tp_alloc:
extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) noexcept; extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) noexcept;
// These are some macros for tying the C++ type hiercharchy to the Pyston type hiercharchy.
// Classes that inherit from Box have a special operator new() that takes a class object (as
// a BoxedClass*) since the class is necessary for object allocation.
// To enable expressions such as `new BoxedString()` instead of having to type
// `new (str_cls) BoxedString()` everywhere, we need to tell C++ what the default class is.
// We can do this by putting `DEFAULT_CLASS(str_cls);` anywhere in the definition of BoxedString.
#define DEFAULT_CLASS(default_cls) \ #define DEFAULT_CLASS(default_cls) \
void* operator new(size_t size, BoxedClass * cls) __attribute__((visibility("default"))) { \ void* operator new(size_t size, BoxedClass * cls) __attribute__((visibility("default"))) { \
assert(cls->tp_itemsize == 0); \ assert(cls->tp_itemsize == 0); \
...@@ -615,6 +695,11 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -615,6 +695,11 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
#endif #endif
// In the simple cases, we can inline the fast paths of the following methods and improve allocation speed quite a bit:
// - Box::operator new
// - cls->tp_alloc
// - PystonType_GenericAlloc
// - PyObject_Init
// The restrictions on when you can use the SIMPLE (ie fast) variant are encoded as // The restrictions on when you can use the SIMPLE (ie fast) variant are encoded as
// asserts in the 1-arg operator new function: // asserts in the 1-arg operator new function:
#define DEFAULT_CLASS_SIMPLE(default_cls) \ #define DEFAULT_CLASS_SIMPLE(default_cls) \
...@@ -623,12 +708,6 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -623,12 +708,6 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
} \ } \
void* operator new(size_t size) __attribute__((visibility("default"))) { \ void* operator new(size_t size) __attribute__((visibility("default"))) { \
ALLOC_STATS(default_cls); \ 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 \
* - PystonType_GenericAlloc \
* - PyObject_Init \
*/ \
assert(default_cls->tp_alloc == PystonType_GenericAlloc); \ assert(default_cls->tp_alloc == PystonType_GenericAlloc); \
assert(default_cls->tp_itemsize == 0); \ assert(default_cls->tp_itemsize == 0); \
assert(default_cls->tp_basicsize == size); \ assert(default_cls->tp_basicsize == size); \
...@@ -650,6 +729,22 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -650,6 +729,22 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
/* TODO: there should be a way to not have to do this nested inlining by hand */ \ /* TODO: there should be a way to not have to do this nested inlining by hand */ \
} }
// This corresponds to CPython's PyVarObject, for objects with a variable number of "items" that are stored inline.
// For example, strings and tuples store their data in line in the main object allocation, so are BoxVars. Lists,
// since they have a changeable size, store their elements in a separate array, and their main object is a fixed
// size and so aren't BoxVar.
class BoxVar : public Box {
public:
// This field gets initialized in operator new.
Py_ssize_t ob_size;
BoxVar() {}
void* operator new(size_t size, BoxedClass* cls, size_t nitems) __attribute__((visibility("default")));
};
static_assert(offsetof(BoxVar, ob_size) == offsetof(struct _varobject, ob_size), "");
// This is the variant of DEFAULT_CLASS that applies to BoxVar objects.
#define DEFAULT_CLASS_VAR(default_cls, itemsize) \ #define DEFAULT_CLASS_VAR(default_cls, itemsize) \
static_assert(itemsize > 0, ""); \ static_assert(itemsize > 0, ""); \
/* asserts that the class in question is a subclass of BoxVar */ \ /* asserts that the class in question is a subclass of BoxVar */ \
...@@ -693,21 +788,6 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -693,21 +788,6 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
return rtn; \ return rtn; \
} }
// CPython C API compatibility class:
class BoxVar : public Box {
public:
// This field gets initialized in operator new.
Py_ssize_t ob_size;
BoxVar() {}
void* operator new(size_t size, BoxedClass* cls, size_t nitems) __attribute__((visibility("default")));
};
static_assert(offsetof(BoxVar, ob_size) == offsetof(struct _varobject, ob_size), "");
std::string getFullTypeName(Box* o);
const char* getTypeName(Box* b);
class BoxedClass; class BoxedClass;
// TODO these shouldn't be here // TODO these shouldn't be here
...@@ -733,6 +813,7 @@ void raiseSyntaxError(const char* msg, int lineno, int col_offset, llvm::StringR ...@@ -733,6 +813,7 @@ void raiseSyntaxError(const char* msg, int lineno, int col_offset, llvm::StringR
void raiseSyntaxErrorHelper(llvm::StringRef file, llvm::StringRef func, AST* node_at, const char* msg, ...) void raiseSyntaxErrorHelper(llvm::StringRef file, llvm::StringRef func, AST* node_at, const char* msg, ...)
__attribute__((format(printf, 4, 5))); __attribute__((format(printf, 4, 5)));
// A data structure used for storing information for tracebacks.
struct LineInfo { struct LineInfo {
public: public:
int line, column; int line, column;
...@@ -742,6 +823,7 @@ public: ...@@ -742,6 +823,7 @@ public:
: line(line), column(column), file(file), func(func) {} : line(line), column(column), file(file), func(func) {}
}; };
// A data structure to simplify passing around all the data about a thrown exception.
struct ExcInfo { struct ExcInfo {
Box* type, *value, *traceback; Box* type, *value, *traceback;
...@@ -750,7 +832,10 @@ struct ExcInfo { ...@@ -750,7 +832,10 @@ struct ExcInfo {
void printExcAndTraceback() const; void printExcAndTraceback() const;
}; };
// Our object that implements Python's "frame" object:
class BoxedFrame; class BoxedFrame;
// Our internal data structure for storing certain information about a stack frame.
struct FrameInfo { struct FrameInfo {
// Note(kmod): we have a number of fields here that all have independent // Note(kmod): we have a number of fields here that all have independent
// initialization rules. We could potentially save time on every function-entry // initialization rules. We could potentially save time on every function-entry
...@@ -779,6 +864,7 @@ struct FrameInfo { ...@@ -779,6 +864,7 @@ struct FrameInfo {
void gcVisit(GCVisitor* visitor); void gcVisit(GCVisitor* visitor);
}; };
// callattr() takes a number of flags and arguments, and for performance we pack them into a single register:
struct CallattrFlags { struct CallattrFlags {
bool cls_only : 1; bool cls_only : 1;
bool null_on_nonexistent : 1; bool null_on_nonexistent : 1;
...@@ -788,7 +874,7 @@ struct CallattrFlags { ...@@ -788,7 +874,7 @@ struct CallattrFlags {
}; };
static_assert(sizeof(CallattrFlags) == sizeof(uint64_t), ""); static_assert(sizeof(CallattrFlags) == sizeof(uint64_t), "");
// A C++-style way of handling a PyArena* // A C++-style RAII way of handling a PyArena*
class ArenaWrapper { class ArenaWrapper {
private: private:
PyArena* arena; PyArena* arena;
...@@ -803,6 +889,7 @@ public: ...@@ -803,6 +889,7 @@ public:
operator PyArena*() const { return arena; } operator PyArena*() const { return arena; }
}; };
// A C++-style RAII way of handling a FILE*
class FileHandle { class FileHandle {
private: private:
FILE* file; FILE* file;
...@@ -841,6 +928,7 @@ int binarySearch(T needle, RandomAccessIterator start, RandomAccessIterator end, ...@@ -841,6 +928,7 @@ int binarySearch(T needle, RandomAccessIterator start, RandomAccessIterator end,
} }
} }
// We need to override these functions so that our GC can know about them.
namespace std { namespace std {
template <> std::pair<pyston::Box**, std::ptrdiff_t> get_temporary_buffer<pyston::Box*>(std::ptrdiff_t count) noexcept; template <> std::pair<pyston::Box**, std::ptrdiff_t> get_temporary_buffer<pyston::Box*>(std::ptrdiff_t count) noexcept;
template <> void return_temporary_buffer<pyston::Box*>(pyston::Box** p); template <> void return_temporary_buffer<pyston::Box*>(pyston::Box** p);
......
...@@ -307,6 +307,10 @@ protected: ...@@ -307,6 +307,10 @@ protected:
friend void setupThread(); friend void setupThread();
}; };
// Corresponds to PyHeapTypeObject. Very similar to BoxedClass, but allocates some extra space for
// structures that otherwise might get allocated statically. For instance, tp_as_number for builtin
// types will usually point to a `static PyNumberMethods` object, but for a heap-allocated class it
// will point to `this->as_number`.
class BoxedHeapClass : public BoxedClass { class BoxedHeapClass : public BoxedClass {
public: public:
PyNumberMethods as_number; PyNumberMethods as_number;
...@@ -336,6 +340,7 @@ private: ...@@ -336,6 +340,7 @@ private:
friend void setupThread(); friend void setupThread();
}; };
// Assert that our data structures have the same layout as the C API ones with which they need to be interchangeable.
static_assert(sizeof(pyston::Box) == sizeof(struct _object), ""); static_assert(sizeof(pyston::Box) == sizeof(struct _object), "");
static_assert(offsetof(pyston::Box, cls) == offsetof(struct _object, ob_type), ""); static_assert(offsetof(pyston::Box, cls) == offsetof(struct _object, ob_type), "");
...@@ -675,7 +680,7 @@ public: ...@@ -675,7 +680,7 @@ public:
// CPython declares ob_item (their version of elts) to have 1 element. We want to // CPython declares ob_item (their version of elts) to have 1 element. We want to
// copy that behavior so that the sizes of the objects match, but we want to also // copy that behavior so that the sizes of the objects match, but we want to also
// have a zero-length array in there since we have some extra compiler warnings turned // have a zero-length array in there since we have some extra compiler warnings turned
// on. _elts[1] will throw an error, but elts[1] will not. // on: _elts[1] will throw an error, but elts[1] will not.
union { union {
Box* elts[0]; Box* elts[0];
Box* _elts[1]; Box* _elts[1];
...@@ -687,6 +692,7 @@ static_assert(offsetof(BoxedTuple, elts) == offsetof(PyTupleObject, ob_item), "" ...@@ -687,6 +692,7 @@ static_assert(offsetof(BoxedTuple, elts) == offsetof(PyTupleObject, ob_item), ""
extern BoxedString* characters[UCHAR_MAX + 1]; extern BoxedString* characters[UCHAR_MAX + 1];
// C++ functor objects that implement Python semantics.
struct PyHasher { struct PyHasher {
size_t operator()(Box* b) const { size_t operator()(Box* b) const {
if (b->cls == str_cls) { if (b->cls == str_cls) {
...@@ -719,8 +725,11 @@ struct PyLt { ...@@ -719,8 +725,11 @@ struct PyLt {
// llvm::DenseMap doesn't store the original hash values, choosing to instead // llvm::DenseMap doesn't store the original hash values, choosing to instead
// check for equality more often. This is probably a good tradeoff when the keys // check for equality more often. This is probably a good tradeoff when the keys
// are pointers and comparison is cheap, but we want to make sure that keys with // are pointers and comparison is cheap, but when the equality function is user-defined
// different hash values don't get compared. // it can be much faster to avoid Python function invocations by doing some integer
// comparisons.
// This also has a user-visible behavior difference of how many times the hash function
// and equality functions get called.
struct BoxAndHash { struct BoxAndHash {
Box* value; Box* value;
size_t hash; size_t hash;
......
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