some progress

This commit is contained in:
veclavtlica
2026-10-07 11:21:21 +03:00
parent 14062e9d00
commit 8d502173a2
26 changed files with 705 additions and 212 deletions
+20 -15
View File
@@ -1,16 +1,8 @@
#include "../def.h"
#include "../std.h"
#include "../str.h"
#include <stdarg.h>
#include <sys/syscall.h>
static inline long com_std_syscall(long number, long arg1, long arg2,
long arg3) {
long ret;
__asm__ volatile("syscall"
: "=a"(ret)
: "a"(number), "D"(arg1), "S"(arg2), "d"(arg3)
: "rcx", "r11", "memory");
return ret;
}
#include <stdint.h>
void *com_std_alloc(void *ptr, size_t size) { return NULL; }
void com_std_assert(bool holds) {
@@ -21,13 +13,26 @@ void com_std_assert(bool holds) {
/* TODO: Figure out a way to make it easy to deal with. */
void com_std_printf(char const *fmt, ...) {
// va_list args;
// va_start(args, fmt);
// vprintf(fmt, args);
// va_end(args);
com_std_syscall(1, 1, (intptr_t)fmt, com_str_ext_len(fmt));
}
void com_std_print_str(char const *str, uint32_t len, bool newline) {
com_std_syscall(1, 1, (intptr_t)str, len);
if (newline)
com_std_syscall(1, 1, (intptr_t)"\n", 1);
}
double com_std_sqrt(double v) { return __builtin_sqrt(v); }
double com_std_fabs(double v) { return __builtin_fabs(v); }
#if COM_DEF_COMPILER == COM_DEF_COMPILER_CLANG
double com_std_sin(double v) { return __builtin_sin(v); }
double com_std_cos(double v) { return __builtin_cos(v); }
#else
/* TODO: Use fsin/fcos, no matter how slow. */
double com_std_sin(double v) { return 0; }
double com_std_cos(double v) { return 0; }
#endif
// void memcpy(void *__restrict dest, const void *__restrict src, size_t n) {
// __builtin_memcpy(dest, src, n);
// };
+10
View File
@@ -6,5 +6,15 @@ void com_std_printf(char const *fmt, ...) {}
double com_std_sqrt(double v) { return __builtin_sqrt(v); }
double com_std_fabs(double v) { return __builtin_fabs(v); }
#if COM_DEF_COMPILER == COM_DEF_COMPILER_CLANG
double com_std_sin(double v) { return __builtin_sin(v); }
double com_std_cos(double v) { return __builtin_cos(v); }
#else
/* TODO: Use fsin/fcos, no matter how slow. */
double com_std_sin(double v) { return 0; }
double com_std_cos(double v) { return 0; }
#endif
// void memcpy(void *__restrict dest, const void *__restrict src, size_t n) {
// __builtin_memcpy(dest, src, n);
// };
+1 -1
View File
@@ -1,4 +1,4 @@
#include "time.h"
#include "../time.h"
#include <stdint.h>
uint64_t com_timer_count_ns(void) { return 0; }
+64
View File
@@ -0,0 +1,64 @@
#include <stddef.h>
#include <stdint.h>
#include "bin.h"
#include "bits.h"
#include "buf.h"
#include "def.h"
#include "std.h"
bool com_bin_read_i16(com_bin *b, int16_t *value) {
int16_t buf;
if (!b->interface.readn(b->ctx, (char *)&buf, 2))
return false;
if (COM_DEF_CPU_LITEND && (b->flags & COM_BIN_BIG_ENDIAN)) {
*value = __builtin_bswap16(buf);
} else
*value = buf;
return true;
}
/* Same thing, really. */
bool com_bin_read_u16(com_bin *b, uint16_t *value) {
return (com_bin_read_i16(b, (int16_t *)value));
}
bool com_bin_read_bufb(com_bin *b, struct com_buf *buf, uint32_t bytes) {
com_std_assert(buf->typesz == 1);
if (!com_buf_make_room(buf, bytes))
return false;
bool s = b->interface.readn(b->ctx, (char *)&buf->data[buf->len], bytes);
if (s)
buf->len += bytes;
return s;
}
bool com_bin_read_bufbi(com_bin *b, struct com_buf *buf, uint32_t *index,
uint32_t bytes) {
com_std_assert(buf->typesz == 1);
uint32_t const alignextra =
com_bits_nalign(buf->len, COM_DEF_ALIGNMENT) - buf->len;
if (!com_buf_make_room(buf, alignextra + sizeof(struct com_buf) + bytes))
return false;
buf->len += alignextra;
struct com_buf *in = (struct com_buf *)&buf->data[buf->len];
buf->len += sizeof(struct com_buf);
bool s = b->interface.readn(b->ctx, (char *)&buf->data[buf->len], bytes);
if (s) {
buf->len += bytes;
*in = (struct com_buf){.cap = bytes,
.len = bytes,
.typesz = 1,
.flags = (buf->flags & COM_BUF_STACK) | COM_BUF_SUB};
*index = ((ptrdiff_t)in - (ptrdiff_t)buf->data) / sizeof(struct com_buf);
}
return s;
}
bool com_bin_skip(com_bin *b, uint32_t bytes) {
return b->interface.skipn(b->ctx, bytes);
}
+45
View File
@@ -0,0 +1,45 @@
/*
Generic interface for binary data parsing.
*/
#ifndef COM_BIN_H
#define COM_BIN_H
#include <stdint.h>
#include "buf.h"
#define COM_BIN_LIT_ENDIAN (1 << 0)
#define COM_BIN_BIG_ENDIAN (1 << 1)
#define COM_BIN_NAT_ENDIAN (1 << 2)
typedef struct com_bin_interface {
/* Attempts to read exactly N bytes, returns false if it cannot. */
bool (*readn)(void *ctx, char *into, uint32_t bytes);
bool (*skipn)(void *ctx, uint32_t bytes);
} com_bin_interface;
typedef struct com_bin {
struct com_bin_interface interface;
void *ctx;
uint32_t flags;
} com_bin;
bool com_bin_read_i16(com_bin *b, int16_t *value);
bool com_bin_read_u16(com_bin *b, uint16_t *value);
/*
Read untyped bytes appending them to buffer.
NOTE: Buffer must have typesz of 1. */
bool com_bin_read_bufb(com_bin *b, struct com_buf *buf, uint32_t bytes);
/*
Inline byte buffer read, making a new buffer inside.
Resulted index can be safely used in case of future reallocations.
NOTE: Buffer must have typesz of 1. */
bool com_bin_read_bufbi(com_bin *b, struct com_buf *buf, uint32_t *index,
uint32_t bytes);
/* TODO: Typed variant, use buf for typesz? */
/* Skip N bytes. */
bool com_bin_skip(com_bin *b, uint32_t n);
#endif
+19 -14
View File
@@ -6,23 +6,28 @@
/* How many bits needed to represent a given number */
/* Alternitive semntic: nearest upper power of two */
static inline int com_bits_needed(uint32_t v) {
#ifdef COM_DEF_COMPILE_MODERN
static inline uint8_t com_bits_needed(uint32_t v) {
// #ifdef COM_DEF_COMPILE_MODERN
return v == 0 ? 1 : 32 - __builtin_clz(v);
#else
#endif
if (v == 0)
return 1;
// #else
// #endif
// if (v == 0)
// return 1;
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
// v--;
// v |= v >> 1;
// v |= v >> 2;
// v |= v >> 4;
// v |= v >> 8;
// v |= v >> 16;
// v++;
return v;
// return v;
}
/* Next aligned value. */
static inline uint32_t com_bits_nalign(uint32_t v, uint8_t a) {
return (v + (a - 1)) & ~(a - 1);
}
#endif
+13
View File
@@ -0,0 +1,13 @@
#include "buf.h"
#include <stddef.h>
#include <stdint.h>
struct com_buf *com_buf_make(uint32_t cap, uint32_t flags) { return NULL; }
bool com_buf_make_room(struct com_buf *b, uint32_t n) {
if (b->cap - b->len >= n)
return true;
return false;
}
void com_buf_free(struct com_buf *b) {}
+57
View File
@@ -0,0 +1,57 @@
/*
Generic buffer structure, both dynamic and static.
Underlying implementation is very simplistic, but it's done so
in assumption that we always bulk allocate, with no fragmentation.
*/
#ifndef COM_BUF_H
#define COM_BUF_H
#include "def.h"
#include "str.h"
#include <stdbool.h>
#include <stdint.h>
#define COM_BUF_GROWABLE (1 << 0) /* Potentially growable. */
#define COM_BUF_STACK (1 << 1) /* Stack residing. */
#define COM_BUF_RING (1 << 2) /* Cycling memory. */
#define COM_BUF_SUB (1 << 3) /* Buffer inside of a buffer. */
typedef struct com_buf {
uint32_t len; /* Length in typed number of elements. */
uint32_t cap; /* Capacity in typed number of elements. */
uint32_t flags;
union {
uint32_t needle; /* Present for ring type. */
} type;
uint8_t typesz;
char data[] __attribute__((aligned(COM_DEF_ALIGNMENT)));
} com_buf;
/* Creates a stack local buffer. It can grow if is set growable, one must still
* free it. */
#define com_buf_make_on_stack(p_t, p_v, p_c, p_f) \
char p_v##_buf[sizeof(p_t) * (p_c)] \
__attribute__((aligned(COM_DEF_ALIGNMENT))); \
struct com_buf *p_v = (struct com_buf *)&p_v##_buf; \
p_v->cap = (p_c); \
p_v->flags = p_f | COM_BUF_STACK; \
p_v->typesz = sizeof(p_t);
#define com_buf_index(p_t, p_v, p_i) \
(p_t *)(&(p_v)->data[(p_v)->typesz * (p_i)])
/* Dynamic allocation to persist no matter the function flow. */
struct com_buf *com_buf_make(uint32_t cap, uint32_t flags);
/* Makes sure there's N elements of capacity at least. Returns false if it
* cannot do so. */
bool com_buf_make_room(struct com_buf *b, uint32_t n);
/* Drops the memory, not touching the cap. */
static inline void com_buf_clear(struct com_buf *b) { b->len = 0; }
void com_buf_free(struct com_buf *b);
#endif
+12
View File
@@ -0,0 +1,12 @@
/*
Configuration file.
*/
#ifndef COM_CFG_H
#define COM_CFG_H
/* Render resolution. */
#define COM_CFG_WIDTH 640
#define COM_CFG_HEIGHT 480
#endif
+34 -14
View File
@@ -1,37 +1,57 @@
/*
Compiler definitions helpers, for portability.
We assume only modern GCC and Clang here.
*/
#ifndef COM_DEF_H
#define COM_DEF_H
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
defined(__GNUC__) || defined(__clang__)
#define COM_DEF_COMPILE_MODERN 1
#define COM_DEF_COMPILER_CLANG 1
#define COM_DEF_COMPILER_GCC 2
#if defined(__GNUC__) && !defined(__clang__)
#define COM_DEF_COMPILER COM_DEF_COMPILER_GCC
#elif defined(__clang__)
#define COM_DEF_COMPILER COM_DEF_COMPILER_CLANG
#endif
#define COM_DEF_TARGET_LINUX 0
#define COM_DEF_TARGET_WASM 1
#define COM_DEF_TARGET_LINUX 1
#define COM_DEF_TARGET_WASM 2
#if defined(__wasm__)
#define COM_DEF_TARGET COM_DEF_TARGET_WASM
#define com_def_export_sym(m_s) __attribute__((export_name(m_s)))
#else
#elif defined(__linux__)
#define COM_DEF_TARGET COM_DEF_TARGET_LINUX
#define com_def_export_sym(m_s) __attribute__((visibility("default")))
#else
#error Unknown target.
#endif
/* TODO: uppercase? */
#ifdef COM_DEF_COMPILE_MODERN
#define com_def_alignedas(v_as) _Alignas(v_as)
#define com_def_vector(v_t, v_v, v_n) \
v_t v_v __attribute__((vector_size(sizeof(v_t) * v_n)))
#define COM_DEF_CPU_AMD64 1
#define COM_DEF_CPU_X86 2
#define COM_DEF_CPU_WASM32 3
#if defined(__wasm__)
#define COM_DEF_CPU COM_DEF_CPU_WASM32
#define COM_DEF_ALIGNMENT 16
#define COM_DEF_CPU_LITEND 1
#elif defined(__x86_64__) || defined(__amd64__)
#define COM_DEF_CPU COM_DEF_CPU_AMD64
#define COM_DEF_CPU_LITEND 1
#define COM_DEF_ALIGNMENT 32
#elif defined(__i386__)
#define COM_DEF_CPU COM_DEF_CPU_X86
#define COM_DEF_ALIGNMENT 16
#define COM_DEF_CPU_LITEND 1
#else
#define com_def_alignedas(v_as)
#define com_def_vector(v_t, v_v, v_n) v_t v_v[v_n]
#error Unknown cpu.
#endif
#define com_def_alignedas(v_as) _Alignas(v_as)
#define com_def_vector(v_t, v_v, v_b) v_t v_v __attribute__((vector_size(v_b)))
// #define COM_DEF_PROFILE_VAR volatile /* Sadly, doesn't work in some cases */
#define COM_DEF_PROFILE_SINK(m_v) __asm__ volatile("" ::"r"(m_v) : "memory")
#define com_def_profile_sink(m_v) __asm__ volatile("" ::"r"(m_v) : "memory")
#endif
+76 -84
View File
@@ -51,12 +51,7 @@ com_fix_t com_fix_sqrt(com_fix_t a) {
if (shift & 1)
shift -= 1;
uint32_t normalized_a;
if (shift > 0) {
normalized_a = a >> shift;
} else {
normalized_a = a << (-shift);
}
uint32_t normalized_a = shift > 0 ? a >> shift : a << (-shift);
// 3. LUT Lookup using 4 MSBs of the normalized value
// Extracted index corresponds to the interval [0.5, 2.0)
@@ -72,11 +67,8 @@ com_fix_t com_fix_sqrt(com_fix_t a) {
// 5. Denormalize back to the target scale: result = x * 2^(shift / 2)
int32_t final_shift = shift / 2;
if (final_shift > 0) {
return (int32_t)(x << final_shift);
} else {
return (int32_t)(x >> (-final_shift));
}
return final_shift > 0 ? (int32_t)(x << final_shift)
: (int32_t)(x >> (-final_shift));
}
void com_fix_print(com_fix_t a) {
@@ -97,21 +89,21 @@ void com_fix_run_bench(void) {
uint64_t start = com_timer_count_ns();
for (int i = 5000000; i--;) {
com_fix_t sqrt = com_fix_sqrt(i);
COM_DEF_PROFILE_SINK(sqrt);
com_def_profile_sink(sqrt);
}
com_timer_profile(start, "fixed: sqrt");
start = com_timer_count_ns();
for (int i = 50000000; i--;) {
com_fix_t sin = com_fix_sin(i);
COM_DEF_PROFILE_SINK(sin);
com_def_profile_sink(sin);
}
com_timer_profile(start, "fixed: sin");
start = com_timer_count_ns();
for (int i = 50000000; i--;) {
com_fix_t cos = com_fix_cos(i);
COM_DEF_PROFILE_SINK(cos);
com_def_profile_sink(cos);
}
com_timer_profile(start, "fixed: cos");
@@ -121,82 +113,82 @@ void com_fix_run_bench(void) {
com_fix_sincos(i, &sin, &cos);
com_fix_t sinv = sin;
com_fix_t cosv = cos;
COM_DEF_PROFILE_SINK(sinv);
COM_DEF_PROFILE_SINK(cosv);
com_def_profile_sink(sinv);
com_def_profile_sink(cosv);
}
com_timer_profile(start, "fixed: sincos");
}
void com_fix_run_tests(void) {
// {
// double max_sqrt_deviation = 0.0f;
// com_fix_t sqrt_accumulator = 0;
// com_fix_t sqrt_test_step = COM_FIX_FRACUNIT >> 2; /* Quarter step */
// for (int i = 0; i < COM_FIX_FRACUNIT << 1; ++i) {
// com_fix_t sqrt = com_fix_sqrt(sqrt_accumulator);
// double const deviation =
// com_std_sqrt(com_fix_as_float(sqrt_accumulator)) -
// com_fix_as_float(sqrt);
// if (deviation > max_sqrt_deviation)
// max_sqrt_deviation = deviation;
// sqrt_accumulator += sqrt_test_step;
// }
// com_std_printf("max sqrt deviation: %f\n", max_sqrt_deviation);
// }
{
double max_sqrt_deviation = 0.0f;
com_fix_t sqrt_accumulator = 0;
com_fix_t sqrt_test_step = COM_FIX_FRACUNIT >> 2; /* Quarter step */
for (int i = 0; i < COM_FIX_FRACUNIT << 1; ++i) {
com_fix_t sqrt = com_fix_sqrt(sqrt_accumulator);
double const deviation =
com_std_sqrt(com_fix_as_float(sqrt_accumulator)) -
com_fix_as_float(sqrt);
if (deviation > max_sqrt_deviation)
max_sqrt_deviation = deviation;
sqrt_accumulator += sqrt_test_step;
}
com_std_printf("max sqrt deviation: %f\n", max_sqrt_deviation);
}
// {
// double max_sin_deviation = 0.0f;
// com_fix_t sin_accumulator = -COM_FIX_PI * 4;
// com_fix_t sin_test_step = (COM_FIX_PI << 1) >> 10;
// for (int i = 0; i < 1024 * 32; ++i) {
// com_fix_t sin = com_fix_sin(sin_accumulator);
// double const deviation =
// com_std_fabs(com_std_sin(com_fix_as_float(sin_accumulator)) -
// com_fix_as_float(sin));
// if (deviation > max_sin_deviation)
// max_sin_deviation = deviation;
// sin_accumulator += sin_test_step;
// }
// com_std_printf("max sin deviation: %f\n", max_sin_deviation);
// }
{
double max_sin_deviation = 0.0f;
com_fix_t sin_accumulator = -COM_FIX_PI * 4;
com_fix_t sin_test_step = (COM_FIX_PI << 1) >> 10;
for (int i = 0; i < 1024 * 32; ++i) {
com_fix_t sin = com_fix_sin(sin_accumulator);
double const deviation =
com_std_fabs(com_std_sin(com_fix_as_float(sin_accumulator)) -
com_fix_as_float(sin));
if (deviation > max_sin_deviation)
max_sin_deviation = deviation;
sin_accumulator += sin_test_step;
}
com_std_printf("max sin deviation: %f\n", max_sin_deviation);
}
// {
// double max_cos_deviation = 0.0f;
// com_fix_t cos_accumulator = -COM_FIX_PI * 4;
// com_fix_t cos_test_step = (COM_FIX_PI << 1) >> 10;
// for (int i = 0; i < 1024 * 32; ++i) {
// com_fix_t cos = com_fix_cos(cos_accumulator);
// double const deviation =
// com_std_fabs(com_std_cos(com_fix_as_float(cos_accumulator)) -
// com_fix_as_float(cos));
// if (deviation > max_cos_deviation)
// max_cos_deviation = deviation;
// cos_accumulator += cos_test_step;
// }
// com_std_printf("max cos deviation: %f\n", max_cos_deviation);
// }
{
double max_cos_deviation = 0.0f;
com_fix_t cos_accumulator = -COM_FIX_PI * 4;
com_fix_t cos_test_step = (COM_FIX_PI << 1) >> 10;
for (int i = 0; i < 1024 * 32; ++i) {
com_fix_t cos = com_fix_cos(cos_accumulator);
double const deviation =
com_std_fabs(com_std_cos(com_fix_as_float(cos_accumulator)) -
com_fix_as_float(cos));
if (deviation > max_cos_deviation)
max_cos_deviation = deviation;
cos_accumulator += cos_test_step;
}
com_std_printf("max cos deviation: %f\n", max_cos_deviation);
}
// {
// double max_sin_deviation = 0.0f;
// double max_cos_deviation = 0.0f;
// com_fix_t sincos_accumulator = -COM_FIX_PI * 4;
// com_fix_t sincos_test_step = (COM_FIX_PI << 1) >> 10;
// for (int i = 0; i < 1024 * 32; ++i) {
// com_fix_t sin, cos;
// com_fix_sincos(sincos_accumulator, &sin, &cos);
// double const sin_deviation =
// com_std_fabs(com_std_sin(com_fix_as_float(sincos_accumulator)) -
// com_fix_as_float(sin));
// double const cos_deviation =
// com_std_fabs(com_std_cos(com_fix_as_float(sincos_accumulator)) -
// com_fix_as_float(cos));
// if (sin_deviation > max_sin_deviation)
// max_sin_deviation = sin_deviation;
// if (cos_deviation > max_cos_deviation)
// max_cos_deviation = cos_deviation;
// sincos_accumulator += sincos_test_step;
// }
// com_std_printf("max sincos deviations: %f %f\n", max_sin_deviation,
// max_cos_deviation);
// }
{
double max_sin_deviation = 0.0f;
double max_cos_deviation = 0.0f;
com_fix_t sincos_accumulator = -COM_FIX_PI * 4;
com_fix_t sincos_test_step = (COM_FIX_PI << 1) >> 10;
for (int i = 0; i < 1024 * 32; ++i) {
com_fix_t sin, cos;
com_fix_sincos(sincos_accumulator, &sin, &cos);
double const sin_deviation =
com_std_fabs(com_std_sin(com_fix_as_float(sincos_accumulator)) -
com_fix_as_float(sin));
double const cos_deviation =
com_std_fabs(com_std_cos(com_fix_as_float(sincos_accumulator)) -
com_fix_as_float(cos));
if (sin_deviation > max_sin_deviation)
max_sin_deviation = sin_deviation;
if (cos_deviation > max_cos_deviation)
max_cos_deviation = cos_deviation;
sincos_accumulator += sincos_test_step;
}
com_std_printf("max sincos deviations: %f %f\n", max_sin_deviation,
max_cos_deviation);
}
}
+18 -18
View File
@@ -5,13 +5,13 @@
#ifndef COM_GEM_H
#define COM_GEM_H
#include "cfg.h"
#include "fix.h"
#include "mat.h"
#include "pnt.h"
#include "vec.h"
#include "zord.h"
#include <stdint.h>
#include <stdio.h>
/* Projects vertex position to a clip space via reordered row-major MVP matrix.
It skips calculation of z depth component, as we assume to never use it.
@@ -24,10 +24,10 @@ static inline com_vec_t com_gem_vec_project_clip(com_mat_t a, com_vec_t b) {
com_vec_t result;
#define CASE(m_c, m_n) \
result.a[m_c] = (((int64_t)a.a[m_n * 4 + 0] * b.a[0]) + \
((int64_t)a.a[m_n * 4 + 1] * b.a[1]) + \
((int64_t)a.a[m_n * 4 + 2] * b.a[2]) + a.a[m_n * 4 + 3]) >> \
COM_FIX_FRACBITS;
result.a[m_c] = \
((com_fix_mul(a.a[m_n * 4 + 0], b.a[0])) + \
(com_fix_mul(a.a[m_n * 4 + 1], b.a[1])) + \
(com_fix_mul(a.a[m_n * 4 + 2], b.a[2])) + a.a[m_n * 4 + 3]);
CASE(0, 0);
CASE(1, 1);
@@ -59,18 +59,18 @@ static inline uint8_t com_gem_clip_vis_project(com_vec_t a, com_pnt_t *out) {
mask ^= COM_GEM_VERTEX_CLIPPED_Y;
// out->a[0] = com_fix_mul((com_fix_div(a.a[0], a.a[2]) + COM_FIX_FRACUNIT) >>
// 1,
// 640 * COM_FIX_FRACUNIT);
// COM_CFG_WIDTH * COM_FIX_FRACUNIT);
// out->a[1] = com_fix_mul((com_fix_div(a.a[1], a.a[2]) + COM_FIX_FRACUNIT) >>
// 1,
// 480 * COM_FIX_FRACUNIT);
out->a[0] =
(com_fix_mul(com_fix_div(a.a[0], a.a[2]), 640 * COM_FIX_FRACUNIT) >>
// COM_CFG_HEIGHT * COM_FIX_FRACUNIT);
out->a[0] = (com_fix_mul(com_fix_div(a.a[0], a.a[2]),
COM_CFG_WIDTH * COM_FIX_FRACUNIT) >>
COM_FIX_FRACBITS) +
320;
out->a[1] =
(com_fix_mul(com_fix_div(a.a[1], a.a[2]), 480 * COM_FIX_FRACUNIT) >>
(COM_CFG_WIDTH / 2);
out->a[1] = (com_fix_mul(com_fix_div(a.a[1], a.a[2]),
COM_CFG_HEIGHT * COM_FIX_FRACUNIT) >>
COM_FIX_FRACBITS) +
240;
(COM_CFG_HEIGHT / 2);
return mask;
}
@@ -91,9 +91,9 @@ static inline void com_gem_draw_triangle(com_pnt_t v0, com_pnt_t v1,
// If the point is inside or on all edges, draw the pixel
// (Assumes Counter Clockwise vertex ordering)
if (ws.a[0] >= 0 && ws.a[1] >= 0 && ws.a[2] >= 0) {
out[(y * 640 + x) * 4 + 0] = 125;
out[(y * 640 + x) * 4 + 1] = 125;
out[(y * 640 + x) * 4 + 2] = 125;
out[(y * COM_CFG_WIDTH + x) * 4 + 0] = 125;
out[(y * COM_CFG_WIDTH + x) * 4 + 1] = 125;
out[(y * COM_CFG_WIDTH + x) * 4 + 2] = 125;
}
}
}
@@ -145,7 +145,7 @@ com_gem_draw_triangle_textured_edge(com_pnt_t v0, com_pnt_t v1, com_pnt_t v2,
// uint32_t idx = com_zord_order(tx, ty);
uint32_t idx = ty * 32 + tx;
// uint32_t bidx = com_zord_order(x, y);
uint32_t bidx = y * 640 + x;
uint32_t bidx = y * COM_CFG_WIDTH + x;
out[bidx * 4 + 0] = tex[idx * 3 + 0];
out[bidx * 4 + 1] = tex[idx * 3 + 1];
@@ -169,7 +169,7 @@ static inline void draw_span(int32_t x_start_fp, int32_t x_end_fp, int y,
for (int x = x1; x < x2; x++) {
uint32_t idx = 0 * 32 + 0;
uint32_t bidx = y * 640 + x;
uint32_t bidx = y * COM_CFG_WIDTH + x;
out[bidx * 4 + 0] = tex[idx * 3 + 0];
out[bidx * 4 + 1] = tex[idx * 3 + 1];
out[bidx * 4 + 2] = tex[idx * 3 + 2];
+10 -12
View File
@@ -22,7 +22,7 @@
#include <stdint.h>
typedef struct {
com_def_alignedas(64) com_def_vector(com_fix_t, a, 4 * 4);
com_def_alignedas(64) com_def_vector(com_fix_t, a, 4 * 4 * sizeof(com_fix_t));
} com_mat_t;
void com_mat_run_bench(void);
@@ -30,7 +30,7 @@ void com_mat_run_bench(void);
#define COM_MAT_PROFILE_SINK(m_m) \
do { \
for (int i = 0; i < 16; ++i) \
COM_DEF_PROFILE_SINK((m_m).a[i]); \
com_def_profile_sink((m_m).a[i]); \
} while (0)
static inline com_mat_t com_mat_identity(void) {
@@ -60,11 +60,10 @@ static inline com_mat_t com_mat_mul(com_mat_t a, com_mat_t b) {
for (int c = 0; c < 4; ++c) {
for (int r = 0; r < 4; ++r) {
result.a[r + c * 4] = (((int64_t)a.a[r + 0 * 4] * b.a[0 + c * 4]) +
((int64_t)a.a[r + 1 * 4] * b.a[1 + c * 4]) +
((int64_t)a.a[r + 2 * 4] * b.a[2 + c * 4]) +
((int64_t)a.a[r + 3 * 4] * b.a[3 + c * 4])) >>
COM_FIX_FRACBITS;
result.a[r + c * 4] = ((com_fix_mul(a.a[r + 0 * 4], b.a[0 + c * 4])) +
(com_fix_mul(a.a[r + 1 * 4], b.a[1 + c * 4])) +
(com_fix_mul(a.a[r + 2 * 4], b.a[2 + c * 4])) +
(com_fix_mul(a.a[r + 3 * 4], b.a[3 + c * 4])));
}
}
@@ -80,11 +79,10 @@ static inline com_mat_t com_mat_mul_reodered(com_mat_t a, com_mat_t b) {
for (int c = 0; c < 4; ++c) {
for (int r = 0; r < 4; ++r) {
result.a[r + c * 4] = (((int64_t)a.a[0 + r * 4] * b.a[0 + c * 4]) +
((int64_t)a.a[1 + r * 4] * b.a[1 + c * 4]) +
((int64_t)a.a[2 + r * 4] * b.a[2 + c * 4]) +
((int64_t)a.a[3 + r * 4] * b.a[3 + c * 4])) >>
COM_FIX_FRACBITS;
result.a[r + c * 4] = ((com_fix_mul(a.a[0 + r * 4], b.a[0 + c * 4])) +
(com_fix_mul(a.a[1 + r * 4], b.a[1 + c * 4])) +
(com_fix_mul(a.a[2 + r * 4], b.a[2 + c * 4])) +
(com_fix_mul(a.a[3 + r * 4], b.a[3 + c * 4])));
}
}
+1 -1
View File
@@ -11,7 +11,7 @@
#include <stdint.h>
typedef struct {
com_def_alignedas(16) com_def_vector(com_fix_t, a, 2);
com_def_alignedas(16) com_def_vector(com_fix_t, a, 2 * sizeof(com_fix_t));
} com_pnt_t;
static inline com_pnt_t com_pnt_from(com_fix_t x, com_fix_t y) {
+24 -7
View File
@@ -8,18 +8,35 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#if COM_DEF_TARGET == COM_DEF_TARGET_LINUX
#include <sys/syscall.h>
static inline long com_std_syscall(long number, long arg1, long arg2,
long arg3) {
long ret;
__asm__ volatile("syscall"
: "=a"(ret)
: "a"(number), "D"(arg1), "S"(arg2), "d"(arg3)
: "rcx", "r11", "memory");
return ret;
}
#endif
/* TODO: Remove, use buffer semantics instead. */
/* Note: Freeing is done by passing 0 size to pointer. */
extern void *com_std_alloc(void *ptr, size_t size);
void *com_std_alloc(void *ptr, size_t size);
/* Note: Functions listed below are only available for dev builds. */
/* Trigonometry functions are for reference testing of our own fixed point
* implementations. Do not use otherwise. */
extern void com_std_assert(bool holds);
extern void com_std_printf(char const *fmt, ...);
extern double com_std_sqrt(double v);
extern double com_std_fabs(double v);
extern double com_std_sin(double v);
extern double com_std_cos(double v);
void com_std_assert(bool holds);
void com_std_printf(char const *fmt, ...);
void com_std_print_str(char const *str, uint32_t len, bool newline);
double com_std_sqrt(double v);
double com_std_fabs(double v);
double com_std_sin(double v);
double com_std_cos(double v);
#endif
+61
View File
@@ -0,0 +1,61 @@
/*
Routines for string processing.
*_ext_* functions are provided for foreign strings we're not in control of.
*/
#ifndef COM_STR_H
#define COM_STR_H
#include "def.h"
#include <stdbool.h>
#include <stdint.h>
#define COM_STR_GROWABLE (1 << 0) /* Data can grow and be reallocated. */
#define COM_STR_STACK (1 << 1) /* Stack residing. */
#define COM_STR_SUB (1 << 2) /* Substring of an existing one. */
#define COM_STR_NULL_TERMINATED (1 << 3) /* Whether there's NULL at last. */
/* Internal strings are assumed to have vectorizable alignment. */
/* Instead of growing strings on joining we implement ropes. */
typedef struct com_str {
struct com_str *next;
/* Actual size is +1, to fit NULL terminator. */
uint32_t len;
uint32_t flags;
char data[] __attribute__((aligned(COM_DEF_ALIGNMENT)));
} com_str;
/* NOTE: Have to be moved to static or global memory. */
#define com_str_lit(p_l) {NULL, sizeof(p_l) - 1, COM_STR_NULL_TERMINATED, (p_l)}
static inline uint32_t com_str_ext_len(char const *s) {
char const *p = s;
for (; *p != 0; ++p) {
}
return p - s;
}
/* Returns index of a char or -1 if it's not found. */
static inline int32_t com_str_ext_find_char(char const *s, char ch) {
for (char const *p = s; *p != 0; ++p)
if (*p == ch)
return p - s;
return -1;
}
/* Returns true if string 'a' is prefixed by 'b'. */
static inline bool com_str_ext_prefixed(char const *__restrict a,
struct com_str prefix) {
uint32_t n = prefix.len;
char *b = prefix.data;
while (n && *a && (*a == *b)) {
++a;
++b;
--n;
}
return n == 0;
}
#endif
+7 -10
View File
@@ -12,7 +12,7 @@
#include <stdint.h>
typedef struct {
com_def_alignedas(16) com_def_vector(com_fix_t, a, 3);
com_def_alignedas(16) com_def_vector(com_fix_t, a, 4 * sizeof(com_fix_t));
} com_vec_t;
static inline com_vec_t com_vec_from(com_fix_t x, com_fix_t y, com_fix_t z) {
@@ -63,20 +63,17 @@ static inline com_vec_t com_vec_scl(com_vec_t a, com_fix_t b) {
/* Resulted range depends on input, it's in -1 to 1 for normalized
* input and otherwise is -ab to +ab */
static inline com_fix_t com_vec_dot(com_vec_t a, com_vec_t b) {
return (((int64_t)a.a[0] * b.a[0]) + ((int64_t)a.a[1] * b.a[1]) +
((int64_t)a.a[2] * b.a[2])) >>
COM_FIX_FRACBITS;
return ((com_fix_mul(a.a[0], b.a[0])) + (com_fix_mul(a.a[1], b.a[1])) +
(com_fix_mul(a.a[2], b.a[2])));
}
/* Cross product produces a perpendicular for normalized vectors, or 0 for
* parallel vectors */
static inline com_vec_t com_vec_crs(com_vec_t a, com_vec_t b) {
int64_t const cx = ((int64_t)a.a[1] * b.a[2]) - ((int64_t)a.a[2] - b.a[1]);
int64_t const cy = ((int64_t)a.a[2] * b.a[0]) - ((int64_t)a.a[0] - b.a[2]);
int64_t const cz = ((int64_t)a.a[0] * b.a[1]) - ((int64_t)a.a[1] - b.a[0]);
return (com_vec_t){.a = {(com_fix_t)(cx >> COM_FIX_FRACBITS),
(com_fix_t)(cy >> COM_FIX_FRACBITS),
(com_fix_t)(cz >> COM_FIX_FRACBITS)}};
return (com_vec_t){
.a = {(com_fix_mul(a.a[1], b.a[2])) - (com_fix_mul(a.a[2], b.a[1])),
(com_fix_mul(a.a[2], b.a[0])) - (com_fix_mul(a.a[0], b.a[2])),
(com_fix_mul(a.a[0], b.a[1])) - (com_fix_mul(a.a[1], b.a[0]))}};
}
/* Normalize vector, making it a unit one (of length 1). */
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef PLR_DISPLAY_H
#define PLR_DISPLAY_H
extern int plr_display_x11_main(int argc, char *argv[]);
__attribute__((used)) extern int plr_display_x11_main(int argc, char *argv[]);
extern int plr_display_dos_main(int argc, char *argv[]);
#endif
+119 -10
View File
@@ -1,22 +1,79 @@
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "../../Common/bin.h"
#include "../../Common/buf.h"
#include "../../Common/cfg.h"
#include "../../Common/lzw.h"
#include "../../Common/mat.h"
#include "../../Common/std.h"
#include "../../Common/str.h"
#include "../draw.h"
#include "display.h"
/* https://hereket.com/posts/from-scratch-x11-windowing/ */
/* https://gaultier.github.io/blog/write_a_video_game_from_scratch_like_1987.html
*/
bool quited = false;
/* TODO: Have a cfg.h configuration header setting those. */
#define WIDTH 640
#define HEIGHT 480
bool fileread(void *ctx, char *into, uint32_t bytes) {
return com_std_syscall(0, (intptr_t)ctx, (intptr_t)into, bytes);
}
bool fileskip(void *ctx, uint32_t bytes) {
return com_std_syscall(8, (intptr_t)ctx, bytes, 1) >= 0;
}
static struct plr_x11_auth_entry {
uint16_t family;
struct com_buf *auth_name;
struct com_buf *auth_data;
bool success;
} plr_x11_read_auth_entry(struct com_bin *b, struct com_buf *buf) {
struct plr_x11_auth_entry result = {0};
if (!com_bin_read_u16(b, &result.family)) {
com_std_printf("Failure to initialize X11\n");
goto BREAK;
}
uint16_t addres_len;
if (!com_bin_read_u16(b, &addres_len))
goto BREAK;
if (!com_bin_skip(b, addres_len))
goto BREAK;
uint16_t display_len;
if (!com_bin_read_u16(b, &display_len))
goto BREAK;
if (!com_bin_skip(b, display_len))
goto BREAK;
uint16_t name_len;
if (!com_bin_read_u16(b, &name_len))
goto BREAK;
uint32_t name_idx;
if (!com_bin_read_bufbi(b, buf, &name_idx, name_len))
goto BREAK;
uint16_t data_len;
if (!com_bin_read_u16(b, &data_len))
goto BREAK;
uint32_t data_idx;
if (!com_bin_read_bufbi(b, buf, &data_idx, name_len))
goto BREAK;
result.auth_name = com_buf_index(struct com_buf, buf, name_idx);
result.auth_data = com_buf_index(struct com_buf, buf, data_idx);
result.success = true;
return result;
BREAK:
com_std_printf("Failure to initialize X11\n");
return result;
}
// TODO: correct error handling.
extern int plr_display_x11_main(int argc, char *argv[]) {
@@ -43,9 +100,61 @@ extern int plr_display_x11_main(int argc, char *argv[]) {
// com_vec_t v0 = com_mat_vec_project(com_mat_reoder(m0), com_vec_identity());
// com_fix_print(v0.a[0]);
com_fix_run_tests();
com_fix_run_bench();
com_mat_run_bench();
// com_fix_run_tests();
// com_fix_run_bench();
// com_mat_run_bench();
/* TODO: Move init to separate func. */
/* Open X11 socket for communication. */
int const xfd = com_std_syscall(41, AF_UNIX, SOCK_STREAM, 0);
if (xfd < 0) {
com_std_printf("Failure to initialize X11\n");
return -1;
}
struct sockaddr_un const addr = {.sun_family = AF_UNIX,
.sun_path = "/tmp/.X11-unix/X0"};
if (com_std_syscall(42, xfd, (intptr_t)&addr, sizeof addr)) {
com_std_printf("Failure to initialize X11\n");
return -1;
}
/* First we need to authenticate X11 connection, which requires some funk. */
/* We assume that $XAUTHORITY is set and I hope it is true. */
char *xauthpath = NULL;
for (char **envp = &argv[argc + 1]; *envp != NULL; ++envp) {
static const struct com_str xauthlit = com_str_lit("XAUTHORITY=");
if (com_str_ext_prefixed(*envp, xauthlit)) {
xauthpath = &((*envp)[xauthlit.len]);
break;
}
}
/* Open it and parse. */
int const xauthfd = com_std_syscall(2, (intptr_t)xauthpath, O_RDONLY, 0);
if (xauthfd < 0) {
com_std_printf("Failure to initialize X11\n");
return -1;
}
com_buf_make_on_stack(char, xauthbuf, 1024, 0);
struct com_bin xauthbin = {
.ctx = (void *)(int64_t)xauthfd,
.flags = COM_BIN_BIG_ENDIAN,
.interface = {.readn = fileread, .skipn = fileskip}};
struct plr_x11_auth_entry auth_entry =
plr_x11_read_auth_entry(&xauthbin, xauthbuf);
com_std_print_str(auth_entry.auth_name->data, auth_entry.auth_name->len,
true);
com_std_print_str(auth_entry.auth_data->data, auth_entry.auth_data->len,
true);
com_buf_free(xauthbuf);
char sendbuf[16 * 1024] = {};
char readbuf[16 * 1024] = {};
// Display *display = XOpenDisplay(NULL);
// if (NULL == display) {
@@ -90,13 +199,13 @@ extern int plr_display_x11_main(int argc, char *argv[]) {
// GC gc = XCreateGC(display, window, 0, NULL);
// Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
// XSetWMProtocols(display, window, &wm_delete_window, 1);
// Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW",
// False); XSetWMProtocols(display, window, &wm_delete_window, 1);
// // TODO: can't be not true
// int bytes_per_pixel = 4;
// // char *pixel_buffer = (char *)malloc(WIDTH * HEIGHT * bytes_per_pixel);
// char pixel_buffer[WIDTH * HEIGHT * bytes_per_pixel];
// // char *pixel_buffer = (char *)malloc(WIDTH * HEIGHT *
// bytes_per_pixel); char pixel_buffer[WIDTH * HEIGHT * bytes_per_pixel];
// XImage *ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
// pixel_buffer, WIDTH, HEIGHT, 32, 0);
+3
View File
@@ -0,0 +1,3 @@
#include "../Display/display.h"
extern int main(int argc, char **argv) { plr_display_x11_main(argc, argv); }
+10 -12
View File
@@ -1,15 +1,13 @@
#include "../Display/display.h"
__attribute__((force_align_arg_pointer)) __attribute__((noreturn)) extern void
/* No libc starting point. Jump straight into action! */
__attribute__((naked)) __attribute__((force_align_arg_pointer))
__attribute__((noreturn)) extern void
_start(void) {
plr_display_x11_main(0, 0);
__asm__ volatile("mov $60, %%rax\n\t"
"xor %%rdi, %%rdi\n\t"
"syscall"
:
:
: "%rax", "%rdi");
__builtin_unreachable();
__asm__ volatile("xor %rbp, %rbp\n"
"movq (%rsp), %rdi\n"
"leaq 8(%rsp), %rsi\n"
"call plr_display_x11_main\n"
"movq %rax, %rdi\n"
"movq $60, %rax\n"
"syscall");
}
+35 -4
View File
@@ -1,6 +1,37 @@
CC = clang
# todo: Only link to libm on debug.
CFLAGS = -Wall -std=c99 -g3 -O3 -flto=full
RELEASE ?= 0
SQUEEZE ?= 0
BITS ?= 64
CFLAGS = -Wall -std=gnu99 -flto=thin -ffast-math -fno-ident -fvisibility=hidden
ifeq ($(MAKECMDGOALS),linux)
ifeq ($(RELEASE), 1)
CFLAGS += -Wl,--ignore-data-address-equality -Wl,--ignore-function-address-equality \
-Wl,--optimize-bb-jumps
endif
ifeq ($(SQUEEZE), 1)
CFLAGS += -Wl,-T,tiny.ld -Wl,--icf=all -Wl,--bp-compression-sort=both \
-Wl,--nmagic
endif
ifeq ($(BITS), 64)
CFLAGS += -m64 -march=x86-64-v3 -mpopcnt
else
CFLAGS += -m32 -msse2
endif
endif
ifeq ($(MAKECMDGOALS),wasm)
ifeq ($(RELEASE), 1)
CFLAGS += -Wl,--compress-relocations
endif
endif
ifeq ($(RELEASE), 0)
CFLAGS += -g3 -O1
else
CFLAGS += -Wl,--strip-all -DNDEBUG -O3 -fuse-ld=lld -fmerge-all-constants -Wl,-O3 \
-ffunction-sections -fdata-sections -Wl,--gc-sections \
-Wl,-mllvm,--vectorize-slp=true \
-Wl,-mllvm,--enable-gvn-hoist=true
endif
# TODO: Just use amalgam build for all?
DEPS = ../Common/lzw.h ../Common/mat.h ../Common/vec.h ../Common/fix.h ../Common/def.h ../Common/bits.h \
../Common/gem.h \
../Common/Timer/time.h \
@@ -15,12 +46,12 @@ DOS = maindos.c Display/dos.c
$(CC) -c -o $@ $< $(CFLAGS)
# TODO: Make it work with no stdlib.
linux: CFLAGS += -msse2 -mbmi2 -nostdlib
linux: CFLAGS += -static -mbmi2 -nostdlib
linux: $(LINUX) | $(SRC)
$(CC) -o player $^ $(CFLAGS)
# Note: lld package might need to be downloaded.
wasm: CFLAGS += --target=wasm32 -nostdlib -Wl,--no-entry -msimd128
wasm: CFLAGS += --target=wasm32 -nostdlib -Wl,--no-entry -msimd128 -mbulk-memory
wasm:
$(CC) -o player.wasm $(WASM) $(CFLAGS)
+4
View File
@@ -2,6 +2,8 @@
Amalgamation file, compiling all in single unit.
*/
#include "../Common/bin.c"
#include "../Common/buf.c"
#include "../Common/def.h"
#include "../Common/fix.c"
#include "../Common/lzw.c"
@@ -14,6 +16,8 @@
#include "../Common/Timer/stub.c"
#include "./Display/x11.c"
#include "./Main/linux.c"
// #include "./Main/libc.c"
#elif COM_DEF_TARGET == COM_DEF_TARGET_WASM
#include "../Common/Std/stub.c"
#include "../Common/Timer/stub.c"
#endif
+7 -6
View File
@@ -1,4 +1,5 @@
#include "../Common/cfg.h"
#include "../Common/def.h"
#include "../Common/gem.h"
#include "../Common/std.h"
@@ -6,7 +7,7 @@
#include <stdint.h>
#if COM_DEF_TARGET == COM_DEF_TARGET_WASM
uint8_t plr_draw_buffer[640 * 480 * 4];
uint8_t plr_draw_buffer[COM_CFG_WIDTH * COM_CFG_HEIGHT * 4];
com_def_export_sym(
"plr_draw_get_pixel_buffer") uint8_t *plr_draw_get_pixel_buffer(void) {
@@ -16,9 +17,9 @@ com_def_export_sym(
com_def_export_sym("plr_draw_screen") void plr_draw_screen(
uint8_t *pixel_buffer) {
for (int y = 0; y < 480; y++) {
for (int x = 0; x < 640; x++) {
int pixel_index = (y * 640 + x) * 4;
for (int y = 0; y < COM_CFG_HEIGHT; y++) {
for (int x = 0; x < COM_CFG_WIDTH; x++) {
int pixel_index = (y * COM_CFG_WIDTH + x) * 4;
// Assuming standard Little-Endian 32-bit BGRA format
pixel_buffer[pixel_index + 0] = (x) % 256; // Blue
@@ -28,8 +29,8 @@ com_def_export_sym("plr_draw_screen") void plr_draw_screen(
}
}
com_mat_t proj =
com_mat_perspective(640, 480, 1, 100 * COM_FIX_FRACUNIT, COM_FIX_PIHALF);
com_mat_t proj = com_mat_perspective(COM_CFG_WIDTH, COM_CFG_HEIGHT, 1,
100 * COM_FIX_FRACUNIT, COM_FIX_PIHALF);
com_mat_t view = com_mat_look_at(com_vec_from(0, 0, 0),
com_vec_from(0, COM_FIX_FRACUNIT, 0),
com_vec_from(-COM_FIX_FRACUNIT, 0, 0));
+4 -1
View File
@@ -1,4 +1,5 @@
<!DOCTYPE html>
<!-- TODO: https://j9t.github.io/html-minifier-next/ -->
<html lang="en">
<head>
<meta charset="UTF-8">
@@ -16,6 +17,8 @@
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// TODO: Throttle FPS.
async function main() {
const wasm = await WebAssembly.instantiateStreaming(fetch("player.wasm"));
const { memory, plr_draw_screen, plr_draw_get_pixel_buffer } = wasm.instance.exports;
@@ -24,7 +27,7 @@
const wasmBuffer = new Uint8ClampedArray(
memory.buffer,
bufferPtr,
640 * 480 * 4
640 * 480 * 4 // TODO: Retrieve actual values.
);
const imgData = new ImageData(wasmBuffer, 640, 480);
+48
View File
@@ -0,0 +1,48 @@
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
PHDRS
{
text PT_LOAD FLAGS(5); /* RX: Read/Execute */
}
SECTIONS
{
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000));
. = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
.text ALIGN(16) : {
*(.text*)
}
.rodata : {
*(.rodata*)
}
.data : {
*(.data*)
}
.bss : {
*(.bss*)
*(COMMON)
}
/* Discard bloat and unnecessary ELF sections */
/DISCARD/ : {
*(.note.GNU-stack)
*(.gnu_debuglink)
*(.gnu.lto_*)
*(.note.gnu.build-id)
*(.eh_frame_hdr)
*(.eh_frame_entry)
*(.eh_frame)
*(.comment)
*(.interp)
*(.dynsym)
*(.dynstr)
*(.hash)
*(.gnu.hash)
}
}