types: Reimplement a range-based type checking

Reimplement a range-based type-checking that does not require a memory
access and a bitmap check every time a type is checked.

This brings the `NQueens` execution time on DM42 from 1215 to 1175,
which is about 3%.

Fixes: #532

Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
Christophe de Dinechin 2023-11-03 21:40:29 +01:00
parent 8fd3220f44
commit 044352cc4c
13 changed files with 97 additions and 327 deletions

View file

@ -12,6 +12,7 @@ all times in milliseconds, best of 5 runs, on USB power, with presumably no GC.
| Version | Time | PGM Size | QSPI Size | Note |
|---------|---------|-----------|-----------|-------------------------|
| 0.4.9+ | 1175 | | | Range-based type checks |
| 0.4.9+ | 1215 | | | Remove busy animation |
| 0.4.9 | 1447 | 646028 | 1531868 | No LastArgs in progs |
| 0.4.8 | 1401 | 633932 | 1531868 | |

View file

@ -2704,6 +2704,7 @@ all times in milliseconds, best of 5 runs, on USB power, with presumably no GC.
| Version | Time | PGM Size | QSPI Size | Note |
|---------|---------|-----------|-----------|-------------------------|
| 0.4.9+ | 1175 | | | Range-based type checks |
| 0.4.9+ | 1215 | | | Remove busy animation |
| 0.4.9 | 1447 | 646028 | 1531868 | No LastArgs in progs |
| 0.4.8 | 1401 | 633932 | 1531868 | |

View file

@ -2704,6 +2704,7 @@ all times in milliseconds, best of 5 runs, on USB power, with presumably no GC.
| Version | Time | PGM Size | QSPI Size | Note |
|---------|---------|-----------|-----------|-------------------------|
| 0.4.9+ | 1175 | | | Range-based type checks |
| 0.4.9+ | 1215 | | | Remove busy animation |
| 0.4.9 | 1447 | 646028 | 1531868 | No LastArgs in progs |
| 0.4.8 | 1401 | 633932 | 1531868 | |

View file

