Compare commits

...

11 Commits
iter ... master

Author SHA1 Message Date
Avril 3461794a68
Changed ifunc resolver for _jr_resolv to use the `static const` initialiser in the new macro-based constant expression xsubi endianess resolver. The ifunc resolver always had resolved the correct function at compile-time and consists of a single `jmp` into it, this doesn"t change anything but make it consistent with the constant expression resolver"s methods (which do matter for that one.)
3 years ago
Avril 045cb7189b
Added compile-time (macro) determiation of endianess shift needed for jr_state"s bitfield over xsubi. Reduces code size and removes 2 levels of indirection.
3 years ago
Avril bd22305f77
started: Adding read_only access qualifier to params of internal X-TU function protos.
3 years ago
Avril 8eb88a65b4
Added attr retain to dynctors. Dunno if this does anything, but it doesn"t complain and still works.
3 years ago
Avril 4da128e510
Moved dynctor registration into `rng.cpp` (seems to work in *this* TU too. Why not `rng/sm64.cpp` specifically???? very annoying.)
3 years ago
Avril 3165ef13fd
Hack to work arround dynctor not being registered when not defined in `crand.cpp" (no idea why, the symbol exists. looking into it.)
3 years ago
Avril dfb3d74816
Added SM64 engine to C hard-coded new API
3 years ago
Avril 8a0ecc0e32
XXX: Why is sm64"s dynctor not being called?
3 years ago
Avril d38f0ebd62
Cannot register sm64 dyn constructor for some reason...
3 years ago
Avril 86b855bda3
Added seeding interface.
3 years ago
Avril d9e34e7e70
Start: Seeding API
3 years ago

1
.gitignore vendored

@ -5,3 +5,4 @@ vgcore.*
*-test
*.so*
*.a
rngrx

@ -4,7 +4,7 @@ PROJECT=rngxx
AUTHOR=Avril (Flanchan) <flanchan@cumallover.me>
VERSION_MAJOR=1
VERSION_MINOR=0
VERSION_MINOR=1
VERSION=$(VERSION_MAJOR).$(VERSION_MINOR)
ifeq ($(PREFIX),)

@ -22,6 +22,7 @@ typedef struct Random rng_t;
enum rng_kind {
RNG_KIND_CRAND,
RNG_KIND_SM64,
};
enum rng_next_flag {

@ -14,6 +14,8 @@
#undef _RNGXX_COMMON_MINIMAL
#endif
#define RNGXX_DCTOR_NAME(T) _rng__init_for_ ## T
#define CTOR_COPY(name) name(const name& copy)
#define CTOR_MOVE(name) name(name&& move)

@ -1,10 +1,11 @@
#pragma once
#include "internal/common.h"
#include <rngxx.hpp>
#include "internal/mem.h"
#include "internal/common.h"
namespace rng
{
struct crand final : public Random

@ -0,0 +1,14 @@
#ifndef _RNGXX_DCTOR_HACK_H
#define _RNGXX_DCTOR_HACK_H
#include <rngxx.hpp>
#include <rngxx/crand.h>
#include <rngxx/sm64.h>
#include <init.hpp>
// For some reason, dctors defined in some files do not work??!
RNGXX_APPLY_CTOR(rng, sm64, u64);
RNGXX_APPLY_CTOR(rng, crand, u64);
#endif /* _RNGXX_DCTOR_HACK_H */

@ -10,9 +10,10 @@
namespace rng::init
{
using ctor_func = std::function<Random* (const void*)>;
struct DCLookupFailed final : std::exception{ inline DCLookupFailed(const std::string_view& n) : std::exception(), name(std::move(n)){} std::string name; };
const std::function<Random* (const void*)>& apply_raw(std::string_view name, std::function<Random* (const void*)> fn) noexcept;
const ctor_func& apply_raw(std::string_view name, ctor_func fn) noexcept;
template<typename T, typename _Ptr = void>
inline auto apply(std::string_view name, auto&& lam) noexcept
@ -22,11 +23,11 @@ namespace rng::init
});
}
std::function<Random* (const void*)>& get(std::string_view name);
ctor_func& get(std::string_view name);
}
#define RNGXX_DCTOR_NAME(T) _rng__init_for_ ## T
// To add to dynamic CTOR map
#define RNGXX_APPLY_CTOR(ns, T, TSeed) \
const static auto RNGXX_DCTOR_NAME(T) = rng::init::apply<ns::T, TSeed>(#T, [](const TSeed* ptr) { return new ns::T(*ptr); })
#define RNGXX_APPLY_CTOR(ns, T, TSeed) __attribute__((used, retain))\
const static auto& RNGXX_DCTOR_NAME(T) = rng::init::apply<ns::T, TSeed>(#T, [](const TSeed* ptr) { return new ns::T(*ptr); })
//const extern init::ctor_func& RNGXX_DCTOR_NAME(sm64);

@ -0,0 +1,37 @@
#pragma once
#include "common.h"
namespace rng
{
template<typename T>
struct seed_gen
{
virtual T generate_seed() = 0;
inline virtual ~seed_gen(){}
};
namespace seed
{
struct splitmix64 : public virtual seed_gen<u64>
{
inline splitmix64(const splitmix64&) = default;
explicit inline splitmix64(seed_gen<u64>& from) : m_state(from.generate_seed()){}
splitmix64(u64 i);
u64 generate_seed() override;
f64 generate_f64();
inline virtual ~splitmix64() override{}
static u64 oneshot(u64& state);
inline static u64 oneshot(const u64& state) { u64 s = state; return oneshot(s); }
static f64 oneshotf(u64& state);
inline static f64 oneshotf(const u64& state) { u64 s = state; return oneshotf(s); }
static splitmix64 random();
private:
u64 m_state;
};
}
}

@ -0,0 +1,22 @@
#pragma once
#include <rngxx.hpp>
#include "internal/seed.h"
namespace rng
{
struct sm64 final : public Random
{
inline sm64() : m_generator(seed::splitmix64::random()){}
inline sm64(const seed::splitmix64& g) : m_generator(g){}
explicit inline sm64(seed_gen<u64>& generator) : m_generator(generator){}
inline sm64(u64 seed) : Random(), m_generator(seed){}
RNG_OVERRIDE(inline u64, next_u64, ()) { return m_generator.generate_seed(); }
protected:
inline f64 _sample() override { return m_generator.generate_f64(); }
private:
seed::splitmix64 m_generator;
};
}

@ -3,6 +3,7 @@
#include <rngxx.hpp>
#include <rngxx/crand.h>
#include <rngxx/sm64.h>
#include <init.hpp>
@ -134,6 +135,8 @@ extern "C" {
{
case RNG_KIND_CRAND:
return new rng::crand(seed[0]);
case RNG_KIND_SM64:
return new rng::sm64(seed[0]);
default: return NULL;
}
}

@ -16,8 +16,10 @@
extern "C" {
#endif
#define _ro_p(n) __attribute__((access (read_only, n)))
rng_t* RNG_IMPL(mkdriver)(enum rng_kind kind, const u64* restrict seed) _export(internal);
int RNG_IMPL(mnext)(rng_t* engine, void* restrict output, const struct rng_next_opt* restrict opt) _export(internal);
int RNG_IMPL(mnext)(rng_t* engine, void* restrict output, const struct rng_next_opt* restrict opt) _ro_p(3) _export(internal);
int RNG_IMPL(m_allocsz_for)(enum rng_next_type ty, size_t* restrict size, size_t* restrict align) _export(internal);
@ -25,6 +27,8 @@ rng_t* RNG_IMPL(dynctor_trycall)(const char* name, const void* seed) _export(int
struct rng_dyn_ctor* RNG_IMPL(dynctor_refer)(const char* name) _export(internal);
rng_t* RNG_IMPL(dynctor_callref)(struct rng_dyn_ctor* ptr, const void* seed) _export(internal);
#undef _ro_p
#ifdef __cplusplus
}
#endif

@ -3,22 +3,22 @@
#include <init.hpp>
using ctor_map = std::unordered_map<std::string_view, std::function<Random* (const void*)>>;
using ctor_map = std::unordered_map<std::string_view, rng::init::ctor_func>;
static ctor_map* INIT_MAP=nullptr;
namespace rng::init
{
const std::function<Random* (const void*)>& apply_raw(std::string_view name, std::function<Random* (const void*)> fn) noexcept
const ctor_func& apply_raw(std::string_view name, ctor_func fn) noexcept
{
if(!INIT_MAP) INIT_MAP = new ctor_map();
auto& map = *INIT_MAP;
map[name] = std::move(fn);
return map[name];
}
std::function<Random* (const void*)>& get(std::string_view name)
ctor_func& get(std::string_view name)
{
if(UNLIKELY(!INIT_MAP)) throw DCLookupFailed ( name );
auto& map = *INIT_MAP;

@ -1,10 +0,0 @@
#include <rngxx.hpp>
//XXX: Doing this is not going to work ...
#if 0
void t(Random& r)
{
for(auto&& i : r.iter<int>()) (void)i;
}
#endif

@ -75,3 +75,6 @@ N_INTS
#undef NEXTT
#undef NEXT
// ---
//TODO: Why does this have to be in a specific TU to work?
#include "../internal/dctor.h"

@ -33,7 +33,7 @@ _Static_assert( sizeof(uint48_t) == (sizeof(uint16_t) * 3), "bad uint48 (ushort[
_Static_assert( sizeof(((struct jr_state*)NULL)->st) == sizeof(uint64_t), "bad uint64 (union st)");
inline static unsigned short* IFUNC_DEF(_jr_st_resolv, (jr_xsub_t* restrict state)
__attribute__((const, nonnull, returns_nonnull)));
__attribute__((const, nonnull, returns_nonnull, access(none, 1))));
inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, low) (jr_xsub_t* restrict state)
{
@ -43,6 +43,18 @@ inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, high) (jr_xsub_t* restri
{
return state->xsubi+1;
}
inline static __attribute__((artificial, always_inline, gnu_inline, const)) int _resv_is_high()
{
// This being `static const` initialised makes this function be seen as a proper constant expression. Nothing is leaked on to the stack of the caller and the function is replaced with a single `lea`.
static const struct jr_state chk = {
.st.xsubl = JR_MAX,
};
return chk.st._xsub
? 1
: 0;
}
__attribute__((const))
inline static unsigned short* IFUNC_RESOLVER(_jr_st_resolv) (jr_xsub_t* restrict state)
{
@ -51,13 +63,31 @@ inline static unsigned short* IFUNC_RESOLVER(_jr_st_resolv) (jr_xsub_t* restrict
// The ifunc `_jr_st_resolv()` is essentially (almost) the same as a symbol-aliased constexpr function.
// Ain't that neat?
#ifdef _RNGXX_JR_RESOLV_IFUNC_OLD_STACKDYN_CHECK
// The old, dynamic stack alloc of the struct, instead of putting it in global r/o data
struct jr_state chk = {0};
chk.st.xsubh = JR_MAX;
chk.st.xsubl = JR_MAX;
#else
// The new, static const alloc of the struct, puts the value in at compile-time. Better optimisation opportunities.
// NOTE: Both stratergies make the ifunc resolve its target at compile-time, there is no difference in this ifunc resolver between the two. However, in `_resv_is_high()`, the old (this) stratergy prevents it being recognised as a constant-expression and stack-allocates the useless memory in the function's (macro-expanded) caller. Causing a spill of useless instructions that the new method changes to one `lea'.
static const struct jr_state chk = {
.st.xsubl = JR_MAX,
};
#endif
return chk.st._xsub
? & IFUNC_NAME(_jr_st_resolv, high)
: & IFUNC_NAME(_jr_st_resolv, low);
}
#ifndef _RNGXX_JR_RESOLV_RUNTIME
#define _jr_st_resolv_f (_resv_is_high() \
? & IFUNC_NAME(_jr_st_resolv, high) \
: & IFUNC_NAME(_jr_st_resolv, low))
//unsigned short* (*const _jr_st_resolv_fp)(jr_xsub_t* restrict state) = _jr_st_resolv_f; // "not a constant expr ://
#define _jr_st_resolv(st) _jr_st_resolv_f (st)
#else
#define _jr_st_resolv_f _jr_st_resolv
#endif
_fspec(readonly) internal long _jr_lastl(const struct jr_state* restrict state)
{

@ -7,7 +7,6 @@
#include <rngxx.hpp>
#include <mem.h>
#include <init.hpp>
#include <rngxx/crand.h>
#include "crand.h"
@ -71,4 +70,3 @@ namespace rng
}
RNGXX_APPLY_CTOR(rng, crand, u64);

