Serene Runtime 1.0.0
C runtime for the Serene programming language
Loading...
Searching...
No Matches
engine.c
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <[email protected]>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include "serene/rt/engine.h"
20
21#include "serene/rt/context.h"
22#include "serene/rt/fiber.h"
25#include "serene/utils.h"
26
27#ifdef SRN_WITH_SERENE
28#include "serene/jit/jit.h"
29#include "serene/rt/core.h"
30#include "serene/rt/errors.h"
31#include "serene/rt/keywords.h"
32#include "serene/rt/strings.h"
33#endif
34
35#include <assert.h>
36#include <string.h>
37
38#define XXH_INLINE_ALL
39#define XXH_STATIC_LINKING_ONLY
40#define XXH_ENABLE_AUTOVECTORIZE
41// Disable invocation of <stdlib.h> functions, notably malloc() and free()
42#define XXH_NO_STDLIB
43// We use xxhash 32bit only, so no need for these
44#define XXH_NO_XXH3
45#define XXH_NO_LONG_LONG
46#define XXH_NO_STREAM
47#define XXH_IMPLEMENTATION
48#include "third_party/xxhash.h"
49
50#if defined(__linux__) || defined(__GLIBC__)
51#include <sys/random.h>
52#elif defined(__APPLE__)
53#include <stdlib.h>
54#elif defined(_WIN32)
55#define NOMINMAX
56#include <bcrypt.h>
57#include <windows.h>
58#pragma comment(lib, "bcrypt.lib")
59#endif
60
61// Allocate a random Seed. This function has to be run every
62// time we allocate a new context
64 srn_seed_t s = 0;
65
66#if defined(__linux__)
67 auto res = getrandom(&s, sizeof(s), 0);
68 if (res < 0) {
69 PANIC("Can't get a random number");
70 }
71#elif defined(__APPLE__)
72 arc4random_buf(&s, sizeof(s));
73#elif defined(_WIN32)
74 NTSTATUS st = BCryptGenRandom(nullptr, (PUCHAR)&s, sizeof(s), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
75 if (st != 0) {
76 PANIC("Couldn't allocate a seed");
77 }
78#else
79 // fallback: /dev/urandom
80 FILE *f = fopen("/dev/urandom", "rb");
81 fread(&s, sizeof(s), 1, f);
82 fclose(f);
83#endif
84
85 // Avoiding zero seed
86 if (s == 0) {
87 PANIC("Couldn't allocate a seed");
88 }
89 return s;
90}
91
93 PANIC_IF_NULL(mm);
95
96 if (engine == nullptr) {
97 return nullptr;
98 }
99
100 engine->seed = srn_generate_seed();
101 // Just reserving 0..100 for any unpredicted needs
102 engine->mm = mm;
103
104#ifdef SRN_WITH_SERENE
105 engine->jit = srn_jit_make(mm);
106#endif
107 engine->scheduler = srn_sched_init(engine);
108
109#ifdef SRN_WITH_SERENE
110 // Both registries start empty. An empty hmap_t is just {.len = 0,
111 // .root = nullptr} and matches what hmap_empty would return, so we can
112 // initialize them inline without needing a context here.
113 engine->namespaces = (hmap_t){.len = 0, .root = nullptr};
114 srn_spinlock_init(&engine->ns_lock);
115 engine->keywords = (hmap_t){.len = 0, .root = nullptr};
116 srn_spinlock_init(&engine->keywords_lock);
117#endif
118 atomic_init(&engine->object_id_counter, ENGINE_FIRST_OBJECT_ID);
119
120 return engine;
121}
122
124 PANIC_IF_NULL(engine);
126#ifdef SRN_WITH_SERENE
127 srn_jit_shutdown(engine->jit);
128#endif
129}
130
131srn_hash_t srn_hash(const srn_engine_t *engine, const void *data, size_t len) {
132 // NULL pointers are only valid if the length is zero
133 size_t length = (data == nullptr) ? 0 : len;
134 return XXH32(data, length, engine->seed);
135}
136
138 PANIC_IF_NULL(engine);
139 return atomic_fetch_add_explicit(&engine->object_id_counter, 1, memory_order_relaxed);
140}
141
142#ifdef SRN_WITH_SERENE
143srn_value_t *srn_engine_intern_keyword(srn_context_t *ctx, srn_metadata_t *metadata,
144 const char *name) {
145 PANIC_IF_NULL(ctx);
146 PANIC_IF_NULL(name);
147
148 srn_engine_t *engine = ctx->engine;
149 srn_mm_t *mm = engine->mm;
150 // Cap the read at the documented string-length limit so a malformed
151 // unbounded `name` does not produce an oversized allocation.
152 size_t name_len = strnlen(name, SRN_STRING_MAX_LEN);
153
154 srn_spinlock_lock(&engine->keywords_lock);
155
156 // Fast path: return any existing entry under the same name.
157 hmap_key_t lookup = {.data = (void *)name, .len = name_len};
158 void *found = hmap_lookup(ctx, &engine->keywords, &lookup, nullptr);
159 if (found != nullptr) {
160 srn_spinlock_unlock(&engine->keywords_lock);
161 return (srn_value_t *)found;
162 }
163
164 if (name_len == SRN_STRING_MAX_LEN) {
165 srn_spinlock_unlock(&engine->keywords_lock);
166 return srn_errors_make_error(ctx, metadata, STRING_LENGTH_LIMIT_EXCEEDED,
167 "Keyword name exceeds the string length limit");
168 }
169
170 // Interned keywords must outlive any individual context, so every piece of
171 // the value lives in the engine's immortal memory: the name buffer, the
172 // keyword payload, the value wrapper, and the hmap key that references the
173 // name buffer.
174
175 // Name string.
176 size_t name_alloc_size = sizeof(srn_string_t) + name_len + 1;
177 srn_string_t *name_str =
178 srn_mm_immortal_allocate_aligned(mm, name_alloc_size, alignof(srn_string_t));
179 name_str->len = name_len;
180 name_str->size = name_len + 1;
181 memcpy(name_str->buffer, name, name_len);
182 name_str->buffer[name_len] = '\0';
183
184 // Keyword payload.
186 kw->name = name_str;
187
188 // Value wrapper.
190 v->type = VKeyword;
191 v->metadata = metadata;
192 v->as.keyword = kw;
193
194 // The hmap_key_t holds a pointer to the key bytes, not a copy of them.
195 // Both the key struct and its bytes must outlive any context, so allocate
196 // the key struct immortally and point it at the interned name buffer.
198 k->data = (void *)name_str->buffer;
199 k->len = name_str->len;
200 engine->keywords = hmap_insert(ctx, &engine->keywords, k, (void *)v);
201
202 srn_spinlock_unlock(&engine->keywords_lock);
203 return v;
204}
205#endif
SRN_HASH_TYPE srn_hash_t
Definition context.h:44
SRN_SEED_TYPE srn_seed_t
Definition context.h:45
@ VKeyword
Definition core.h:125
void * srn_mm_immortal_allocate_aligned(srn_mm_t *mm, size_t size, size_t alignment)
Allocate memory on the importal block which will never gets freed.
Definition default.c:356
srn_engine_t * srn_engine_make(srn_mm_t *mm)
Definition engine.c:92
void srn_engine_shutdown(srn_engine_t *engine)
Definition engine.c:123
srn_hash_t srn_hash(const srn_engine_t *engine, const void *data, size_t len)
Definition engine.c:131
static srn_seed_t srn_generate_seed()
Definition engine.c:63
srn_object_id_t srn_allocate_object_id(srn_engine_t *engine)
Definition engine.c:137
uint64_t srn_object_id_t
Definition engine.h:40
#define ENGINE_FIRST_OBJECT_ID
Start allocating IDs from 100, earlier IDs are reserved.
Definition engine.h:42
@ STRING_LENGTH_LIMIT_EXCEEDED
Definition errors.h:52
AI Generated (🤦) Fiber subsystem overview.
void * hmap_lookup(srn_context_t *ctx, const hmap_t *hmap, const hmap_key_t *k, void *default_value)
Lookup the given k in the given hmap and return the value if it's been found.
Definition hashmap.c:622
hmap_t hmap_insert(srn_context_t *ctx, const hmap_t *hmap, hmap_key_t *k, void *v)
Insert the given key k with the value v in the given hash hmap and return the new map.
Definition hashmap.c:618
This is an implementation of Compressed Hash-Array Mapped Prefix-tree, which is a bit-partitioned,...
#define srn_mm_immortal_allocate(mm, T)
Definition interface.h:169
int srn_jit_shutdown(srn_jit_t *jit)
Definition jit.c:75
srn_jit_t * srn_jit_make(srn_mm_t *mm)
Definition jit.c:44
void srn_sched_shutdown(srn_scheduler_t *sched)
The one stop tear down of the fiber subsystem, should be called once srn_sched_run has returned.
Definition scheduler.c:314
srn_scheduler_t * srn_sched_init(srn_engine_t *engine)
Definition scheduler.c:246
#define SRN_STRING_MAX_LEN
On top of my head, no other reason.
Definition strings.h:31
Note: For key equality we use the memcpy function.
Definition hashmap.h:66
void * data
len 0 -> data == nullptr
Definition hashmap.h:68
srn_engine_t * engine
Long term state of the compiler.
Definition context.h:49
Engine is a structure to own the long living and main pieces of the compiler.
Definition engine.h:49
_Atomic srn_object_id_t object_id_counter
An unsigned counter to allocate object ids atomically.
Definition engine.h:55
srn_scheduler_t * scheduler
The fiber scheduler, set by srn_sched_init.
Definition engine.h:67
srn_seed_t seed
We use the seed for hashing and the value will be generated at random for each new context.
Definition engine.h:75
srn_mm_t * mm
Memory manager.
Definition engine.h:58
A keyword: just a name.
Definition keywords.h:29
srn_string_t * name
Definition keywords.h:30
Main memory manager structure that will own all the allocated blocks and data.
Definition interface.h:112
size_t size
Size of the buffer.
Definition strings.h:35
uint8_t buffer[]
The buffer that holds the WTF8 sequence.
Definition strings.h:39
size_t len
length of the WTF-8 sequence in bytes
Definition strings.h:37
srn_metadata_t * metadata
Definition core.h:133
union srn_value_t::@033047061046230251001111174367071167226300135003 as
IMPORTANT NOTE: The size of this union should never be larger than a word.
srn_value_tag_t type
Definition core.h:132
srn_keyword_t * keyword
Definition core.h:146
#define PANIC_IF_NULL(ptr)
Definition utils.h:64
static void srn_spinlock_lock(srn_spinlock_t *lock)
Definition utils.h:286
static void srn_spinlock_unlock(srn_spinlock_t *lock)
Definition utils.h:277
static void srn_spinlock_init(srn_spinlock_t *lock)
Definition utils.h:281
#define PANIC(msg)
Definition utils.h:51