blob: b00c14c2a837437d72286b5547bb71eed8831e0e [file] [edit]
/*
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <string>
#include <cstdlib>
#if !defined(__APPLE__)
#include <malloc.h>
#endif
#include "runtime/compiler_hints.h"
#include "runtime/mpz.h"
#include "runtime/int64.h"
#include "runtime/thread.h"
namespace lean {
inline void * alloca(size_t s) {
#ifdef _MSC_VER
return ::_alloca(s);
#else
return ::alloca(s);
#endif
}
enum class object_kind { Constructor, Closure, Array, ScalarArray, String, MPZ, External };
/* The reference counter is a uintptr_t, because at deletion time, we use this field to implement
a linked list of objects to be deleted. */
typedef uintptr_t rc_type;
struct object {
atomic<rc_type> m_rc;
unsigned m_kind:16;
object(object_kind k):m_rc(1), m_kind(static_cast<unsigned>(k)) {}
};
/* We can represent inductive datatypes that have:
1) At most 2^16 constructors
2) At most 2^16 - 1 object fields per constructor
3) At most 2^16 - 1 bytes for scalar/unboxed fields
We only need m_scalar_size for implementing sanity checks at runtime.
Header size: 12 bytes in 32 bit machines and 16 bytes in 64 bit machines. */
struct constructor_object : public object {
unsigned m_tag:16;
unsigned m_num_objs:16;
unsigned m_scalar_size:16;
constructor_object(unsigned tag, unsigned num_objs, unsigned scalar_sz):
object(object_kind::Constructor), m_tag(tag), m_num_objs(num_objs), m_scalar_size(scalar_sz) {}
};
/* Array of objects.
Header size: 16 bytes in 32 bit machines and 32 bytes in 64 bit machines. */
struct array_object : public object {
size_t m_size;
size_t m_capacity;
array_object(size_t sz, size_t c):
object(object_kind::Array), m_size(sz), m_capacity(c) {}
};
/* Array of scalar values.
We support arrays with upto 2^64 elements in 64 bit machines.
The field m_elem_size is only needed for implementing sanity checks at runtime.
Header size: 16 bytes in 32 bit machines and 32 bytes in 64 bit machines. */
struct sarray_object : public object {
unsigned m_elem_size:16; // in bytes
size_t m_size;
size_t m_capacity;
sarray_object(unsigned esz, size_t sz, size_t c):
object(object_kind::ScalarArray), m_elem_size(esz), m_size(sz), m_capacity(c) {}
};
struct string_object : public object {
size_t m_size;
size_t m_capacity;
size_t m_length; // UTF8 length
string_object(size_t sz, size_t c, size_t len):
object(object_kind::String), m_size(sz), m_capacity(c), m_length(len) {}
};
typedef object * (*lean_cfun)(object *); // NOLINT
/* Note that `lean_cfun` is a function pointer for a C function of
arity 1. The `apply` operator performs a cast operation.
It casts m_fun to a C function of the right arity.
Header size: 16 bytes in 32 bit machines and 24 bytes in 64 bit machines.
Note that this structure may also be used to simulate closures built
from bytecodes. We just store an extra argument: the virtual machine
function descriptor. We store in m_fun a pointer to a C function
that extracts the function descriptor and then invokes the VM. */
struct closure_object : public object {
unsigned m_arity:16; // number of arguments expected by m_fun.
unsigned m_num_fixed:16; // number of arguments that have been already fixed.
lean_cfun m_fun;
closure_object(lean_cfun f, unsigned arity, unsigned n):
object(object_kind::Closure), m_arity(arity), m_num_fixed(n), m_fun(f) {}
};
struct mpz_object : public object {
mpz m_value;
mpz_object(mpz const & v):
object(object_kind::MPZ), m_value(v) {}
};
/* Base class for wrapping external_object data.
For example, we use it to wrap the Lean environment object. */
struct external_object : public object {
virtual void dealloc() {}
virtual ~external_object() {}
};
inline bool is_null(object * o) { return o == nullptr; }
inline bool is_scalar(object * o) { return (reinterpret_cast<uintptr_t>(o) & 1) == 1; }
inline object * box(unsigned n) { return reinterpret_cast<object*>((static_cast<uintptr_t>(n) << 1) | 1); }
inline unsigned unbox(object * o) { return reinterpret_cast<uintptr_t>(o) >> 1; }
/* Generic Lean object delete operation.
The generic delete must be used when we are compiling:
1- Polymorphic code.
2- Code using `any` type.
The `any` type is introduced when we translate Lean expression into the Core language based on System-F.
We are planning to generate delete operations for non-polymorphic code.
They can be faster because:
1- They do not need to test whether nested pointers are boxed scalars or not.
2- They do not need to test the kind.
3- They can unfold the loop that decrements the reference counters for nested objects.
\pre !is_scalar(o); */
void del(object * o);
inline unsigned get_rc(object * o) { lean_assert(!is_scalar(o)); return atomic_load_explicit(&(o->m_rc), memory_order_acquire); }
inline bool is_shared(object * o) { return get_rc(o) > 1; }
inline void inc_ref(object * o) { atomic_fetch_add_explicit(&o->m_rc, static_cast<rc_type>(1), memory_order_relaxed); }
inline void dec_shared_ref(object * o) { lean_assert(is_shared(o)); atomic_fetch_sub_explicit(&o->m_rc, static_cast<rc_type>(1), memory_order_acq_rel); }
inline bool dec_ref_core(object * o) { lean_assert(get_rc(o) > 0); return atomic_fetch_sub_explicit(&o->m_rc, static_cast<rc_type>(1), memory_order_acq_rel) == 1; }
inline void dec_ref(object * o) { if (dec_ref_core(o)) del(o); }
inline void inc(object * o) { if (!is_scalar(o)) inc_ref(o); }
inline void dec(object * o) { if (!is_scalar(o)) dec_ref(o); }
/* Testers */
inline object_kind get_kind(object * o) { return static_cast<object_kind>(o->m_kind); }
inline bool is_cnstr(object * o) { return get_kind(o) == object_kind::Constructor; }
inline bool is_closure(object * o) { return get_kind(o) == object_kind::Closure; }
inline bool is_array(object * o) { return get_kind(o) == object_kind::Array; }
inline bool is_sarray(object * o) { return get_kind(o) == object_kind::ScalarArray; }
inline bool is_string(object * o) { return get_kind(o) == object_kind::String; }
inline bool is_mpz(object * o) { return get_kind(o) == object_kind::MPZ; }
inline bool is_external(object * o) { return get_kind(o) == object_kind::External; }
/* Casting */
inline constructor_object * to_cnstr(object * o) { lean_assert(is_cnstr(o)); return static_cast<constructor_object*>(o); }
inline closure_object * to_closure(object * o) { lean_assert(is_closure(o)); return static_cast<closure_object*>(o); }
inline array_object * to_array(object * o) { lean_assert(is_array(o)); return static_cast<array_object*>(o); }
inline sarray_object * to_sarray(object * o) { lean_assert(is_sarray(o)); return static_cast<sarray_object*>(o); }
inline string_object * to_string(object * o) { lean_assert(is_string(o)); return static_cast<string_object*>(o); }
inline mpz_object * to_mpz(object * o) { lean_assert(is_mpz(o)); return static_cast<mpz_object*>(o); }
inline external_object * to_external(object * o) { lean_assert(is_external(o)); return static_cast<external_object*>(o); }
/* The memory associated with all Lean objects but `mpz_object` and `external_object` can be deallocated using `free`.
All fields in these objects are integral types, but `std::atomic<uintptr_t> m_rc`.
However, `std::atomic<Integral>` has a trivial destructor.
In the C++ reference manual (http://en.cppreference.com/w/cpp/atomic/atomic), we find the following sentence:
"Additionally, the resulting std::atomic<Integral> specialization has standard layout, a trivial default constructor,
and a trivial destructor." */
inline void dealloc_mpz(object * o) { delete to_mpz(o); }
inline void dealloc_external(object * o) { delete to_external(o); }
inline void dealloc(object * o) {
switch (get_kind(o)) {
case object_kind::External: dealloc_external(o); break;
case object_kind::MPZ: dealloc_mpz(o); break;
default: free(o); break;
}
}
/* Size of the object in bytes. This function is used for debugging purposes.
\pre !is_scalar(o) && !is_external(o) */
size_t obj_byte_size(object * o);
/* Size of the object header in bytes. This function is used for debugging purposes.
\pre !is_scalar(o) && !is_external(o) */
size_t obj_header_size(object * o);
/* Retrieves data of type `T` stored offset bytes inside of `o` */
template<typename T>
inline T obj_data(object * o, size_t offset) {
lean_assert(obj_header_size(o) <= offset);
lean_assert(offset + sizeof(T) <= obj_byte_size(o));
return *(reinterpret_cast<T *>(reinterpret_cast<char *>(o) + offset));
}
/* Set object data of type T */
template<typename T>
inline void obj_set_data(object * o, size_t offset, T v) {
lean_assert(!is_shared(o));
lean_assert(obj_header_size(o) <= offset);
lean_assert(offset + sizeof(T) <= obj_byte_size(o));
*(reinterpret_cast<T *>(reinterpret_cast<char *>(o) + offset)) = v;
}
/* Constructor objects */
inline object * alloc_cnstr(unsigned tag, unsigned num_objs, unsigned scalar_sz) {
lean_assert(tag < 65536 && num_objs < 65536 && scalar_sz < 65536);
return new (malloc(sizeof(constructor_object) + num_objs * sizeof(object *) + scalar_sz)) constructor_object(tag, num_objs, scalar_sz); // NOLINT
}
inline unsigned cnstr_tag(object * o) { return to_cnstr(o)->m_tag; }
inline unsigned cnstr_num_objs(object * o) { return to_cnstr(o)->m_num_objs; }
inline unsigned cnstr_scalar_size(object * o) { return to_cnstr(o)->m_scalar_size; }
inline size_t cnstr_byte_size(object * o) { return sizeof(constructor_object) + cnstr_num_objs(o)*sizeof(object*) + cnstr_scalar_size(o); } // NOLINT
inline object * cnstr_obj(object * o, unsigned i) {
lean_assert(i < cnstr_num_objs(o));
return obj_data<object*>(o, sizeof(constructor_object) + sizeof(object*)*i); // NOLINT
}
inline object ** cnstr_obj_cptr(object * o) {
lean_assert(is_cnstr(o));
return reinterpret_cast<object**>(reinterpret_cast<char*>(o) + sizeof(constructor_object));
}
template<typename T>
inline T cnstr_scalar(object * o, size_t offset) {
return obj_data<T>(o, sizeof(constructor_object) + offset);
}
inline unsigned char * cnstr_scalar_cptr(object * o) {
lean_assert(is_cnstr(o));
return reinterpret_cast<unsigned char*>(reinterpret_cast<char*>(o) + sizeof(constructor_object) + cnstr_num_objs(o)*sizeof(object*)); // NOLINT
}
inline void cnstr_set_obj(object * o, unsigned i, object * v) {
lean_assert(i < cnstr_num_objs(o));
obj_set_data(o, sizeof(constructor_object) + sizeof(object*)*i, v); // NOLINT
}
template<typename T>
inline void cnstr_set_scalar(object * o, unsigned i, T v) {
obj_set_data(o, sizeof(constructor_object) + i, v);
}
inline unsigned obj_tag(object * o) { if (is_scalar(o)) return unbox(o); else return cnstr_tag(o); }
/* Closures */
inline object * alloc_closure(lean_cfun fun, unsigned arity, unsigned num_fixed) {
lean_assert(arity > 0);
lean_assert(num_fixed < arity);
return new (malloc(sizeof(closure_object) + num_fixed * sizeof(object *))) closure_object(fun, arity, num_fixed); // NOLINT
}
inline lean_cfun closure_fun(object * o) { return to_closure(o)->m_fun; }
inline unsigned closure_arity(object * o) { return to_closure(o)->m_arity; }
inline unsigned closure_num_fixed(object * o) { return to_closure(o)->m_num_fixed; }
inline size_t closure_byte_size(object * o) { return sizeof(closure_object) + (closure_arity(o) - 1)*sizeof(object*); } // NOLINT
inline object * closure_arg(object * o, unsigned i) {
lean_assert(i < closure_num_fixed(o));
return obj_data<object*>(o, sizeof(closure_object) + sizeof(object*)*i); // NOLINT
}
inline object ** closure_arg_cptr(object * o) {
lean_assert(is_closure(o));
return reinterpret_cast<object**>(reinterpret_cast<char*>(o) + sizeof(closure_object));
}
inline void closure_set_arg(object * o, unsigned i, object * a) {
lean_assert(i < closure_num_fixed(o));
obj_set_data(o, sizeof(closure_object) + sizeof(object*)*i, a); // NOLINT
}
/* Array of objects */
inline object * alloc_array(size_t size, size_t capacity) {
return new (malloc(sizeof(array_object) + capacity * sizeof(object *))) array_object(size, capacity); // NOLINT
}
inline size_t array_size(object * o) { return to_array(o)->m_size; }
inline size_t array_capacity(object * o) { return to_array(o)->m_capacity; }
inline size_t array_byte_size(object * o) { return sizeof(array_object) + array_capacity(o)*sizeof(object*); } // NOLINT
inline object * array_obj(object * o, size_t i) {
lean_assert(i < array_size(o));
return obj_data<object*>(o, sizeof(array_object) + sizeof(object*)*i); // NOLINT
}
inline object ** array_cptr(object * o) {
lean_assert(is_array(o));
return reinterpret_cast<object**>(reinterpret_cast<char*>(o) + sizeof(array_object));
}
inline void array_set_size(object * o, size_t sz) {
lean_assert(is_array(o));
lean_assert(!is_shared(o));
lean_assert(sz <= array_capacity(o));
to_array(o)->m_size = sz;
}
inline void array_set_obj(object * o, size_t i, object * v) {
lean_assert(i < array_size(o));
obj_set_data(o, sizeof(array_object) + sizeof(object*)*i, v); // NOLINT
}
/* Array of scalars */
inline object * alloc_sarray(unsigned elem_size, size_t size, size_t capacity) {
return new (malloc(sizeof(sarray_object) + capacity * elem_size)) sarray_object(elem_size, size, capacity); // NOLINT
}
inline unsigned sarray_elem_size(object * o) { return to_sarray(o)->m_elem_size; }
inline size_t sarray_size(object * o) { return to_sarray(o)->m_size; }
inline size_t sarray_capacity(object * o) { return to_sarray(o)->m_capacity; }
inline size_t sarray_byte_size(object * o) { return sizeof(sarray_object) + sarray_capacity(o)*sarray_elem_size(o); } // NOLINT
template<typename T>
T * sarray_cptr_core(object * o) { lean_assert(is_sarray(o)); return reinterpret_cast<T*>(reinterpret_cast<char*>(o) + sizeof(sarray_object)); }
template<typename T>
T * sarray_cptr(object * o) { lean_assert(sarray_elem_size(o) == sizeof(T)); return sarray_cptr_core<T>(o); }
template<typename T> T sarray_data(object * o, size_t i) { return sarray_cptr<T>(o)[i]; }
template<typename T> void sarray_set_data(object * o, size_t i, T v) {
obj_set_data(o, sizeof(sarray_object) + sizeof(T)*i, v);
}
inline void sarray_set_size(object * o, size_t sz) {
lean_assert(is_sarray(o));
lean_assert(!is_shared(o));
lean_assert(sz <= sarray_capacity(o));
to_sarray(o)->m_size = sz;
}
/* MPZ */
inline object * alloc_mpz(mpz const & m) { return new mpz_object(m); }
inline mpz const & mpz_value(object * o) { return to_mpz(o)->m_value; }
/* String */
inline object * alloc_string(size_t size, size_t capacity, size_t len) {
return new (malloc(sizeof(string_object) + capacity)) string_object(size, capacity, len); // NOLINT
}
object * mk_string(char const * s);
object * mk_string(std::string const & s);
inline char const * string_data(object * o) { lean_assert(is_string(o)); return reinterpret_cast<char*>(o) + sizeof(string_object); }
inline size_t string_capacity(object * o) { return to_string(o)->m_capacity; }
inline size_t string_size(object * o) { return to_string(o)->m_size; }
inline size_t string_len(object * o) { return to_string(o)->m_length; }
inline size_t string_byte_size(object * o) { return sizeof(string_object) + string_capacity(o); } // NOLINT
object * string_push(object * s, unsigned c);
object * string_append(object * s1, object * s2);
bool string_eq(object * s1, object * s2);
inline bool string_ne(object * s1, object * s2) { return !string_eq(s1, s2); }
bool string_eq(object * s1, char const * s2);
bool string_lt(object * s1, object * s2);
/* Natural numbers */
#define LEAN_MAX_SMALL_NAT (sizeof(void*) == 8 ? std::numeric_limits<unsigned>::max() : (std::numeric_limits<unsigned>::max() >> 1)) // NOLINT
inline object * mk_nat_obj_core(mpz const & m) {
lean_assert(m > LEAN_MAX_SMALL_NAT);
return alloc_mpz(m);
}
inline object * mk_nat_obj(mpz const & m) {
if (m > LEAN_MAX_SMALL_NAT)
return mk_nat_obj_core(m);
else
return box(m.get_unsigned_int());
}
inline object * mk_nat_obj(unsigned n) {
if (sizeof(void*) == 8) { // NOLINT
return box(n);
} else if (n <= LEAN_MAX_SMALL_NAT) {
return box(n);
} else {
return mk_nat_obj_core(mpz(n));
}
}
inline object * mk_nat_obj(uint64 n) {
if (LEAN_LIKELY(n <= LEAN_MAX_SMALL_NAT)) {
return box(n);
} else {
return mk_nat_obj_core(mpz(n));
}
}
inline uint64 nat2uint64(object * a) {
lean_assert(is_scalar(a));
return unbox(a);
}
inline object * nat_succ(object * a) {
if (LEAN_LIKELY(is_scalar(a))) {
return mk_nat_obj(nat2uint64(a) + 1);
} else {
return mk_nat_obj_core(mpz_value(a) + 1);
}
}
object * nat_big_add(object * a1, object * a2);
inline object * nat_add(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return mk_nat_obj(nat2uint64(a1) + nat2uint64(a2));
} else {
return nat_big_add(a1, a2);
}
}
object * nat_big_sub(object * a1, object * a2);
inline object * nat_sub(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
unsigned n1 = unbox(a1);
unsigned n2 = unbox(a2);
if (n1 < n2)
return box(0);
else
return box(n1 - n2);
} else {
return nat_big_sub(a1, a2);
}
}
object * nat_big_mul(object * a1, object * a2);
inline object * nat_mul(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return mk_nat_obj(nat2uint64(a1) * nat2uint64(a2));
} else {
return nat_big_mul(a1, a2);
}
}
object * nat_big_div(object * a1, object * a2);
inline object * nat_div(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
unsigned n1 = unbox(a1);
unsigned n2 = unbox(a2);
if (n2 == 0)
return box(0);
else
return box(n1 / n2);
} else {
return nat_big_div(a1, a2);
}
}
object * nat_big_mod(object * a1, object * a2);
inline object * nat_mod(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
unsigned n1 = unbox(a1);
unsigned n2 = unbox(a2);
if (n2 == 0)
return box(0);
else
return box(n1 % n2);
} else {
return nat_big_mod(a1, a2);
}
}
bool nat_big_eq(object * a1, object * a2);
inline bool nat_eq(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return a1 == a2;
} else {
return nat_big_eq(a1, a2);
}
}
inline bool nat_ne(object * a1, object * a2) {
return !nat_eq(a1, a2);
}
bool nat_big_le(object * a1, object * a2);
inline bool nat_le(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return a1 <= a2;
} else {
return nat_big_le(a1, a2);
}
}
bool nat_big_lt(object * a1, object * a2);
inline bool nat_lt(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return a1 < a2;
} else {
return nat_big_lt(a1, a2);
}
}
object * nat_big_land(object * a1, object * a2);
inline object * nat_land(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return reinterpret_cast<object*>(reinterpret_cast<uintptr_t>(a1) & reinterpret_cast<uintptr_t>(a2));
} else {
return nat_big_land(a1, a2);
}
}
object * nat_big_lor(object * a1, object * a2);
inline object * nat_lor(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return reinterpret_cast<object*>(reinterpret_cast<uintptr_t>(a1) | reinterpret_cast<uintptr_t>(a2));
} else {
return nat_big_lor(a1, a2);
}
}
object * nat_big_xor(object * a1, object * a2);
inline object * nat_lxor(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return box(unbox(a1) ^ unbox(a2));
} else {
return nat_big_xor(a1, a2);
}
}
/* Integers */
#define LEAN_MAX_SMALL_INT (sizeof(void*) == 8 ? std::numeric_limits<int>::max() : (1 << 30)) // NOLINT
#define LEAN_MIN_SMALL_INT (sizeof(void*) == 8 ? std::numeric_limits<int>::min() : -(1 << 30)) // NOLINT
inline object * mk_int_obj_core(mpz const & m) {
lean_assert(m < LEAN_MIN_SMALL_INT || m > LEAN_MAX_SMALL_INT);
return alloc_mpz(m);
}
inline object * mk_int_obj(mpz const & m) {
if (m < LEAN_MIN_SMALL_INT || m > LEAN_MAX_SMALL_INT)
return mk_int_obj_core(m);
else
return box(static_cast<unsigned>(m.get_int()));
}
inline object * mk_int_obj(int n) {
if (sizeof(void*) == 8) { // NOLINT
return box(static_cast<unsigned>(n));
} else if (LEAN_MIN_SMALL_INT <= n && n <= LEAN_MAX_SMALL_INT) {
return box(static_cast<unsigned>(n));
} else {
return alloc_mpz(mpz(n));
}
}
inline object * mk_int_obj(int64 n) {
if (LEAN_LIKELY(LEAN_MIN_SMALL_INT <= n && n <= LEAN_MAX_SMALL_INT)) {
return box(static_cast<unsigned>(static_cast<int>(n)));
} else {
return mk_int_obj_core(mpz(n));
}
}
inline int64 int2int64(object * a) {
lean_assert(is_scalar(a));
if (sizeof(void*) == 8) { // NOLINT
return static_cast<int>(unbox(a));
} else {
return static_cast<int>(reinterpret_cast<size_t>(a)) >> 1;
}
}
inline int int2int(object * a) {
lean_assert(is_scalar(a));
if (sizeof(void*) == 8) { // NOLINT
return static_cast<int>(unbox(a));
} else {
return static_cast<int>(reinterpret_cast<size_t>(a)) >> 1;
}
}
inline object * nat2int(object * a) {
if (is_scalar(a)) {
unsigned v = unbox(a);
if (v <= LEAN_MAX_SMALL_INT) {
return a;
} else {
return alloc_mpz(mpz(v));
}
} else {
return a;
}
}
inline object * int_neg(object * a) {
if (LEAN_LIKELY(is_scalar(a))) {
return mk_int_obj(-int2int64(a));
} else {
return mk_int_obj(neg(mpz_value(a)));
}
}
object * int_big_add(object * a1, object * a2);
inline object * int_add(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return mk_int_obj(int2int64(a1) + int2int64(a2));
} else {
return int_big_add(a1, a2);
}
}
object * int_big_sub(object * a1, object * a2);
inline object * int_sub(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return mk_int_obj(int2int64(a1) - int2int64(a2));
} else {
return int_big_sub(a1, a2);
}
}
object * int_big_mul(object * a1, object * a2);
inline object * int_mul(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return mk_int_obj(int2int64(a1) * int2int64(a2));
} else {
return int_big_mul(a1, a2);
}
}
object * int_big_div(object * a1, object * a2);
inline object * int_div(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
int v1 = int2int(a1);
int v2 = int2int(a2);
if (v2 == 0)
return box(0);
else
return mk_int_obj(v1 / v2);
} else {
return int_big_div(a1, a2);
}
}
object * int_big_rem(object * a1, object * a2);
inline object * int_rem(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
int v1 = int2int(a1);
int v2 = int2int(a2);
if (v2 == 0)
return box(0);
else
return mk_int_obj(v1 % v2);
} else {
return int_big_rem(a1, a2);
}
}
bool int_big_eq(object * a1, object * a2);
inline bool int_eq(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return a1 == a2;
} else {
return int_big_eq(a1, a2);
}
}
inline bool int_ne(object * a1, object * a2) {
return !int_eq(a1, a2);
}
bool int_big_le(object * a1, object * a2);
inline bool int_le(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return int2int(a1) <= int2int(a2);
} else {
return int_big_le(a1, a2);
}
}
bool int_big_lt(object * a1, object * a2);
inline bool int_lt(object * a1, object * a2) {
if (LEAN_LIKELY(is_scalar(a1) && is_scalar(a2))) {
return int2int(a1) < int2int(a2);
} else {
return int_big_lt(a1, a2);
}
}
}