@ -0,0 +1,14 @@
#include <bit>
#include <climits>
#include <cmath>
#include <rngxx.hpp>
#include <mem.h>
#include <init.hpp>
#include <rngxx/crand.h>
#include <rngxx/sm64.h>
#include "crand.h"
//TODO: Why does dynctors defined in this TU not function properly? Is it because there's nothing else in here?

@ -0,0 +1,31 @@
#include <cmath>
#include <ctime>
#include <seed.h>
#include <common.h>
static u64 _sm64_next(u64& x)
{
u64 z = (x += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
namespace rng::seed
{
splitmix64::splitmix64(u64 u) : m_state(u){}
u64 splitmix64::generate_seed() { return _sm64_next(m_state); }
f64 splitmix64::generate_f64() { return generate_seed() / pow(2.0, 64); }
u64 splitmix64::oneshot(u64& state) { return _sm64_next(state); }
f64 splitmix64::oneshotf(u64& state) { return _sm64_next(state) / pow(2.0, 64); }
splitmix64 splitmix64::random()
{
splitmix64 r (oneshot(time(NULL)));
return r;
}
}

@ -18,10 +18,17 @@ static int next(rng_t* rng, const int* min, const int* max)
#define TREF(x) ( (const __typeof(x)[]){ (x) } )
int main()
const struct { const char* const name; const enum rng_kind static_kind; } DEFAULT_ALG = { "crand", RNG_KIND_CRAND };
int main(int argc, char** argv)
{
rng_dyn_ctor_ref rcref = rng_ctor_ref("crand");
rng_ctor_fn rctor = rng_ctor("crand");
(void)argc;
const char* alg_name = argv[1] ?: DEFAULT_ALG.name;
const enum rng_kind* alg_dev = ((int)DEFAULT_ALG.static_kind) == -1 ? NULL : &DEFAULT_ALG.static_kind;
printf("constructing for %s...\n", alg_name);
rng_dyn_ctor_ref rcref = rng_ctor_ref(alg_name);
rng_ctor_fn rctor = rng_ctor(alg_name);
rng_t* engine = NULL;
if(rctor) {
@ -33,11 +40,11 @@ int main()
}
if(!engine) {
printf("fallback to `rng_new_named()`\n");
engine = rng_new_named("crand", TREF((u64)time(NULL))); //rng_new(rng_kind_crand, (const u64[]){ time(null) });
engine = rng_new_named(alg_name, TREF((u64)time(NULL))); //rng_new(rng_kind_crand, (const u64[]){ time(null) });
}
if(!engine) {
printf("fallback to static init\n");
engine = rng_new(RNG_KIND_CRAND, (const u64[]){ time(NULL) });
if(!engine && alg_dev) {
printf("fallback to static init of default arg (%d)\n", *alg_dev);
engine = rng_new(*alg_dev, (const u64[]){ time(NULL) });
}
if(!engine) {
fprintf(stderr, "failed to create engine\n");

Loading…
Cancel
Save