Lime Parser Generator 0.1.0
Runtime-extensible LALR(1) parser with SIMD tokenization and LLVM JIT
Loading...
Searching...
No Matches
lime_time.h
1/*
2** lime_time.h -- portable monotonic / wall-clock nanosecond timer.
3**
4** On POSIX (Linux, macOS, FreeBSD, Solaris, ...) this header is
5** a thin wrapper over clock_gettime(CLOCK_MONOTONIC, ...) /
6** clock_gettime(CLOCK_REALTIME, ...). On Windows it uses
7** QueryPerformanceCounter / QueryPerformanceFrequency for the
8** monotonic clock and GetSystemTimePreciseAsFileTime for the
9** wall-clock equivalent.
10**
11** Both functions return a uint64_t nanosecond value. The
12** monotonic clock starts at an arbitrary epoch (only diffs are
13** meaningful); the realtime clock returns nanoseconds since the
14** Unix epoch.
15**
16** Provided as static inline so the compiler can elide the call
17** to nothing more than the underlying syscall / Win32 API
18** invocation on the hot path (timing benchmarks). Including
19** this header from a translation unit incurs only the
20** declarations -- no extra link-time code.
21*/
22#ifndef LIME_TIME_H
23#define LIME_TIME_H
24
25#include <stdint.h>
26
27#if defined(_WIN32)
28#define WIN32_LEAN_AND_MEAN
29#include <windows.h>
30
31/* MinGW already supplies <time.h> with `struct timespec`,
32** `clock_gettime`, and the CLOCK_* constants -- using the same
33** definitions as the rest of the toolchain. Including our shim
34** there triggers `redefinition of 'struct timespec'` errors
35** because <sys/types.h> on MinGW pulls them in transitively.
36** Detect MinGW and skip the shim; only MSVC (and clang-cl) need
37** the locally-defined POSIX-style stubs. */
38/* MinGW supplies the full POSIX layer (clock_gettime + CLOCK_*
39** + struct timespec). UCRT supplies struct timespec but not
40** clock_gettime. Legacy MSVCRT supplies neither. */
41#if defined(__MINGW32__)
42#include <time.h>
43#else
44#include <time.h>
45
46/* Define struct timespec only when neither MinGW nor UCRT have
47** already done it. _UCRT is defined by clang/MSVC against the
48** Universal CRT (Win10 1809+) which supplies the C11 struct. */
49#if !defined(_TIMESPEC_DEFINED) && !defined(__struct_timespec_defined) \
50 && !defined(_UCRT)
51struct timespec {
52 long long tv_sec;
53 long tv_nsec;
54};
55#define _TIMESPEC_DEFINED
56#endif
57
58#ifndef CLOCK_MONOTONIC
59#define CLOCK_MONOTONIC 1
60#endif
61#ifndef CLOCK_REALTIME
62#define CLOCK_REALTIME 0
63#endif
64
65/* clock_gettime is NOT in UCRT or legacy MSVC; provide it.
66** MinGW's POSIX layer already does, so the !defined(__MINGW32__)
67** outer guard prevents a redefinition there. */
68static inline int clock_gettime(int clk_id, struct timespec *tp) {
69 if (clk_id == CLOCK_MONOTONIC) {
70 LARGE_INTEGER counter, frequency;
71 QueryPerformanceCounter(&counter);
72 QueryPerformanceFrequency(&frequency);
73 long long secs = counter.QuadPart / frequency.QuadPart;
74 long long rem = counter.QuadPart % frequency.QuadPart;
75 tp->tv_sec = secs;
76 tp->tv_nsec = (long)((rem * 1000000000LL) / frequency.QuadPart);
77 return 0;
78 } else if (clk_id == CLOCK_REALTIME) {
79 FILETIME ft;
80 GetSystemTimePreciseAsFileTime(&ft);
81 unsigned long long t =
82 ((unsigned long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
83 if (t < 116444736000000000ULL) { tp->tv_sec = 0; tp->tv_nsec = 0; return 0; }
84 unsigned long long ns_epoch = (t - 116444736000000000ULL) * 100ULL;
85 tp->tv_sec = (long long)(ns_epoch / 1000000000ULL);
86 tp->tv_nsec = (long)(ns_epoch % 1000000000ULL);
87 return 0;
88 }
89 return -1;
90}
91#endif /* !__MINGW32__ */
92#else
93#include <time.h>
94#endif
95
96static inline uint64_t lime_now_ns(void) {
97#if defined(_WIN32)
98 LARGE_INTEGER counter, frequency;
99 QueryPerformanceCounter(&counter);
100 QueryPerformanceFrequency(&frequency);
101 /* Compute (counter * 1e9) / frequency without overflowing
102 ** 64-bit on long-uptime systems: split the multiply. */
103 uint64_t secs = (uint64_t)counter.QuadPart / (uint64_t)frequency.QuadPart;
104 uint64_t rem = (uint64_t)counter.QuadPart % (uint64_t)frequency.QuadPart;
105 return secs * 1000000000ULL
106 + (rem * 1000000000ULL) / (uint64_t)frequency.QuadPart;
107#else
108 struct timespec ts;
109 clock_gettime(CLOCK_MONOTONIC, &ts);
110 return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
111#endif
112}
113
114static inline uint64_t lime_now_realtime_ns(void) {
115#if defined(_WIN32)
116 /* GetSystemTimePreciseAsFileTime returns 100-ns intervals
117 ** since 1601-01-01. 116444736000000000 is the offset to
118 ** 1970-01-01 (the Unix epoch). */
119 FILETIME ft;
120 GetSystemTimePreciseAsFileTime(&ft);
121 uint64_t t = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
122 if (t < 116444736000000000ULL) return 0;
123 return (t - 116444736000000000ULL) * 100ULL;
124#else
125 struct timespec ts;
126 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) return 0;
127 return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
128#endif
129}
130
131/*
132** LIME_USE(x) -- prevent the compiler from optimising away a
133** computation whose result the program doesn't otherwise need.
134** Useful in benchmarks where we time the cost of producing a
135** value we don't actually consume.
136**
137** On gcc/clang the canonical pattern is `__asm__ volatile(""
138** : : "r"(x))` which forces x into a register and stops the
139** dead-code eliminator at that point. MSVC has no equivalent
140** inline-asm syntax in C; instead we route x through a
141** `volatile` storage location which the compiler cannot
142** optimise across.
143*/
144#if defined(__GNUC__) || defined(__clang__)
145#define LIME_USE(x) __asm__ volatile("" : : "r"(x))
146#elif defined(_MSC_VER)
147#define LIME_USE(x) do { volatile long long _lime_use_sink = (long long)(x); \
148 (void)_lime_use_sink; } while (0)
149#else
150#define LIME_USE(x) ((void)(x))
151#endif
152
153/*
154** LIME_LIKELY(x) / LIME_UNLIKELY(x) -- branch-prediction hints.
155**
156** On gcc/clang these wrap __builtin_expect, which has been used
157** in src/parse_engine.c's per-token hot path since commit
158** 602c305 ("perf(x86): hoist JIT pointer cache + branch hints").
159**
160** MSVC has no __builtin_expect in C. C++23 introduces the
161** [[likely]] / [[unlikely]] attributes but C has no equivalent.
162** On MSVC the macros expand to plain (x): the optimizer's
163** built-in heuristics do a reasonable job on the parser's hot
164** path even without explicit hints.
165*/
166#if defined(__GNUC__) || defined(__clang__)
167#define LIME_LIKELY(x) __builtin_expect(!!(x), 1)
168#define LIME_UNLIKELY(x) __builtin_expect(!!(x), 0)
169#else
170#define LIME_LIKELY(x) (x)
171#define LIME_UNLIKELY(x) (x)
172#endif
173
174/*
175** LIME_HOT / LIME_COLD -- function-placement attributes.
176**
177** gcc/clang group LIME_HOT functions into .text.hot and LIME_COLD
178** functions into .text.unlikely, so the linker keeps frequently-
179** called code dense (better icache density) and rarely-called
180** code (error paths, destructors) out of the way. No-op on MSVC
181** and other compilers.
182**
183** Per .agent/notes/c-perf-audit.md item #4: lime had zero usage
184** of these annotations despite multiple obvious candidates.
185*/
186#if defined(__GNUC__) || defined(__clang__)
187#define LIME_HOT __attribute__((hot))
188#define LIME_COLD __attribute__((cold))
189#else
190#define LIME_HOT
191#define LIME_COLD
192#endif
193
194/*
195** LIME_RESTRICT -- pointer-aliasing-free attribute.
196**
197** Marks a pointer parameter as not aliasing any other pointer in
198** the same function -- the compiler can promote loads to registers
199** across stores through other pointers. C99 has the `restrict`
200** keyword; gcc/clang accept both `restrict` and `__restrict__`.
201** MSVC has `__restrict` (single underscore). No-op elsewhere.
202*/
203#if defined(__GNUC__) || defined(__clang__)
204#define LIME_RESTRICT __restrict__
205#elif defined(_MSC_VER)
206#define LIME_RESTRICT __restrict
207#else
208#define LIME_RESTRICT
209#endif
210
211#endif /* LIME_TIME_H */