@ -289,14 +289,13 @@ bool algebraic::complex_promotion(algebraic_g &x, object::id type)
x = polar_p(z->as_polar());
return x.Safe();
}
else if (is_strictly_symbolic(xt))
else if (is_symbolic(xt))
{
// Assume a symbolic value is complex for now
// TODO: Implement `REALASSUME`
return false;
}
else if (is_integer(xt) || is_real(xt) || is_symbolic(xt) ||
is_algebraic(xt))
else if (is_symbolic_arg(xt) || is_algebraic(xt))
{
algebraic_g zero = algebraic_p(integer::make(0));
if (type == ID_polar)

View file

@ -314,7 +314,7 @@ algebraic_p arithmetic::non_numeric<sub>(algebraic_r x, algebraic_r y)
return x;
if (x->is_same_as(y)) // X - X = 0
return integer::make(0);
if (x->is_zero(false) && y->is_strictly_symbolic())
if (x->is_zero(false) && y->is_symbolic())
return neg::run(y); // 0 - X = -X
}
@ -459,7 +459,7 @@ algebraic_p arithmetic::non_numeric<mul>(algebraic_r x, algebraic_r y)
if (y->type() == ID_ImaginaryUnit)
if (x->is_real())
return rectangular::make(integer::make(0), x);
if (x->is_strictly_symbolic() && x->is_same_as(y))
if (x->is_symbolic() && x->is_same_as(y))
return sq::run(x); // X * X = X²
}
@ -602,7 +602,7 @@ algebraic_p arithmetic::non_numeric<struct div>(algebraic_r x, algebraic_r y)
}
if (y->is_one(false)) // X / 1 = X
return x;
if (x->is_one(false) && y->is_strictly_symbolic())
if (x->is_one(false) && y->is_symbolic())
return inv::run(y); // 1 / X = X⁻¹
if (x->is_same_as(y))
return integer::make(1); // X / X = 1
@ -917,7 +917,7 @@ algebraic_p arithmetic::non_numeric<struct pow>(algebraic_r x, algebraic_r y)
}
// Do not expand X^3 or integers when y>=0
if (x->is_strictly_symbolic())
if (x->is_symbolic())
return expression::make(ID_pow, x, y);
// Deal with X^N where N is a positive integer
@ -1306,7 +1306,7 @@ algebraic_p arithmetic::evaluate(id op,
if (!x.Safe() || !y.Safe())
return nullptr;
if (x->is_symbolic() && y->is_symbolic())
if (x->is_symbolic_arg() && y->is_symbolic_arg())
{
x = expression::make(op, x, y);
return x;

View file

@ -843,7 +843,7 @@ RENDER_BODY(polar)
m->render(r);
r.put(unicode(ANGLE_MARK));
a->render(r);
if (!a->is_strictly_symbolic())
if (!a->is_symbolic())
{
switch(Settings.angle_mode)
{
@ -877,8 +877,8 @@ COMMAND_BODY(RealToComplex)
object_g im = rt.stack(0);
if (!re || !im)
return ERROR;
if (!(re->is_real() || re->is_strictly_symbolic()) ||
!(im->is_real() || im->is_strictly_symbolic()))
if (!(re->is_real() || re->is_symbolic()) ||
!(im->is_real() || im->is_symbolic()))
{
rt.type_error();
return ERROR;
@ -998,7 +998,7 @@ COMPLEX_BODY(sqrt)
algebraic_g im = sqrt::run((znorm - a) / two);
if (b->is_negative(false))
im = neg::run(im);
else if (b->is_strictly_symbolic())
else if (b->is_symbolic())
im = sign::run(im) * im;
return rectangular::make(re, im);
}

View file

@ -46,7 +46,7 @@ bool function::should_be_symbolic(id type)
// Check if we should treat the type symbolically
// ----------------------------------------------------------------------------
{
return is_strictly_symbolic(type);
return is_symbolic(type);
}
@ -510,7 +510,7 @@ FUNCTION_BODY(inv)
{
if (!x.Safe())
return nullptr;
if (x->is_strictly_symbolic())
if (x->is_symbolic())
return symbolic(ID_inv, x);
else if (x->type() == ID_array)
return array_p(x.Safe())->invert();
@ -544,7 +544,7 @@ FUNCTION_BODY(neg)
v = neg::run(v);
return unit::simple(v, u);
}
if (x->is_strictly_symbolic())
if (x->is_symbolic())
return symbolic(ID_neg, x);
algebraic_g zero = rt.make<integer>(ID_integer, 0);
return zero - x;
@ -558,7 +558,7 @@ FUNCTION_BODY(sq)
{
if (!x.Safe())
return nullptr;
if (x->is_strictly_symbolic())
if (x->is_symbolic())
if (!Settings.auto_simplify || x->type() != ID_ImaginaryUnit)
return expression::make(ID_sq, x);
return x * x;
@ -582,7 +582,7 @@ FUNCTION_BODY(cubed)
{
if (!x.Safe())
return nullptr;
if (x->is_strictly_symbolic())
if (x->is_symbolic())
if (!Settings.auto_simplify || x->type() != ID_ImaginaryUnit)
return expression::make(ID_cubed, x);
return x * x * x;
@ -637,7 +637,7 @@ FUNCTION_BODY(fact)
if (!x.Safe())
return nullptr;
if (x->is_strictly_symbolic())
if (x->is_symbolic())
return expression::make(ID_fact, x);
if (integer_p ival = x->as<integer>())

View file

@ -82,8 +82,10 @@ FLAGS(is_real, bignum, decimal128)
FLAGS(is_complex, rectangular, polar)
FLAGS(is_command, Drop, Unimplemented)
FLAGS(is_symbolic, local, expression, pi, ImaginaryUnit)
FLAGS(is_algebraic, add, ToFraction)
FLAGS(is_algebraic_fn, add, ToFraction)
FLAGS(is_immediate, MainMenu, SelfInsert)
FLAGS(is_unit, unit, unit)
FLAGS(is_plot, Function, Parametric)

View file

@ -208,7 +208,7 @@ object::result list::list_parse(id type,
}
// TODO: A symbol could be a function, need to deal with that
if (is_algebraic_function(type))
if (is_algebraic_fn(type))
{
prefix = obj;
obj = nullptr;

View file

@ -82,32 +82,6 @@ RECORDER(object_errors, 16, "Runtime errors on objects");
RECORDER(assert_error, 16, "Assertion failures");
template <typename first, typename last, typename ...rest>
struct handler_flag
{
static constexpr bool set(object::id id)
{
return (id >= first::static_id && id <= last::static_id)
|| handler_flag<rest...>::set(id);
}
};
template <typename first, typename last>
struct handler_flag<first, last>
{
static constexpr bool set(object::id id)
{
return id >= first::static_id && id <= last::static_id;
}
};
#define ID(id)
#define FLAGS(name, ...) \
static constexpr auto name = handler_flag<__VA_ARGS__>();
#include "ids.tbl"
const object::dispatch object::handler[NUM_IDS] =
// ----------------------------------------------------------------------------
// Table of handlers for each object type
@ -131,18 +105,6 @@ const object::dispatch object::handler[NUM_IDS] =
.menu_marker = (menu_marker_fn) id::do_menu_marker, \
.arity = id::ARITY, \
.precedence = id::PRECEDENCE, \
.is_type = ::is_type.set(ID_##id), \
.is_integer = ::is_integer.set(ID_##id), \
.is_based = ::is_based.set(ID_##id), \
.is_bignum = ::is_bignum.set(ID_##id), \
.is_fraction = ::is_fraction.set(ID_##id), \
.is_real = ::is_real.set(ID_##id), \
.is_decimal = ::is_decimal.set(ID_##id), \
.is_complex = ::is_complex.set(ID_##id), \
.is_command = ::is_command.set(ID_##id), \
.is_symbolic = ::is_symbolic.set(ID_##id), \
.is_algebraic = ::is_algebraic.set(ID_##id), \
.is_immediate = ::is_immediate.set(ID_##id), \
},
#include "ids.tbl"
};

View file

@ -207,18 +207,6 @@ struct object
menu_marker_fn menu_marker; // Show marker
uint arity; // Number of input arguments
uint precedence; // Precedence in equations
bool is_type :1; // Is a data type
bool is_integer :1; // Is an integer type
bool is_based :1; // Is a based integer type
bool is_bignum :1; // Is a bignum type
bool is_fraction :1; // Is a fraction type
bool is_real :1; // Is a real type (excludes based ints)
bool is_decimal :1; // Is a decimal type
bool is_complex :1; // Is a complex (but not real) type
bool is_command :1; // Is an RPL command
bool is_symbolic :1; // Is a symbol or an equation
bool is_algebraic :1; // Algebraic functions (in equations)
bool is_immediate :1; // Commands that execute immediately
};
@ -493,76 +481,73 @@ struct object
//
// ========================================================================
static bool is_type(id ty)
// -------------------------------------------------------------------------
// Check if a type is for an RPL data type
// -------------------------------------------------------------------------
struct id_map
// ------------------------------------------------------------------------
// Used to isolate the type range checking names
// ------------------------------------------------------------------------
{
return handler[ty].is_type;
enum ids
// --------------------------------------------------------------------
// Createa a local name matching the class name
// --------------------------------------------------------------------
{
#define ID(i) i,
#include "ids.tbl"
NUM_IDS
};
template <ids first, ids last>
static INLINE bool in_range(id ty)
// --------------------------------------------------------------------
// Check if a given type is in the given range
// --------------------------------------------------------------------
{
return ids(ty) >= first && ids(ty) <= last;
}
bool is_type() const
// -------------------------------------------------------------------------
// Check if an object is an integer
// -------------------------------------------------------------------------
template <ids first, ids last, ids more, ids ...rest>
static INLINE bool in_range(id ty)
// --------------------------------------------------------------------
// Check if a given type is in the given ranges
// --------------------------------------------------------------------
{
return is_type(type());
return (ids(ty) >= first && ids(ty) <= last)
|| in_range<more, rest...>(ty);
}
static bool is_integer(id ty)
// -------------------------------------------------------------------------
// Check if a type is an integer
// -------------------------------------------------------------------------
{
return handler[ty].is_integer;
#define ID(x)
#define FLAGS(name, ...) \
static INLINE bool name(id ty) \
/* ------------------------------------------------------- */ \
/* Range-based type checking (faster than memory reads) */ \
/* ------------------------------------------------------- */ \
{ \
return in_range<__VA_ARGS__>(ty); \
}
#include "ids.tbl"
};
bool is_integer() const
// -------------------------------------------------------------------------
// Check if an object is an integer
// -------------------------------------------------------------------------
{
return is_integer(type());
}
static bool is_based(id ty)
// -------------------------------------------------------------------------
// Check if a type is a based integer
// -------------------------------------------------------------------------
{
return handler[ty].is_based;
}
bool is_based() const
// -------------------------------------------------------------------------
// Check if an object is a based integer
// -------------------------------------------------------------------------
{
return is_based(type());
}
static bool is_bignum(id ty)
// -------------------------------------------------------------------------
// Check if a type is a big integer
// -------------------------------------------------------------------------
{
return handler[ty].is_bignum;
}
bool is_bignum() const
// -------------------------------------------------------------------------
// Check if an object is a big integer
// -------------------------------------------------------------------------
{
return is_bignum(type());
#define ID(x)
#define FLAGS(name, ...) \
static INLINE bool name(id ty) \
/* ------------------------------------------------------- */ \
/* Range-based type checking (faster than memory reads) */ \
/* ------------------------------------------------------- */ \
{ \
return id_map::name(ty); \
} \
\
\
INLINE bool name() const \
/* ------------------------------------------------------- */ \
/* Range-based type checking (faster than memory reads) */ \
/* ------------------------------------------------------- */ \
{ \
return id_map::name(type()); \
}
#include "ids.tbl"
bool is_big() const;
@ -571,31 +556,12 @@ struct object
// ------------------------------------------------------------------------
static bool is_fraction(id ty)
// -------------------------------------------------------------------------
// Check if a type is a fraction
// -------------------------------------------------------------------------
{
return handler[ty].is_fraction;
}
bool is_fraction() const
// -------------------------------------------------------------------------
// Check if an object is an integer
// -------------------------------------------------------------------------
{
return is_fraction(type());
}
static bool is_fractionable(id ty)
// -------------------------------------------------------------------------
// Check if a type is a fraction or a non-based integer
// -------------------------------------------------------------------------
{
return handler[ty].is_fraction ||
(handler[ty].is_integer && handler[ty].is_real);
return is_fraction(ty) || (is_integer(ty) && is_real(ty));
}
@ -608,114 +574,6 @@ struct object
}
static bool is_decimal(id ty)
// -------------------------------------------------------------------------
// Check if a type is a decimal
// -------------------------------------------------------------------------
{
return handler[ty].is_decimal;
}
bool is_decimal() const
// -------------------------------------------------------------------------
// Check if an object is a decimal
// -------------------------------------------------------------------------
{
return is_decimal(type());
}
static bool is_real(id ty)
// -------------------------------------------------------------------------
// Check if a type is a real number
// -------------------------------------------------------------------------
{
return handler[ty].is_real;
}
bool is_real() const
// -------------------------------------------------------------------------
// Check if an object is a real number
// -------------------------------------------------------------------------
{
return is_real(type());
}
static bool is_complex(id ty)
// -------------------------------------------------------------------------
// Check if a type is a complex number
// -------------------------------------------------------------------------
{
return handler[ty].is_complex;
}
bool is_complex() const
// -------------------------------------------------------------------------
// Check if an object is a complex number
// -------------------------------------------------------------------------
{
return is_complex(type());
}
static bool is_unit(id ty)
// -------------------------------------------------------------------------
// Check if a type is a unit object
// -------------------------------------------------------------------------
{
return ty == ID_unit;
}
bool is_unit() const
// -------------------------------------------------------------------------
// Check if an object is a unit object
// -------------------------------------------------------------------------
{
return is_unit(type());
}
static bool is_command(id ty)
// ------------------------------------------------------------------------
// Check if a type denotes a command
// ------------------------------------------------------------------------
{
return handler[ty].is_command;
}
bool is_command() const
// ------------------------------------------------------------------------
// Check if an object is a command
// ------------------------------------------------------------------------
{
return is_command(type());
}
static bool is_immediate(id ty)
// ------------------------------------------------------------------------
// Check if a type denotes an immediate command (e.g. menus)
// ------------------------------------------------------------------------
{
return handler[ty].is_immediate;
}
bool is_immediate() const
// ------------------------------------------------------------------------
// Check if an object is an immediate command (e.g. menus)
// ------------------------------------------------------------------------
{
return is_immediate(type());
}
static bool is_algebraic_number(id ty)
// ------------------------------------------------------------------------
// Check if something is a number (real or complex)
@ -734,57 +592,21 @@ struct object
}
static bool is_symbolic(id ty)
static bool is_symbolic_arg(id ty)
// ------------------------------------------------------------------------
// Check if a type denotes a symbolic argument (symbol, equation, number)
// ------------------------------------------------------------------------
{
return handler[ty].is_symbolic || is_algebraic_number(ty);
return is_symbolic(ty) || is_algebraic_number(ty);
}
bool is_symbolic() const
bool is_symbolic_arg() const
// ------------------------------------------------------------------------
// Check if an object is a symbolic argument
// ------------------------------------------------------------------------
{
return is_symbolic(type());
}
static bool is_strictly_symbolic(id ty)
// ------------------------------------------------------------------------
// Check if a type denotes a symbol or equation
// ------------------------------------------------------------------------
{
return handler[ty].is_symbolic;
}
bool is_strictly_symbolic() const
// ------------------------------------------------------------------------
// Check if an object is a symbol or equation
// ------------------------------------------------------------------------
{
return is_strictly_symbolic(type());
}
static bool is_algebraic_function(id ty)
// ------------------------------------------------------------------------
// Check if a type denotes an algebraic function
// ------------------------------------------------------------------------
{
return handler[ty].is_algebraic;
}
bool is_algebraic_function() const
// ------------------------------------------------------------------------
// Check if an object is an algebraic function
// ------------------------------------------------------------------------
{
return is_algebraic_function(type());
return is_symbolic_arg(type());
}
@ -793,7 +615,7 @@ struct object
// Check if a type denotes an algebraic value or function
// ------------------------------------------------------------------------
{
return is_algebraic_function(ty) || is_symbolic(ty);
return is_algebraic_fn(ty) || is_symbolic_arg(ty);
}
@ -817,24 +639,6 @@ struct object
}
static bool is_plot(id ty)
// ------------------------------------------------------------------------
// Check if a type name denotes a plot type
// ------------------------------------------------------------------------
{
return ty >= ID_Function && ty <= ID_Parametric;
}
bool is_plot() const
// ------------------------------------------------------------------------
// Check if an object is a plot type
// ------------------------------------------------------------------------
{
return is_plot(type());
}
uint arity() const
// ------------------------------------------------------------------------
// Return the arity for arithmetic operators

View file

@ -256,7 +256,7 @@ algebraic_p solve(object_g eq, symbol_g name, object_g guess)
}
// Check if there are unresolved symbols
if (x->is_strictly_symbolic())
if (x->is_symbolic())
{
rt.invalid_function_error();
return x;

View file

@ -867,7 +867,7 @@ FUNCTION_BODY(UVal)
{
if (!x.Safe())
return nullptr;
if (x->is_strictly_symbolic())
if (x->is_symbolic())
return symbolic(ID_UVal, x);
if (unit_p u = x->as<unit>())
return u->value();