Merge remote-tracking branch 'EpicGamesExt/master'

This commit is contained in:
2025-10-10 21:50:09 -04:00
232 changed files with 23973 additions and 23030 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
call build %${{ matrix.target }}% %${{ matrix.compiler }}% %${{ matrix.mode }}% || exit /b 1
call build "${{ matrix.target }}" "${{ matrix.compiler }}" "${{ matrix.mode }}" || exit /b 1
run-torture:
runs-on: windows-2022
+16
View File
@@ -0,0 +1,16 @@
# Change Log v9.22.0-alpha
## Linker
- Changed symbol resolution in libaries to match MSVC behavior.
- Optimized image building step to reduce memory usage.
- Linker memory maps all input files by default to lower memory usage. (`/RAD_MEMORY_MAP_FILES`)
- If debug info is available, linker uses it to show file and line number for unresolved relocations.
- Improved base relocation build performance for large images, cutting build time by 70%.
- Added stubs for `/Brepro`, `/D2`, and /ErrorReport to improve compatability with existing response files
- Implemented section garbage collection (`/OPT:REF`)
- Fixed bug where thread local variables pointed to incorrect types.
- Changed rules for weak and undefined symbols, now weak symbol is not allowed to replace an undefined symbol.
- Linker no longer creates thunks for imports that don't require them.
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2024 Epic Games Tools
Copyright (c) Epic Games Tools
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
+172 -141
View File
@@ -1,45 +1,93 @@
# The RAD Debugger Project
_**Note:** This README does not document usage instructions and tips for the
_**NOTE:** This README does not document usage instructions and tips for the
debugger itself, and is intended as a technical overview of the project. The
debugger's README, which includes usage instructions and tips, can be found
packaged along with debugger releases, or within the `build` folder after a
local copy has been built._
local copy has been built. You can find pre-built release binaries
[here](https://github.com/EpicGamesExt/raddebugger/releases)._
The RAD Debugger is a native, user-mode, multi-process, graphical debugger. It
currently only supports local-machine Windows x64 debugging with PDBs, with
plans to expand and port in the future. In the future we'll expand to also
support native Linux debugging and DWARF debug info.
The RAD Debugger is currently in *ALPHA*. In order to get the debugger bullet-
proof, it'd greatly help out if you submitted the issues you find here, along
with any information you can gather, like dump files (along with the build you
used), instructions to reproduce, test executables, and so on.
The debugger is currently in *ALPHA*. In order to get the debugger
bullet-proof, it'd greatly help out if you submitted the issues you find
[here](https://github.com/EpicGamesExt/raddebugger/issues), along with any
information you can gather, like dump files (along with the build you used),
instructions to reproduce, test executables, and so on.
You can download pre-built binaries for the debugger
[here](https://github.com/EpicGamesExt/raddebugger/releases).
In addition to the debugger, we aim to further improve the toolchain with two
additional related technologies: **(1)** the RAD Debug Info (RDI) format, and
**(2)** the RAD Linker.
The RAD Debugger project aims to simplify the debugger by simplifying and
unifying the underlying debug info format. In that pursuit we've built the RAD
Debug Info (RDI) format, which is what the debugger parses and uses. To work
with existing toolchains, we convert PDB (and eventually PE/ELF files with
embedded DWARF) into the RDI format on-demand.
## The RAD Debug Info (RDI) Format
The RAD Debug Info (RDI) format is our custom debug information format, which
the debugger parses and uses, rather than the debug information natively
produced by toolchains, like PDB or DWARF. To work with these existing
toolchains, we convert PDB (and eventually PE/ELF files with embedded DWARF)
into the RDI format on-demand.
The RDI format is currently specified in code, in the files within the
`src/lib_rdi_format` folder. The other relevant folders for working with the
format are:
`src/lib_rdi` folder. In [`rdi.h`](src/lib_rdi/rdi.h) and
[`rdi.c`](src/lib_rdi/rdi.c), the types and functions which define the format
itself are specified. In [`rdi_parse.h`](src/lib_rdi/rdi_parse.h) and
[`rdi_parse.c`](src/lib_rdi/rdi_parse.c), helpers for parsing the format are
included.
- `lib_rdi_make`: The "RAD Debug Info Make" library, for making RDI debug info.
- `rdi_from_pdb`: Our PDB-to-RDI converter. Can be used as a helper codebase
layer, or built as an executable with a command line interface frontend.
- `rdi_from_dwarf`: Our in-progress DWARF-to-RDI converter.
- `rdi_dump`: Our RDI textual dumping utility.
We also have an in-progress library for constructing and serializing RDI data,
located within the `src/lib_rdi_make` folder.
## Development Setup Instructions
Our `radbin` utility (accessible through the debugger too, via the `--bin`
command line argument) is capable of converting native debug information formats
to RDI, and of producing textual dumps of contents stored within RDI files.
**Note: Currently, only x64 Windows development is supported.**
## The RAD Linker
### 1. Installing the Required Tools (MSVC & Windows SDK)
The RAD Linker is a new performance linker for generating x64 PE/COFF binaries.
It is designed to be very fast when creating gigantic executables. It generates
standard PDB files for debugging, but it can also (optionally) natively create
RAD Debug Info too, which is useful both to eliminate on-demand conversion time
when debugging, but also for huge executables that otherwise create broken
PDBs that overflow internal 32-bit tables.
The RAD Linker is primarily optimized to handle huge linking projects. In our
test cases (where debug info is multiple gigabytes), we see 50% faster link
times.
The command line syntax is fully compatible with MSVC; you can get a full list
of implemented switches from `/help`.
Our current designed-for use case for the linker is to help with the
compile-debug cycle of huge projects. We don't yet have support for
link-time-optimizations, but this feature is on the road map.
By default, the linker spawns as many threads as there are cores, so if you plan
to run multiple linkers in parallel, you can limit the number of thread workers
via `/rad_workers`.
We also have support for large memory pages, which, when enabled, reduce link
time by another 25%. To link with large pages, you need to explicitly request
them via `/rad_large_pages`. Large pages are off by default, since Windows
support for large pages is a bit buggy; we recommend they only be used in Docker
or VM images where the environment is reset after each link. In a standard
Windows environment, using large pages otherwise will fragment memory quickly,
forcing a reboot. We are working on a Linux port of the linker that will be able
to build with large pages robustly.
A benchmark of the linker's performance is below:
![AMD Ryzen Threadripper PRO 3995WX 64-Cores, 256 GiB RAM (Windows x64)](https://github.com/user-attachments/assets/a95b382a-76b4-4a4c-b809-b61fe25e667a)
---
# Project Development Setup Instructions
**NOTE: Currently, only x64 Windows development is supported for the project.**
## 1. Installing the Required Tools (MSVC & Windows SDK)
In order to work with the codebase, you'll need the [Microsoft C/C++ Build Tools
v15 (2017) or later](https://aka.ms/vs/17/release/vs_BuildTools.exe), for both
@@ -48,7 +96,7 @@ the Windows SDK and the MSVC compiler and linker.
If the Windows SDK is installed (e.g. via installation of the Microsoft C/C++
Build Tools), you may also build with [Clang](https://releases.llvm.org/).
### 2. Build Environment Setup
## 2. Build Environment Setup
Building the codebase can be done in a terminal which is equipped with the
ability to call either MSVC or Clang from command line.
@@ -92,9 +140,9 @@ You should see the following output:
[msvc compile]
[default mode, assuming `raddbg` build]
metagen_main.c
searching C:\devel\raddebugger/src... 447 files found
parsing metadesk... 14 metadesk files parsed
gathering tables... 93 tables found
searching C:\devel\raddebugger/src... 458 files found
parsing metadesk... 16 metadesk files parsed
gathering tables... 97 tables found
generating layer code...
raddbg_main.c
```
@@ -112,23 +160,43 @@ build release
This build will take significantly longer.
## Short-To-Medium-Term Roadmap
By default, `build.bat` only builds the debugger if no arguments (or just
`release`) are passed, but additional arguments can be passed to build the RAD
Linker, or the `radbin` CLI binary file utility:
```
build radlink release
build radbin release
```
---
# Project Roadmap
### The Initial Alpha Battle-Testing Phase
The first priority for the project is to ensure that the most crucial debugger
components are functioning extremely reliably for local, x64, Windows
debugging. This would include parts like debug info conversion, debug info
loading, process control, stepping, evaluation (correct usage of both location
info and type info), and a robust frontend which ensures the lower level parts
are usable.
The first priority for the project is to ensure that the most crucial components
are functioning extremely reliably for local, x64, Windows development.
For the debugger, this would include parts like debug info conversion, debug
info loading, process control, stepping, evaluation (correct usage of both
location info and type info), and a robust frontend which ensures the lower
level parts are usable. For the linker, this is a matter of reliability and
convergence with existing linker behavior.
We feel that the debugger has already come a long way in all of these respects,
but given the massive set of possible combinations of languages, build
settings, toolchains, used language features, and patterns of generated code,
there are still cases where the debugger has not been tested, and so there are
still issues. So, we feel that the top priority is eliminating these issues,
such that the debugging experience is rock solid.
We feel that we've already come a long way in all of these respects, but given
the massive set of possible combinations of languages, build settings,
toolchains, used language features, and patterns of generated code, we still
expect some issues, and are prioritizing these issues being resolved first.
We also hope to continue to improve performance in this phase. For the debugger,
this primarily includes frontend performance, introducing caches when economical
to do so, and tightening existing systems up. For the linker, it has been mostly
tuned thus far for giant projects, and so we'd like to improve linking speed for
small-to-mid sized projects as well.
For the linker, there are also a number of features to come, like
dead-code-elimination (`/opt:ref`), and link-time-optimizations with the help
of `clang` (we won't support LTCG from MSVC, since it is undocumented).
### Local x64 Linux Debugging Phase
@@ -171,42 +239,7 @@ But for now, we're mostly focused on those first two phases.
---
# The RAD Linker
The RAD Linker is a new performance linker for generating x64 PE/COFF binaries. It is designed to be very fast when creating gigantic executables. It generates standard PDB files for debugging, but it can also optionally create RAD Debugger debug info too (useful for huge executables that otherwise create broken PDBs that overflow internal 32-bit tables).
The RAD Linker is primarily optimized to handle huge linking projects - in our test cases (where debug info is multiple gigabytes), we see 50% faster link times.
The command line syntax is fully compatible with MSVC and you can get a full list of implemented switches from `/help`.
Our current designed-for use case for the linker is to help with the compile-debug cycle of huge projects. We don't yet have support for dead-code-elimination or link-time-optimizations, but these features are on the road map.
By default, the RAD linker spawns as many threads as there are cores, so if you plan to run multiple linkers in parallel, you can limit the number of thread workers via `/rad_workers`.
We also have support for large memory pages, which, when enabled, reduce link time by
another 25%. To link with large pages, you need to explicitly request them via `/rad_large_pages`. Large pages are off by default, since Windows support for large pages is a bit buggy - we recommend they only be used in Docker or VM images where the environment is reset after each link. In a standard Windows environment, using large pages otherwise will fragment memory quickly forcing a reboot. We are working on a Linux port of the linker that will be able to build with large pages robustly.
## Short Term Roadmap
- Porting linker to Linux (for Windows executables, just running on Linux).
- Debug info features
- Get DWARF debug info converter up-and-running.
- Smooth out rough edges in RADDBGI builder.
- Improve build speed further (especially for tiny and mid sizes projects).
- Other features to come
- Dead-code-elimination via `/opt:ref`.
- Link Time Optimizations with the help of clang (we won't support LTCG from MSVC compiler since it is undocumented).
## To build the RAD Linker
- Setup development environment, [see](#Development-Setup-Instructions)
- Run `build radlink release` or if you have clang installed `build radlink release clang`. We favor latter option for better code generation.
If build was successful linker executable is placed in `build` folder under `radlink.exe`.
## Benchmarks
![AMD Ryzen Threadripper PRO 3995WX 64-Cores, 256 GiB RAM (Windows x64)](https://github.com/user-attachments/assets/a95b382a-76b4-4a4c-b809-b61fe25e667a)
---
# Codebase Introduction
## Top-Level Directory Descriptions
@@ -221,7 +254,7 @@ also exist:
- `local`: Local files, used for local build configuration input files. Not
checked in to version control.
## Codebase Introduction
## Layer Descriptions
The codebase is organized into *layers*. Layers are separated either to isolate
certain problems, and to allow inclusion into various builds without needing to
@@ -249,35 +282,33 @@ so in other words, layers are arranged into a directed acyclic graph.
A few layers are built to be used completely independently from the rest of the
codebase, as libraries in other codebases and projects. As such, these layers do
not depend on any other layers in the codebase. The folders which contain these
layers are prefixed with `lib_`, like `lib_rdi_format`.
layers are prefixed with `lib_`, like `lib_rdi`.
A list of the layers in the codebase and their associated namespaces is below:
- `async` (`ASYNC_`): Implements a system for asynchronous work to be queued
and executed on a thread pool.
- `base` (no namespace): Universal, codebase-wide constructs. Strings, math,
memory allocators, helper macros, command-line parsing, and so on. Depends
on no other codebase layers.
- `codeview` (`CV_`): Code for parsing and/or writing the CodeView format.
- `coff` (`COFF_`): Code for parsing and/or writing the COFF (Common Object File
memory allocators, helper macros, command-line parsing, and so on. Requires
no other codebase layers.
- `codeview` (`CV_`): Code for parsing and writing the CodeView format.
- `coff` (`COFF_`): Code for parsing and writing the COFF (Common Object File
Format) file format.
- `ctrl` (`CTRL_`): The debugger's "control system" layer. Implements
asynchronous process control, stepping, and breakpoints for all attached
processes. Runs in lockstep with attached processes. When it runs, attached
processes are halted. When attached processes are running, it is halted.
Driven by a debugger frontend on another thread.
- `dasm_cache` (`DASM_`): An asynchronous disassembly decoder and cache. Users
ask for disassembly for some data, with a particular architecture, and other
various parameters, and threads implemented in this layer decode and cache the
disassembly for that data with those parameters.
- `dbgi` (`DI_`): An asynchronous debug info loader and cache. Loads debug info
stored in the RDI format. Users ask for debug info for a particular path, and
on separate threads, this layer loads the associated debug info file. If
necessary, it will launch a separate conversion process to convert original
debug info into the RDI format.
- `dasm_cache` (`DASM_`): Asynchronous disassembly computation, and a cache to
store asynchronously produced disassembly artifacts.
- `dbgi` (`DI_`): Asynchronous debug info loading, and a cache for loaded
debug info. Loads RAD Debug Info (RDI) files. Launches separate processes for
on-demand conversion to the RDI format if necessary. Also provides various
asynchronous operations for using debug information, like fuzzy searching
across all records in loaded debug information.
- `dbg_engine` (`D_`): Implements the core debugger system, without any
graphical components. This contains top-level logic for things like stepping,
launching, freezing threads, mid-run breakpoint addition, some caching layers,
and so on.
launching, freezing threads, mid-run breakpoint addition, some caches, and so
on.
- `demon` (`DMN_`): An abstraction layer for local-machine, low-level process
control. The abstraction is used to provide a common interface for process
control on target platforms. Used to implement part of `ctrl`.
@@ -285,15 +316,17 @@ A list of the layers in the codebase and their associated namespaces is below:
debugger's purposes, using the underlying `render` abstraction layer. Provides
high-level APIs for various draw commands, but takes care of batching them,
and so on.
- `eval` (`E_`): Implements a compiler for an expression language built for
evaluation of variables, registers, types, and more, from debugger-attached
processes, debug info, debugger state, and files. Broken into several phases
mostly corresponding to traditional compiler phases - lexer, parser,
type-checker, IR generation, and IR evaluation.
- `dwarf` (`DW_`): Code for parsing the DWARF format.
- `elf` (`ELF_`): Code for parsing the ELF format.
- `eval` (`E_`): A compiler for an expression language, built for evaluation of
variables, registers, types, and more, from debugger-attached processes,
debug info, debugger state, and files. Broken into several phases mostly
corresponding to traditional compiler phases: lexer, parser, type-checker, IR
generation, and IR evaluation.
- `eval_visualization` (`EV_`): Implements the core non-graphical evaluation
visualization engine, which can be used to visualize evaluations (provided by
the `eval` layer) in a number of ways. Implements core data structures and
transforms for the `Watch` view.
transforms for watch tables.
- `file_stream` (`FS_`): Provides asynchronous file loading, storing the
artifacts inside of the cache implemented by the `hash_store` layer, and
hot-reloading the contents of files when they change. Allows callers to map
@@ -304,31 +337,29 @@ A list of the layers in the codebase and their associated namespaces is below:
layer.
- `font_provider` (`FP_`): An abstraction layer for various font file decoding
and font rasterization backends.
- `fuzzy_search` (`FZY_`): Provides a fuzzy searching engine for doing
large, asynchronous fuzzy searches. Used by the debugger for implementing
things like the symbol lister or the `Procedures` view, which search across
all loaded debug info records, using fuzzy matching rules.
- `geo_cache` (`GEO_`): Implements an asynchronously-filled cache for GPU
geometry data, filled by data sourced in the `hash_store` layer's cache. Used
for asynchronously preparing data for visualization.
- `hash_store` (`HS_`): Implements a cache for general data blobs, keyed by a
128-bit hash of the data. Also implements a 128-bit key cache on top, where
the keys refer to a unique identity, associated with a 128-bit hash, where the
hash may change across time. Used as a general data store by other layers.
128-bit hash of the data. Also implements a keying system on top, where keys
refer to a unique identity which corresponds to a history of 128-bit hashes.
Used as a general data store by other layers.
- `lib_raddbg_markup` (`RADDBG_`): Standalone library for marking up user
programs to work with various features in the debugger. Does not depend on
`base`, and can be independently relocated to other codebases.
- `lib_rdi_format` (`RDI_`): Standalone library which defines the core RDI types
- `lib_rdi` (`RDI_`): Standalone library which defines the core RDI types
and helper functions for reading and writing the RDI debug info file format.
Does not depend on `base`, and can be independently relocated to other
codebases.
- `lib_rdi_make` (`RDIM_`): Standalone library for constructing RDI debug info
data. Does not depend on `base`, and can be independently relocated
to other codebases.
- `linker` (`LNK_`): The layer which implements the RAD Linker executable
itself.
- `mdesk` (`MD_`): Code for parsing Metadesk files (stored as `.mdesk`), which
is the JSON-like (technically a JSON superset) text format used for the
debugger's user and project configuration files, view rules, and metacode,
which is parsed and used to generate code with the `metagen` layer.
debugger's user and project configuration files and metacode, which is parsed
and used to generate code with the `metagen` layer.
- `metagen` (`MG_`): A metaprogram which is used to generate primarily code and
data tables. Consumes Metadesk files, stored with the extension `.mdesk`, and
generates C code which is then included by hand-written C code. Currently, it
@@ -337,18 +368,13 @@ A list of the layers in the codebase and their associated namespaces is below:
tables, which are then used to produce e.g. C `enum`s and a number of
associated data tables. There are also a number of other generation features,
like embedding binary files or complex multi-line strings into source code.
This layer cannot depend on any other layer in the codebase directly,
including `base`, because it may be used to generate code for those layers. To
still use `base` and `os` layer features in the `metagen` program, a separate,
duplicate version of `base` and `os` are included in this layer. They are
updated manually, as needed. This is to ensure the stability of the
metaprogram.
- `msf` (`MSF_`): Code for parsing and/or writing the MSF file format.
- `msf` (`MSF_`): Code for parsing and writing the MSF file format.
- `msvc_crt` (`MSCRT_`): Code for parsing that's specific to the MSVC CRT.
- `mule` (no namespace): Test executables for battle testing debugger
functionality.
- `mutable_text` (`MTX_`): Implements an asynchronously-filled-and-mutated
cache for text buffers which are mutated across time. In the debugger, this is
used to implement the `Output` view.
used to implement the `Output` log.
- `natvis` (no namespace): NatVis files for type visualization of the codebase's
types in other debuggers.
- `os/core` (`OS_`): An abstraction layer providing core, non-graphical
@@ -357,26 +383,30 @@ A list of the layers in the codebase and their associated namespaces is below:
- `os/gfx` (`OS_`): An abstraction layer, building on `os/core`, providing
graphical operating system features under an abstract API, which is
implemented per-target-operating-system.
- `path` (`PATH_`): Small helpers for manipulating file path strings.
- `pdb` (`PDB_`): Code for parsing and/or writing the PDB file format.
- `pe` (`PE_`): Code for parsing and/or writing the PE (Portable Executable)
file format.
- `pdb` (`PDB_`): Code for parsing and writing the PDB file format.
- `pe` (`PE_`): Code for parsing and writing the PE (Portable Executable) file
format.
- `ptr_graph_cache` (`PG_`): An in-progress layer which will supply
asynchronously-computed pointer graphs, used for graph visualization in the
debugger, including structures like trees and linked lists.
- `radbin` (`RB_`): The layer implementing the `radbin` binary utility
executable.
- `raddbg` (`RD_`): The layer which ties everything together for the main
graphical debugger. Implements the debugger's graphical frontend, all of the
debugger-specific UI, the debugger executable's command line interface, and
all of the built-in visualizers.
- `rdi_breakpad_from_pdb` (`P2B_`): Our implementation, using the codebase's RDI
technology, for extracting information from PDBs and generating Breakpad text
dumps.
- `rdi_dump` (no namespace): A dumper utility program for dumping
textualizations of RDI debug info files.
- `rdi_format` (no namespace): A layer which includes the `lib_rdi_format` layer
and bundles it with codebase-specific helpers, to easily include the library
in codebase programs, and have it be integrated with codebase constructs.
- `rdi_from_dwarf` (`D2R_`): Our in-progress implementation of DWARF-to-RDI
conversion.
- `rdi_from_pdb` (`P2R_`): Our implementation of PDB-to-RDI conversion.
- `rdi_make` (no namespace): A layer which includes the `lib_rdi_make` layer and
graphical debugger executable. Implements the debugger's graphical frontend,
all of the debugger-specific UI, the debugger executable's command line
interface, and all of the built-in visualizers.
- `rdi` (`RDI_`): A layer which includes the `lib_rdi` layer and bundles it with
codebase-specific helpers, to easily include the library in codebase programs,
and have it be integrated with codebase constructs.
- `rdi_from_coff` (`C2R_`): Code for converting information in COFF files to the
equivalent RDI data.
- `rdi_from_dwarf` (`D2R_`): In-progress code for converting DWARF to the
equivalent RDI data.
- `rdi_from_elf` (`E2R_`)): Code for converting ELF data to the equivalent RDI
data.
- `rdi_from_pdb` (`P2R_`): Code for converting PDB data to the equivalent RDI
data.
- `rdi_make` (`RDIM_`): A layer which includes the `lib_rdi_make` layer and
bundles it with codebase-specific helpers, to easily include the library in
codebase programs, and have it be integrated with codebase constructs.
- `regs` (`REGS_`): Types, helper functions, and metadata for registers on
@@ -388,11 +418,12 @@ A list of the layers in the codebase and their associated namespaces is below:
as-needed basis. Higher level drawing features are implemented in the `draw`
layer.
- `scratch` (no namespace): Scratch space for small and transient test programs.
- `texture_cache` (`TEX_`): Implements an asynchronously-filled cache for GPU
texture data, filled by data sourced in the `hash_store` layer's cache. Used
for asynchronously preparing data for visualization.
- `text_cache` (`TXT_`): Implements an asynchronously-filled cache for textual
analysis data (tokens, line ranges, and so on), filled by data sourced in the
- `tester` (no namespace): A program used for automated testing.
- `texture_cache` (`TEX_`): An asynchronously-filled cache for GPU texture data,
filled by data sourced in the `hash_store` layer's cache. Used for
asynchronously preparing data for visualization.
- `text_cache` (`TXT_`): An asynchronously-filled cache for textual analysis
data (tokens, line ranges, and so on), filled by data sourced in the
`hash_store` layer's cache. Used for asynchronously preparing data for
visualization (like for the source code viewer).
- `third_party` (no namespace): External code from other projects, which some
+33 -4
View File
@@ -27,7 +27,7 @@ cd /D "%~dp0"
:: - `spall`: enable spall profiling support
:: --- Unpack Arguments -------------------------------------------------------
for %%a in (%*) do set "%%a=1"
for %%a in (%*) do set "%%~a=1"
if not "%msvc%"=="1" if not "%clang%"=="1" set msvc=1
if not "%release%"=="1" set debug=1
if "%debug%"=="1" set release=0 && echo [debug mode]
@@ -43,18 +43,37 @@ if "%telemetry%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_TELE
if "%spall%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_SPALL=1 && echo [spall profiling enabled]
if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=address && echo [asan enabled]
if "%opengl%"=="1" set auto_compile_flags=%auto_compile_flags% -DR_BACKEND=R_BACKEND_OPENGL && echo [opengl render backend]
if "%pgo%"=="1" (
if "%no_meta%"=="" echo ERROR: PGO build must have no_meta argument || exit /b 1
where llvm-profdata /q || echo llvm-profdata is not in the PATH || exit /b 1
if "%clang%"=="1" (
if "%pgo_run%" == "1" (
call llvm-profdata merge %LLVM_PROFILE_FILE% -output=%~dp0build\build.profdata || exit /b 1
set auto_compile_flags=%auto_compile_flags% -fprofile-use=%~dp0build\build.profdata
set pgo_run=0
) else (
echo [pgo enabled]
set auto_compile_flags=%auto_compile_flags% -fprofile-generate -mllvm -vp-counters-per-site=5
set LLVM_PROFILE_FILE=%~dp0build\build.profraw
set pgo_run=1
)
) else (
echo ERROR: PGO build is not supported with current compiler
exit /b 1
)
)
:: --- Compile/Link Line Definitions ------------------------------------------
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7
set cl_debug= call cl /Od /Ob1 /DBUILD_DEBUG=1 %cl_common% %auto_compile_flags%
set cl_release= call cl /O2 /DBUILD_DEBUG=0 %cl_common% %auto_compile_flags%
set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /pdbaltpath:%%%%_PDB%%%% /NATVIS:"%~dp0\src\natvis\base.natvis" /noexp
set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /pdbaltpath:%%%%_PDB%%%% /NATVIS:"%~dp0\src\natvis\base.natvis" /noexp /nocoffgrpinfo /opt:ref /opt:icf
set cl_out= /out:
set cl_linker=
set clang_common= -I..\src\ -I..\local\ -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-unused-parameter -Wno-writable-strings -Wno-missing-field-initializers -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf -ferror-limit=10000
set clang_common= -I..\src\ -I..\local\ -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-unused-parameter -Wno-writable-strings -Wno-missing-field-initializers -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf -ferror-limit=10000 -mcx16
set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 %clang_common% %auto_compile_flags%
set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 %clang_common% %auto_compile_flags%
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /pdbaltpath:%%%%_PDB%%%% -Xlinker /NATVIS:"%~dp0\src\natvis\base.natvis"
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /pdbaltpath:%%%%_PDB%%%% -Xlinker /NATVIS:"%~dp0\src\natvis\base.natvis" -Xlinker /opt:ref -Xlinker /opt:icf
set clang_out= -o
set clang_linker= -Xlinker
@@ -139,3 +158,13 @@ if "%didbuild%"=="" (
echo [WARNING] no valid build target specified; must use build target names as arguments to this script, like `build raddbg` or `build rdi_from_pdb`.
exit /b 1
)
:: --- PGO Run ----------------------------------------------------------------
if "%pgo_run%"=="1" (
if "%radlink%"=="1" (
pushd local\lyra_pgo
call %~dp0build\radlink @lyra.rsp /rad_alt_pch_dir:%~dp0local\lyra_pgo || exit /b 1
popd
)
call %0 %*
)
+2 -2
View File
@@ -47,13 +47,13 @@ commands =
{
//- rjf: [raddbg]
.f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
//- rjf: [raddbg wsl]
// .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
//- rjf: [scratch]
.f2 = { .win = "build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
.f2 = { .win = "build radbin debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
//- rjf: [textperf]
// .f1 = { .win = "raddbg_stable --ipc kill_all && build no_meta telemetry textperf && raddbg_stable --ipc bring_to_front && raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
+565
View File
@@ -0,0 +1,565 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Layer Initialization
internal void
ac_init(void)
{
Arena *arena = arena_alloc();
ac_shared = push_array(arena, AC_Shared, 1);
ac_shared->arena = arena;
ac_shared->cache_slots_count = 256;
ac_shared->cache_slots = push_array(arena, AC_Cache *, ac_shared->cache_slots_count);
ac_shared->cache_stripes = stripe_array_alloc(arena);
for EachElement(idx, ac_shared->req_batches)
{
ac_shared->req_batches[idx].mutex = mutex_alloc();
ac_shared->req_batches[idx].arena = arena_alloc();
}
}
////////////////////////////////
//~ rjf: Cache Lookups
internal AC_Artifact
ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U64 endt_us)
{
ProfBeginFunction();
AC_RequestBatch *req_batch = &ac_shared->req_batches[params->flags & AC_Flag_HighPriority ? 0 : 1];
//- rjf: create function -> cache
AC_Cache *cache = 0;
{
U64 cache_hash = u64_hash_from_str8(str8_struct(&params->create));
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
{
RWMutexScope(cache_stripe->rw_mutex, write_mode)
{
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
{
if(c->create == params->create)
{
cache = c;
break;
}
}
if(write_mode && cache == 0)
{
cache = push_array(cache_stripe->arena, AC_Cache, 1);
SLLStackPush(ac_shared->cache_slots[cache_slot_idx], cache);
cache->create = params->create;
cache->destroy = params->destroy;
cache->slots_count = Max(256, params->slots_count);
cache->slots = push_array(cache_stripe->arena, AC_Slot, cache->slots_count);
cache->stripes = stripe_array_alloc(cache_stripe->arena);
}
}
if(cache != 0)
{
break;
}
}
}
//- rjf: unpack key
U64 hash = u64_hash_from_str8(key);
U64 slot_idx = hash%cache->slots_count;
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
//- rjf: cache * key -> existing artifact
B32 artifact_is_stale = 0;
B32 got_artifact = 0;
B32 need_request = 0;
AC_Artifact artifact = {0};
RWMutexScope(stripe->rw_mutex, 0)
{
for(AC_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key, key, 0))
{
ins_atomic_u64_eval_assign(&n->last_requested_gen, params->gen);
B32 is_stale = (n->last_completed_gen != params->gen);
if(ins_atomic_u64_eval(&n->completion_count) != 0 && (!is_stale || !(params->flags & AC_Flag_WaitForFresh)))
{
got_artifact = 1;
artifact_is_stale = is_stale;
artifact = n->val;
access_touch(access, &n->access_pt, stripe->cv);
}
if(is_stale)
{
B32 got_task = (ins_atomic_u64_eval_cond_assign(&n->working_count, 1, 0) == 0);
need_request = got_task;
}
break;
}
}
}
//- rjf: didn't get artifact we want? -> fall back to slow path
if(!got_artifact || need_request)
{
RWMutexScope(stripe->rw_mutex, 1) for(;;)
{
B32 out_of_time = (os_now_microseconds() >= endt_us);
// rjf: find node in cache
AC_Node *node = 0;
for(AC_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key, key, 0))
{
node = n;
break;
}
}
// rjf: no node? -> create
if(node == 0)
{
need_request = 1;
node = stripe->free;
if(node)
{
stripe->free = node->next;
}
else
{
node = push_array_no_zero(stripe->arena, AC_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
// TODO(rjf): string allocator for keys
node->key = str8_copy(stripe->arena, key);
node->working_count = 1;
node->evict_threshold_us = params->evict_threshold_us;
node->access_pt.last_time_touched_us = os_now_microseconds();
node->access_pt.last_update_idx_touched = update_tick_idx();
}
// rjf: request
if(need_request)
{
need_request = 0;
MutexScope(req_batch->mutex)
{
AC_RequestNode *n = push_array(req_batch->arena, AC_RequestNode, 1);
if(params->flags & AC_Flag_Wide)
{
SLLQueuePush(req_batch->first_wide, req_batch->last_wide, n);
req_batch->wide_count += 1;
}
else
{
SLLQueuePush(req_batch->first_thin, req_batch->last_thin, n);
req_batch->thin_count += 1;
}
n->v.key = str8_copy(req_batch->arena, key);
n->v.gen = params->gen;
n->v.last_requested_gen = &node->last_requested_gen;
n->v.create = params->create;
}
cond_var_broadcast(async_tick_start_cond_var);
ins_atomic_u32_eval_assign(&async_loop_again, 1);
}
// rjf: get value from node, if possible
if(!got_artifact && ins_atomic_u64_eval(&node->completion_count) != 0 && ((node->last_completed_gen == params->gen) || !(params->flags & AC_Flag_WaitForFresh) || out_of_time))
{
got_artifact = 1;
artifact_is_stale = (node->last_completed_gen == params->gen);
artifact = node->val;
access_touch(access, &node->access_pt, stripe->cv);
}
// rjf: abort if needed
if(out_of_time || got_artifact || is_async_thread)
{
break;
}
// rjf: wait for results
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, endt_us);
}
}
//- rjf: report staleness
if(params->stale_out)
{
params->stale_out[0] = artifact_is_stale;
}
ProfEnd();
return artifact;
}
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void
ac_async_tick(void)
{
Temp scratch = scratch_begin(0, 0);
//////////////////////////////
//- rjf: do eviction pass across all caches
//
for EachIndex(cache_slot_idx, ac_shared->cache_slots_count)
{
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
RWMutexScope(cache_stripe->rw_mutex, 0)
{
for EachNode(cache, AC_Cache, ac_shared->cache_slots[cache_slot_idx])
{
Rng1U64 slot_range = lane_range(cache->slots_count);
for EachInRange(slot_idx, slot_range)
{
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
{
B32 slot_has_work = 0;
RWMutexScope(stripe->rw_mutex, write_mode)
{
for(AC_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(access_pt_is_expired(&n->access_pt, .time = n->evict_threshold_us) && ins_atomic_u64_eval(&n->working_count) == 0)
{
slot_has_work = 1;
if(!write_mode)
{
break;
}
else
{
DLLRemove(slot->first, slot->last, n);
n->next = (AC_Node *)stripe->free;
stripe->free = n;
if(cache->destroy)
{
cache->destroy(n->val);
}
}
}
}
}
if(!slot_has_work)
{
break;
}
}
}
}
}
}
//////////////////////////////
//- rjf: gather requests
//
typedef struct RequestBatchTask RequestBatchTask;
struct RequestBatchTask
{
AC_Request *wide;
U64 wide_count;
AC_Request *thin;
U64 thin_count;
};
RequestBatchTask *tasks = 0;
U64 tasks_count = 0;
if(lane_idx() == 0)
{
tasks_count = 2;
tasks = push_array(scratch.arena, RequestBatchTask, tasks_count);
for EachElement(task_idx, ac_shared->req_batches)
{
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
MutexScope(batch->mutex)
{
tasks[task_idx].wide_count = batch->wide_count;
tasks[task_idx].thin_count = batch->thin_count;
tasks[task_idx].wide = push_array(scratch.arena, AC_Request, tasks[task_idx].wide_count);
tasks[task_idx].thin = push_array(scratch.arena, AC_Request, tasks[task_idx].thin_count);
{
U64 idx = 0;
for EachNode(n, AC_RequestNode, batch->first_wide)
{
MemoryCopyStruct(&tasks[task_idx].wide[idx], &n->v);
tasks[task_idx].wide[idx].key = str8_copy(scratch.arena, tasks[task_idx].wide[idx].key);
idx += 1;
}
}
{
U64 idx = 0;
for EachNode(n, AC_RequestNode, batch->first_thin)
{
MemoryCopyStruct(&tasks[task_idx].thin[idx], &n->v);
tasks[task_idx].thin[idx].key = str8_copy(scratch.arena, tasks[task_idx].thin[idx].key);
idx += 1;
}
}
arena_clear(batch->arena);
batch->first_wide = batch->last_wide = batch->first_thin = batch->last_thin = 0;
batch->wide_count = batch->thin_count = 0;
}
}
}
lane_sync_u64(&tasks, 0);
lane_sync_u64(&tasks_count, 0);
lane_sync();
//////////////////////////////
//- rjf: do all requests
//
for EachIndex(task_idx, tasks_count)
{
lane_sync();
RequestBatchTask *task = &tasks[task_idx];
//- rjf: set up cancellation signal
U64 cancelled = 0;
U64 *cancelled_ptr = 0;
if(lane_idx() == 0)
{
cancelled_ptr = &cancelled;
}
lane_sync_u64(&cancelled_ptr, 0);
//- rjf: do all wide requests for this priority
U64 done_wide_count = 0;
ProfScope("wide requests (p%I64u)", task_idx)
{
for EachIndex(idx, task->wide_count)
{
lane_sync();
AC_Request *r = &task->wide[idx];
// rjf: any new higher priority tasks? -> cancel
if(task_idx == 1 && idx != 0) MutexScope(ac_shared->req_batches[0].mutex)
{
if(ac_shared->req_batches[0].wide_count != 0 || ac_shared->req_batches[0].thin_count != 0)
{
ins_atomic_u64_eval_assign(cancelled_ptr, 1);
}
}
// rjf: cancelled? -> exit
if(ins_atomic_u64_eval(cancelled_ptr))
{
break;
}
// rjf: compute val
B32 retry = 0;
AC_Artifact val = r->create(r->key, r->gen, r->last_requested_gen, &retry);
// rjf: retry? -> resubmit request
if(retry && lane_idx() == 0)
{
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
MutexScope(batch->mutex)
{
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
SLLQueuePush(batch->first_wide, batch->last_wide, n);
batch->wide_count += 1;
MemoryCopyStruct(&n->v, r);
n->v.key = str8_copy(batch->arena, n->v.key);
}
ins_atomic_u32_eval_assign(&async_loop_again, 1);
}
// rjf: create function -> cache
AC_Cache *cache = 0;
if(!retry && lane_idx() == 0)
{
U64 cache_hash = u64_hash_from_str8(str8_struct(&r->create));
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
RWMutexScope(cache_stripe->rw_mutex, 0)
{
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
{
if(c->create == r->create)
{
cache = c;
break;
}
}
}
}
// rjf: write value into cache
if(!retry && lane_idx() == 0)
{
U64 hash = u64_hash_from_str8(r->key);
U64 slot_idx = hash%cache->slots_count;
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
RWMutexScope(stripe->rw_mutex, 1)
{
for(AC_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key, r->key, 0))
{
n->last_completed_gen = r->gen;
n->val = val;
ins_atomic_u64_dec_eval(&n->working_count);
ins_atomic_u64_inc_eval(&n->completion_count);
}
}
}
cond_var_broadcast(stripe->cv);
}
// rjf: increment count
lane_sync();
done_wide_count += 1;
}
}
lane_sync();
//- rjf: do all thin requests for this priority
U64 done_thin_count = 0;
ProfScope("thin requests (p%I64u)", task_idx)
{
U64 req_take_counter = 0;
U64 *req_take_counter_ptr = 0;
if(lane_idx() == 0)
{
req_take_counter_ptr = &req_take_counter;
}
lane_sync_u64(&req_take_counter_ptr, 0);
for(;;)
{
// rjf: any new higher priority tasks? -> cancel
if(task_idx == 1 && ins_atomic_u64_eval(req_take_counter_ptr) >= task->thin_count/2) MutexScope(ac_shared->req_batches[0].mutex)
{
if(ac_shared->req_batches[0].wide_count != 0 || ac_shared->req_batches[0].thin_count != 0)
{
ins_atomic_u64_eval_assign(cancelled_ptr, 1);
}
}
// rjf: cancelled? -> exit
if(ins_atomic_u64_eval(cancelled_ptr))
{
break;
}
// rjf: take next task
U64 req_idx = ins_atomic_u64_inc_eval(req_take_counter_ptr) - 1;
if(req_idx >= task->thin_count) { break; }
AC_Request *r = &task->thin[req_idx];
// rjf: compute val
B32 retry = 0;
AC_Artifact val = r->create(r->key, r->gen, r->last_requested_gen, &retry);
// rjf: retry? -> resubmit request
if(retry)
{
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
MutexScope(batch->mutex)
{
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
SLLQueuePush(batch->first_thin, batch->last_thin, n);
batch->thin_count += 1;
MemoryCopyStruct(&n->v, r);
n->v.key = str8_copy(batch->arena, n->v.key);
}
ins_atomic_u32_eval_assign(&async_loop_again, 1);
}
// rjf: create function -> cache
AC_Cache *cache = 0;
if(!retry)
{
U64 cache_hash = u64_hash_from_str8(str8_struct(&r->create));
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
RWMutexScope(cache_stripe->rw_mutex, 0)
{
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
{
if(c->create == r->create)
{
cache = c;
break;
}
}
}
}
// rjf: write value into cache
if(!retry)
{
U64 hash = u64_hash_from_str8(r->key);
U64 slot_idx = hash%cache->slots_count;
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
RWMutexScope(stripe->rw_mutex, 1)
{
for(AC_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key, r->key, 0))
{
n->last_completed_gen = r->gen;
n->val = val;
ins_atomic_u64_dec_eval(&n->working_count);
ins_atomic_u64_inc_eval(&n->completion_count);
}
}
}
cond_var_broadcast(stripe->cv);
}
}
lane_sync();
done_thin_count = ins_atomic_u64_eval(req_take_counter_ptr);
lane_sync();
}
//- rjf: cancelled early, unfinished tasks? -> defer to next tick
if(lane_idx() == 0 && task_idx > 0)
{
B32 need_another_try = (done_wide_count < task->wide_count || done_thin_count < task->thin_count);
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
MutexScope(batch->mutex)
{
// rjf: push leftover wide tasks
for(U64 idx = done_wide_count; idx < task->wide_count; idx += 1)
{
AC_Request *r = &task->wide[idx];
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
SLLQueuePush(batch->first_wide, batch->last_wide, n);
batch->wide_count += 1;
MemoryCopyStruct(&n->v, r);
n->v.key = str8_copy(batch->arena, n->v.key);
}
// rjf: push leftover thin tasks
for(U64 idx = done_thin_count; idx < task->thin_count; idx += 1)
{
AC_Request *r = &task->thin[idx];
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
SLLQueuePush(batch->first_thin, batch->last_thin, n);
batch->thin_count += 1;
MemoryCopyStruct(&n->v, r);
n->v.key = str8_copy(batch->arena, n->v.key);
}
}
if(need_another_try)
{
ins_atomic_u32_eval_assign(&async_loop_again, 1);
}
}
lane_sync();
}
lane_sync();
scratch_end(scratch);
}
+152
View File
@@ -0,0 +1,152 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef ARTIFACT_CACHE_H
#define ARTIFACT_CACHE_H
////////////////////////////////
//~ rjf: Artifact Handle Type
typedef struct AC_Artifact AC_Artifact;
struct AC_Artifact
{
U64 u64[4];
};
////////////////////////////////
//~ rjf: Artifact Computation Function Types
typedef AC_Artifact AC_CreateFunctionType(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out);
typedef void AC_DestroyFunctionType(AC_Artifact artifact);
typedef U32 AC_Flags;
typedef enum AC_FlagsEnum
{
AC_Flag_WaitForFresh = (1<<0),
AC_Flag_HighPriority = (1<<1),
AC_Flag_Wide = (1<<2),
}
AC_FlagsEnum;
typedef struct AC_ArtifactParams AC_ArtifactParams;
struct AC_ArtifactParams
{
AC_CreateFunctionType *create;
AC_DestroyFunctionType *destroy;
U64 slots_count;
U64 gen;
U64 evict_threshold_us;
B32 *stale_out;
AC_Flags flags;
};
////////////////////////////////
//~ rjf: Cache Types
typedef struct AC_Request AC_Request;
struct AC_Request
{
String8 key;
U64 gen;
U64 *last_requested_gen;
AC_CreateFunctionType *create;
};
typedef struct AC_RequestNode AC_RequestNode;
struct AC_RequestNode
{
AC_RequestNode *next;
AC_Request v;
};
typedef struct AC_Node AC_Node;
struct AC_Node
{
AC_Node *next;
AC_Node *prev;
// rjf: key/gen/value
String8 key;
U64 last_requested_gen;
U64 last_completed_gen;
AC_Artifact val;
// rjf: metadata
AccessPt access_pt;
U64 working_count;
U64 completion_count;
U64 evict_threshold_us;
B32 cancelled;
U64 _unused_;
};
typedef struct AC_Slot AC_Slot;
struct AC_Slot
{
AC_Node *first;
AC_Node *last;
};
typedef struct AC_Cache AC_Cache;
struct AC_Cache
{
// rjf: link / key for cache-cache
AC_Cache *next;
AC_CreateFunctionType *create;
AC_DestroyFunctionType *destroy;
// rjf: artifact cache
U64 slots_count;
AC_Slot *slots;
StripeArray stripes;
};
typedef struct AC_RequestBatch AC_RequestBatch;
struct AC_RequestBatch
{
Mutex mutex;
Arena *arena;
AC_RequestNode *first_wide;
AC_RequestNode *last_wide;
AC_RequestNode *first_thin;
AC_RequestNode *last_thin;
U64 wide_count;
U64 thin_count;
};
typedef struct AC_Shared AC_Shared;
struct AC_Shared
{
Arena *arena;
// rjf: cache cache
U64 cache_slots_count;
AC_Cache **cache_slots;
StripeArray cache_stripes;
// rjf: requests
AC_RequestBatch req_batches[2]; // 0: high priority, 1: low priority
};
////////////////////////////////
//~ rjf: Globals
global AC_Shared *ac_shared = 0;
////////////////////////////////
//~ rjf: Layer Initialization
internal void ac_init(void);
////////////////////////////////
//~ rjf: Cache Lookups
internal AC_Artifact ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U64 endt_us);
#define ac_artifact_from_key(access, key, create_fn, destroy_fn, endt_us, ...) ac_artifact_from_key_((access), (key), &(AC_ArtifactParams){.create = (create_fn), .destroy = (destroy_fn), .evict_threshold_us = (2000000), __VA_ARGS__}, (endt_us))
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void ac_async_tick(void);
#endif // ARTIFACT_CACHE_H
+19 -19
View File
@@ -18,21 +18,21 @@ async_init(CmdLine *cmdline)
ASYNC_Ring *ring = &async_shared->rings[p];
ring->ring_size = MB(8);
ring->ring_base = push_array_no_zero(arena, U8, ring->ring_size);
ring->ring_mutex = os_mutex_alloc();
ring->ring_cv = os_condition_variable_alloc();
ring->ring_mutex = mutex_alloc();
ring->ring_cv = cond_var_alloc();
}
async_shared->ring_mutex = os_mutex_alloc();
async_shared->ring_cv = os_condition_variable_alloc();
async_shared->ring_mutex = mutex_alloc();
async_shared->ring_cv = cond_var_alloc();
String8 work_thread_count_string = cmd_line_string(cmdline, str8_lit("work_threads_count"));
if(work_thread_count_string.size == 0 || !try_u64_from_str8_c_rules(work_thread_count_string, &async_shared->work_threads_count))
{
async_shared->work_threads_count = Max(4, os_get_system_info()->logical_processor_count-1);
}
async_shared->work_threads_count = Max(4, async_shared->work_threads_count);
async_shared->work_threads = push_array(arena, OS_Handle, async_shared->work_threads_count);
async_shared->work_threads = push_array(arena, Thread, async_shared->work_threads_count);
for EachIndex(idx, async_shared->work_threads_count)
{
async_shared->work_threads[idx] = os_thread_launch(async_work_thread__entry_point, (void *)idx, 0);
async_shared->work_threads[idx] = thread_launch(async_work_thread__entry_point, (void *)idx);
}
}
@@ -68,7 +68,7 @@ async_push_work_(ASYNC_WorkFunctionType *work_function, ASYNC_WorkParams *params
// thread, and skip ring buffer if so.
B32 queued_in_ring_buffer = 0;
B32 need_to_execute_on_this_thread = 0;
OS_MutexScope(ring->ring_mutex) for(;;)
MutexScope(ring->ring_mutex) for(;;)
{
U64 num_available_work_threads = (async_shared->work_threads_count - ins_atomic_u64_eval(&async_shared->work_threads_live_count));
if(num_available_work_threads == 0 && async_work_thread_depth > 0)
@@ -81,7 +81,7 @@ async_push_work_(ASYNC_WorkFunctionType *work_function, ASYNC_WorkParams *params
if(available_size >= sizeof(work))
{
queued_in_ring_buffer = 1;
if(!os_handle_match(params->semaphore, os_handle_zero()))
if(!MemoryIsZeroStruct(&params->semaphore))
{
os_semaphore_take(params->semaphore, max_U64);
}
@@ -92,14 +92,14 @@ async_push_work_(ASYNC_WorkFunctionType *work_function, ASYNC_WorkParams *params
{
break;
}
os_condition_variable_wait(ring->ring_cv, ring->ring_mutex, params->endt_us);
cond_var_wait(ring->ring_cv, ring->ring_mutex, params->endt_us);
}
// rjf: broadcast ring buffer cv if we wrote successfully
if(queued_in_ring_buffer)
{
os_condition_variable_broadcast(ring->ring_cv);
os_condition_variable_broadcast(async_shared->ring_cv);
cond_var_broadcast(ring->ring_cv);
cond_var_broadcast(async_shared->ring_cv);
}
// rjf: if we did not queue successfully, and we have determined that
@@ -148,7 +148,7 @@ internal void *
async_task_join(ASYNC_Task *task)
{
void *result = 0;
if(task != 0 && !os_handle_match(task->semaphore, os_handle_zero()))
if(task != 0 && !MemoryIsZeroStruct(&task->semaphore))
{
os_semaphore_take(task->semaphore, max_U64);
os_semaphore_release(task->semaphore);
@@ -167,12 +167,12 @@ async_pop_work(void)
ASYNC_Work work = {0};
B32 done = 0;
ASYNC_Priority taken_priority = ASYNC_Priority_Low;
OS_MutexScope(async_shared->ring_mutex) for(;!done;)
MutexScope(async_shared->ring_mutex) for(;!done;)
{
for(ASYNC_Priority priority = ASYNC_Priority_High;; priority = (ASYNC_Priority)(priority - 1))
{
ASYNC_Ring *ring = &async_shared->rings[priority];
OS_MutexScope(ring->ring_mutex)
MutexScope(ring->ring_mutex)
{
U64 unconsumed_size = ring->ring_write_pos - ring->ring_read_pos;
if(unconsumed_size >= sizeof(work))
@@ -193,11 +193,11 @@ async_pop_work(void)
}
if(!done)
{
os_condition_variable_wait(async_shared->ring_cv, async_shared->ring_mutex, max_U64);
cond_var_wait(async_shared->ring_cv, async_shared->ring_mutex, max_U64);
}
}
os_condition_variable_broadcast(async_shared->ring_cv);
os_condition_variable_broadcast(async_shared->rings[taken_priority].ring_cv);
cond_var_broadcast(async_shared->ring_cv);
cond_var_broadcast(async_shared->rings[taken_priority].ring_cv);
return work;
}
@@ -216,7 +216,7 @@ async_execute_work(ASYNC_Work work)
}
//- rjf: release semaphore
if(!os_handle_match(work.semaphore, os_handle_zero()))
if(!MemoryIsZeroStruct(&work.semaphore))
{
os_semaphore_drop(work.semaphore);
}
@@ -274,7 +274,7 @@ internal void
async_work_thread__entry_point(void *p)
{
U64 thread_idx = (U64)p;
ThreadNameF("[async] work thread #%I64u", thread_idx);
ThreadNameF("async_work_thread_%I64u", thread_idx);
async_work_thread_idx = thread_idx;
for(;;)
{
+8 -8
View File
@@ -27,7 +27,7 @@ struct ASYNC_WorkParams
{
void *input;
void **output;
OS_Handle semaphore;
Semaphore semaphore;
U64 *completion_counter;
U64 *working_counter;
U64 endt_us;
@@ -40,7 +40,7 @@ struct ASYNC_Work
ASYNC_WorkFunctionType *work_function;
void *input;
void **output;
OS_Handle semaphore;
Semaphore semaphore;
U64 *completion_counter;
U64 *working_counter;
};
@@ -51,7 +51,7 @@ struct ASYNC_Work
typedef struct ASYNC_Task ASYNC_Task;
struct ASYNC_Task
{
OS_Handle semaphore;
Semaphore semaphore;
void *output;
};
@@ -89,8 +89,8 @@ struct ASYNC_Ring
U8 *ring_base;
U64 ring_write_pos;
U64 ring_read_pos;
OS_Handle ring_mutex;
OS_Handle ring_cv;
Mutex ring_mutex;
CondVar ring_cv;
};
typedef struct ASYNC_Shared ASYNC_Shared;
@@ -100,11 +100,11 @@ struct ASYNC_Shared
// rjf: user -> work thread ring buffers
ASYNC_Ring rings[ASYNC_Priority_COUNT];
OS_Handle ring_mutex;
OS_Handle ring_cv;
Mutex ring_mutex;
CondVar ring_cv;
// rjf: work threads
OS_Handle *work_threads;
Thread *work_threads;
U64 work_threads_count;
U64 work_threads_live_count;
};
+25 -16
View File
@@ -62,7 +62,6 @@ arena_alloc_(ArenaParams *params)
arena->allocation_site_file = params->allocation_site_file;
arena->allocation_site_line = params->allocation_site_line;
#if ARENA_FREE_LIST
arena->free_size = 0;
arena->free_last = 0;
#endif
AsanPoisonMemoryRegion(base, commit_size);
@@ -83,7 +82,7 @@ arena_release(Arena *arena)
//- rjf: arena push/pop core functions
internal void *
arena_push(Arena *arena, U64 size, U64 align)
arena_push(Arena *arena, U64 size, U64 align, B32 zero)
{
Arena *current = arena->current;
U64 pos_pre = AlignPow2(current->pos, align);
@@ -95,22 +94,22 @@ arena_push(Arena *arena, U64 size, U64 align)
Arena *new_block = 0;
#if ARENA_FREE_LIST
Arena *prev_block;
for(new_block = arena->free_last, prev_block = 0; new_block != 0; prev_block = new_block, new_block = new_block->prev)
{
if(new_block->res >= AlignPow2(size, align))
Arena *prev_block;
for(new_block = arena->free_last, prev_block = 0; new_block != 0; prev_block = new_block, new_block = new_block->prev)
{
if(prev_block)
if(new_block->res >= AlignPow2(new_block->pos, align) + size)
{
prev_block->prev = new_block->prev;
if(prev_block)
{
prev_block->prev = new_block->prev;
}
else
{
arena->free_last = new_block->prev;
}
break;
}
else
{
arena->free_last = new_block->prev;
}
arena->free_size -= new_block->res_size;
AsanUnpoisonMemoryRegion((U8*)new_block + ARENA_HEADER_SIZE, new_block->res_size - ARENA_HEADER_SIZE);
break;
}
}
#endif
@@ -139,6 +138,13 @@ arena_push(Arena *arena, U64 size, U64 align)
pos_pst = pos_pre + size;
}
// rjf: compute the size we need to zero
U64 size_to_zero = 0;
if(zero)
{
size_to_zero = Min(current->cmt, pos_pst) - pos_pre;
}
// rjf: commit new pages, if needed
if(current->cmt < pos_pst)
{
@@ -165,6 +171,10 @@ arena_push(Arena *arena, U64 size, U64 align)
result = (U8 *)current+pos_pre;
current->pos = pos_pst;
AsanUnpoisonMemoryRegion(result, size);
if(size_to_zero != 0)
{
MemoryZero(result, size_to_zero);
}
}
// rjf: panic on failure
@@ -198,9 +208,8 @@ arena_pop_to(Arena *arena, U64 pos)
{
prev = current->prev;
current->pos = ARENA_HEADER_SIZE;
arena->free_size += current->res_size;
SLLStackPush_N(arena->free_last, current, prev);
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res_size - ARENA_HEADER_SIZE);
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res - ARENA_HEADER_SIZE);
}
#else
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
+5 -12
View File
@@ -5,13 +5,10 @@
#define BASE_ARENA_H
////////////////////////////////
//~ rjf: Constants
//~ rjf: Arena Types
#define ARENA_HEADER_SIZE 128
////////////////////////////////
//~ rjf: Types
typedef U64 ArenaFlags;
enum
{
@@ -45,7 +42,6 @@ struct Arena
char *allocation_site_file;
int allocation_site_line;
#if ARENA_FREE_LIST
U64 free_size;
Arena *free_last;
#endif
};
@@ -59,22 +55,19 @@ struct Temp
};
////////////////////////////////
//~ rjf: Global Defaults
//~ rjf: Arena Functions
global U64 arena_default_reserve_size = MB(64);
global U64 arena_default_commit_size = KB(64);
global ArenaFlags arena_default_flags = 0;
////////////////////////////////
//~ rjf: Arena Functions
//- rjf: arena creation/destruction
internal Arena *arena_alloc_(ArenaParams *params);
#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = arena_default_reserve_size, .commit_size = arena_default_commit_size, .flags = arena_default_flags, .allocation_site_file = __FILE__, .allocation_site_line = __LINE__, __VA_ARGS__})
internal void arena_release(Arena *arena);
//- rjf: arena push/pop/pos core functions
internal void *arena_push(Arena *arena, U64 size, U64 align);
internal void *arena_push(Arena *arena, U64 size, U64 align, B32 zero);
internal U64 arena_pos(Arena *arena);
internal void arena_pop_to(Arena *arena, U64 pos);
@@ -87,8 +80,8 @@ internal Temp temp_begin(Arena *arena);
internal void temp_end(Temp temp);
//- rjf: push helper macros
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align))
#define push_array_aligned(a, T, c, align) (T *)MemoryZero(push_array_no_zero_aligned(a, T, c, align), sizeof(T)*(c))
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (0))
#define push_array_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (1))
#define push_array_no_zero(a, T, c) push_array_no_zero_aligned(a, T, c, Max(8, AlignOf(T)))
#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, AlignOf(T)))
+1 -1
View File
@@ -104,7 +104,7 @@ cmd_line_from_string_list(Arena *arena, String8List command_line)
{
option_name = str8_skip(option_name, 1);
}
else if(operating_system_from_context() == OperatingSystem_Windows &&
else if(OperatingSystem_CURRENT == OperatingSystem_Windows &&
str8_match(str8_prefix(node->string, 1), str8_lit("/"), 0))
{
option_name = str8_skip(option_name, 1);
+1 -1
View File
@@ -159,7 +159,7 @@
#endif
#if !defined(BUILD_VERSION_PATCH)
# define BUILD_VERSION_PATCH 21
# define BUILD_VERSION_PATCH 22
#endif
#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH)
+77 -48
View File
@@ -41,7 +41,7 @@ u128_zero(void)
internal U128
u128_make(U64 v0, U64 v1)
{
U128 v = {v0, v1};
U128 v = { .u64 = { v0, v1 }};
return v;
}
@@ -246,7 +246,8 @@ sign_from_side_F32(Side side){
//~ rjf: Memory Functions
internal B32
memory_is_zero(void *ptr, U64 size){
memory_is_zero(void *ptr, U64 size)
{
B32 result = 1;
// break down size
@@ -257,8 +258,10 @@ memory_is_zero(void *ptr, U64 size){
U64 *p64 = (U64*)ptr;
if(result)
{
for (U64 i = 0; i < count8; i += 1, p64 += 1){
if (*p64 != 0){
for(U64 i = 0; i < count8; i += 1, p64 += 1)
{
if(*p64 != 0)
{
result = 0;
goto done;
}
@@ -269,8 +272,10 @@ memory_is_zero(void *ptr, U64 size){
if(result)
{
U8 *p8 = (U8*)p64;
for (U64 i = 0; i < extra; i += 1, p8 += 1){
if (*p8 != 0){
for(U64 i = 0; i < extra; i += 1, p8 += 1)
{
if(*p8 != 0)
{
result = 0;
goto done;
}
@@ -278,7 +283,7 @@ memory_is_zero(void *ptr, U64 size){
}
done:;
return(result);
return result;
}
////////////////////////////////
@@ -409,47 +414,6 @@ max_instruction_size_from_arch(Arch arch)
return 64;
}
internal OperatingSystem
operating_system_from_context(void){
OperatingSystem os = OperatingSystem_Null;
#if OS_WINDOWS
os = OperatingSystem_Windows;
#elif OS_LINUX
os = OperatingSystem_Linux;
#elif OS_MAC
os = OperatingSystem_Mac;
#endif
return os;
}
internal Arch
arch_from_context(void){
Arch arch = Arch_Null;
#if ARCH_X64
arch = Arch_x64;
#elif ARCH_X86
arch = Arch_x86;
#elif ARCH_ARM64
arch = Arch_arm64;
#elif ARCH_ARM32
arch = Arch_arm32;
#endif
return arch;
}
internal Compiler
compiler_from_context(void){
Compiler compiler = Compiler_Null;
#if COMPILER_MSVC
compiler = Compiler_msvc;
#elif COMPILER_GCC
compiler = Compiler_gcc;
#elif COMPILER_CLANG
compiler = Compiler_clang;
#endif
return compiler;
}
////////////////////////////////
//~ rjf: Time Functions
@@ -659,3 +623,68 @@ index_of_zero_u64(U64 *ptr, U64 count)
}
return max_U64;
}
////////////////////////////////
//~ rjf: Third Party Includes
#if !BUILD_SUPPLEMENTARY_UNIT
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-conversion"
#elif defined(_MSC_VER)
#pragma warning (push, 0)
#endif
# define STB_SPRINTF_IMPLEMENTATION
# define STB_SPRINTF_STATIC
# include "third_party/stb/stb_sprintf.h"
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop, 0)
#endif
////////////////////////////////
//~ rjf: String <-> Integer Tables
read_only global U8 integer_symbols[16] =
{
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F',
};
// NOTE(rjf): Includes reverses for uppercase and lowercase hex.
read_only global U8 integer_symbol_reverse[128] =
{
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
read_only global U8 base64[64] =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'_', '$',
};
read_only global U8 base64_reverse[128] =
{
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,
0xFF,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,
0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0x3E,
0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,
};
+126 -65
View File
@@ -13,6 +13,13 @@
#include <string.h>
#include <stdint.h>
////////////////////////////////
//~ rjf: Third Party Includes
#define STB_SPRINTF_DECORATE(name) raddbg_##name
#define STB_SPRINTF_STATIC
#include "third_party/stb/stb_sprintf.h"
////////////////////////////////
//~ rjf: Codebase Keywords
@@ -38,12 +45,24 @@
# define thread_static __declspec(thread)
#elif COMPILER_CLANG || COMPILER_GCC
# define thread_static __thread
#else
# error thread_static not defined for this compiler.
#endif
#if COMPILER_MSVC
# define force_inline __forceinline
#elif COMPILER_CLANG || COMPILER_GCC
# define force_inline __attribute__((always_inline))
#else
# error force_inline not defined for this compiler.
#endif
#if COMPILER_MSVC
# define no_inline __declspec(noinline)
#elif COMPILER_CLANG || COMPILER_GCC
# define no_inline __attribute__((noinline))
#else
# error no_inline not defined for this compiler.
#endif
////////////////////////////////
@@ -118,6 +137,14 @@
# error AlignOf not defined for this compiler.
#endif
#if COMPILER_MSVC
# define AlignType(x) __declspec(align(x))
#elif COMPILER_CLANG || COMPILER_GCC
# define AlignType(x) __attribute__((aligned(x)))
#else
# error AlignType not defined for this compiler.
#endif
////////////////////////////////
//~ rjf: Member Offsets
@@ -136,6 +163,8 @@
#define EachElement(it, array) (U64 it = 0; it < ArrayCount(array); it += 1)
#define EachEnumVal(type, it) (type it = (type)0; it < type##_COUNT; it = (type)(it+1))
#define EachNonZeroEnumVal(type, it) (type it = (type)1; it < type##_COUNT; it = (type)(it+1))
#define EachInRange(it, range) (U64 it = (range).min; it < (range).max; it += 1)
#define EachNode(it, T, first) (T *it = first; it != 0; it = it->next)
////////////////////////////////
//~ rjf: Memory Operation Macros
@@ -159,6 +188,8 @@
#define MemoryMatchStruct(a,b) MemoryMatch((a),(b),sizeof(*(a)))
#define MemoryMatchArray(a,b) MemoryMatch((a),(b),sizeof(a))
#define MemoryIsZeroStruct(ptr) memory_is_zero((ptr), sizeof(*(ptr)))
#define MemoryRead(T,p,e) ( ((p)+sizeof(T)<=(e))?(*(T*)(p)):(0) )
#define MemoryConsume(T,p,e) ( ((p)+sizeof(T)<=(e))?((p)+=sizeof(T),*(T*)((p)-sizeof(T))):((p)=(e),0) )
@@ -190,44 +221,46 @@
#if COMPILER_MSVC
# include <intrin.h>
# if ARCH_X64
# define ins_atomic_u64_eval(x) *((volatile U64 *)(x))
# define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((volatile __int64 *)(x))
# define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((volatile __int64 *)(x))
# define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((volatile __int64 *)(x),(c))
# define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((volatile __int64 *)(x), c)
# define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((volatile __int64 *)(x),(k),(c))
# define ins_atomic_u32_eval(x) *((volatile U32 *)(x))
# define ins_atomic_u32_inc_eval(x) InterlockedIncrement((volatile LONG *)x)
# define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((volatile LONG *)(x),(c))
# define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((volatile LONG *)(x),(k),(c))
# define ins_atomic_u32_add_eval(x,c) InterlockedAdd((volatile LONG *)(x), c)
# define ins_atomic_u128_eval_cond_assign(x,k,c) (B32)InterlockedCompareExchange128((__int64 *)(x), ((__int64 *)&(k))[1], ((__int64 *)&(k))[0], (__int64 *)c)
# define ins_atomic_u64_eval(x) *((volatile U64 *)(x))
# define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((__int64 *)(x))
# define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((__int64 *)(x))
# define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((__int64 *)(x),(c))
# define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((__int64 *)(x), c)
# define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((__int64 *)(x),(k),(c))
# define ins_atomic_u32_eval(x) *((volatile U32 *)(x))
# define ins_atomic_u32_inc_eval(x) InterlockedIncrement((LONG *)(x))
# define ins_atomic_u32_dec_eval(x) InterlockedDecrement((LONG *)(x))
# define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((LONG *)(x),(c))
# define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((LONG *)(x),(k),(c))
# define ins_atomic_u32_add_eval(x,c) InterlockedAdd((LONG *)(x), (c))
# define ins_atomic_u8_eval_assign(x,c) InterlockedExchange8((CHAR *)(x), (c))
# else
# error Atomic intrinsics not defined for this compiler / architecture combination.
# endif
#elif COMPILER_CLANG || COMPILER_GCC
# define ins_atomic_u64_eval(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
# define ins_atomic_u64_inc_eval(x) (__atomic_fetch_add((volatile U64 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
# define ins_atomic_u64_dec_eval(x) (__atomic_fetch_sub((volatile U64 *)(x), 1, __ATOMIC_SEQ_CST) - 1)
# define ins_atomic_u64_eval_assign(x,c) __atomic_exchange_n(x, c, __ATOMIC_SEQ_CST)
# define ins_atomic_u64_add_eval(x,c) (__atomic_fetch_add((volatile U64 *)(x), c, __ATOMIC_SEQ_CST) + (c))
# define ins_atomic_u64_eval_cond_assign(x,k,c) ({ U64 _new = (c); __atomic_compare_exchange_n((volatile U64 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
# define ins_atomic_u32_eval(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
# define ins_atomic_u32_inc_eval(x) (__atomic_fetch_add((volatile U32 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
# define ins_atomic_u32_add_eval(x,c) (__atomic_fetch_add((volatile U32 *)(x), c, __ATOMIC_SEQ_CST) + (c))
# define ins_atomic_u32_eval_assign(x,c) __atomic_exchange_n(x, c, __ATOMIC_SEQ_CST)
# define ins_atomic_u32_eval_cond_assign(x,k,c) ({ U32 _new = (c); __atomic_compare_exchange_n((volatile U32 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
# define ins_atomic_u128_eval_cond_assign(x,k,c) (B32)__atomic_compare_exchange_n((__int128 *)(x),(__int128 *)(c),*(__int128 *)(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST)
# define ins_atomic_u64_eval(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
# define ins_atomic_u64_inc_eval(x) (__atomic_fetch_add((U64 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
# define ins_atomic_u64_dec_eval(x) (__atomic_fetch_sub((U64 *)(x), 1, __ATOMIC_SEQ_CST) - 1)
# define ins_atomic_u64_eval_assign(x,c) __atomic_exchange_n(x, c, __ATOMIC_SEQ_CST)
# define ins_atomic_u64_add_eval(x,c) (__atomic_fetch_add((U64 *)(x), c, __ATOMIC_SEQ_CST) + (c))
# define ins_atomic_u64_eval_cond_assign(x,k,c) ({ U64 _new = (c); __atomic_compare_exchange_n((U64 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
# define ins_atomic_u32_eval(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
# define ins_atomic_u32_inc_eval(x) (__atomic_fetch_add((U32 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
# define ins_atomic_u32_dec_eval(x) (__atomic_fetch_sub((U32 *)(x), 1, __ATOMIC_SEQ_CST) - 1)
# define ins_atomic_u32_add_eval(x,c) (__atomic_fetch_add((U32 *)(x), c, __ATOMIC_SEQ_CST) + (c))
# define ins_atomic_u32_eval_assign(x,c) __atomic_exchange_n((x), (c), __ATOMIC_SEQ_CST)
# define ins_atomic_u32_eval_cond_assign(x,k,c) ({ U32 _new = (c); __atomic_compare_exchange_n((U32 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
# define ins_atomic_u8_eval_assign(x,c) __atomic_exchange_n((x), (c), __ATOMIC_SEQ_CST)
#else
# error Atomic intrinsics not defined for this compiler / architecture.
#endif
#if ARCH_64BIT
# define ins_atomic_ptr_eval_cond_assign(x,k,c) (void*)ins_atomic_u64_eval_cond_assign((volatile U64 *)(x), (U64)(k), (U64)(c))
# define ins_atomic_ptr_eval_assign(x,c) (void*)ins_atomic_u64_eval_assign((volatile U64 *)(x), (U64)(c))
# define ins_atomic_ptr_eval(x) (void*)ins_atomic_u64_eval((volatile U64 *)x)
#elif ARCH_32BIT
# define ins_atomic_ptr_eval_cond_assign(x,k,c) (void*)ins_atomic_u32_eval_cond_assign((volatile U32 *)(x), (U32)(k), (U32)(c))
# define ins_atomic_ptr_eval_assign(x,c) (void*)ins_atomic_u32_eval_assign((volatile U32 *)(x), (U32)(c))
# define ins_atomic_ptr_eval(x) (void*)ins_atomic_u32_eval((volatile U32 *)x)
# define ins_atomic_ptr_eval_cond_assign(x,k,c) (void *)ins_atomic_u64_eval_cond_assign((U64 *)(x), (U64)(k), (U64)(c))
# define ins_atomic_ptr_eval_assign(x,c) (void *)ins_atomic_u64_eval_assign((U64 *)(x), (U64)(c))
# define ins_atomic_ptr_eval(x) (void *)ins_atomic_u64_eval((U64 *)x)
#else
# error Atomic intrinsics for pointers not defined for this architecture.
#endif
@@ -419,6 +452,37 @@ union U512
U256 u256[2];
};
////////////////////////////////
//~ rjf: Basic Type Structures
typedef struct U16Array U16Array;
struct U16Array
{
U64 count;
U16 *v;
};
typedef struct U32Array U32Array;
struct U32Array
{
U64 count;
U32 *v;
};
typedef struct U64Array U64Array;
struct U64Array
{
U64 count;
U64 *v;
};
typedef struct U128Array U128Array;
struct U128Array
{
U64 count;
U128 *v;
};
////////////////////////////////
//~ rjf: Basic Types & Spaces
@@ -485,6 +549,15 @@ typedef enum OperatingSystem
OperatingSystem_Linux,
OperatingSystem_Mac,
OperatingSystem_COUNT,
#if OS_WINDOWS
OperatingSystem_CURRENT = OperatingSystem_Windows,
#elif OS_LINUX
OperatingSystem_CURRENT = OperatingSystem_Linux,
#elif OS_MAC
OperatingSystem_CURRENT = OperatingSystem_Mac,
#else
OperatingSystem_CURRENT = OperatingSystem_Null,
#endif
}
OperatingSystem;
@@ -507,6 +580,17 @@ typedef enum Arch
Arch_arm64,
Arch_arm32,
Arch_COUNT,
#if ARCH_X64
Arch_CURRENT = Arch_x64,
#elif ARCH_X86
Arch_CURRENT = Arch_x86,
#elif ARCH_ARM64
Arch_CURRENT = Arch_arm64,
#elif ARCH_ARM32
Arch_CURRENT = Arch_arm32,
#else
Arch_CURRENT = Arch_Null,
#endif
}
Arch;
@@ -517,6 +601,15 @@ typedef enum Compiler
Compiler_gcc,
Compiler_clang,
Compiler_COUNT,
#if COMPILER_MSVC
Compiler_CURRENT = Compiler_msvc,
#elif COMPILER_GCC
Compiler_CURRENT = Compiler_gcc,
#elif COMPILER_CLANG
Compiler_CURRENT = Compiler_clang,
#else
Compiler_CURRENT = Compiler_Null,
#endif
}
Compiler;
@@ -538,7 +631,7 @@ struct TxtRng
};
////////////////////////////////
//~ Globally Unique Ids
//~ rjf: Globally Unique Ids
typedef union Guid Guid;
union Guid
@@ -555,35 +648,7 @@ union Guid
StaticAssert(sizeof(Guid) == 16, g_guid_size_check);
////////////////////////////////
//~ Arrays
typedef struct U16Array U16Array;
struct U16Array
{
U64 count;
U16 *v;
};
typedef struct U32Array U32Array;
struct U32Array
{
U64 count;
U32 *v;
};
typedef struct U64Array U64Array;
struct U64Array
{
U64 count;
U64 *v;
};
typedef struct U128Array U128Array;
struct U128Array
{
U64 count;
U128 *v;
};
////////////////////////////////
//~ NOTE(allen): Constants
//~ rjf: Basic Constants
global U32 sign32 = 0x80000000;
global U32 exponent32 = 0x7F800000;
@@ -744,7 +809,7 @@ global const U64 bit63 = (1ull<<62);
global const U64 bit64 = (1ull<<63);
////////////////////////////////
//~ allen: Time
//~ rjf: Time Types
typedef enum WeekDay
{
@@ -802,7 +867,7 @@ struct DateTime
typedef U64 DenseTime;
////////////////////////////////
//~ allen: Files
//~ rjf: File Types
typedef U32 FilePropertyFlags;
enum
@@ -896,10 +961,6 @@ internal B32 txt_rng_contains(TxtRng r, TxtPt pt);
internal U64 bit_size_from_arch(Arch arch);
internal U64 max_instruction_size_from_arch(Arch arch);
internal OperatingSystem operating_system_from_context(void);
internal Arch arch_from_context(void);
internal Compiler compiler_from_context(void);
////////////////////////////////
//~ rjf: Time Functions
+119 -24
View File
@@ -2,12 +2,24 @@
// Licensed under the MIT license (https://opensource.org/license/mit/)
global U64 global_update_tick_idx = 0;
global U64 async_threads_count = 0;
global CondVar async_tick_start_cond_var = {0};
global Mutex async_tick_start_mutex = {0};
global Mutex async_tick_stop_mutex = {0};
global B32 async_loop_again = 0;
global B32 global_async_exit = 0;
thread_static B32 is_async_thread = 0;
internal void
main_thread_base_entry_point(int arguments_count, char **arguments)
{
ThreadNameF("main_thread");
Temp scratch = scratch_begin(0, 0);
ThreadNameF("[main thread]");
//- rjf: set up async thread group info
async_tick_start_cond_var = cond_var_alloc();
async_tick_start_mutex = mutex_alloc();
async_tick_stop_mutex = mutex_alloc();
//- rjf: set up telemetry
#if PROFILE_TELEMETRY
@@ -23,7 +35,13 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
#endif
//- rjf: parse command line
String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, arguments_count, arguments);
String8List command_line_argument_strings = {0};
{
for EachIndex(idx, arguments_count)
{
str8_list_push(scratch.arena, &command_line_argument_strings, str8_cstring(arguments[idx]));
}
}
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
//- rjf: begin captures
@@ -31,34 +49,31 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
if(capture)
{
ProfBeginCapture(arguments[0]);
ProfMsg(BUILD_TITLE);
}
#if PROFILE_TELEMETRY
tmMessage(0, TMMF_ICON_NOTE, BUILD_TITLE);
#endif
//- rjf: initialize all included layers
#if defined(ARTIFACT_CACHE_H) && !defined(AC_INIT_MANUAL)
ac_init();
#endif
#if defined(ASYNC_H) && !defined(ASYNC_INIT_MANUAL)
async_init(&cmdline);
#endif
#if defined(HASH_STORE_H) && !defined(HS_INIT_MANUAL)
hs_init();
#if defined(CONTENT_H) && !defined(C_INIT_MANUAL)
c_init();
#endif
#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL)
fs_init();
#endif
#if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL)
txt_init();
#endif
#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL)
mtx_init();
#endif
#if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL)
dasm_init();
#endif
#if defined(DBGI_H) && !defined(DI_INIT_MANUAL)
#if defined(DBG_INFO_H) && !defined(DI_INIT_MANUAL)
di_init();
#endif
#if defined(DBG_INFO2_H) && !defined(DI_INIT_MANUAL)
di2_init(&cmdline);
#endif
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
dmn_init();
#endif
@@ -74,12 +89,6 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL)
r_init(&cmdline);
#endif
#if defined(TEXTURE_CACHE_H) && !defined(TEX_INIT_MANUAL)
tex_init();
#endif
#if defined(GEO_CACHE_H) && !defined(GEO_INIT_MANUAL)
geo_init();
#endif
#if defined(FONT_CACHE_H) && !defined(FNT_INIT_MANUAL)
fnt_init();
#endif
@@ -90,9 +99,43 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
rd_init(&cmdline);
#endif
//- rjf: launch async threads
Thread *async_threads = 0;
U64 lane_broadcast_val = 0;
{
U64 num_main_threads = 1;
#if defined(CTRL_CORE_H)
num_main_threads += 1;
#endif
U64 num_async_threads = os_get_system_info()->logical_processor_count;
U64 num_main_threads_clamped = Min(num_async_threads, num_main_threads);
num_async_threads -= num_main_threads_clamped;
num_async_threads = Max(1, num_async_threads);
Barrier barrier = barrier_alloc(num_async_threads);
LaneCtx *lane_ctxs = push_array(scratch.arena, LaneCtx, num_async_threads);
async_threads_count = num_async_threads;
async_threads = push_array(scratch.arena, Thread, async_threads_count);
for EachIndex(idx, num_async_threads)
{
lane_ctxs[idx].lane_idx = idx;
lane_ctxs[idx].lane_count = async_threads_count;
lane_ctxs[idx].barrier = barrier;
lane_ctxs[idx].broadcast_memory = &lane_broadcast_val;
async_threads[idx] = thread_launch(async_thread_entry_point, &lane_ctxs[idx]);
}
}
//- rjf: call into entry point
entry_point(&cmdline);
//- rjf: join async threads
ins_atomic_u32_inc_eval(&global_async_exit);
cond_var_broadcast(async_tick_start_cond_var);
for EachIndex(idx, async_threads_count)
{
thread_join(async_threads[idx], max_U64);
}
//- rjf: end captures
if(capture)
{
@@ -105,10 +148,10 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
internal void
supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params)
{
TCTX tctx;
tctx_init_and_equip(&tctx);
TCTX *tctx = tctx_alloc();
tctx_select(tctx);
entry_point(params);
tctx_release();
tctx_release(tctx);
}
internal U64
@@ -133,3 +176,55 @@ update(void)
#endif
return result;
}
internal void
async_thread_entry_point(void *params)
{
LaneCtx lctx = *(LaneCtx *)params;
lane_ctx(lctx);
is_async_thread = 1;
ThreadNameF("async_thread_%I64u", lane_idx());
for(;;)
{
// rjf: wait for signal if we need, otherwise reset loop signal & continue
if(lane_idx() == 0)
{
if(!ins_atomic_u32_eval(&async_loop_again))
{
MutexScope(async_tick_start_mutex) cond_var_wait(async_tick_start_cond_var, async_tick_start_mutex, os_now_microseconds()+100000);
}
ins_atomic_u32_eval_assign(&async_loop_again, 0);
}
lane_sync();
// rjf: do all ticks for all layers
ProfScope("async tick")
{
#if defined(ARTIFACT_CACHE_H)
ac_async_tick();
#endif
#if defined(CONTENT_H)
c_async_tick();
#endif
#if defined(FILE_STREAM_H)
fs_async_tick();
#endif
#if defined(DBG_INFO2_H)
di2_async_tick();
#endif
}
// rjf: take exit signal; break if set
lane_sync();
B32 need_exit = 0;
if(lane_idx() == 0)
{
need_exit = ins_atomic_u32_eval(&global_async_exit);
}
lane_sync_u64(&need_exit, 0);
if(need_exit)
{
break;
}
}
}
+1
View File
@@ -8,5 +8,6 @@ internal void main_thread_base_entry_point(int argc, char **argv);
internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params);
internal U64 update_tick_idx(void);
internal B32 update(void);
internal void async_thread_entry_point(void *params);
#endif // BASE_ENTRY_POINT_H
+2 -1
View File
@@ -5,13 +5,14 @@
//~ rjf: Base Includes
#undef LAYER_COLOR
#define LAYER_COLOR 0.20f, 0.60f, 0.80f
#define LAYER_COLOR 0x3399ccff
#include "base_core.c"
#include "base_profile.c"
#include "base_arena.c"
#include "base_math.c"
#include "base_strings.c"
#include "base_threads.c"
#include "base_thread_context.c"
#include "base_command_line.c"
#include "base_markup.c"
+1
View File
@@ -14,6 +14,7 @@
#include "base_arena.h"
#include "base_math.h"
#include "base_strings.h"
#include "base_threads.h"
#include "base_thread_context.h"
#include "base_command_line.h"
#include "base_markup.h"
+17 -6
View File
@@ -2,7 +2,7 @@
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Scalar Ops
//~ rjf: Scalar Math Ops
internal F32
mix_1f32(F32 a, F32 b, F32 t)
@@ -42,7 +42,6 @@ internal Vec2S64 scale_2s64(Vec2S64 v, S64 s) {Vec2S64 c = {v.
internal S64 dot_2s64(Vec2S64 a, Vec2S64 b) {S64 c = a.x*b.x + a.y*b.y; return c;}
internal S64 length_squared_2s64(Vec2S64 v) {S64 c = v.x*v.x + v.y*v.y; return c;}
internal S64 length_2s64(Vec2S64 v) {S64 c = (S64)sqrt_f64((F64)(v.x*v.x + v.y*v.y)); return c;}
internal Vec2S64 normalize_2s64(Vec2S64 v) {v = scale_2s64(v, (S64)(1.f/length_2s64(v))); return v;}
internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t) {Vec2S64 c = {(S64)mix_1f32((F32)a.x, (F32)b.x, t), (S64)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec2S32 vec_2s32(S32 x, S32 y) {Vec2S32 v = {x, y}; return v;}
@@ -54,7 +53,6 @@ internal Vec2S32 scale_2s32(Vec2S32 v, S32 s) {Vec2S32 c = {v.
internal S32 dot_2s32(Vec2S32 a, Vec2S32 b) {S32 c = a.x*b.x + a.y*b.y; return c;}
internal S32 length_squared_2s32(Vec2S32 v) {S32 c = v.x*v.x + v.y*v.y; return c;}
internal S32 length_2s32(Vec2S32 v) {S32 c = (S32)sqrt_f32((F32)v.x*(F32)v.x + (F32)v.y*(F32)v.y); return c;}
internal Vec2S32 normalize_2s32(Vec2S32 v) {v = scale_2s32(v, (S32)(1.f/length_2s32(v))); return v;}
internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t) {Vec2S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec2S16 vec_2s16(S16 x, S16 y) {Vec2S16 v = {x, y}; return v;}
@@ -66,7 +64,6 @@ internal Vec2S16 scale_2s16(Vec2S16 v, S16 s) {Vec2S16 c = {(S
internal S16 dot_2s16(Vec2S16 a, Vec2S16 b) {S16 c = a.x*b.x + a.y*b.y; return c;}
internal S16 length_squared_2s16(Vec2S16 v) {S16 c = v.x*v.x + v.y*v.y; return c;}
internal S16 length_2s16(Vec2S16 v) {S16 c = (S16)sqrt_f32((F32)(v.x*v.x + v.y*v.y)); return c;}
internal Vec2S16 normalize_2s16(Vec2S16 v) {v = scale_2s16(v, (S16)(1.f/length_2s16(v))); return v;}
internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t) {Vec2S16 c = {(S16)mix_1f32((F32)a.x, (F32)b.x, t), (S16)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z) {Vec3F32 v = {x, y, z}; return v;}
@@ -99,7 +96,6 @@ internal Vec3S32 scale_3s32(Vec3S32 v, S32 s) {Vec3S32 c = {v.
internal S32 dot_3s32(Vec3S32 a, Vec3S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;}
internal S32 length_squared_3s32(Vec3S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;}
internal S32 length_3s32(Vec3S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z)); return c;}
internal Vec3S32 normalize_3s32(Vec3S32 v) {v = scale_3s32(v, (S32)(1.f/length_3s32(v))); return v;}
internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t) {Vec3S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t)}; return c;}
internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;}
@@ -124,7 +120,6 @@ internal Vec4S32 scale_4s32(Vec4S32 v, S32 s) {Vec4S32 c = {v.
internal S32 dot_4s32(Vec4S32 a, Vec4S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;}
internal S32 length_squared_4s32(Vec4S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;}
internal S32 length_4s32(Vec4S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)); return c;}
internal Vec4S32 normalize_4s32(Vec4S32 v) {v = scale_4s32(v, (S32)(1.f/length_4s32(v))); return v;}
internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t) {Vec4S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t), (S32)mix_1f32((F32)a.w, (F32)b.w, t)}; return c;}
////////////////////////////////
@@ -810,3 +805,19 @@ rng1s64_array_from_list(Arena *arena, Rng1S64List *list)
return arr;
}
////////////////////////////////
//~ rjf: N -> M Element Subdivision
internal Rng1U64
m_range_from_n_idx_m_count(U64 n_idx, U64 n_count, U64 m_count)
{
U64 main_idxes_per_lane = m_count/n_count;
U64 leftover_idxes_count = m_count - main_idxes_per_lane*n_count;
U64 leftover_idxes_before_this_lane_count = Min(n_idx, leftover_idxes_count);
U64 lane_base_idx = n_idx*main_idxes_per_lane + leftover_idxes_before_this_lane_count;
U64 lane_base_idx__clamped = Min(lane_base_idx, m_count);
U64 lane_opl_idx = lane_base_idx__clamped + main_idxes_per_lane + ((n_idx < leftover_idxes_count) ? 1 : 0);
U64 lane_opl_idx__clamped = Min(lane_opl_idx, m_count);
Rng1U64 result = r1u64(lane_base_idx__clamped, lane_opl_idx__clamped);
return result;
}
+694 -694
View File
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -20,6 +20,7 @@
#if PROFILE_TELEMETRY
# include "rad_tm.h"
# if OS_WINDOWS
# pragma comment(lib, "ws2_32.lib")
# pragma comment(lib, "rad_tm_win64.lib")
# endif
#elif PROFILE_SPALL
@@ -43,9 +44,9 @@
# define ProfEndLockWait(...) tmEndWaitForLock(0)
# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__)
# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__)
# define ProfColor(color) tmZoneColorSticky(color)
# define ProfBeginV(...) \
if (TM_API_PTR) { \
# define ProfColor(color) tmZoneColor((((color) & 0xff000000) >> 24) / 255.f, (((color) & 0x00ff0000) >> 16) / 255.f, (((color) & 0x0000ff00) >> 8) / 255.f)
# define ProfBeginV(...) \
if (TM_API_PTR) { \
static tm_uint64 file_id = 0; TM_API_PTR->_tmStaticString(&file_id, __FILE__); \
Temp scratch = scratch_begin(0,0); \
String8 string = push_str8f(scratch.arena, __VA_ARGS__); \
@@ -54,8 +55,8 @@ hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \
TM_API_PTR->_tmEnterZoneFast_Core(0, 0, file_id, __LINE__, hash); \
scratch_end(scratch); \
}
# define ProfNoteV(...) \
if (TM_API_PTR) { \
# define ProfNoteV(...) \
if (TM_API_PTR) { \
static tm_uint64 file_id = 0; TM_API_PTR->_tmStaticString(&file_id, __FILE__); \
Temp scratch = scratch_begin(0,0); \
String8 string = push_str8f(scratch.arena, __VA_ARGS__); \
+509 -442
View File
File diff suppressed because it is too large Load Diff
+19 -41
View File
@@ -4,12 +4,6 @@
#ifndef BASE_STRINGS_H
#define BASE_STRINGS_H
////////////////////////////////
//~ rjf: Third Party Includes
#define STB_SPRINTF_DECORATE(name) raddbg_##name
#include "third_party/stb/stb_sprintf.h"
////////////////////////////////
//~ rjf: String Types
@@ -44,13 +38,6 @@ struct String8Node
String8 string;
};
typedef struct String8MetaNode String8MetaNode;
struct String8MetaNode
{
String8MetaNode *next;
String8Node *node;
};
typedef struct String8List String8List;
struct String8List
{
@@ -65,6 +52,7 @@ struct String8Array
{
String8 *v;
U64 count;
U64 total_size;
};
////////////////////////////////
@@ -158,9 +146,9 @@ internal B32 char_is_lower(U8 c);
internal B32 char_is_alpha(U8 c);
internal B32 char_is_slash(U8 c);
internal B32 char_is_digit(U8 c, U32 base);
internal U8 char_to_lower(U8 c);
internal U8 char_to_upper(U8 c);
internal U8 char_to_correct_slash(U8 c);
internal U8 lower_from_char(U8 c);
internal U8 upper_from_char(U8 c);
internal U8 correct_slash_from_char(U8 c);
////////////////////////////////
//~ rjf: C-String Measurement
@@ -211,8 +199,8 @@ internal String8 backslashed_from_str8(Arena *arena, String8 string);
internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags);
internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
internal U64 str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
internal B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags);
#define str8_ends_with_lit(string, end_lit, flags) str8_ends_with((string), str8_lit(end_lit), (flags))
internal B32 str8_is_before(String8 a, String8 b);
#define str8_ends_with(string, end, flags) str8_match(str8_postfix((string), (end).size), (end), (flags))
////////////////////////////////
//~ rjf: String Slicing
@@ -245,7 +233,6 @@ internal String8 push_cstr(Arena *arena, String8 str); // TODO(rjf): this is unn
//- rjf: string -> integer
internal S64 sign_from_str8(String8 string, String8 *string_tail);
internal B32 str8_is_integer(String8 string, U32 radix);
internal U64 u64_from_str8(String8 string, U32 radix);
internal S64 s64_from_str8(String8 string, U32 radix);
internal U32 u32_from_str8(String8 string, U32 radix);
@@ -269,12 +256,12 @@ internal F64 f64_from_str8(String8 string);
////////////////////////////////
//~ rjf: String List Construction Functions
internal String8Node* str8_list_push_node(String8List *list, String8Node *node);
internal String8Node* str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string);
internal String8Node* str8_list_push_node_front(String8List *list, String8Node *node);
internal String8Node* str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string);
internal String8Node* str8_list_push(Arena *arena, String8List *list, String8 string);
internal String8Node* str8_list_push_front(Arena *arena, String8List *list, String8 string);
internal String8Node *str8_list_push_node(String8List *list, String8Node *node);
internal String8Node *str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string);
internal String8Node *str8_list_push_node_front(String8List *list, String8Node *node);
internal String8Node *str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string);
internal String8Node *str8_list_push(Arena *arena, String8List *list, String8 string);
internal String8Node *str8_list_push_front(Arena *arena, String8List *list, String8 string);
internal void str8_list_concat_in_place(String8List *list, String8List *to_push);
internal String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align);
internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...);
@@ -287,9 +274,7 @@ internal String8List str8_list_copy(Arena *arena, String8List *list);
internal String8List str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags);
internal String8List str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags);
internal String8List str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags);
internal String8 str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params);
internal void str8_list_from_flags(Arena *arena, String8List *list, U32 flags, String8 *flag_string_table, U32 flag_string_count);
////////////////////////////////
//~ rjf: Basic Data Stringification Helpers
@@ -305,7 +290,7 @@ internal String8Array str8_array_reserve(Arena *arena, U64 count);
internal String8Array str8_array_copy(Arena *arena, String8Array array);
////////////////////////////////
//~ rjf: String Version Helpers
//~ rjf: String <-> Version Helpers
internal U64 version_from_str8(String8 string);
internal String8 str8_from_version(Arena *arena, U64 version);
@@ -369,7 +354,6 @@ internal UnicodeDecode utf8_decode(U8 *str, U64 max);
internal UnicodeDecode utf16_decode(U16 *str, U64 max);
internal U32 utf8_encode(U8 *str, U32 codepoint);
internal U32 utf16_encode(U16 *str, U32 codepoint);
internal U32 utf8_from_utf32_single(U8 *buffer, U32 character);
////////////////////////////////
//~ rjf: Unicode String Conversions
@@ -380,29 +364,21 @@ internal String8 str8_from_32(Arena *arena, String32 in);
internal String32 str32_from_8(Arena *arena, String8 in);
////////////////////////////////
//~ String -> Enum Conversions
//~ rjf: Basic Types & Space Enum <-> String Conversions
internal OperatingSystem operating_system_from_string(String8 string);
////////////////////////////////
//~ rjf: Basic Types & Space Enum -> String Conversions
internal String8 string_from_dimension(Dimension dimension);
internal String8 string_from_side(Side side);
internal String8 string_from_operating_system(OperatingSystem os);
internal String8 string_from_arch(Arch arch);
////////////////////////////////
//~ rjf: Time Types -> String
internal String8 string_from_week_day(WeekDay week_day);
internal String8 string_from_month(Month month);
internal String8 push_date_time_string(Arena *arena, DateTime *date_time);
internal String8 push_file_name_date_time_string(Arena *arena, DateTime *date_time);
internal String8 string_from_date_time(Arena *arena, DateTime *date_time);
internal String8 string_from_date_time__file_name(Arena *arena, DateTime *date_time);
internal String8 string_from_elapsed_time(Arena *arena, DateTime dt);
////////////////////////////////
//~ Globally Unique Ids
//~ rjf: String <-> Globally Unique IDs
internal String8 string_from_guid(Arena *arena, Guid guid);
internal B32 try_guid_from_string(String8 string, Guid *guid_out);
@@ -473,5 +449,7 @@ internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, Stri
internal U64 u64_hash_from_seed_str8(U64 seed, String8 string);
internal U64 u64_hash_from_str8(String8 string);
internal U128 u128_hash_from_seed_str8(U64 seed, String8 string);
internal U128 u128_hash_from_str8(String8 string);
#endif // BASE_STRINGS_H
+154 -26
View File
@@ -2,44 +2,54 @@
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
// NOTE(allen): Thread Context Functions
//~ rjf: Globals
C_LINKAGE thread_static TCTX* tctx_thread_local;
C_LINKAGE thread_static TCTX *tctx_thread_local;
#if !BUILD_SUPPLEMENTARY_UNIT
C_LINKAGE thread_static TCTX* tctx_thread_local = 0;
C_LINKAGE thread_static TCTX *tctx_thread_local = 0;
#endif
internal void
tctx_init_and_equip(TCTX *tctx)
////////////////////////////////
//~ rjf: Thread Context Functions
//- rjf: thread-context allocation & selection
internal TCTX *
tctx_alloc(void)
{
Arena *arena = arena_alloc();
TCTX *tctx = push_array(arena, TCTX, 1);
tctx->arenas[0] = arena;
tctx->arenas[1] = arena_alloc();
tctx->lane_ctx.lane_count = 1;
return tctx;
}
internal void
tctx_release(TCTX *tctx)
{
arena_release(tctx->arenas[1]);
arena_release(tctx->arenas[0]);
}
internal void
tctx_select(TCTX *tctx)
{
MemoryZeroStruct(tctx);
Arena **arena_ptr = tctx->arenas;
for(U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1)
{
*arena_ptr = arena_alloc();
}
tctx_thread_local = tctx;
}
internal void
tctx_release(void)
{
for(U64 i = 0; i < ArrayCount(tctx_thread_local->arenas); i += 1)
{
arena_release(tctx_thread_local->arenas[i]);
}
}
internal TCTX *
tctx_get_equipped(void)
tctx_selected(void)
{
return tctx_thread_local;
}
//- rjf: scratch arenas
internal Arena *
tctx_get_scratch(Arena **conflicts, U64 count)
{
TCTX *tctx = tctx_get_equipped();
TCTX *tctx = tctx_selected();
Arena *result = 0;
Arena **arena_ptr = tctx->arenas;
for(U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1)
@@ -63,10 +73,55 @@ tctx_get_scratch(Arena **conflicts, U64 count)
return result;
}
//- rjf: lane metadata
internal LaneCtx
tctx_set_lane_ctx(LaneCtx lane_ctx)
{
TCTX *tctx = tctx_selected();
LaneCtx restore = tctx->lane_ctx;
tctx->lane_ctx = lane_ctx;
return restore;
}
internal void
tctx_lane_barrier_wait(void *broadcast_ptr, U64 broadcast_size, U64 broadcast_src_lane_idx)
{
ProfBeginFunction();
ProfColor(0x00000ff);
TCTX *tctx = tctx_selected();
// rjf: doing broadcast -> copy to broadcast memory on source lane
U64 broadcast_size_clamped = ClampTop(broadcast_size, sizeof(tctx->lane_ctx.broadcast_memory[0]));
if(broadcast_ptr != 0 && lane_idx() == broadcast_src_lane_idx)
{
MemoryCopy(tctx->lane_ctx.broadcast_memory, broadcast_ptr, broadcast_size_clamped);
}
// rjf: all cases: barrier
os_barrier_wait(tctx->lane_ctx.barrier);
// rjf: doing broadcast -> copy from broadcast memory on destination lanes
if(broadcast_ptr != 0 && lane_idx() != broadcast_src_lane_idx)
{
MemoryCopy(broadcast_ptr, tctx->lane_ctx.broadcast_memory, broadcast_size_clamped);
}
// rjf: doing broadcast -> barrier on all lanes
if(broadcast_ptr != 0)
{
os_barrier_wait(tctx->lane_ctx.barrier);
}
ProfEnd();
}
//- rjf: thread names
internal void
tctx_set_thread_name(String8 string)
{
TCTX *tctx = tctx_get_equipped();
TCTX *tctx = tctx_selected();
U64 size = ClampTop(string.size, sizeof(tctx->thread_name));
MemoryCopy(tctx->thread_name, string.str, size);
tctx->thread_name_size = size;
@@ -75,15 +130,17 @@ tctx_set_thread_name(String8 string)
internal String8
tctx_get_thread_name(void)
{
TCTX *tctx = tctx_get_equipped();
TCTX *tctx = tctx_selected();
String8 result = str8(tctx->thread_name, tctx->thread_name_size);
return result;
}
//- rjf: thread source-locations
internal void
tctx_write_srcloc(char *file_name, U64 line_number)
{
TCTX *tctx = tctx_get_equipped();
TCTX *tctx = tctx_selected();
tctx->file_name = file_name;
tctx->line_number = line_number;
}
@@ -91,7 +148,78 @@ tctx_write_srcloc(char *file_name, U64 line_number)
internal void
tctx_read_srcloc(char **file_name, U64 *line_number)
{
TCTX *tctx = tctx_get_equipped();
TCTX *tctx = tctx_selected();
*file_name = tctx->file_name;
*line_number = tctx->line_number;
}
////////////////////////////////
//~ rjf: Touch Scope Functions
internal Access *
access_open(void)
{
if(tctx_thread_local->access_arena == 0)
{
tctx_thread_local->access_arena = arena_alloc();
}
Access *access = tctx_thread_local->free_access;
if(access != 0)
{
SLLStackPop(tctx_thread_local->free_access);
}
else
{
access = push_array_no_zero(tctx_thread_local->access_arena, Access, 1);
}
MemoryZeroStruct(access);
return access;
}
internal void
access_close(Access *access)
{
for(Touch *touch = access->top_touch, *next = 0; touch != 0; touch = next)
{
next = touch->next;
ins_atomic_u64_dec_eval(&touch->pt->access_refcount);
if(touch->cv.u64[0] != 0) { cond_var_broadcast(touch->cv); }
SLLStackPush(tctx_thread_local->free_touch, touch);
}
SLLStackPush(tctx_thread_local->free_access, access);
}
internal void
access_touch(Access *access, AccessPt *pt, CondVar cv)
{
ins_atomic_u64_inc_eval(&pt->access_refcount);
ins_atomic_u64_eval_assign(&pt->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&pt->last_update_idx_touched, update_tick_idx());
Touch *touch = tctx_thread_local->free_touch;
if(touch != 0)
{
SLLStackPop(tctx_thread_local->free_touch);
}
else
{
touch = push_array_no_zero(tctx_thread_local->access_arena, Touch, 1);
}
MemoryZeroStruct(touch);
SLLStackPush(access->top_touch, touch);
touch->cv = cv;
touch->pt = pt;
}
//- rjf: access points
internal B32
access_pt_is_expired_(AccessPt *pt, AccessPtExpireParams *params)
{
U64 access_refcount = ins_atomic_u64_eval(&pt->access_refcount);
U64 last_time_touched_us = ins_atomic_u64_eval(&pt->last_time_touched_us);
U64 last_update_idx_touched = ins_atomic_u64_eval(&pt->last_update_idx_touched);
B32 result = (access_refcount == 0 &&
last_time_touched_us + params->time < os_now_microseconds() &&
last_update_idx_touched + params->update_idxs < update_tick_idx());
return result;
}
+105 -14
View File
@@ -5,37 +5,128 @@
#define BASE_THREAD_CONTEXT_H
////////////////////////////////
// NOTE(allen): Thread Context
//~ rjf: Lane Context
typedef struct LaneCtx LaneCtx;
struct LaneCtx
{
U64 lane_idx;
U64 lane_count;
Barrier barrier;
U64 *broadcast_memory;
};
////////////////////////////////
//~ rjf: Access Scopes
typedef struct AccessPt AccessPt;
struct AccessPt
{
U64 access_refcount;
U64 last_time_touched_us;
U64 last_update_idx_touched;
};
typedef struct AccessPtExpireParams AccessPtExpireParams;
struct AccessPtExpireParams
{
U64 time;
U64 update_idxs;
};
typedef struct Touch Touch;
struct Touch
{
Touch *next;
AccessPt *pt;
CondVar cv;
};
typedef struct Access Access;
struct Access
{
Access *next;
Touch *top_touch;
};
////////////////////////////////
//~ rjf: Base Per-Thread State Bundle
typedef struct TCTX TCTX;
struct TCTX
{
// rjf: scratch arenas
Arena *arenas[2];
// rjf: thread name
U8 thread_name[32];
U64 thread_name_size;
// rjf: lane context
LaneCtx lane_ctx;
// rjf: source location info
char *file_name;
U64 line_number;
// rjf: accesses
Arena *access_arena;
Access *free_access;
Touch *free_touch;
// rjf: progress
U64 *progress_counter_ptr;
U64 *progress_target_ptr;
};
////////////////////////////////
// NOTE(allen): Thread Context Functions
//~ rjf: Thread Context Functions
internal void tctx_init_and_equip(TCTX *tctx);
internal void tctx_release(void);
internal TCTX* tctx_get_equipped(void);
internal Arena* tctx_get_scratch(Arena **conflicts, U64 count);
internal void tctx_set_thread_name(String8 name);
internal String8 tctx_get_thread_name(void);
internal void tctx_write_srcloc(char *file_name, U64 line_number);
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
//- rjf: thread-context allocation & selection
internal TCTX *tctx_alloc(void);
internal void tctx_release(TCTX *tctx);
internal void tctx_select(TCTX *tctx);
internal TCTX *tctx_selected(void);
//- rjf: scratch arenas
internal Arena *tctx_get_scratch(Arena **conflicts, U64 count);
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
#define scratch_end(scratch) temp_end(scratch)
//- rjf: lane metadata
internal LaneCtx tctx_set_lane_ctx(LaneCtx lane_ctx);
internal void tctx_lane_barrier_wait(void *broadcast_ptr, U64 broadcast_size, U64 broadcast_src_lane_idx);
#define lane_idx() (tctx_selected()->lane_ctx.lane_idx)
#define lane_count() (tctx_selected()->lane_ctx.lane_count)
#define lane_from_task_idx(idx) ((idx)%lane_count())
#define lane_ctx(ctx) tctx_set_lane_ctx((ctx))
#define lane_sync() tctx_lane_barrier_wait(0, 0, 0)
#define lane_sync_u64(ptr, src_lane_idx) tctx_lane_barrier_wait((ptr), sizeof(*(ptr)), (src_lane_idx))
#define lane_range(count) m_range_from_n_idx_m_count(lane_idx(), lane_count(), (count))
//- rjf: thread names
internal void tctx_set_thread_name(String8 name);
internal String8 tctx_get_thread_name(void);
//- rjf: thread source-locations
internal void tctx_write_srcloc(char *file_name, U64 line_number);
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
//- rjf: access scopes
internal Access *access_open(void);
internal void access_close(Access *access);
internal void access_touch(Access *access, AccessPt *pt, CondVar cv);
//- rjf: access points
internal B32 access_pt_is_expired_(AccessPt *pt, AccessPtExpireParams *params);
#define access_pt_is_expired(pt, ...) access_pt_is_expired_((pt), &(AccessPtExpireParams){.time = 2000000, .update_idxs = 10, __VA_ARGS__})
//- rjf: progress counters
#define set_progress_ptr(ptr) (tctx_selected()->progress_counter_ptr = (ptr))
#define set_progress_target_ptr(ptr) (tctx_selected()->progress_target_ptr = (ptr))
#define set_progress(val) (tctx_selected()->progress_counter_ptr ? ins_atomic_u64_eval_assign(tctx_selected()->progress_counter_ptr, (val)) : (void)0)
#define add_progress(val) (tctx_selected()->progress_counter_ptr ? ins_atomic_u64_add_eval(tctx_selected()->progress_counter_ptr, (val)) : (void)0)
#define set_progress_target(val) (tctx_selected()->progress_target_ptr ? ins_atomic_u64_eval_assign(tctx_selected()->progress_target_ptr, (val)) : (void)0)
#endif // BASE_THREAD_CONTEXT_H
+102
View File
@@ -0,0 +1,102 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Thread Functions
internal Thread
thread_launch(ThreadEntryPointFunctionType *f, void *p)
{
Thread thread = os_thread_launch(f, p);
return thread;
}
internal B32
thread_join(Thread thread, U64 endt_us)
{
B32 result = os_thread_join(thread, endt_us);
return result;
}
internal void
thread_detach(Thread thread)
{
os_thread_detach(thread);
}
////////////////////////////////
//~ rjf: Synchronization Primitive Functions
//- rjf: recursive mutexes
internal Mutex mutex_alloc(void) {return os_mutex_alloc();}
internal void mutex_release(Mutex mutex) {os_mutex_release(mutex);}
internal void mutex_take(Mutex mutex) {os_mutex_take(mutex);}
internal void mutex_drop(Mutex mutex) {os_mutex_drop(mutex);}
//- rjf: reader/writer mutexes
internal RWMutex rw_mutex_alloc(void) {return os_rw_mutex_alloc();}
internal void rw_mutex_release(RWMutex mutex) {os_rw_mutex_release(mutex);}
internal void rw_mutex_take(RWMutex mutex, B32 write_mode) {os_rw_mutex_take(mutex, write_mode);}
internal void rw_mutex_drop(RWMutex mutex, B32 write_mode) {os_rw_mutex_drop(mutex, write_mode);}
//- rjf: condition variables
internal CondVar cond_var_alloc(void) {return os_cond_var_alloc();}
internal void cond_var_release(CondVar cv) {os_cond_var_release(cv);}
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us) {return os_cond_var_wait(cv, mutex, endt_us);}
internal B32 cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us) {return os_cond_var_wait_rw(cv, mutex_rw, write_mode, endt_us);}
internal void cond_var_signal(CondVar cv) {os_cond_var_signal(cv);}
internal void cond_var_broadcast(CondVar cv) {os_cond_var_broadcast(cv);}
//- rjf: cross-process semaphores
internal Semaphore semaphore_alloc(U32 initial_count, U32 max_count, String8 name) {return os_semaphore_alloc(initial_count, max_count, name);}
internal void semaphore_release(Semaphore semaphore) {os_semaphore_release(semaphore);}
internal Semaphore semaphore_open(String8 name) {return os_semaphore_open(name);}
internal void semaphore_close(Semaphore semaphore) {os_semaphore_close(semaphore);}
internal B32 semaphore_take(Semaphore semaphore, U64 endt_us) {return os_semaphore_take(semaphore, endt_us);}
internal void semaphore_drop(Semaphore semaphore) {os_semaphore_drop(semaphore);}
//- rjf: barriers
internal Barrier barrier_alloc(U64 count) {return os_barrier_alloc(count);}
internal void barrier_release(Barrier barrier) {os_barrier_release(barrier);}
internal void barrier_wait(Barrier barrier) {os_barrier_wait(barrier);}
////////////////////////////////
//~ rjf: Table Stripe Functions
internal StripeArray
stripe_array_alloc(Arena *arena)
{
StripeArray array = {0};
array.count = os_get_system_info()->logical_processor_count;
array.v = push_array(arena, Stripe, array.count);
for EachIndex(idx, array.count)
{
array.v[idx].arena = arena_alloc();
array.v[idx].rw_mutex = rw_mutex_alloc();
array.v[idx].cv = cond_var_alloc();
}
return array;
}
internal void
stripe_array_release(StripeArray *stripes)
{
for EachIndex(idx, stripes->count)
{
arena_release(stripes->v[idx].arena);
rw_mutex_release(stripes->v[idx].rw_mutex);
cond_var_release(stripes->v[idx].cv);
}
}
internal Stripe *
stripe_from_slot_idx(StripeArray *stripes, U64 slot_idx)
{
Stripe *stripe = &stripes->v[slot_idx%stripes->count];
return stripe;
}
+133
View File
@@ -0,0 +1,133 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_THREADS_H
#define BASE_THREADS_H
////////////////////////////////
//~ rjf: Thread Types
typedef struct Thread Thread;
struct Thread
{
U64 u64[1];
};
typedef void ThreadEntryPointFunctionType(void *p);
////////////////////////////////
//~ rjf: Synchronization Primitive Types
typedef struct Mutex Mutex;
struct Mutex
{
U64 u64[1];
};
typedef struct RWMutex RWMutex;
struct RWMutex
{
U64 u64[1];
};
typedef struct CondVar CondVar;
struct CondVar
{
U64 u64[1];
};
typedef struct Semaphore Semaphore;
struct Semaphore
{
U64 u64[1];
};
typedef struct Barrier Barrier;
struct Barrier
{
U64 u64[1];
};
////////////////////////////////
//~ rjf: Table Stripes
typedef struct Stripe Stripe;
struct Stripe
{
Arena *arena;
RWMutex rw_mutex;
CondVar cv;
void *free;
};
typedef struct StripeArray StripeArray;
struct StripeArray
{
Stripe *v;
U64 count;
};
////////////////////////////////
//~ rjf: Thread Functions
internal Thread thread_launch(ThreadEntryPointFunctionType *f, void *p);
internal B32 thread_join(Thread thread, U64 endt_us);
internal void thread_detach(Thread thread);
////////////////////////////////
//~ rjf: Synchronization Primitive Functions
//- rjf: recursive mutexes
internal Mutex mutex_alloc(void);
internal void mutex_release(Mutex mutex);
internal void mutex_take(Mutex mutex);
internal void mutex_drop(Mutex mutex);
//- rjf: reader/writer mutexes
internal RWMutex rw_mutex_alloc(void);
internal void rw_mutex_release(RWMutex mutex);
internal void rw_mutex_take(RWMutex mutex, B32 write_mode);
internal void rw_mutex_drop(RWMutex mutex, B32 write_mode);
#define rw_mutex_take_r(m) rw_mutex_take((m), (0))
#define rw_mutex_take_w(m) rw_mutex_take((m), (1))
#define rw_mutex_drop_r(m) rw_mutex_drop((m), (0))
#define rw_mutex_drop_w(m) rw_mutex_drop((m), (1))
//- rjf: condition variables
internal CondVar cond_var_alloc(void);
internal void cond_var_release(CondVar cv);
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us);
internal B32 cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us);
#define cond_var_wait_rw_r(cv, m, endt) cond_var_wait_rw((cv), (m), (0), (endt))
#define cond_var_wait_rw_w(cv, m, endt) cond_var_wait_rw((cv), (m), (1), (endt))
internal void cond_var_signal(CondVar cv);
internal void cond_var_broadcast(CondVar cv);
//- rjf: cross-process semaphores
internal Semaphore semaphore_alloc(U32 initial_count, U32 max_count, String8 name);
internal void semaphore_release(Semaphore semaphore);
internal Semaphore semaphore_open(String8 name);
internal void semaphore_close(Semaphore semaphore);
internal B32 semaphore_take(Semaphore semaphore, U64 endt_us);
internal void semaphore_drop(Semaphore semaphore);
//- rjf: barriers
internal Barrier barrier_alloc(U64 count);
internal void barrier_release(Barrier barrier);
internal void barrier_wait(Barrier barrier);
//- rjf: scope macros
#define MutexScope(mutex) DeferLoop(mutex_take(mutex), mutex_drop(mutex))
#define RWMutexScope(mutex, write_mode) DeferLoop(rw_mutex_take((mutex), (write_mode)), rw_mutex_drop((mutex), (write_mode)))
#define MutexScopeR(mutex) DeferLoop(rw_mutex_take_r(mutex), rw_mutex_drop_r(mutex))
#define MutexScopeW(mutex) DeferLoop(rw_mutex_take_w(mutex), rw_mutex_drop_w(mutex))
#define MutexScopeRWPromote(mutex) DeferLoop((rw_mutex_drop_r(mutex), rw_mutex_take_w(mutex)), (rw_mutex_drop_w(mutex), rw_mutex_take_r(mutex)))
////////////////////////////////
//~ rjf: Table Stripe Functions
internal StripeArray stripe_array_alloc(Arena *arena);
internal void stripe_array_release(StripeArray *stripes);
internal Stripe *stripe_from_slot_idx(StripeArray *stripes, U64 slot_idx);
#endif // BASE_THREADS_H
+114 -99
View File
@@ -31,21 +31,21 @@ coff_section_flag_from_align_size(U64 align)
{
COFF_SectionFlags flags = 0;
switch (align) {
case 0: flags = COFF_SectionAlign_None; break;
case 1: flags = COFF_SectionAlign_1Bytes; break;
case 2: flags = COFF_SectionAlign_2Bytes; break;
case 4: flags = COFF_SectionAlign_4Bytes; break;
case 8: flags = COFF_SectionAlign_8Bytes; break;
case 16: flags = COFF_SectionAlign_16Bytes; break;
case 32: flags = COFF_SectionAlign_32Bytes; break;
case 64: flags = COFF_SectionAlign_64Bytes; break;
case 128: flags = COFF_SectionAlign_128Bytes; break;
case 256: flags = COFF_SectionAlign_256Bytes; break;
case 512: flags = COFF_SectionAlign_512Bytes; break;
case 1024: flags = COFF_SectionAlign_1024Bytes; break;
case 2048: flags = COFF_SectionAlign_2048Bytes; break;
case 4096: flags = COFF_SectionAlign_4096Bytes; break;
case 8192: flags = COFF_SectionAlign_8192Bytes; break;
case 0: flags = COFF_SectionAlign_None; break;
case 1: flags = COFF_SectionAlign_1Bytes; break;
case 2: flags = COFF_SectionAlign_2Bytes; break;
case 4: flags = COFF_SectionAlign_4Bytes; break;
case 8: flags = COFF_SectionAlign_8Bytes; break;
case 16: flags = COFF_SectionAlign_16Bytes; break;
case 32: flags = COFF_SectionAlign_32Bytes; break;
case 64: flags = COFF_SectionAlign_64Bytes; break;
case 128: flags = COFF_SectionAlign_128Bytes; break;
case 256: flags = COFF_SectionAlign_256Bytes; break;
case 512: flags = COFF_SectionAlign_512Bytes; break;
case 1024: flags = COFF_SectionAlign_1024Bytes; break;
case 2048: flags = COFF_SectionAlign_2048Bytes; break;
case 4096: flags = COFF_SectionAlign_4096Bytes; break;
case 8192: flags = COFF_SectionAlign_8192Bytes; break;
}
flags <<= COFF_SectionFlag_AlignShift;
return flags;
@@ -103,6 +103,21 @@ coff_read_symbol_name(String8 string_table, COFF_SymbolName *name)
return name_str;
}
internal U64
coff_is_addr_reloc(COFF_MachineType machine, U32 type)
{
U64 is_addr = 0;
switch (machine) {
case COFF_MachineType_Unknown: is_addr = 0; break;
case COFF_MachineType_X64: {
if (type == COFF_Reloc_X64_Addr32) is_addr = 4;
else if (type == COFF_Reloc_X64_Addr64) is_addr = 8;
} break;
default: NotImplemented; break;
}
return is_addr;
}
internal U64
coff_apply_size_from_reloc_x64(COFF_Reloc_X64 x)
{
@@ -182,67 +197,67 @@ coff_pick_reloc_value_x64(COFF_Reloc_X64 type,
{
U64 reloc_value_size = 0;
S64 reloc_value = 0;
switch (type) {
case COFF_Reloc_X64_Abs: {} break;
case COFF_Reloc_X64_Addr64: {
reloc_value_size = 8;
reloc_value = symbol_virtual_offset + (S64)image_base;
} break;
case COFF_Reloc_X64_Addr32: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset + (S64)image_base);
} break;
case COFF_Reloc_X64_Addr32Nb: {
reloc_value_size = 4;
reloc_value = symbol_virtual_offset;
} break;
case COFF_Reloc_X64_Rel32: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 0));
} break;
case COFF_Reloc_X64_Rel32_1: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 1));
} break;
case COFF_Reloc_X64_Rel32_2: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 2));
} break;
case COFF_Reloc_X64_Rel32_3: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 3));
} break;
case COFF_Reloc_X64_Rel32_4: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 4));
} break;
case COFF_Reloc_X64_Rel32_5: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 5));
} break;
case COFF_Reloc_X64_Section: {
reloc_value_size = 4;
reloc_value = symbol_section_number;
} break;
case COFF_Reloc_X64_SecRel: {
reloc_value_size = 4;
reloc_value = symbol_section_offset;
} break;
case COFF_Reloc_X64_SecRel7:
case COFF_Reloc_X64_Token:
case COFF_Reloc_X64_SRel32:
case COFF_Reloc_X64_Pair:
case COFF_Reloc_X64_SSpan32:
case COFF_Reloc_X64_Unknown_11: {
NotImplemented;
} break;
case COFF_Reloc_X64_Abs: {} break;
case COFF_Reloc_X64_Addr64: {
reloc_value_size = 8;
reloc_value = symbol_virtual_offset + (S64)image_base;
} break;
case COFF_Reloc_X64_Addr32: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset + (S64)image_base);
} break;
case COFF_Reloc_X64_Addr32Nb: {
reloc_value_size = 4;
reloc_value = symbol_virtual_offset;
} break;
case COFF_Reloc_X64_Rel32: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 0));
} break;
case COFF_Reloc_X64_Rel32_1: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 1));
} break;
case COFF_Reloc_X64_Rel32_2: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 2));
} break;
case COFF_Reloc_X64_Rel32_3: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 3));
} break;
case COFF_Reloc_X64_Rel32_4: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 4));
} break;
case COFF_Reloc_X64_Rel32_5: {
reloc_value_size = 4;
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 5));
} break;
case COFF_Reloc_X64_Section: {
reloc_value_size = 4;
reloc_value = symbol_section_number;
} break;
case COFF_Reloc_X64_SecRel: {
reloc_value_size = 4;
reloc_value = symbol_section_offset;
} break;
case COFF_Reloc_X64_SecRel7:
case COFF_Reloc_X64_Token:
case COFF_Reloc_X64_SRel32:
case COFF_Reloc_X64_Pair:
case COFF_Reloc_X64_SSpan32:
case COFF_Reloc_X64_Unknown_11: {
NotImplemented;
} break;
}
COFF_RelocValue result = {0};
result.size = reloc_value_size;
result.value = reloc_value;
return result;
}
@@ -265,7 +280,7 @@ coff_make_lib_member_header(Arena *arena, String8 name, COFF_TimeStamp time_stam
str8_list_pushf(scratch.arena, &list, "%-10u", size);
str8_list_pushf(scratch.arena, &list, "`\n");
String8 result = str8_list_join(arena, &list, 0);
Assert(result.size == sizeof(COFF_ArchiveMemberHeader));
scratch_end(scratch);
return result;
@@ -302,18 +317,18 @@ coff_ordinal_data_from_hint(Arena *arena, COFF_MachineType machine, U16 hint)
{
String8 ordinal_data = {0};
switch (machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: {
U64 *ordinal = push_array(arena, U64, 1);
*ordinal = coff_make_ordinal64(hint);
ordinal_data = str8_struct(ordinal);
} break;
case COFF_MachineType_X86: {
U32 *ordinal = push_array(arena, U32, 1);
*ordinal = coff_make_ordinal32(hint);
ordinal_data = str8_struct(ordinal);
} break;
default: { NotImplemented; } break;
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: {
U64 *ordinal = push_array(arena, U64, 1);
*ordinal = coff_make_ordinal64(hint);
ordinal_data = str8_struct(ordinal);
} break;
case COFF_MachineType_X86: {
U32 *ordinal = push_array(arena, U32, 1);
*ordinal = coff_make_ordinal32(hint);
ordinal_data = str8_struct(ordinal);
} break;
default: { NotImplemented; } break;
}
return ordinal_data;
}
@@ -331,7 +346,7 @@ coff_make_import_header(Arena *arena,
COFF_ImportHeaderFlags flags = 0;
flags |= (type & COFF_ImportHeader_TypeMask) << COFF_ImportHeader_TypeShift;
flags |= import_by << COFF_ImportHeader_ImportByShift;
COFF_ImportHeader header = {0};
header.sig1 = COFF_MachineType_Unknown;
header.sig2 = max_U16;
@@ -368,12 +383,12 @@ coff_code_align_byte_from_machine(COFF_MachineType machine)
{
U8 align_byte = 0;
switch (machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64:
case COFF_MachineType_X86: {
align_byte = 0xCC;
} break;
default: { NotImplemented; } break;
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64:
case COFF_MachineType_X86: {
align_byte = 0xCC;
} break;
default: { NotImplemented; } break;
}
return align_byte;
}
@@ -383,11 +398,11 @@ coff_default_align_from_machine(COFF_MachineType machine)
{
U16 align = 0;
switch (machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: {
align = 16;
} break;
default: { NotImplemented; } break;
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: {
align = 16;
} break;
default: { NotImplemented; } break;
}
return align;
}
@@ -472,12 +487,12 @@ coff_string_from_time_stamp(Arena *arena, COFF_TimeStamp time_stamp)
result = str8_lit("-1");
} else {
DateTime dt = date_time_from_unix_time(time_stamp);
result = push_date_time_string(arena, &dt);
result = string_from_date_time(arena, &dt);
}
return result;
}
read_only struct
global read_only struct
{
String8 string;
COFF_MachineType machine;
@@ -509,7 +524,7 @@ read_only struct
{ str8_lit_comp("WceMipsV2"), COFF_MachineType_WceMipsV2 },
};
read_only static struct {
global read_only struct {
char * name;
COFF_ImportType type;
} g_coff_import_header_type_map[] = {
+4 -1
View File
@@ -294,10 +294,11 @@ typedef struct COFF_Symbol32
typedef U32 COFF_WeakExtType;
enum
{
COFF_WeakExt_Null = 0,
COFF_WeakExt_NoLibrary = 1,
COFF_WeakExt_SearchLibrary = 2,
COFF_WeakExt_SearchAlias = 3,
COFF_WeakExt_AntiDependency = 4, // default symbol must not reference a weak symbol
COFF_WeakExt_AntiDependency = 4, // search libraries only if the default symbol is weak, even if the weak symbol points to itself
};
// storage class: External
@@ -600,6 +601,8 @@ internal String8 coff_read_symbol_name(String8 string_table, COFF_SymbolName *na
////////////////////////////////
// Reloc
internal U64 coff_is_addr_reloc(COFF_MachineType machine, U32 type);
internal U64 coff_apply_size_from_reloc_x64(COFF_Reloc_X64 x);
internal U64 coff_apply_size_from_reloc_x86(COFF_Reloc_X86 x);
+140 -118
View File
@@ -1,12 +1,12 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal COFF_LibWriterSymbolNode *
coff_lib_writer_symbol_list_push(Arena *arena, COFF_LibWriterSymbolList *list, COFF_LibWriterSymbol symbol)
{
COFF_LibWriterSymbolNode *node = push_array_no_zero(arena, COFF_LibWriterSymbolNode, 1);
node->next = 0;
node->data = symbol;
node->next = 0;
node->data = symbol;
SLLQueuePush(list->first, list->last, node);
list->count += 1;
return node;
@@ -16,8 +16,8 @@ internal COFF_LibWriterMemberNode *
coff_lib_writer_member_list_push(Arena *arena, COFF_LibWriterMemberList *list, COFF_LibWriterMember member)
{
COFF_LibWriterMemberNode *node = push_array_no_zero(arena, COFF_LibWriterMemberNode, 1);
node->next = 0;
node->data = member;
node->next = 0;
node->data = member;
SLLQueuePush(list->first, list->last, node);
list->count += 1;
return node;
@@ -29,8 +29,7 @@ coff_lib_writer_symbol_array_from_list(Arena *arena, COFF_LibWriterSymbolList li
COFF_LibWriterSymbol *arr = push_array_no_zero(arena, COFF_LibWriterSymbol, list.count + 2);
COFF_LibWriterSymbol *ptr = arr + 1;
for (COFF_LibWriterSymbolNode *i = list.first; i != 0; i = i->next, ptr += 1) {
ptr->name = push_str8_copy(arena, i->data.name);
ptr->member_idx = i->data.member_idx;
*ptr = i->data;
}
MemoryZeroStruct(&arr[0]);
MemoryZeroStruct(&arr[list.count+1]);
@@ -58,17 +57,30 @@ coff_lib_writer_symbol_name_compar(const void *raw_a, const void *raw_b)
}
internal int
coff_lib_writer_symbol_is_before(void *raw_a, void *raw_b)
coff_lib_writer_symbol_is_before_name(void *raw_a, void *raw_b)
{
int compar = coff_lib_writer_symbol_name_compar(raw_a, raw_b);
return compar < 0;
return coff_lib_writer_symbol_name_compar(raw_a, raw_b) < 0;
}
internal int
coff_lib_writer_symbol_is_before_member_idx(void *raw_a, void *raw_b)
{
COFF_LibWriterSymbol *a = raw_a, *b = raw_b;
return a->member_idx < b->member_idx;
}
internal void
coff_lib_writer_symbol_array_sort(COFF_LibWriterSymbol *arr, U64 count)
coff_lib_writer_symbol_array_sort_on_name(COFF_LibWriterSymbol *arr, U64 count)
{
Assert(count >= 2);
radsort(arr + 1, count - 2, coff_lib_writer_symbol_is_before);
radsort(arr + 1, count - 2, coff_lib_writer_symbol_is_before_name);
}
internal void
coff_lib_writer_symbol_array_sort_on_member_idx(COFF_LibWriterSymbol *arr, U64 count)
{
Assert(count >= 2);
radsort(arr + 1, count - 2, coff_lib_writer_symbol_is_before_member_idx);
}
internal COFF_LibWriter *
@@ -93,36 +105,21 @@ coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_d
U64 member_idx = writer->member_list.count;
// push obj member
COFF_LibWriterMember member = {0};
member.name = obj_path;
member.data = obj_data;
coff_lib_writer_member_list_push(writer->arena, &writer->member_list, member);
COFF_LibWriterMember member = { .name = obj_path, .data = obj_data };
COFF_LibWriterMemberNode *member_node = coff_lib_writer_member_list_push(writer->arena, &writer->member_list, member);
// push external symbols
{
COFF_FileHeaderInfo obj_header = coff_file_header_info_from_data(obj_data);
String8 string_table = str8_substr(obj_data, obj_header.string_table_range);
String8 symbol_table = str8_substr(obj_data, obj_header.symbol_table_range);
COFF_ParsedSymbol symbol;
for (U64 symbol_idx = 0; symbol_idx < obj_header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) {
void *symbol_ptr;
if (obj_header.is_big_obj) {
symbol_ptr = &((COFF_Symbol32 *)symbol_table.str)[symbol_idx];
symbol = coff_parse_symbol32(string_table, symbol_ptr);
} else {
symbol_ptr = &((COFF_Symbol16 *)symbol_table.str)[symbol_idx];
symbol = coff_parse_symbol16(string_table, symbol_ptr);
}
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
if (interp == COFF_SymbolValueInterp_Regular) {
if (symbol.storage_class == COFF_SymStorageClass_External) {
COFF_LibWriterSymbol lib_symbol = {0};
lib_symbol.name = symbol.name;
lib_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, lib_symbol);
}
COFF_FileHeaderInfo obj_header = coff_file_header_info_from_data(obj_data);
String8 string_table = str8_substr(obj_data, obj_header.string_table_range);
String8 symbol_table = str8_substr(obj_data, obj_header.symbol_table_range);
COFF_ParsedSymbol symbol;
for (U64 symbol_idx = 0; symbol_idx < obj_header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) {
symbol = coff_parse_symbol(obj_header, string_table, symbol_table, symbol_idx);
COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol);
if (interp == COFF_SymbolValueInterp_Regular || interp == COFF_SymbolValueInterp_Common || interp == COFF_SymbolValueInterp_Abs) {
if (symbol.storage_class == COFF_SymStorageClass_External) {
COFF_LibWriterSymbol lib_symbol = { .name = symbol.name, .member_idx = member_idx };
coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, lib_symbol);
}
}
}
@@ -133,30 +130,29 @@ coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_d
internal void
coff_lib_writer_push_import(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportByType import_by, String8 name, U16 hint_or_ordinal, COFF_ImportType import_type)
{
// push import member
U64 member_idx = lib_writer->member_list.count;
COFF_LibWriterMember member = {0};
member.name = dll_name;
member.data = coff_make_import_header(lib_writer->arena, machine, time_stamp, dll_name, import_by, name, hint_or_ordinal, import_type);
coff_lib_writer_member_list_push(lib_writer->arena, &lib_writer->member_list, member);
String8 member_data = coff_make_import_header(lib_writer->arena, machine, time_stamp, dll_name, import_by, name, hint_or_ordinal, import_type);
COFF_LibWriterMember member = { .name = dll_name, .data = member_data };
COFF_LibWriterMemberNode *member_node = coff_lib_writer_member_list_push(lib_writer->arena, &lib_writer->member_list, member);
if (name.size) {
switch (import_type) {
case COFF_ImportHeader_Code: {
COFF_LibWriterSymbol thunk_symbol = {0};
thunk_symbol.name = push_str8_copy(lib_writer->arena, name);
thunk_symbol.member_idx = member_idx;
thunk_symbol.name = push_str8_copy(lib_writer->arena, name);
thunk_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, thunk_symbol);
COFF_LibWriterSymbol imp_symbol = {0};
imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name);
imp_symbol.member_idx = member_idx;
imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name);
imp_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, imp_symbol);
} break;
case COFF_ImportHeader_Data: {
COFF_LibWriterSymbol imp_symbol = {0};
imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name);
imp_symbol.member_idx = member_idx;
imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name);
imp_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, imp_symbol);
} break;
case COFF_ImportHeader_Const: { NotImplemented; } break;
@@ -165,34 +161,22 @@ coff_lib_writer_push_import(COFF_LibWriter *lib_writer, COFF_MachineType machine
}
}
internal String8List
internal String8
coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member)
{
Temp scratch = scratch_begin(&arena, 1);
// symbol & member lists -> arrays
U64 symbols_count;
COFF_LibWriterSymbol *symbols;
U64 member_count;
COFF_LibWriterMember *member_array;
{
U64 symbols_count_with_null = lib_writer->symbol_list.count + 2;
COFF_LibWriterSymbol *symbols_with_null = coff_lib_writer_symbol_array_from_list(scratch.arena, lib_writer->symbol_list);
coff_lib_writer_symbol_array_sort(symbols_with_null, symbols_count_with_null);
symbols_count = symbols_count_with_null - 2;
symbols = symbols_with_null + 1;
member_count = lib_writer->member_list.count;
member_array = coff_lib_writer_member_array_from_list(scratch.arena, lib_writer->member_list);
}
// member lists -> arrays
U64 member_count = lib_writer->member_list.count;
COFF_LibWriterMember *member_array = coff_lib_writer_member_array_from_list(scratch.arena, lib_writer->member_list);
// serialize members
U64 *member_offsets = push_array_no_zero(scratch.arena, U64, member_count);
String8List long_names_list = {0};
String8List member_data_list = {0};
U64 *member_offsets = push_array_no_zero(scratch.arena, U64, member_count);
String8List long_names_list = {0};
String8List member_data_list = {0};
{
HashTable *name_ht = hash_table_init(scratch.arena, 1024);
for (U64 member_idx = 0; member_idx < member_count; member_idx += 1) {
for EachIndex(member_idx, member_count) {
COFF_LibWriterMember *member = &member_array[member_idx];
// make member name
@@ -215,54 +199,44 @@ coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeSta
member_offsets[member_idx] = member_data_list.total_size;
String8 member_data = member->data;
String8 member_header = coff_make_lib_member_header(arena, name, time_stamp, 0, 0, mode, member_data.size);
String8 member_header = coff_make_lib_member_header(scratch.arena, name, time_stamp, 0, 0, mode, member_data.size);
str8_list_push(arena, &member_data_list, member_header);
str8_list_push(arena, &member_data_list, member_data);
str8_list_push(scratch.arena, &member_data_list, member_header);
str8_list_push(scratch.arena, &member_data_list, member_data);
{
U64 pad_size = AlignPadPow2(member_data_list.total_size, COFF_Archive_MemberAlign);
U8 *pad = push_array(arena, U8, pad_size);
str8_list_push(arena, &member_data_list, str8(pad, pad_size));
U8 *pad = push_array(scratch.arena, U8, pad_size);
str8_list_push(scratch.arena, &member_data_list, str8(pad, pad_size));
}
}
}
// long names member
// write long names member
if (long_names_list.total_size) {
String8 header = coff_make_lib_member_header(arena, str8_lit("//"), time_stamp, 0, 0, mode, long_names_list.total_size);
String8 data = str8_list_join(arena, &long_names_list, 0);
String8 header = coff_make_lib_member_header(scratch.arena, str8_lit("//"), time_stamp, 0, 0, mode, long_names_list.total_size);
String8 data = str8_list_join(scratch.arena, &long_names_list, 0);
U64 member_offset = member_data_list.total_size + data.size + header.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(arena, U8, pad_size);
str8_list_push_front(arena, &member_data_list, str8(pad, pad_size));
U8 *pad = push_array(scratch.arena, U8, pad_size);
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(arena, &member_data_list, data);
str8_list_push_front(arena, &member_data_list, header);
str8_list_push_front(scratch.arena, &member_data_list, data);
str8_list_push_front(scratch.arena, &member_data_list, header);
}
// compute size for symbol string table
U32 name_buffer_size = 0;
for (COFF_LibWriterSymbol *ptr = &symbols[0], *opl = ptr + symbols_count; ptr < opl; ptr += 1) {
name_buffer_size += ptr->name.size;
for (COFF_LibWriterSymbolNode *node = lib_writer->symbol_list.first; node != 0; node = node->next) {
name_buffer_size += node->data.name.size;
name_buffer_size += 1; // null
}
// write symbol name buffer
U8 *name_buffer = push_array_no_zero(scratch.arena, U8, name_buffer_size);
{
U64 name_cursor = 0;
for (COFF_LibWriterSymbol *ptr = &symbols[0], *opl = ptr + symbols_count; ptr < opl; ptr += 1) {
MemoryCopy(name_buffer + name_cursor, ptr->name.str, ptr->name.size);
name_buffer[name_cursor + ptr->name.size] = '\0';
name_cursor += ptr->name.size + 1;
}
}
// compute members base offset
U64 members_base_offset;
{
U64 sizeof_first_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * symbols_count + name_buffer_size;
U64 sizeof_second_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * member_count + sizeof(U32) + sizeof(U16) * symbols_count + name_buffer_size;
U64 sizeof_first_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * lib_writer->symbol_list.count + name_buffer_size;
U64 sizeof_second_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * member_count + sizeof(U32) + sizeof(U16) * lib_writer->symbol_list.count + name_buffer_size;
U64 sizeof_long_names = sizeof(COFF_ArchiveMemberHeader) + long_names_list.total_size;
sizeof_first_header = AlignPow2(sizeof_first_header, COFF_Archive_MemberAlign);
@@ -279,8 +253,31 @@ coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeSta
}
}
// second linker member
// write second linker member
if (emit_second_member) {
U64 symbols_count;
COFF_LibWriterSymbol *symbols;
{
U64 symbols_count_with_null = lib_writer->symbol_list.count + 2;
COFF_LibWriterSymbol *symbols_with_null = coff_lib_writer_symbol_array_from_list(scratch.arena, lib_writer->symbol_list);
coff_lib_writer_symbol_array_sort_on_name(symbols_with_null, symbols_count_with_null);
symbols_count = symbols_count_with_null - 2;
symbols = symbols_with_null + 1;
}
// write symbol name buffer
U8 *name_buffer = push_array_no_zero(scratch.arena, U8, name_buffer_size);
{
U64 name_cursor = 0;
for EachIndex(symbol_idx, symbols_count) {
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
MemoryCopy(name_buffer + name_cursor, symbol->name.str, symbol->name.size);
name_buffer[name_cursor + symbol->name.size] = '\0';
name_cursor += symbol->name.size + 1;
}
}
U32 member_count32 = safe_cast_u32(member_count);
U32 symbol_count32 = safe_cast_u32(symbols_count);
@@ -288,14 +285,14 @@ coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeSta
U16 *member_idx16_arr = push_array_no_zero(scratch.arena, U16, symbols_count);
// write member offset array
for (U64 member_idx = 0; member_idx < member_count; member_idx += 1) {
for EachIndex(member_idx, member_count) {
U64 member_offset = members_base_offset + member_offsets[member_idx];
U32 member_off32 = safe_cast_u32(member_offset);
member_off32_arr[member_idx] = member_off32;
}
// write member offset indices for each symbol
for (U64 symbol_idx = 0; symbol_idx < symbols_count; symbol_idx += 1) {
for EachIndex(symbol_idx, symbols_count) {
// member offset indices are 1-based
U64 member_idx = symbols[symbol_idx].member_idx + 1;
U16 member_idx16 = safe_cast_u16(member_idx);
@@ -310,25 +307,48 @@ coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeSta
str8_list_push(scratch.arena, &second_member_data_list, str8_array(member_idx16_arr, symbols_count));
str8_list_push(scratch.arena, &second_member_data_list, str8(name_buffer, name_buffer_size));
String8 member_data = str8_list_join(arena, &second_member_data_list, 0);
String8 member_header = coff_make_lib_member_header(arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
String8 member_data = str8_list_join(scratch.arena, &second_member_data_list, 0);
String8 member_header = coff_make_lib_member_header(scratch.arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
U64 member_offset = member_data_list.total_size + member_data.size + member_header.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(arena, U8, pad_size);
str8_list_push_front(arena, &member_data_list, str8(pad, pad_size));
U8 *pad = push_array(scratch.arena, U8, pad_size);
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(arena, &member_data_list, member_data);
str8_list_push_front(arena, &member_data_list, member_header);
str8_list_push_front(scratch.arena, &member_data_list, member_data);
str8_list_push_front(scratch.arena, &member_data_list, member_header);
}
// first linker member (obsolete, but kept for compatability reasons)
// write first linker member (obsolete, but kept for compatability reasons)
{
U64 symbols_count;
COFF_LibWriterSymbol *symbols;
{
U64 symbols_count_with_null = lib_writer->symbol_list.count + 2;
COFF_LibWriterSymbol *symbols_with_null = coff_lib_writer_symbol_array_from_list(scratch.arena, lib_writer->symbol_list);
coff_lib_writer_symbol_array_sort_on_member_idx(symbols_with_null, symbols_count_with_null);
symbols_count = symbols_count_with_null - 2;
symbols = symbols_with_null + 1;
}
// write symbol name buffer
U8 *name_buffer = push_array_no_zero(scratch.arena, U8, name_buffer_size);
{
U64 name_cursor = 0;
for EachIndex(symbol_idx, symbols_count) {
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
MemoryCopy(name_buffer + name_cursor, symbol->name.str, symbol->name.size);
name_buffer[name_cursor + symbol->name.size] = '\0';
name_cursor += symbol->name.size + 1;
}
}
U32 symbol_count_be = from_be_u32(symbols_count);
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, symbols_count);
for (U64 symbol_idx = 0; symbol_idx < symbols_count; symbol_idx += 1) {
for EachIndex(symbol_idx, symbols_count) {
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
// write big endian member offset
@@ -343,23 +363,25 @@ coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeSta
str8_list_push(scratch.arena, &first_member_data_list, str8_array(member_off32_arr, symbols_count));
str8_list_push(scratch.arena, &first_member_data_list, str8(name_buffer, name_buffer_size));
String8 member_data = str8_list_join(arena, &first_member_data_list, 0);
String8 member_header = coff_make_lib_member_header(arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
String8 member_data = str8_list_join(scratch.arena, &first_member_data_list, 0);
String8 member_header = coff_make_lib_member_header(scratch.arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
U64 member_offset = sizeof(g_coff_archive_sig) + member_header.size + member_data.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(arena, U8, pad_size);
str8_list_push_front(arena, &member_data_list, str8(pad, pad_size));
U8 *pad = push_array(scratch.arena, U8, pad_size);
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(arena, &member_data_list, member_data);
str8_list_push_front(arena, &member_data_list, member_header);
str8_list_push_front(scratch.arena, &member_data_list, member_data);
str8_list_push_front(scratch.arena, &member_data_list, member_header);
}
// archive signature
str8_list_push_front(arena, &member_data_list, str8_struct(&g_coff_archive_sig));
str8_list_push_front(scratch.arena, &member_data_list, str8_struct(&g_coff_archive_sig));
String8 raw_lib = str8_list_join(arena, &member_data_list, 0);
scratch_end(scratch);
return member_data_list;
return raw_lib;
}
+4 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef COFF_LIB_WRITER_H
@@ -57,13 +57,14 @@ internal COFF_LibWriterMemberNode * coff_lib_writer_member_list_push(Arena *aren
internal COFF_LibWriterSymbol * coff_lib_writer_symbol_array_from_list(Arena *arena, COFF_LibWriterSymbolList list);
internal COFF_LibWriterMember * coff_lib_writer_member_array_from_list(Arena *arena, COFF_LibWriterMemberList list);
internal void coff_lib_writer_symbol_array_sort(COFF_LibWriterSymbol *arr, U64 count);
internal void coff_lib_writer_symbol_array_sort_on_name(COFF_LibWriterSymbol *arr, U64 count);
internal void coff_lib_writer_symbol_array_sort_on_member_idx(COFF_LibWriterSymbol *arr, U64 count);
internal COFF_LibWriter * coff_lib_writer_alloc(void);
internal void coff_lib_writer_release(COFF_LibWriter **writer_ptr);
internal U64 coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_data);
internal void coff_lib_writer_push_import(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportByType import_by, String8 name, U16 hint_or_ordinal, COFF_ImportType import_type);
internal String8List coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member);
internal String8 coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member);
#endif // COFF_LIB_WRITER_H
+90 -60
View File
@@ -20,14 +20,14 @@ internal String8
coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
{
Temp scratch = scratch_begin(&arena, 1);
String8List srl = {0};
String8List string_table = {0};
U32 *string_table_size = push_array(scratch.arena, U32, 1);
*string_table_size = sizeof(*string_table_size);
str8_list_push(scratch.arena, &string_table, str8_struct(string_table_size));
//
// assing section numbers
//
@@ -41,32 +41,41 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
COFF_ObjSection *sect = &sect_n->v;
sect->section_number = sect_idx+1;
obj_sections[sect_idx] = sect;
}
}
AssertAlways(obj_sections_count <= max_U16);
//
// serialize symbol table
//
String8List symbol_table = {0};
{
{
U64 symbol_idx = 0;
for (COFF_ObjSymbolNode *symbol_n = obj_writer->symbol_first; symbol_n != 0; symbol_n = symbol_n->next) {
COFF_ObjSymbol *s = &symbol_n->v;
// assign symbol index
s->idx = symbol_idx++;
symbol_idx += s->aux_symbols.node_count;
}
}
U64 symbol_idx = 0;
for (COFF_ObjSymbolNode *symbol_n = obj_writer->symbol_first; symbol_n != 0; symbol_n = symbol_n->next) {
COFF_ObjSymbol *s = &symbol_n->v;
// assign symbol index
s->idx = symbol_idx++;
COFF_Symbol16 *d = push_array(scratch.arena, COFF_Symbol16, 1);
str8_list_push(scratch.arena, &symbol_table, str8_struct(d));
COFF_SymbolName name = {0};
// long name
if (s->name.size > sizeof(name.short_name)) {
U64 string_table_offset = string_table.total_size;
str8_list_push_cstr(scratch.arena, &string_table, s->name);
str8_list_push(scratch.arena, &string_table, s->name);
str8_list_push(scratch.arena, &string_table, str8_lit("\0"));
name.long_name.zeroes = 0;
name.long_name.string_table_offset = safe_cast_u32(string_table_offset);
}
@@ -75,29 +84,30 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
MemoryCopyStr8(name.short_name, s->name);
MemoryZeroTyped(name.short_name + s->name.size, sizeof(name.short_name) - s->name.size);
}
// symbol header
AssertAlways(s->aux_symbols.node_count <= max_U8);
d->name = name;
d->value = s->value;
switch (s->loc.type) {
case COFF_SymbolLocation_Null: break;
case COFF_SymbolLocation_Section: d->section_number = safe_cast_u16(s->loc.u.section->section_number); break;
case COFF_SymbolLocation_Abs: d->section_number = COFF_Symbol_AbsSection16; break;
case COFF_SymbolLocation_Undef: d->section_number = COFF_Symbol_UndefinedSection; break;
case COFF_SymbolLocation_Null: break;
case COFF_SymbolLocation_Section: d->section_number = safe_cast_u16(s->loc.u.section->section_number); break;
case COFF_SymbolLocation_Abs: d->section_number = COFF_Symbol_AbsSection16; break;
case COFF_SymbolLocation_Undef: d->section_number = COFF_Symbol_UndefinedSection; break;
case COFF_SymbolLocation_Common: d->section_number = COFF_Symbol_UndefinedSection; break;
}
d->type = s->type;
d->storage_class = s->storage_class;
d->aux_symbol_count = 0;
U64 start_symbol_idx = symbol_idx;
if (s->storage_class == COFF_SymStorageClass_WeakExternal) {
if (s->aux_symbols.node_count > 0) {
COFF_ObjSymbolWeak *s_weak = (COFF_ObjSymbolWeak *)s->aux_symbols.first->string.str;
COFF_SymbolWeakExt *d_weak = push_array(scratch.arena, COFF_SymbolWeakExt, 1);
d_weak->tag_index = s_weak->tag->idx;
d_weak->tag_index = s_weak->tag ? s_weak->tag->idx : max_U32;
d_weak->characteristics = s_weak->characteristics;
str8_list_push(scratch.arena, &symbol_table, str8_struct(d_weak));
symbol_idx += 1;
}
@@ -105,32 +115,32 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
if (s->aux_symbols.node_count > 0) {
Assert(s->loc.type == COFF_SymbolLocation_Section);
COFF_ObjSection *sect = s->loc.u.section;
COFF_ObjSymbolSecDef *s_sd = (COFF_ObjSymbolSecDef *)s->aux_symbols.first->string.str;
COFF_SymbolSecDef *d_sd = push_array(scratch.arena, COFF_SymbolSecDef, 1);
d_sd->length = safe_cast_u32(sect->data.total_size);
d_sd->number_of_relocations = (U16)sect->reloc_count;
d_sd->check_sum = 0;
d_sd->number_lo = s_sd->selection == COFF_ComdatSelect_Associative ? safe_cast_u16(s_sd->associate->section_number) : 0;
d_sd->selection = s_sd->selection;
str8_list_push(scratch.arena, &symbol_table, str8_struct(d_sd));
symbol_idx += 1;
}
}
U8 processed_aux_symbol_count = (U8)(symbol_idx - start_symbol_idx);
for (U64 aux_idx = processed_aux_symbol_count; aux_idx < s->aux_symbols.node_count; aux_idx += 1) {
COFF_Symbol16 *a = push_array(scratch.arena, COFF_Symbol16, 1);
str8_list_push(scratch.arena, &symbol_table, str8_struct(a));
}
d->aux_symbol_count = (U8)s->aux_symbols.node_count;
}
}
//
// file header
//
@@ -143,35 +153,35 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
file_header->optional_header_size = 0;
file_header->flags = 0;
str8_list_push(scratch.arena, &srl, str8_struct(file_header));
//
// section table
//
COFF_SectionHeader *sectab = push_array(scratch.arena, COFF_SectionHeader, obj_sections_count);
str8_list_push(scratch.arena, &srl, str8_array(sectab, obj_sections_count));
{
for (U64 sect_idx = 0; sect_idx < obj_sections_count; sect_idx += 1) {
COFF_ObjSection *s = obj_sections[sect_idx];
COFF_SectionHeader *d = &sectab[sect_idx];
// section name
String8 sect_name = s->name;
if (sect_name.size > sizeof(d->name)) {
U64 sect_name_off = string_table.total_size;
str8_list_push_cstr(scratch.arena, &string_table, sect_name);
str8_list_push(scratch.arena, &string_table, push_cstr(scratch.arena, sect_name));
sect_name = push_str8f(scratch.arena, "/%u", sect_name_off);
AssertAlways(sect_name.size <= sizeof(d->name));
}
// alloc zero nodes
for (String8Node *data_n = s->data.first; data_n != 0; data_n = data_n->next) {
if (data_n->string.str == 0 && data_n->string.size > 0) {
data_n->string = str8(push_array(scratch.arena, U8, data_n->string.size), data_n->string.size);
}
}
// section data
U64 data_foff = 0;
U64 data_size = 0;
@@ -180,7 +190,7 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
data_size = s->data.total_size;
str8_list_concat_in_place(&srl, &s->data);
}
// section relocs
U64 relocs_foff = 0;
if (s->reloc_count) {
@@ -197,7 +207,7 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
relocs_foff = srl.total_size;
str8_list_push(scratch.arena, &srl, str8_array(relocs, s->reloc_count));
}
// section header
MemoryCopyStr8(d->name, sect_name);
MemoryZeroTyped(d->name + sect_name.size, sizeof(d->name) - sect_name.size);
@@ -212,7 +222,7 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
d->flags = s->flags;
}
}
//
// symbol table
//
@@ -220,7 +230,7 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
file_header->symbol_table_foff = srl.total_size;
str8_list_concat_in_place(&srl, &symbol_table);
}
//
// string table
//
@@ -228,12 +238,12 @@ coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
*string_table_size = safe_cast_u32(string_table.total_size);
str8_list_concat_in_place(&srl, &string_table);
}
//
// join
//
String8 obj = str8_list_join(arena, &srl, 0);
scratch_end(scratch);
return obj;
}
@@ -244,15 +254,15 @@ coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_Sect
COFF_ObjSectionNode *sect_n = push_array(obj_writer->arena, COFF_ObjSectionNode, 1);
SLLQueuePush(obj_writer->sect_first, obj_writer->sect_last, sect_n);
obj_writer->sect_count += 1;
COFF_ObjSection *sect = &sect_n->v;
sect->name = name;
sect->flags = flags;
if (data.size) {
str8_list_push(obj_writer->arena, &sect->data, data);
}
return sect;
}
@@ -262,14 +272,14 @@ coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value,
COFF_ObjSymbolNode *n = push_array(obj_writer->arena, COFF_ObjSymbolNode, 1);
SLLQueuePush(obj_writer->symbol_first, obj_writer->symbol_last, n);
obj_writer->symbol_count += 1;
COFF_ObjSymbol *s = &n->v;
s->name = name;
s->value = value;
s->loc = loc;
s->type = type;
s->storage_class = storage_class;
return s;
}
@@ -298,9 +308,9 @@ coff_obj_writer_push_symbol_static(COFF_ObjWriter *obj_writer, String8 name, U32
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Section;
loc.u.section = section;
COFF_SymbolType symtype = {0};
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, off, loc, symtype, COFF_SymStorageClass_Static);
return s;
}
@@ -332,13 +342,13 @@ coff_obj_writer_push_symbol_weak(COFF_ObjWriter *obj_writer, String8 name, COFF_
COFF_SymbolLocation loc = {0};
COFF_SymbolType symtype = {0};
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, COFF_Symbol_UndefinedSection, loc, symtype, COFF_SymStorageClass_WeakExternal);
COFF_ObjSymbolWeak *weak_ext = push_array(obj_writer->arena, COFF_ObjSymbolWeak, 1);
weak_ext->tag = tag;
weak_ext->characteristics = characteristics;
str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(weak_ext));
return s;
}
@@ -389,10 +399,10 @@ coff_obj_writer_push_symbol_sect(COFF_ObjWriter *obj_writer, String8 name, COFF_
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Section;
loc.u.section = sect;
// strip align flags
COFF_SectionFlags expected_flags = sect->flags & ~(COFF_SectionFlag_AlignMask << COFF_SectionFlag_AlignShift);
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, expected_flags, loc, type, COFF_SymStorageClass_Section);
return s;
}
@@ -407,29 +417,49 @@ coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32
return s;
}
internal void
coff_obj_writer_set_default_symbol(COFF_ObjSymbol *weak_symbol, COFF_ObjSymbol *default_symbol)
{
AssertAlways(weak_symbol->storage_class == COFF_SymStorageClass_WeakExternal);
COFF_ObjSymbolWeak *w = (COFF_ObjSymbolWeak *)weak_symbol->aux_symbols.first->string.str;
w->tag = default_symbol;
}
internal COFF_ObjReloc*
coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType type)
{
COFF_ObjRelocNode *reloc_n = push_array(obj_writer->arena, COFF_ObjRelocNode, 1);
SLLQueuePush(sect->reloc_first, sect->reloc_last, reloc_n);
sect->reloc_count += 1;
COFF_ObjReloc *reloc = &reloc_n->v;
reloc->apply_off = apply_off;
reloc->symbol = symbol;
reloc->type = type;
return reloc;
}
internal COFF_ObjReloc *
coff_obj_writer_section_push_reloc_addr32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol)
{
COFF_RelocType reloc_type = 0;
switch (obj_writer->machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr32; break;
default: { NotImplemented; } break;
}
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
}
internal COFF_ObjReloc *
coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol)
{
COFF_RelocType reloc_type = 0;
switch (obj_writer->machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr64; break;
default: { NotImplemented; } break;
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr64; break;
default: { NotImplemented; } break;
}
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
}
@@ -439,9 +469,9 @@ coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSect
{
COFF_RelocType reloc_type = 0;
switch (obj_writer->machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr32Nb; break;
default: { NotImplemented; } break;
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr32Nb; break;
default: { NotImplemented; } break;
}
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
}
+3
View File
@@ -115,7 +115,10 @@ internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_sect(COFF_ObjWriter
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_sect(COFF_ObjWriter *obj_writer, String8 name, COFF_ObjSection *sect);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32 size);
internal void coff_obj_writer_set_default_symbol(COFF_ObjSymbol *weak_symbol, COFF_ObjSymbol *default_symbol);
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType reloc_type);
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
+19 -1
View File
@@ -285,6 +285,13 @@ coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol)
return coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
}
internal B32
coff_is_undefined_data_symbol(COFF_ParsedSymbol symbol)
{
COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol);
return interp == COFF_SymbolValueInterp_Undefined && symbol.storage_class == COFF_SymStorageClass_External;
}
internal void
coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out)
{
@@ -724,7 +731,7 @@ coff_parse_second_archive_member(COFF_ArchiveMember *member)
}
internal String8
coff_parse_long_name(String8 long_names, String8 name)
coff_decode_raw_member_name(String8 long_names, String8 name)
{
String8 result = name;
if (name.size > 0 && name.str[0] == '/') {
@@ -744,6 +751,17 @@ coff_parse_long_name(String8 long_names, String8 name)
return result;
}
internal String8
coff_decode_member_name(String8 long_names, String8 name)
{
String8 member_name = coff_decode_raw_member_name(long_names, name);
String8 slash = str8_lit("/");
if (str8_ends_with(member_name, slash, 0)) {
member_name = str8_chop(member_name, slash.size);
}
return member_name;
}
internal U64
coff_parse_import(String8 raw_archive_member, U64 offset, COFF_ParsedArchiveImportHeader *header_out)
{
+3 -1
View File
@@ -268,6 +268,7 @@ internal COFF_Symbol16Node *coff_symbol16_list_push(Arena *arena, COFF_Symbol16L
internal COFF_SymbolValueInterpType coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_class);
internal COFF_SymbolValueInterpType coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol);
internal B32 coff_is_undefined_data_symbol(COFF_ParsedSymbol symbol);
internal void coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out);
internal COFF_SymbolWeakExt * coff_parse_weak_tag(COFF_ParsedSymbol symbol, B32 is_big_obj);
@@ -306,7 +307,8 @@ internal COFF_ArchiveType coff_archive_type_from_data(String8 raw_archive);
internal U64 coff_parse_archive_member_header(String8 raw_archive, U64 offset, COFF_ParsedArchiveMemberHeader *header_out);
internal COFF_ArchiveFirstMember coff_parse_first_archive_member (COFF_ArchiveMember *member);
internal COFF_ArchiveSecondMember coff_parse_second_archive_member(COFF_ArchiveMember *member);
internal String8 coff_parse_long_name (String8 long_names, String8 name);
internal String8 coff_decode_raw_member_name (String8 long_names, String8 name);
internal String8 coff_decode_member_name (String8 long_names, String8 name);
internal U64 coff_parse_import (String8 raw_archive_member, U64 offset, COFF_ParsedArchiveImportHeader *header_out);
internal COFF_ArchiveMember coff_archive_member_from_offset(String8 raw_archive, U64 offset);
+532
View File
@@ -0,0 +1,532 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#undef LAYER_COLOR
#define LAYER_COLOR 0x684123ff
////////////////////////////////
//~ rjf: Basic Helpers
#if !defined(XXH_IMPLEMENTATION)
# define XXH_IMPLEMENTATION
# define XXH_STATIC_LINKING_ONLY
# include "third_party/xxHash/xxhash.h"
#endif
internal U128
c_hash_from_data(String8 data)
{
U128 u128 = {0};
XXH128_hash_t hash = XXH3_128bits(data.str, data.size);
MemoryCopy(&u128, &hash, sizeof(u128));
return u128;
}
internal C_ID
c_id_make(U64 u64_0, U64 u64_1)
{
C_ID id;
id.u128[0].u64[0] = u64_0;
id.u128[0].u64[1] = u64_1;
return id;
}
internal B32
c_id_match(C_ID a, C_ID b)
{
B32 result = MemoryMatchStruct(&a, &b);
return result;
}
internal C_Key
c_key_make(C_Root root, C_ID id)
{
C_Key key = {root, 0, id};
return key;
}
internal B32
c_key_match(C_Key a, C_Key b)
{
return (MemoryMatchStruct(&a.root, &b.root) && c_id_match(a.id, b.id));
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
c_init(void)
{
Arena *arena = arena_alloc();
c_shared = push_array(arena, C_Shared, 1);
c_shared->arena = arena;
c_shared->blob_slots_count = 16384;
c_shared->blob_stripes_count = Min(c_shared->blob_slots_count, os_get_system_info()->logical_processor_count);
c_shared->blob_slots = push_array(arena, C_BlobSlot, c_shared->blob_slots_count);
c_shared->blob_stripes = push_array(arena, C_Stripe, c_shared->blob_stripes_count);
c_shared->blob_stripes_free_nodes = push_array(arena, C_BlobNode *, c_shared->blob_stripes_count);
for(U64 idx = 0; idx < c_shared->blob_stripes_count; idx += 1)
{
C_Stripe *stripe = &c_shared->blob_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = rw_mutex_alloc();
stripe->cv = cond_var_alloc();
}
c_shared->key_slots_count = 4096;
c_shared->key_stripes_count = Min(c_shared->key_slots_count, os_get_system_info()->logical_processor_count);
c_shared->key_slots = push_array(arena, C_KeySlot, c_shared->key_slots_count);
c_shared->key_stripes = push_array(arena, C_Stripe, c_shared->key_stripes_count);
c_shared->key_stripes_free_nodes = push_array(arena, C_KeyNode *, c_shared->key_stripes_count);
for(U64 idx = 0; idx < c_shared->key_stripes_count; idx += 1)
{
C_Stripe *stripe = &c_shared->key_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = rw_mutex_alloc();
stripe->cv = cond_var_alloc();
}
c_shared->root_slots_count = 4096;
c_shared->root_stripes_count = Min(c_shared->root_slots_count, os_get_system_info()->logical_processor_count);
c_shared->root_slots = push_array(arena, C_RootSlot, c_shared->root_slots_count);
c_shared->root_stripes = push_array(arena, C_Stripe, c_shared->root_stripes_count);
c_shared->root_stripes_free_nodes = push_array(arena, C_RootNode *, c_shared->root_stripes_count);
for(U64 idx = 0; idx < c_shared->root_stripes_count; idx += 1)
{
C_Stripe *stripe = &c_shared->root_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = rw_mutex_alloc();
stripe->cv = cond_var_alloc();
}
}
////////////////////////////////
//~ rjf: Root Allocation/Deallocation
internal C_Root
c_root_alloc(void)
{
C_Root root = {0};
root.u64[0] = ins_atomic_u64_inc_eval(&c_shared->root_id_gen);
U64 slot_idx = root.u64[0]%c_shared->root_slots_count;
U64 stripe_idx = slot_idx%c_shared->root_stripes_count;
C_RootSlot *slot = &c_shared->root_slots[slot_idx];
C_Stripe *stripe = &c_shared->root_stripes[stripe_idx];
MutexScopeW(stripe->rw_mutex)
{
C_RootNode *node = c_shared->root_stripes_free_nodes[stripe_idx];
if(node != 0)
{
SLLStackPop(c_shared->root_stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array(stripe->arena, C_RootNode, 1);
}
DLLPushBack(slot->first, slot->last, node);
node->root = root;
node->arena = arena_alloc();
}
return root;
}
internal void
c_root_release(C_Root root)
{
//- rjf: unpack root
U64 slot_idx = root.u64[0]%c_shared->root_slots_count;
U64 stripe_idx = slot_idx%c_shared->root_stripes_count;
C_RootSlot *slot = &c_shared->root_slots[slot_idx];
C_Stripe *stripe = &c_shared->root_stripes[stripe_idx];
//- rjf: release root node, grab its arena / ID list
Arena *root_arena = 0;
C_RootIDChunkList root_ids = {0};
MutexScopeW(stripe->rw_mutex)
{
for(C_RootNode *n = slot->first; n != 0; n = n->next)
{
if(MemoryMatchStruct(&root, &n->root))
{
DLLRemove(slot->first, slot->last, n);
root_arena = n->arena;
root_ids = n->ids;
SLLStackPush(c_shared->root_stripes_free_nodes[stripe_idx], n);
break;
}
}
}
//- rjf: release all IDs
for(C_RootIDChunkNode *id_chunk_n = root_ids.first; id_chunk_n != 0; id_chunk_n = id_chunk_n->next)
{
for EachIndex(chunk_idx, id_chunk_n->count)
{
C_ID id = id_chunk_n->v[chunk_idx];
C_Key key = c_key_make(root, id);
c_close_key(key);
}
}
}
////////////////////////////////
//~ rjf: Cache Submission
internal U128
c_submit_data(C_Key key, Arena **data_arena, String8 data)
{
//- rjf: unpack key
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
//- rjf: hash data, unpack hash
U128 hash = c_hash_from_data(data);
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
//- rjf: commit data to cache - if already there, just bump key refcount
ProfScope("commit data to cache - if already there, just bump key refcount") MutexScopeW(stripe->rw_mutex)
{
C_BlobNode *existing_node = 0;
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
existing_node = n;
break;
}
}
if(existing_node == 0)
{
C_BlobNode *node = c_shared->blob_stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(c_shared->blob_stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array_no_zero(stripe->arena, C_BlobNode, 1);
}
MemoryZeroStruct(node);
node->hash = hash;
if(data_arena != 0)
{
node->arena = *data_arena;
}
node->data = data;
node->key_ref_count = 1;
DLLPushBack(slot->first, slot->last, node);
}
else
{
existing_node->key_ref_count += 1;
if(data_arena != 0)
{
arena_release(*data_arena);
}
}
if(data_arena != 0)
{
*data_arena = 0;
}
}
//- rjf: commit this hash to key cache
U128 key_expired_hash = {0};
ProfScope("commit this hash to key cache") MutexScopeW(key_stripe->rw_mutex)
{
// rjf: find existing key
B32 key_is_new = 0;
C_KeyNode *key_node = 0;
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(c_key_match(n->key, key))
{
key_node = n;
break;
}
}
// rjf: create key node if it doesn't exist
if(!key_node)
{
key_is_new = 1;
key_node = c_shared->key_stripes_free_nodes[key_stripe_idx];
if(key_node)
{
SLLStackPop(c_shared->key_stripes_free_nodes[key_stripe_idx]);
}
else
{
key_node = push_array(key_stripe->arena, C_KeyNode, 1);
}
key_node->key = key;
DLLPushBack(key_slot->first, key_slot->last, key_node);
}
// rjf: push hash into key's history
if(key_node)
{
if(key_node->hash_history_gen >= C_KEY_HASH_HISTORY_STRONG_REF_COUNT)
{
key_expired_hash = key_node->hash_history[(key_node->hash_history_gen-C_KEY_HASH_HISTORY_STRONG_REF_COUNT)%ArrayCount(key_node->hash_history)];
}
key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)] = hash;
key_node->hash_history_gen += 1;
}
// rjf: key is new -> add this key to the associated root
if(key_is_new)
{
U64 root_hash = u64_hash_from_str8(str8_struct(&key.root));
U64 root_slot_idx = root_hash%c_shared->root_slots_count;
U64 root_stripe_idx = root_slot_idx%c_shared->root_stripes_count;
C_RootSlot *root_slot = &c_shared->root_slots[root_slot_idx];
C_Stripe *root_stripe = &c_shared->root_stripes[root_stripe_idx];
MutexScopeW(root_stripe->rw_mutex)
{
for(C_RootNode *n = root_slot->first; n != 0; n = n->next)
{
if(MemoryMatchStruct(&n->root, &key.root))
{
C_RootIDChunkNode *chunk = n->ids.last;
if(chunk == 0 || chunk->count >= chunk->cap)
{
chunk = push_array(n->arena, C_RootIDChunkNode, 1);
SLLQueuePush(n->ids.first, n->ids.last, chunk);
n->ids.chunk_count += 1;
chunk->cap = 1024;
chunk->v = push_array_no_zero(n->arena, C_ID, chunk->cap);
}
chunk->v[chunk->count] = key.id;
chunk->count += 1;
n->ids.total_count += 1;
break;
}
}
}
}
}
//- rjf: decrement key ref count of expired hash
ProfScope("decrement key ref count of expired hash")
if(!u128_match(key_expired_hash, u128_zero()))
{
U64 old_hash_slot_idx = key_expired_hash.u64[1]%c_shared->blob_slots_count;
U64 old_hash_stripe_idx = old_hash_slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *old_hash_slot = &c_shared->blob_slots[old_hash_slot_idx];
C_Stripe *old_hash_stripe = &c_shared->blob_stripes[old_hash_stripe_idx];
MutexScopeR(old_hash_stripe->rw_mutex)
{
for(C_BlobNode *n = old_hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, key_expired_hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
return hash;
}
////////////////////////////////
//~ rjf: Key Closing
internal void
c_close_key(C_Key key)
{
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
RWMutexScope(key_stripe->rw_mutex, 1)
{
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(c_key_match(n->key, key))
{
for(U64 history_idx = 0;
history_idx < C_KEY_HASH_HISTORY_STRONG_REF_COUNT && history_idx < n->hash_history_gen;
history_idx += 1)
{
U128 hash = n->hash_history[(n->hash_history_gen-1-history_idx) % ArrayCount(n->hash_history)];
U64 hash_slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 hash_stripe_idx = hash_slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *hash_slot = &c_shared->blob_slots[hash_slot_idx];
C_Stripe *hash_stripe = &c_shared->blob_stripes[hash_stripe_idx];
MutexScopeR(hash_stripe->rw_mutex)
{
for(C_BlobNode *n = hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
DLLRemove(key_slot->first, key_slot->last, n);
SLLStackPush(c_shared->key_stripes_free_nodes[key_stripe_idx], n);
break;
}
}
}
}
////////////////////////////////
//~ rjf: Downstream Accesses
internal void
c_hash_downstream_inc(U128 hash)
{
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
MutexScopeR(stripe->rw_mutex)
{
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_inc_eval(&n->downstream_ref_count);
break;
}
}
}
}
internal void
c_hash_downstream_dec(U128 hash)
{
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
MutexScopeR(stripe->rw_mutex)
{
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->downstream_ref_count);
break;
}
}
}
}
////////////////////////////////
//~ rjf: Cache Lookup
internal U128
c_hash_from_key(C_Key key, U64 rewind_count)
{
U128 result = {0};
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
RWMutexScope(key_stripe->rw_mutex, 0)
{
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(c_key_match(n->key, key) && n->hash_history_gen > 0 && n->hash_history_gen-1 >= rewind_count)
{
result = n->hash_history[(n->hash_history_gen-1-rewind_count)%ArrayCount(n->hash_history)];
break;
}
}
}
return result;
}
internal String8
c_data_from_hash(Access *access, U128 hash)
{
ProfBeginFunction();
String8 result = {0};
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
MutexScopeR(stripe->rw_mutex)
{
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
result = n->data;
access_touch(access, &n->access_pt, stripe->cv);
break;
}
}
}
ProfEnd();
return result;
}
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void
c_async_tick(void)
{
ProfBeginFunction();
//- rjf: garbage collect blobs
{
Rng1U64 range = lane_range(c_shared->blob_slots_count);
for EachInRange(slot_idx, range)
{
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
{
B32 slot_has_work = 0;
RWMutexScope(stripe->rw_mutex, write_mode)
{
for(C_BlobNode *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
U64 downstream_ref_count = ins_atomic_u64_eval(&n->downstream_ref_count);
if(access_pt_is_expired(&n->access_pt, .time = 5000000) && key_ref_count == 0 && downstream_ref_count == 0)
{
slot_has_work = 1;
if(!write_mode)
{
break;
}
else
{
DLLRemove(slot->first, slot->last, n);
SLLStackPush(c_shared->blob_stripes_free_nodes[stripe_idx], n);
if(n->arena != 0)
{
arena_release(n->arena);
}
}
}
}
}
if(!slot_has_work)
{
break;
}
}
}
}
ProfEnd();
}
+244
View File
@@ -0,0 +1,244 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CONTENT_H
#define CONTENT_H
////////////////////////////////
//~ NOTE(rjf): Hash Store Notes (2025/05/18)
//
// The hash store is a general-purpose data cache. It offers three layers of
// caching: (a) content (hash of data), (b) key (unique identity correllated
// with history of hashes), and (c) root (bucket for many keys, manually
// allocated / deallocated).
//
// (a) The "content" level of cache access is a simply hash(data) -> data
// mapping. This bypasses all identity/key/root mechanisms and provides a
// way to just talk about unique (and deduplicated) blobs of data.
//
// (b) The "key" level of cache access is used to encode a history of hashes
// for some unique "identity", where the "identity" is a concept managed
// by the user. One example of an identity would be a particular address
// range inside of some process to which the debugger is attached. Another
// might be a range inside of some file.
//
// (c) The "root" level is to provide a top-level allocation/deallocation
// mechanism for a large set of keys. It also provides an extra level of
// key uniqueness. For instance, each process to which the debugger is
// attached might have its own root, and each key might correspond to a
// particular address range within that process. This way, when the
// process ends, all of its keys can be easily destroyed using a single
// deallocation of the root.
//
// The way this might be generally used inside of the debugger would be that
// some evaluation - let's say it's some variable `x` - is mapped (via debug
// info) to some address range. If `x` is a `char[4096]`, then it might map
// to some address range [&x, &x + 4096). This, together with the process
// within which `x` is evaluated, forms both a `root` (for the process) and
// a `key` (for the address range). Some asynchronous memory streaming system
// can then, together with the root and key, read memory for that range, then
// submit that data to the hash store, correllating with the root and key
// combo.
////////////////////////////////
//~ rjf: Key Types
typedef struct C_Root C_Root;
struct C_Root
{
U64 u64[1];
};
typedef struct C_ID C_ID;
struct C_ID
{
U128 u128[1];
};
typedef struct C_Key C_Key;
struct C_Key
{
C_Root root;
U64 _padding_;
C_ID id;
};
////////////////////////////////
//~ rjf: Cache Stripe Type
typedef struct C_Stripe C_Stripe;
struct C_Stripe
{
Arena *arena;
RWMutex rw_mutex;
CondVar cv;
};
////////////////////////////////
//~ rjf: Root Cache Types
typedef struct C_RootIDChunkNode C_RootIDChunkNode;
struct C_RootIDChunkNode
{
C_RootIDChunkNode *next;
C_ID *v;
U64 count;
U64 cap;
};
typedef struct C_RootIDChunkList C_RootIDChunkList;
struct C_RootIDChunkList
{
C_RootIDChunkNode *first;
C_RootIDChunkNode *last;
U64 chunk_count;
U64 total_count;
};
typedef struct C_RootNode C_RootNode;
struct C_RootNode
{
C_RootNode *next;
C_RootNode *prev;
Arena *arena;
C_Root root;
C_RootIDChunkList ids;
};
typedef struct C_RootSlot C_RootSlot;
struct C_RootSlot
{
C_RootNode *first;
C_RootNode *last;
};
////////////////////////////////
//~ rjf: Key Cache Types
#define C_KEY_HASH_HISTORY_COUNT 64
#define C_KEY_HASH_HISTORY_STRONG_REF_COUNT 2
typedef struct C_KeyNode C_KeyNode;
struct C_KeyNode
{
C_KeyNode *next;
C_KeyNode *prev;
C_Key key;
U128 hash_history[C_KEY_HASH_HISTORY_COUNT];
U64 hash_history_gen;
};
typedef struct C_KeySlot C_KeySlot;
struct C_KeySlot
{
C_KeyNode *first;
C_KeyNode *last;
};
////////////////////////////////
//~ rjf: Content Blob Cache Types
typedef struct C_BlobNode C_BlobNode;
struct C_BlobNode
{
C_BlobNode *next;
C_BlobNode *prev;
U128 hash;
Arena *arena;
String8 data;
AccessPt access_pt;
U64 key_ref_count;
U64 downstream_ref_count;
};
typedef struct C_BlobSlot C_BlobSlot;
struct C_BlobSlot
{
C_BlobNode *first;
C_BlobNode *last;
};
////////////////////////////////
//~ rjf: Shared State
typedef struct C_Shared C_Shared;
struct C_Shared
{
Arena *arena;
// rjf: main data blob cache
U64 blob_slots_count;
U64 blob_stripes_count;
C_BlobSlot *blob_slots;
C_Stripe *blob_stripes;
C_BlobNode **blob_stripes_free_nodes;
// rjf: key cache
U64 key_slots_count;
U64 key_stripes_count;
C_KeySlot *key_slots;
C_Stripe *key_stripes;
C_KeyNode **key_stripes_free_nodes;
// rjf: root cache
U64 root_slots_count;
U64 root_stripes_count;
C_RootSlot *root_slots;
C_Stripe *root_stripes;
C_RootNode **root_stripes_free_nodes;
U64 root_id_gen;
};
////////////////////////////////
//~ rjf: Globals
global C_Shared *c_shared = 0;
////////////////////////////////
//~ rjf: Basic Helpers
internal U128 c_hash_from_data(String8 data);
internal C_ID c_id_make(U64 u64_0, U64 u64_1);
internal B32 c_id_match(C_ID a, C_ID b);
internal C_Key c_key_make(C_Root root, C_ID id);
internal B32 c_key_match(C_Key a, C_Key b);
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void c_init(void);
////////////////////////////////
//~ rjf: Root Allocation/Deallocation
internal C_Root c_root_alloc(void);
internal void c_root_release(C_Root root);
////////////////////////////////
//~ rjf: Cache Submission
internal U128 c_submit_data(C_Key key, Arena **data_arena, String8 data);
////////////////////////////////
//~ rjf: Key Closing
internal void c_close_key(C_Key key);
////////////////////////////////
//~ rjf: Downstream Accesses
internal void c_hash_downstream_inc(U128 hash);
internal void c_hash_downstream_dec(U128 hash);
////////////////////////////////
//~ rjf: Cache Lookups
internal U128 c_hash_from_key(C_Key key, U64 rewind_count);
internal String8 c_data_from_hash(Access *access, U128 hash);
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void c_async_tick(void);
#endif // CONTENT_H
+78 -2
View File
@@ -16,8 +16,8 @@ CTRL_EntityKindTable:
{DebugInfoPath debug_info_path "Debug Info Path" }
{PendingThreadName pending_thread_name "Pending Thread Name" }
{PendingThreadColor pending_thread_color "Pending Thread Color" }
{Breakpoint breakpoint "Breakpoint" }
{AddressRangeAnnotation address_range_annotation "Address Range Annotation" }
{Breakpoint breakpoint "Breakpoint" }
{AddressRangeAnnotation address_range_annotation "Address Range Annotation" }
}
@enum CTRL_EntityKind:
@@ -114,3 +114,79 @@ CTRL_ExceptionCodeKindTable:
`0`;
@expand(CTRL_ExceptionCodeKindTable a) `$(a.default)`;
}
////////////////////////////////
//~ rjf: Exception Sub-Codes
@table(name, value)
CTRL_ExceptionSubCodeKindTable:
{
{ W32_FAST_FAIL_LEGACY_GS_VIOLATION 0 }
{ W32_FAST_FAIL_VTGUARD_CHECK_FAILURE 1 }
{ W32_FAST_FAIL_STACK_COOKIE_CHECK_FAILURE 2 }
{ W32_FAST_FAIL_CORRUPT_LIST_ENTRY 3 }
{ W32_FAST_FAIL_INCORRECT_STACK 4 }
{ W32_FAST_FAIL_INVALID_ARG 5 }
{ W32_FAST_FAIL_GS_COOKIE_INIT 6 }
{ W32_FAST_FAIL_FATAL_APP_EXIT 7 }
{ W32_FAST_FAIL_RANGE_CHECK_FAILURE 8 }
{ W32_FAST_FAIL_UNSAFE_REGISTRY_ACCESS 9 }
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_FAILURE 10 }
{ W32_FAST_FAIL_GUARD_WRITE_CHECK_FAILURE 11 }
{ W32_FAST_FAIL_INVALID_FIBER_SWITCH 12 }
{ W32_FAST_FAIL_INVALID_SET_OF_CONTEXT 13 }
{ W32_FAST_FAIL_INVALID_REFERENCE_COUNT 14 }
{ W32_FAST_FAIL_INVALID_JUMP_BUFFER 18 }
{ W32_FAST_FAIL_MRDATA_MODIFIED 19 }
{ W32_FAST_FAIL_CERTIFICATION_FAILURE 20 }
{ W32_FAST_FAIL_INVALID_EXCEPTION_CHAIN 21 }
{ W32_FAST_FAIL_CRYPTO_LIBRARY 22 }
{ W32_FAST_FAIL_INVALID_CALL_IN_DLL_CALLOUT 23 }
{ W32_FAST_FAIL_INVALID_IMAGE_BASE 24 }
{ W32_FAST_FAIL_DLOAD_PROTECTION_FAILURE 25 }
{ W32_FAST_FAIL_UNSAFE_EXTENSION_CALL 26 }
{ W32_FAST_FAIL_DEPRECATED_SERVICE_INVOKED 27 }
{ W32_FAST_FAIL_INVALID_BUFFER_ACCESS 28 }
{ W32_FAST_FAIL_INVALID_BALANCED_TREE 29 }
{ W32_FAST_FAIL_INVALID_NEXT_THREAD 30 }
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_SUPPRESSED 31 }
{ W32_FAST_FAIL_APCS_DISABLED 32 }
{ W32_FAST_FAIL_INVALID_IDLE_STATE 33 }
{ W32_FAST_FAIL_MRDATA_PROTECTION_FAILURE 34 }
{ W32_FAST_FAIL_UNEXPECTED_HEAP_EXCEPTION 35 }
{ W32_FAST_FAIL_INVALID_LOCK_STATE 36 }
{ W32_FAST_FAIL_GUARD_JUMPTABLE 37 }
{ W32_FAST_FAIL_INVALID_LONGJUMP_TARGET 38 }
{ W32_FAST_FAIL_INVALID_DISPATCH_CONTEXT 39 }
{ W32_FAST_FAIL_INVALID_THREAD 40 }
{ W32_FAST_FAIL_INVALID_SYSCALL_NUMBER 41 }
{ W32_FAST_FAIL_INVALID_FILE_OPERATION 42 }
{ W32_FAST_FAIL_LPAC_ACCESS_DENIED 43 }
{ W32_FAST_FAIL_GUARD_SS_FAILURE 44 }
{ W32_FAST_FAIL_LOADER_CONTINUITY_FAILURE 45 }
{ W32_FAST_FAIL_GUARD_EXPORT_SUPPRESSION_FAILURE 46 }
{ W32_FAST_FAIL_INVALID_CONTROL_STACK 47 }
{ W32_FAST_FAIL_SET_CONTEXT_DENIED 48 }
{ W32_FAST_FAIL_INVALID_IAT 49 }
{ W32_FAST_FAIL_HEAP_METADATA_CORRUPTION 50 }
{ W32_FAST_FAIL_PAYLOAD_RESTRICTION_VIOLATION 51 }
{ W32_FAST_FAIL_LOW_LABEL_ACCESS_DENIED 52 }
{ W32_FAST_FAIL_ENCLAVE_CALL_FAILURE 53 }
{ W32_FAST_FAIL_UNHANDLED_LSS_EXCEPTON 54 }
{ W32_FAST_FAIL_ADMINLESS_ACCESS_DENIED 55 }
{ W32_FAST_FAIL_UNEXPECTED_CALL 56 }
{ W32_FAST_FAIL_CONTROL_INVALID_RETURN_ADDRESS 57 }
{ W32_FAST_FAIL_UNEXPECTED_HOST_BEHAVIOR 58 }
{ W32_FAST_FAIL_FLAGS_CORRUPTION 59 }
{ W32_FAST_FAIL_VEH_CORRUPTION 60 }
{ W32_FAST_FAIL_ETW_CORRUPTION 61 }
{ W32_FAST_FAIL_RIO_ABORT 62 }
{ W32_FAST_FAIL_INVALID_PFN 63 }
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_FAILURE_XFG 64 }
{ W32_FAST_FAIL_CAST_GUARD 65 }
{ W32_FAST_FAIL_HOST_VISIBILITY_CHANGE 66 }
{ W32_FAST_FAIL_KERNEL_CET_SHADOW_STACK_ASSIST 67 }
{ W32_FAST_FAIL_PATCH_CALLBACK_FAILED 68 }
{ W32_FAST_FAIL_NTDLL_PATCH_FAILED 69 }
{ W32_FAST_FAIL_INVALID_FLS_DATA 70 }
}
+706 -1126
View File
File diff suppressed because it is too large Load Diff
+74 -201
View File
@@ -84,6 +84,13 @@ struct CTRL_HandleList
U64 count;
};
typedef struct CTRL_HandleArray CTRL_HandleArray;
struct CTRL_HandleArray
{
CTRL_Handle *v;
U64 count;
};
////////////////////////////////
//~ rjf: Generated Code
@@ -271,6 +278,34 @@ struct CTRL_CallStack
U64 concrete_frames_count;
};
////////////////////////////////
//~ rjf: Call Stack Tree Types
typedef struct CTRL_CallStackTreeNode CTRL_CallStackTreeNode;
struct CTRL_CallStackTreeNode
{
CTRL_CallStackTreeNode *hash_next;
CTRL_CallStackTreeNode *first;
CTRL_CallStackTreeNode *last;
CTRL_CallStackTreeNode *next;
CTRL_CallStackTreeNode *parent;
U64 child_count;
U64 id;
CTRL_Handle process;
U64 vaddr;
U64 depth;
CTRL_HandleList threads;
U64 all_descendant_threads_count;
};
typedef struct CTRL_CallStackTree CTRL_CallStackTree;
struct CTRL_CallStackTree
{
CTRL_CallStackTreeNode *root;
U64 slots_count;
CTRL_CallStackTreeNode **slots;
};
////////////////////////////////
//~ rjf: Trap Types
@@ -500,68 +535,7 @@ struct CTRL_EventList
};
////////////////////////////////
//~ rjf: Process Memory Cache Types
typedef struct CTRL_ProcessMemoryRangeHashNode CTRL_ProcessMemoryRangeHashNode;
struct CTRL_ProcessMemoryRangeHashNode
{
CTRL_ProcessMemoryRangeHashNode *next;
// rjf: key
Rng1U64 vaddr_range;
B32 zero_terminated;
HS_ID id;
// rjf: staleness info
U64 mem_gen;
// rjf: metadata
U64 working_count;
U64 last_time_requested_us;
U64 last_user_clock_idx_touched;
};
typedef struct CTRL_ProcessMemoryRangeHashSlot CTRL_ProcessMemoryRangeHashSlot;
struct CTRL_ProcessMemoryRangeHashSlot
{
CTRL_ProcessMemoryRangeHashNode *first;
CTRL_ProcessMemoryRangeHashNode *last;
};
typedef struct CTRL_ProcessMemoryCacheNode CTRL_ProcessMemoryCacheNode;
struct CTRL_ProcessMemoryCacheNode
{
CTRL_ProcessMemoryCacheNode *next;
CTRL_ProcessMemoryCacheNode *prev;
Arena *arena;
CTRL_Handle handle;
HS_Root root;
U64 range_hash_slots_count;
CTRL_ProcessMemoryRangeHashSlot *range_hash_slots;
};
typedef struct CTRL_ProcessMemoryCacheSlot CTRL_ProcessMemoryCacheSlot;
struct CTRL_ProcessMemoryCacheSlot
{
CTRL_ProcessMemoryCacheNode *first;
CTRL_ProcessMemoryCacheNode *last;
};
typedef struct CTRL_ProcessMemoryCacheStripe CTRL_ProcessMemoryCacheStripe;
struct CTRL_ProcessMemoryCacheStripe
{
OS_Handle rw_mutex;
OS_Handle cv;
};
typedef struct CTRL_ProcessMemoryCache CTRL_ProcessMemoryCache;
struct CTRL_ProcessMemoryCache
{
U64 slots_count;
CTRL_ProcessMemoryCacheSlot *slots;
U64 stripes_count;
CTRL_ProcessMemoryCacheStripe *stripes;
};
//~ rjf: Process Memory Types
typedef struct CTRL_ProcessMemorySlice CTRL_ProcessMemorySlice;
struct CTRL_ProcessMemorySlice
@@ -599,7 +573,7 @@ typedef struct CTRL_ThreadRegCacheStripe CTRL_ThreadRegCacheStripe;
struct CTRL_ThreadRegCacheStripe
{
Arena *arena;
OS_Handle rw_mutex;
RWMutex rw_mutex;
};
typedef struct CTRL_ThreadRegCache CTRL_ThreadRegCache;
@@ -611,53 +585,6 @@ struct CTRL_ThreadRegCache
CTRL_ThreadRegCacheStripe *stripes;
};
////////////////////////////////
//~ rjf: Call Stack Cache Types
typedef struct CTRL_CallStackCacheNode CTRL_CallStackCacheNode;
struct CTRL_CallStackCacheNode
{
CTRL_CallStackCacheNode *next;
CTRL_CallStackCacheNode *prev;
// rjf: key
CTRL_Handle thread;
U64 reg_gen;
U64 mem_gen;
// rjf: counters
U64 scope_touch_count;
U64 working_count;
// rjf: value
Arena *arena;
CTRL_CallStack call_stack;
};
typedef struct CTRL_CallStackCacheSlot CTRL_CallStackCacheSlot;
struct CTRL_CallStackCacheSlot
{
CTRL_CallStackCacheNode *first;
CTRL_CallStackCacheNode *last;
};
typedef struct CTRL_CallStackCacheStripe CTRL_CallStackCacheStripe;
struct CTRL_CallStackCacheStripe
{
Arena *arena;
OS_Handle rw_mutex;
OS_Handle cv;
};
typedef struct CTRL_CallStackCache CTRL_CallStackCache;
struct CTRL_CallStackCache
{
U64 slots_count;
CTRL_CallStackCacheSlot *slots;
U64 stripes_count;
CTRL_CallStackCacheStripe *stripes;
};
////////////////////////////////
//~ rjf: Module Image Info Cache Types
@@ -688,7 +615,7 @@ typedef struct CTRL_ModuleImageInfoCacheStripe CTRL_ModuleImageInfoCacheStripe;
struct CTRL_ModuleImageInfoCacheStripe
{
Arena *arena;
OS_Handle rw_mutex;
RWMutex rw_mutex;
};
typedef struct CTRL_ModuleImageInfoCache CTRL_ModuleImageInfoCache;
@@ -729,33 +656,6 @@ struct CTRL_EvalScope
E_InterpretCtx interpret_ctx;
};
////////////////////////////////
//~ rjf: Control Cache Accessing Scopes
typedef struct CTRL_ScopeCallStackTouch CTRL_ScopeCallStackTouch;
struct CTRL_ScopeCallStackTouch
{
CTRL_ScopeCallStackTouch *next;
CTRL_CallStackCacheStripe *stripe;
CTRL_CallStackCacheNode *node;
};
typedef struct CTRL_Scope CTRL_Scope;
struct CTRL_Scope
{
CTRL_Scope *next;
CTRL_ScopeCallStackTouch *first_call_stack_touch;
CTRL_ScopeCallStackTouch *last_call_stack_touch;
};
typedef struct CTRL_TCTX CTRL_TCTX;
struct CTRL_TCTX
{
Arena *arena;
CTRL_Scope *free_scope;
CTRL_ScopeCallStackTouch *free_call_stack_touch;
};
////////////////////////////////
//~ rjf: Module Requirement Cache Types
@@ -787,9 +687,7 @@ struct CTRL_State
E_String2NumMap arch_string2alias_tables[Arch_COUNT];
// rjf: caches
CTRL_ProcessMemoryCache process_memory_cache;
CTRL_ThreadRegCache thread_reg_cache;
CTRL_CallStackCache call_stack_cache;
CTRL_ModuleImageInfoCache module_image_info_cache;
// rjf: generations
@@ -802,8 +700,8 @@ struct CTRL_State
U8 *u2c_ring_base;
U64 u2c_ring_write_pos;
U64 u2c_ring_read_pos;
OS_Handle u2c_ring_mutex;
OS_Handle u2c_ring_cv;
Mutex u2c_ring_mutex;
CondVar u2c_ring_cv;
// rjf: ctrl -> user event ring buffer
U64 c2u_ring_size;
@@ -811,15 +709,15 @@ struct CTRL_State
U8 *c2u_ring_base;
U64 c2u_ring_write_pos;
U64 c2u_ring_read_pos;
OS_Handle c2u_ring_mutex;
OS_Handle c2u_ring_cv;
Mutex c2u_ring_mutex;
CondVar c2u_ring_cv;
// rjf: ctrl thread state
U64 ctrl_thread_run_state;
String8 ctrl_thread_log_path;
OS_Handle ctrl_thread;
Thread ctrl_thread;
Log *ctrl_thread_log;
OS_Handle ctrl_thread_entity_ctx_rw_mutex;
RWMutex ctrl_thread_entity_ctx_rw_mutex;
CTRL_EntityCtxRWStore *ctrl_thread_entity_store;
E_Cache *ctrl_thread_eval_cache;
Arena *ctrl_thread_msg_process_arena;
@@ -837,22 +735,6 @@ struct CTRL_State
CTRL_ModuleReqCacheNode **module_req_cache_slots;
String8List msg_user_bp_touched_files;
String8List msg_user_bp_touched_symbols;
// rjf: user -> memstream ring buffer
U64 u2ms_ring_size;
U8 *u2ms_ring_base;
U64 u2ms_ring_write_pos;
U64 u2ms_ring_read_pos;
OS_Handle u2ms_ring_mutex;
OS_Handle u2ms_ring_cv;
// rjf: user -> call stack builder ring buffer
U64 u2csb_ring_size;
U8 *u2csb_ring_base;
U64 u2csb_ring_write_pos;
U64 u2csb_ring_read_pos;
OS_Handle u2csb_ring_mutex;
OS_Handle u2csb_ring_cv;
};
////////////////////////////////
@@ -867,7 +749,14 @@ read_only global CTRL_Entity ctrl_entity_nil =
&ctrl_entity_nil,
&ctrl_entity_nil,
};
thread_static CTRL_TCTX *ctrl_tctx = 0;
read_only global CTRL_CallStackTreeNode ctrl_call_stack_tree_node_nil =
{
0,
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
};
thread_static CTRL_EntityCtxLookupAccel *ctrl_entity_ctx_lookup_accel = 0;
////////////////////////////////
@@ -896,6 +785,7 @@ internal CTRL_Handle ctrl_handle_make(CTRL_MachineID machine_id, DMN_Handle dmn_
internal B32 ctrl_handle_match(CTRL_Handle a, CTRL_Handle b);
internal void ctrl_handle_list_push(Arena *arena, CTRL_HandleList *list, CTRL_Handle *pair);
internal CTRL_HandleList ctrl_handle_list_copy(Arena *arena, CTRL_HandleList *src);
internal CTRL_HandleArray ctrl_handle_array_from_list(Arena *arena, CTRL_HandleList *src);
internal String8 ctrl_string_from_handle(Arena *arena, CTRL_Handle handle);
internal CTRL_Handle ctrl_handle_from_string(String8 string);
@@ -993,13 +883,6 @@ internal CTRL_Entity *ctrl_thread_from_id(CTRL_EntityCtx *ctx, U64 id);
//- rjf: applying events to entity caches
internal void ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *list);
////////////////////////////////
//~ rjf: Cache Accessing Scopes
internal CTRL_Scope *ctrl_scope_open(void);
internal void ctrl_scope_close(CTRL_Scope *scope);
internal void ctrl_scope_touch_call_stack_node__stripe_r_guarded(CTRL_Scope *scope, CTRL_CallStackCacheStripe *stripe, CTRL_CallStackCacheNode *node);
////////////////////////////////
//~ rjf: Main Layer Initialization
@@ -1010,20 +893,6 @@ internal void ctrl_init(void);
internal void ctrl_set_wakeup_hook(CTRL_WakeupFunctionType *wakeup_hook);
////////////////////////////////
//~ rjf: Process Memory Functions
//- rjf: process memory cache key reading
internal HS_Key ctrl_key_from_process_vaddr_range(CTRL_Handle process, Rng1U64 vaddr_range, B32 zero_terminated, U64 endt_us, B32 *out_is_stale);
//- rjf: process memory cache reading helpers
internal CTRL_ProcessMemorySlice ctrl_process_memory_slice_from_vaddr_range(Arena *arena, CTRL_Handle process, Rng1U64 range, U64 endt_us);
internal B32 ctrl_process_memory_read(CTRL_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us);
#define ctrl_process_memory_read_struct(process, vaddr, is_stale_out, ptr, endt_us) ctrl_process_memory_read((process), r1u64((vaddr), (vaddr)+(sizeof(*(ptr)))), (is_stale_out), (ptr), (endt_us))
//- rjf: process memory writing
internal B32 ctrl_process_write(CTRL_Handle process, Rng1U64 range, void *src);
////////////////////////////////
//~ rjf: Thread Register Functions
@@ -1068,11 +937,6 @@ internal CTRL_Unwind ctrl_unwind_from_thread(Arena *arena, CTRL_EntityCtx *ctx,
internal CTRL_CallStack ctrl_call_stack_from_unwind(Arena *arena, CTRL_Entity *process, CTRL_Unwind *base_unwind);
internal CTRL_CallStackFrame *ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U64 unwind_count, U64 inline_depth);
////////////////////////////////
//~ rjf: Call Stack Cache Functions
internal CTRL_CallStack ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_Entity *thread, B32 high_priority, U64 endt_us);
////////////////////////////////
//~ rjf: Halting All Attached Processes
@@ -1136,23 +1000,32 @@ internal void ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
internal void ctrl_thread__single_step(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
////////////////////////////////
//~ rjf: Asynchronous Memory Streaming Functions
//~ rjf: Process Memory Artifact Cache Hooks / Lookups
//- rjf: user -> memory stream communication
internal B32 ctrl_u2ms_enqueue_req(HS_Key key, CTRL_Handle process, Rng1U64 vaddr_range, B32 zero_terminated, U64 endt_us);
internal void ctrl_u2ms_dequeue_req(HS_Key *out_key, CTRL_Handle *out_process, Rng1U64 *out_vaddr_range, B32 *out_zero_terminated);
internal AC_Artifact ctrl_memory_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out);
internal void ctrl_memory_artifact_destroy(AC_Artifact artifact);
internal C_Key ctrl_key_from_process_vaddr_range(CTRL_Handle process, Rng1U64 vaddr_range, B32 zero_terminated, U64 endt_us, B32 *out_is_stale);
//- rjf: entry point
ASYNC_WORK_DEF(ctrl_mem_stream_work);
//- rjf: process memory reading helpers
internal CTRL_ProcessMemorySlice ctrl_process_memory_slice_from_vaddr_range(Arena *arena, CTRL_Handle process, Rng1U64 range, U64 endt_us);
internal B32 ctrl_process_memory_read(CTRL_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us);
#define ctrl_process_memory_read_struct(process, vaddr, is_stale_out, ptr, endt_us) ctrl_process_memory_read((process), r1u64((vaddr), (vaddr)+(sizeof(*(ptr)))), (is_stale_out), (ptr), (endt_us))
//- rjf: process memory writing
internal B32 ctrl_process_write(CTRL_Handle process, Rng1U64 range, void *src);
////////////////////////////////
//~ rjf: Asynchronous Call Stack Building Functions
//~ rjf: Call Stack Artifact Cache Hooks / Lookups
//- rjf: user -> memory stream communication
internal B32 ctrl_u2csb_enqueue_req(CTRL_Handle thread, U64 endt_us);
internal void ctrl_u2csb_dequeue_req(CTRL_Handle *out_thread);
internal AC_Artifact ctrl_call_stack_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out);
internal void ctrl_call_stack_artifact_destroy(AC_Artifact artifact);
internal CTRL_CallStack ctrl_call_stack_from_thread(Access *access, CTRL_Handle thread_handle, B32 high_priority, U64 endt_us);
//- rjf: entry point
ASYNC_WORK_DEF(ctrl_call_stack_build_work);
////////////////////////////////
//~ rjf: Call Stack Tree Artifact Cache Hooks / Lookups
internal AC_Artifact ctrl_call_stack_tree_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out);
internal void ctrl_call_stack_tree_artifact_destroy(AC_Artifact artifact);
internal CTRL_CallStackTree ctrl_call_stack_tree(Access *access, U64 endt_us);
#endif // CTRL_CORE_H
-830
View File
@@ -1,830 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#undef LAYER_COLOR
#define LAYER_COLOR 0xe34cd4ff
////////////////////////////////
//~ rjf: Instruction Decoding/Disassembling Type Functions
#if !defined(ZYDIS_H)
#include "third_party/zydis/zydis.h"
#include "third_party/zydis/zydis.c"
#endif
internal DASM_Inst
dasm_inst_from_code(Arena *arena, Arch arch, U64 vaddr, String8 code, DASM_Syntax syntax)
{
DASM_Inst inst = {0};
switch(arch)
{
default:{}break;
//- rjf: x86/x64 disassembly
case Arch_x86:
case Arch_x64:
{
// rjf: determine zydis formatter style
ZydisFormatterStyle style = ZYDIS_FORMATTER_STYLE_INTEL;
switch(syntax)
{
default:{}break;
case DASM_Syntax_Intel:{style = ZYDIS_FORMATTER_STYLE_INTEL;}break;
case DASM_Syntax_ATT: {style = ZYDIS_FORMATTER_STYLE_ATT;}break;
}
// rjf: disassemble one instruction
ZydisDisassembledInstruction zinst = {0};
ZyanStatus status = ZydisDisassemble(ZYDIS_MACHINE_MODE_LONG_64, vaddr, code.str, code.size, &zinst, style);
// rjf: analyze
DASM_InstFlags flags = 0;
U64 jump_dest_vaddr = 0;
{
ZydisDecodedOperand *first_visible_op = (zinst.info.operand_count_visible > 0 ? &zinst.operands[0] : 0);
ZydisDecodedOperand *first_op = (zinst.info.operand_count > 0 ? &zinst.operands[0] : 0);
ZydisDecodedOperand *second_op = (zinst.info.operand_count > 1 ? &zinst.operands[1] : 0);
if(first_visible_op != 0 &&
(first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM8 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM32 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM64 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16_32_64 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM32_32_64 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16_32_32))
{
ZydisCalcAbsoluteAddress(&zinst.info, first_visible_op, vaddr, &jump_dest_vaddr);
}
if(first_op != 0 && second_op != 0 && first_op->type == ZYDIS_OPERAND_TYPE_REGISTER &&
(first_op->reg.value == ZYDIS_REGISTER_RSP ||
first_op->reg.value == ZYDIS_REGISTER_ESP ||
first_op->reg.value == ZYDIS_REGISTER_SP))
{
flags |= DASM_InstFlag_ChangesStackPointer;
if(second_op->type != ZYDIS_OPERAND_TYPE_IMMEDIATE)
{
flags |= DASM_InstFlag_ChangesStackPointerVariably;
}
}
if(zinst.info.attributes & (ZYDIS_ATTRIB_HAS_REP|
ZYDIS_ATTRIB_HAS_REPE|
ZYDIS_ATTRIB_HAS_REPZ|
ZYDIS_ATTRIB_HAS_REPNZ|
ZYDIS_ATTRIB_HAS_REPNE))
{
flags |= DASM_InstFlag_Repeats;
}
switch(zinst.info.mnemonic)
{
case ZYDIS_MNEMONIC_CALL:
{
flags |= DASM_InstFlag_Call;
}break;
case ZYDIS_MNEMONIC_JB:
case ZYDIS_MNEMONIC_JBE:
case ZYDIS_MNEMONIC_JCXZ:
case ZYDIS_MNEMONIC_JECXZ:
case ZYDIS_MNEMONIC_JKNZD:
case ZYDIS_MNEMONIC_JKZD:
case ZYDIS_MNEMONIC_JL:
case ZYDIS_MNEMONIC_JLE:
case ZYDIS_MNEMONIC_JNB:
case ZYDIS_MNEMONIC_JNBE:
case ZYDIS_MNEMONIC_JNL:
case ZYDIS_MNEMONIC_JNLE:
case ZYDIS_MNEMONIC_JNO:
case ZYDIS_MNEMONIC_JNP:
case ZYDIS_MNEMONIC_JNS:
case ZYDIS_MNEMONIC_JNZ:
case ZYDIS_MNEMONIC_JO:
case ZYDIS_MNEMONIC_JP:
case ZYDIS_MNEMONIC_JRCXZ:
case ZYDIS_MNEMONIC_JS:
case ZYDIS_MNEMONIC_JZ:
case ZYDIS_MNEMONIC_LOOP:
case ZYDIS_MNEMONIC_LOOPE:
case ZYDIS_MNEMONIC_LOOPNE:
{
flags |= DASM_InstFlag_Branch;
}break;
case ZYDIS_MNEMONIC_JMP:
{
flags |= DASM_InstFlag_UnconditionalJump;
}break;
case ZYDIS_MNEMONIC_RET:
{
flags |= DASM_InstFlag_Return;
}break;
case ZYDIS_MNEMONIC_PUSH:
case ZYDIS_MNEMONIC_POP:
{
flags |= DASM_InstFlag_ChangesStackPointer;
}break;
default:
{
flags |= DASM_InstFlag_NonFlow;
}break;
}
}
// rjf: convert
{
inst.flags = flags;
inst.size = zinst.info.length;
inst.string = push_str8_copy(arena, str8_cstring(zinst.text));
inst.jump_dest_vaddr = jump_dest_vaddr;
}
}break;
}
return inst;
}
////////////////////////////////
//~ rjf: Control Flow Analysis
internal DASM_CtrlFlowInfo
dasm_ctrl_flow_info_from_arch_vaddr_code(Arena *arena, DASM_InstFlags exit_points_mask, Arch arch, U64 vaddr, String8 code)
{
Temp scratch = scratch_begin(&arena, 1);
DASM_CtrlFlowInfo info = {0};
for(U64 offset = 0; offset < code.size;)
{
DASM_Inst inst = dasm_inst_from_code(scratch.arena, arch, vaddr+offset, str8_skip(code, offset), DASM_Syntax_Intel);
U64 inst_vaddr = vaddr+offset;
offset += inst.size;
info.total_size += inst.size;
if(inst.flags & exit_points_mask)
{
DASM_CtrlFlowPoint point = {0};
point.inst_flags = inst.flags;
point.vaddr = inst_vaddr;
point.jump_dest_vaddr = inst.jump_dest_vaddr;
DASM_CtrlFlowPointNode *node = push_array(arena, DASM_CtrlFlowPointNode, 1);
node->v = point;
SLLQueuePush(info.exit_points.first, info.exit_points.last, node);
info.exit_points.count += 1;
}
}
scratch_end(scratch);
return info;
}
////////////////////////////////
//~ rjf: Parameter Type Functions
internal B32
dasm_params_match(DASM_Params *a, DASM_Params *b)
{
B32 result = (a->vaddr == b->vaddr &&
a->arch == b->arch &&
a->style_flags == b->style_flags &&
a->syntax == b->syntax &&
a->base_vaddr == b->base_vaddr &&
di_key_match(&a->dbgi_key, &b->dbgi_key));
return result;
}
////////////////////////////////
//~ rjf: Line Type Functions
internal void
dasm_line_chunk_list_push(Arena *arena, DASM_LineChunkList *list, U64 cap, DASM_Line *inst)
{
DASM_LineChunkNode *node = list->last;
if(node == 0 || node->count >= node->cap)
{
node = push_array(arena, DASM_LineChunkNode, 1);
node->v = push_array_no_zero(arena, DASM_Line, cap);
node->cap = cap;
SLLQueuePush(list->first, list->last, node);
list->node_count += 1;
}
MemoryCopyStruct(&node->v[node->count], inst);
node->count += 1;
list->line_count += 1;
}
internal DASM_LineArray
dasm_line_array_from_chunk_list(Arena *arena, DASM_LineChunkList *list)
{
DASM_LineArray array = {0};
array.count = list->line_count;
array.v = push_array_no_zero(arena, DASM_Line, array.count);
U64 idx = 0;
for(DASM_LineChunkNode *n = list->first; n != 0; n = n->next)
{
MemoryCopy(array.v+idx, n->v, sizeof(DASM_Line)*n->count);
idx += n->count;
}
return array;
}
internal U64
dasm_line_array_idx_from_code_off__linear_scan(DASM_LineArray *array, U64 off)
{
U64 result = 0;
for(U64 idx = 0; idx < array->count; idx += 1)
{
U64 next_off = (idx+1 < array->count ? array->v[idx+1].code_off : max_U64);
if(array->v[idx].code_off <= off && off < next_off)
{
result = idx;
if(!(array->v[idx].flags & DASM_LineFlag_Decorative))
{
break;
}
}
}
return result;
}
internal U64
dasm_line_array_code_off_from_idx(DASM_LineArray *array, U64 idx)
{
U64 off = 0;
if(idx < array->count)
{
off = array->v[idx].code_off;
}
return off;
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
dasm_init(void)
{
Arena *arena = arena_alloc();
dasm_shared = push_array(arena, DASM_Shared, 1);
dasm_shared->arena = arena;
dasm_shared->slots_count = 1024;
dasm_shared->stripes_count = Min(dasm_shared->slots_count, os_get_system_info()->logical_processor_count);
dasm_shared->slots = push_array(arena, DASM_Slot, dasm_shared->slots_count);
dasm_shared->stripes = push_array(arena, DASM_Stripe, dasm_shared->stripes_count);
for(U64 idx = 0; idx < dasm_shared->stripes_count; idx += 1)
{
dasm_shared->stripes[idx].arena = arena_alloc();
dasm_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
dasm_shared->stripes[idx].cv = os_condition_variable_alloc();
}
dasm_shared->u2p_ring_size = KB(64);
dasm_shared->u2p_ring_base = push_array_no_zero(arena, U8, dasm_shared->u2p_ring_size);
dasm_shared->u2p_ring_cv = os_condition_variable_alloc();
dasm_shared->u2p_ring_mutex = os_mutex_alloc();
dasm_shared->evictor_detector_thread = os_thread_launch(dasm_evictor_detector_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Scoped Access
internal DASM_Scope *
dasm_scope_open(void)
{
if(dasm_tctx == 0)
{
Arena *arena = arena_alloc();
dasm_tctx = push_array(arena, DASM_TCTX, 1);
dasm_tctx->arena = arena;
}
U64 base_pos = arena_pos(dasm_tctx->arena);
DASM_Scope *scope = push_array(dasm_tctx->arena, DASM_Scope, 1);
scope->base_pos = base_pos;
return scope;
}
internal void
dasm_scope_close(DASM_Scope *scope)
{
for(DASM_Touch *t = scope->top_touch, *next = 0; t != 0; t = next)
{
next = t->next;
U64 slot_idx = t->hash.u64[1]%dasm_shared->slots_count;
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(t->hash, n->hash) && dasm_params_match(&t->params, &n->params))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
}
arena_pop_to(dasm_tctx->arena, scope->base_pos);
}
internal void
dasm_scope_touch_node__stripe_r_guarded(DASM_Scope *scope, DASM_Node *node)
{
DASM_Touch *touch = push_array(dasm_tctx->arena, DASM_Touch, 1);
ins_atomic_u64_inc_eval(&node->scope_ref_count);
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, update_tick_idx());
touch->hash = node->hash;
MemoryCopyStruct(&touch->params, &node->params);
touch->params.dbgi_key = di_key_copy(dasm_tctx->arena, &touch->params.dbgi_key);
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal DASM_Info
dasm_info_from_hash_params(DASM_Scope *scope, U128 hash, DASM_Params *params)
{
DASM_Info info = {0};
if(!u128_match(hash, u128_zero()))
{
//- rjf: unpack hash
U64 slot_idx = hash.u64[1]%dasm_shared->slots_count;
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
//- rjf: try to get existing results
B32 found = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && dasm_params_match(params, &n->params))
{
MemoryCopyStruct(&info, &n->info);
found = 1;
dasm_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
//- rjf: miss -> kick off work to fill cache
if(!found)
{
B32 node_is_new = 0;
U64 *node_working_count = 0;
HS_Root root = {0};
OS_MutexScopeW(stripe->rw_mutex)
{
DASM_Node *node = 0;
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && dasm_params_match(params, &n->params))
{
node = n;
break;
}
}
if(node == 0)
{
// rjf: allocate node
node = stripe->free_node;
if(node)
{
SLLStackPop(stripe->free_node);
}
else
{
node = push_array_no_zero(stripe->arena, DASM_Node, 1);
}
MemoryZeroStruct(node);
// rjf: fill node
DLLPushBack(slot->first, slot->last, node);
node->hash = hash;
MemoryCopyStruct(&node->params, params);
node->root = hs_root_alloc();
// TODO(rjf): need to make this releasable - currently all exe_paths just leak
node->params.dbgi_key = di_key_copy(stripe->arena, &node->params.dbgi_key);
// rjf: gather work kickoff params
node_is_new = 1;
ins_atomic_u64_inc_eval(&node->working_count);
node_working_count = &node->working_count;
root = node->root;
}
}
if(node_is_new)
{
dasm_u2p_enqueue_req(root, hash, params, max_U64);
async_push_work(dasm_parse_work, .working_counter = node_working_count);
}
}
}
return info;
}
internal DASM_Info
dasm_info_from_key_params(DASM_Scope *scope, HS_Key key, DASM_Params *params, U128 *hash_out)
{
DASM_Info result = {0};
for(U64 rewind_idx = 0; rewind_idx < HS_KEY_HASH_HISTORY_COUNT; rewind_idx += 1)
{
U128 hash = hs_hash_from_key(key, rewind_idx);
result = dasm_info_from_hash_params(scope, hash, params);
if(result.lines.count != 0)
{
if(hash_out)
{
*hash_out = hash;
}
break;
}
}
return result;
}
////////////////////////////////
//~ rjf: Parse Threads
internal B32
dasm_u2p_enqueue_req(HS_Root root, U128 hash, DASM_Params *params, U64 endt_us)
{
B32 good = 0;
OS_MutexScope(dasm_shared->u2p_ring_mutex) for(;;)
{
U64 unconsumed_size = dasm_shared->u2p_ring_write_pos - dasm_shared->u2p_ring_read_pos;
U64 available_size = dasm_shared->u2p_ring_size - unconsumed_size;
if(available_size >= sizeof(root)+sizeof(hash)+sizeof(U64)+sizeof(Arch)+sizeof(DASM_StyleFlags)+sizeof(DASM_Syntax)+sizeof(U64)+sizeof(U64)+params->dbgi_key.path.size+sizeof(U64))
{
good = 1;
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &root);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &hash);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->vaddr);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->arch);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->style_flags);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->syntax);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->base_vaddr);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->dbgi_key.path.size);
dasm_shared->u2p_ring_write_pos += ring_write(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, params->dbgi_key.path.str, params->dbgi_key.path.size);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->dbgi_key.min_timestamp);
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(dasm_shared->u2p_ring_cv, dasm_shared->u2p_ring_mutex, endt_us);
}
if(good)
{
os_condition_variable_broadcast(dasm_shared->u2p_ring_cv);
}
return good;
}
internal void
dasm_u2p_dequeue_req(Arena *arena, HS_Root *root_out, U128 *hash_out, DASM_Params *params_out)
{
OS_MutexScope(dasm_shared->u2p_ring_mutex) for(;;)
{
U64 unconsumed_size = dasm_shared->u2p_ring_write_pos - dasm_shared->u2p_ring_read_pos;
if(unconsumed_size >= sizeof(*hash_out)+sizeof(U64)+sizeof(Arch)+sizeof(DASM_StyleFlags)+sizeof(DASM_Syntax)+sizeof(U64)+sizeof(U64)+sizeof(U64))
{
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, root_out);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, hash_out);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->vaddr);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->arch);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->style_flags);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->syntax);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->base_vaddr);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->dbgi_key.path.size);
params_out->dbgi_key.path.str = push_array(arena, U8, params_out->dbgi_key.path.size);
dasm_shared->u2p_ring_read_pos += ring_read(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, params_out->dbgi_key.path.str, params_out->dbgi_key.path.size);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->dbgi_key.min_timestamp);
break;
}
os_condition_variable_wait(dasm_shared->u2p_ring_cv, dasm_shared->u2p_ring_mutex, max_U64);
}
os_condition_variable_broadcast(dasm_shared->u2p_ring_cv);
}
ASYNC_WORK_DEF(dasm_parse_work)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
HS_Scope *hs_scope = hs_scope_open();
DI_Scope *di_scope = di_scope_open();
TXT_Scope *txt_scope = txt_scope_open();
//- rjf: get next request
HS_Root root = {0};
U128 hash = {0};
DASM_Params params = {0};
dasm_u2p_dequeue_req(scratch.arena, &root, &hash, &params);
U64 change_gen = fs_change_gen();
//- rjf: unpack hash
U64 slot_idx = hash.u64[1]%dasm_shared->slots_count;
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
//- rjf: get dbg info
RDI_Parsed *rdi = &rdi_parsed_nil;
if(params.dbgi_key.path.size != 0)
{
rdi = di_rdi_from_key(di_scope, &params.dbgi_key, 1, max_U64);
}
//- rjf: hash -> data
String8 data = hs_data_from_hash(hs_scope, hash);
//- rjf: data * arch * addr * dbg -> decode artifacts
DASM_LineChunkList line_list = {0};
String8List inst_strings = {0};
{
switch(params.arch)
{
default:{}break;
//- rjf: x86/x64 decoding
case Arch_x64:
case Arch_x86:
{
// rjf: disassemble
RDI_SourceFile *last_file = &rdi_nil_element_union.source_file;
RDI_Line *last_line = 0;
for(U64 off = 0; off < data.size;)
{
// rjf: disassemble one instruction
DASM_Inst inst = dasm_inst_from_code(scratch.arena, params.arch, params.vaddr+off, str8_skip(data, off), params.syntax);
if(inst.size == 0)
{
break;
}
// rjf: push strings derived from voff -> line info
if(params.style_flags & (DASM_StyleFlag_SourceFilesNames|DASM_StyleFlag_SourceLines))
{
if(rdi != &rdi_parsed_nil)
{
U64 voff = (params.vaddr+off) - params.base_vaddr;
U32 unit_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_UnitVMap, voff);
RDI_Unit *unit = rdi_element_from_name_idx(rdi, Units, unit_idx);
RDI_LineTable *line_table = rdi_element_from_name_idx(rdi, LineTables, unit->line_table_idx);
RDI_ParsedLineTable unit_line_info = {0};
rdi_parsed_from_line_table(rdi, line_table, &unit_line_info);
U64 line_info_idx = rdi_line_info_idx_from_voff(&unit_line_info, voff);
if(line_info_idx < unit_line_info.count)
{
RDI_Line *line = &unit_line_info.lines[line_info_idx];
RDI_SourceFile *file = rdi_element_from_name_idx(rdi, SourceFiles, line->file_idx);
String8 file_normalized_full_path = {0};
file_normalized_full_path.str = rdi_string_from_idx(rdi, file->normal_full_path_string_idx, &file_normalized_full_path.size);
if(file != last_file)
{
if(params.style_flags & DASM_StyleFlag_SourceFilesNames &&
file->normal_full_path_string_idx != 0 && file_normalized_full_path.size != 0)
{
String8 inst_string = push_str8f(scratch.arena, "> %S", file_normalized_full_path);
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
str8_list_push(scratch.arena, &inst_strings, inst_string);
}
if(params.style_flags & DASM_StyleFlag_SourceFilesNames && file->normal_full_path_string_idx == 0)
{
String8 inst_string = str8_lit(">");
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
str8_list_push(scratch.arena, &inst_strings, inst_string);
}
last_file = file;
}
if(line && line != last_line && file->normal_full_path_string_idx != 0 &&
params.style_flags & DASM_StyleFlag_SourceLines &&
file_normalized_full_path.size != 0)
{
FileProperties props = os_properties_from_file_path(file_normalized_full_path);
if(props.modified != 0)
{
// TODO(rjf): need redirection path - this may map to a different path on the local machine,
// need frontend to communicate path remapping info to this layer
HS_Key key = fs_key_from_path_range(file_normalized_full_path, r1u64(0, max_U64), 0);
TXT_LangKind lang_kind = txt_lang_kind_from_extension(file_normalized_full_path);
U64 endt_us = max_U64;
U128 hash = {0};
TXT_TextInfo text_info = {0};
for(;os_now_microseconds() <= endt_us;)
{
text_info = txt_text_info_from_key_lang(txt_scope, key, lang_kind, &hash);
if(!u128_match(hash, u128_zero()))
{
break;
}
}
if(0 < line->line_num && line->line_num < text_info.lines_count)
{
String8 data = hs_data_from_hash(hs_scope, hash);
String8 line_text = str8_skip_chop_whitespace(str8_substr(data, text_info.lines_ranges[line->line_num-1]));
if(line_text.size != 0)
{
String8 inst_string = push_str8f(scratch.arena, "> %S", line_text);
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
str8_list_push(scratch.arena, &inst_strings, inst_string);
}
}
}
last_line = line;
}
}
}
}
// rjf: push line
String8 addr_part = {0};
if(params.style_flags & DASM_StyleFlag_Addresses)
{
addr_part = push_str8f(scratch.arena, "%s0x%016I64x ", rdi != &rdi_parsed_nil ? " " : "", params.vaddr+off);
}
String8 code_bytes_part = {0};
if(params.style_flags & DASM_StyleFlag_CodeBytes)
{
String8List code_bytes_strings = {0};
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit("{"));
for(U64 byte_idx = 0; byte_idx < inst.size || byte_idx < 16; byte_idx += 1)
{
if(byte_idx < inst.size)
{
str8_list_pushf(scratch.arena, &code_bytes_strings, "%02x%s ", (U32)data.str[off+byte_idx], byte_idx == inst.size-1 ? "}" : "");
}
else if(byte_idx < 8)
{
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit(" "));
}
}
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit(" "));
code_bytes_part = str8_list_join(scratch.arena, &code_bytes_strings, 0);
}
String8 symbol_part = {0};
if(inst.jump_dest_vaddr != 0 && rdi != &rdi_parsed_nil && params.style_flags & DASM_StyleFlag_SymbolNames)
{
RDI_U32 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, inst.jump_dest_vaddr-params.base_vaddr);
if(scope_idx != 0)
{
RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx);
RDI_U32 procedure_idx = scope->proc_idx;
RDI_Procedure *procedure = rdi_element_from_name_idx(rdi, Procedures, procedure_idx);
String8 procedure_name = {0};
procedure_name.str = rdi_string_from_idx(rdi, procedure->name_string_idx, &procedure_name.size);
if(procedure_name.size != 0)
{
symbol_part = push_str8f(scratch.arena, " (%S)", procedure_name);
}
}
}
String8 inst_string = push_str8f(scratch.arena, "%S%S%S%S", addr_part, code_bytes_part, inst.string, symbol_part);
DASM_Line line = {u32_from_u64_saturate(off), 0, inst.jump_dest_vaddr, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &line);
str8_list_push(scratch.arena, &inst_strings, inst_string);
// rjf: increment
off += inst.size;
}
}break;
}
}
//- rjf: artifacts -> value bundle
Arena *info_arena = 0;
DASM_Info info = {0};
{
//- rjf: produce joined text
Arena *text_arena = arena_alloc();
StringJoin text_join = {0};
text_join.sep = str8_lit("\n");
String8 text = str8_list_join(text_arena, &inst_strings, &text_join);
//- rjf: produce unique key for this disassembly's text
HS_Key text_key = hs_key_make(root, hs_id_make(0, 0));
//- rjf: submit text data to hash store
U128 text_hash = hs_submit_data(text_key, &text_arena, text);
//- rjf: produce value bundle
info_arena = arena_alloc();
info.text_key = text_key;
info.lines = dasm_line_array_from_chunk_list(info_arena, &line_list);
}
//- rjf: commit results to cache
OS_MutexScopeW(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash) && dasm_params_match(&n->params, &params))
{
n->info_arena = info_arena;
MemoryCopyStruct(&n->info, &info);
if(rdi != &rdi_parsed_nil && params.style_flags & (DASM_StyleFlag_SourceLines|DASM_StyleFlag_SourceFilesNames))
{
n->change_gen = change_gen;
}
else
{
n->change_gen = 0;
}
break;
}
}
}
txt_scope_close(txt_scope);
di_scope_close(di_scope);
hs_scope_close(hs_scope);
scratch_end(scratch);
ProfEnd();
return 0;
}
////////////////////////////////
//~ rjf: Evictor/Detector Thread
internal void
dasm_evictor_detector_thread__entry_point(void *p)
{
ThreadNameF("[dasm] evictor/detector thread");
for(;;)
{
U64 change_gen = fs_change_gen();
U64 check_time_us = os_now_microseconds();
U64 check_time_user_clocks = update_tick_idx();
U64 evict_threshold_us = 10*1000000;
U64 retry_threshold_us = 1*1000000;
U64 evict_threshold_user_clocks = 10;
U64 retry_threshold_user_clocks = 10;
for(U64 slot_idx = 0; slot_idx < dasm_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
ins_atomic_u64_eval(&n->working_count) == 0)
{
slot_has_work = 1;
break;
}
if(n->change_gen != 0 && n->change_gen != change_gen &&
n->last_time_requested_us+retry_threshold_us <= check_time_us &&
n->last_user_clock_idx_requested+retry_threshold_user_clocks <= check_time_user_clocks)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
ins_atomic_u64_eval(&n->working_count) == 0)
{
DLLRemove(slot->first, slot->last, n);
if(n->info_arena != 0)
{
arena_release(n->info_arena);
}
SLLStackPush(stripe->free_node, n);
}
if(n->change_gen != 0 && n->change_gen != change_gen &&
n->last_time_requested_us+retry_threshold_us <= check_time_us &&
n->last_user_clock_idx_requested+retry_threshold_user_clocks <= check_time_user_clocks)
{
if(dasm_u2p_enqueue_req(n->root, n->hash, &n->params, max_U64))
{
async_push_work(dasm_parse_work);
n->last_time_requested_us = os_now_microseconds();
n->last_user_clock_idx_requested = check_time_user_clocks;
}
}
}
}
}
os_sleep_milliseconds(100);
}
}
+9 -9
View File
@@ -37,7 +37,7 @@ d_hash_from_seed_string__case_insensitive(U64 seed, String8 string)
U64 result = seed;
for(U64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + char_to_lower(string.str[i]);
result = ((result << 5) + result) + lower_from_char(string.str[i]);
}
return result;
}
@@ -1208,13 +1208,13 @@ d_query_cached_rip_from_thread_unwind(CTRL_Entity *thread, U64 unwind_count)
}
else
{
CTRL_Scope *ctrl_scope = ctrl_scope_open();
CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, 0);
Access *access = access_open();
CTRL_CallStack callstack = ctrl_call_stack_from_thread(access, thread->handle, 1, 0);
if(callstack.concrete_frames_count != 0)
{
result = regs_rip_from_arch_block(thread->arch, callstack.concrete_frames[unwind_count%callstack.concrete_frames_count]->regs);
}
ctrl_scope_close(ctrl_scope);
access_close(access);
}
return result;
}
@@ -1421,8 +1421,8 @@ d_init(void)
d_state = push_array(arena, D_State, 1);
d_state->arena = arena;
d_state->cmds_arena = arena_alloc();
d_state->output_log_key = hs_key_make(hs_root_alloc(), hs_id_make(0, 0));
hs_submit_data(d_state->output_log_key, 0, str8_zero());
d_state->output_log_key = c_key_make(c_root_alloc(), c_id_make(0, 0));
c_submit_data(d_state->output_log_key, 0, str8_zero());
d_state->ctrl_entity_store = ctrl_entity_ctx_rw_store_alloc();
d_state->ctrl_stop_arena = arena_alloc();
d_state->ctrl_msg_arena = arena_alloc();
@@ -1886,10 +1886,10 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P
case D_CmdKind_StepOverLine: {traps = d_trap_net_from_thread__step_over_line(scratch.arena, thread);}break;
case D_CmdKind_StepOut:
{
CTRL_Scope *ctrl_scope = ctrl_scope_open();
Access *access = access_open();
// rjf: thread => call stack
CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000);
CTRL_CallStack callstack = ctrl_call_stack_from_thread(access, thread->handle, 1, os_now_microseconds()+10000);
// rjf: use first unwind frame to generate trap
if(callstack.concrete_frames_count > 1)
@@ -1904,7 +1904,7 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P
good = 0;
}
ctrl_scope_close(ctrl_scope);
access_close(access);
}break;
}
if(good && traps.count != 0)
+1 -1
View File
@@ -283,7 +283,7 @@ struct D_State
D_CmdList cmds;
// rjf: output log key
HS_Key output_log_key;
C_Key output_log_key;
// rjf: per-run caches
U64 tls_base_cache_reggen_idx;
+72 -72
View File
@@ -13,7 +13,7 @@ di_hash_from_seed_string(U64 seed, String8 string, StringMatchFlags match_flags)
U64 result = seed;
for(U64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + ((match_flags & StringMatchFlag_CaseInsensitive) ? char_to_lower(string.str[i]) : string.str[i]);
result = ((result << 5) + result) + ((match_flags & StringMatchFlag_CaseInsensitive) ? lower_from_char(string.str[i]) : string.str[i]);
}
return result;
}
@@ -209,8 +209,8 @@ di_init(void)
for(U64 idx = 0; idx < di_shared->stripes_count; idx += 1)
{
di_shared->stripes[idx].arena = arena_alloc();
di_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
di_shared->stripes[idx].cv = os_condition_variable_alloc();
di_shared->stripes[idx].rw_mutex = rw_mutex_alloc();
di_shared->stripes[idx].cv = cond_var_alloc();
}
di_shared->search_slots_count = 512;
di_shared->search_slots = push_array(arena, DI_SearchSlot, di_shared->search_slots_count);
@@ -219,28 +219,28 @@ di_init(void)
for(U64 idx = 0; idx < di_shared->search_stripes_count; idx += 1)
{
di_shared->search_stripes[idx].arena = arena_alloc();
di_shared->search_stripes[idx].rw_mutex = os_rw_mutex_alloc();
di_shared->search_stripes[idx].cv = os_condition_variable_alloc();
di_shared->search_stripes[idx].rw_mutex = rw_mutex_alloc();
di_shared->search_stripes[idx].cv = cond_var_alloc();
}
di_shared->u2p_ring_mutex = os_mutex_alloc();
di_shared->u2p_ring_cv = os_condition_variable_alloc();
di_shared->u2p_ring_mutex = mutex_alloc();
di_shared->u2p_ring_cv = cond_var_alloc();
di_shared->u2p_ring_size = KB(64);
di_shared->u2p_ring_base = push_array_no_zero(arena, U8, di_shared->u2p_ring_size);
di_shared->p2u_ring_mutex = os_mutex_alloc();
di_shared->p2u_ring_cv = os_condition_variable_alloc();
di_shared->p2u_ring_mutex = mutex_alloc();
di_shared->p2u_ring_cv = cond_var_alloc();
di_shared->p2u_ring_size = KB(64);
di_shared->p2u_ring_base = push_array_no_zero(arena, U8, di_shared->p2u_ring_size);
di_shared->search_threads_count = 1;
di_shared->search_threads = push_array(arena, DI_SearchThread, di_shared->search_threads_count);
for EachIndex(idx, di_shared->search_threads_count)
{
di_shared->search_threads[idx].ring_mutex = os_mutex_alloc();
di_shared->search_threads[idx].ring_cv = os_condition_variable_alloc();
di_shared->search_threads[idx].ring_mutex = mutex_alloc();
di_shared->search_threads[idx].ring_cv = cond_var_alloc();
di_shared->search_threads[idx].ring_size = KB(64);
di_shared->search_threads[idx].ring_base = push_array_no_zero(arena, U8, di_shared->search_threads[idx].ring_size);
di_shared->search_threads[idx].thread = os_thread_launch(di_search_thread__entry_point, (void *)idx, 0);
di_shared->search_threads[idx].thread = thread_launch(di_search_thread__entry_point, (void *)idx);
}
di_shared->search_evictor_thread = os_thread_launch(di_search_evictor_thread__entry_point, 0, 0);
di_shared->search_evictor_thread = thread_launch(di_search_evictor_thread__entry_point, 0);
}
////////////////////////////////
@@ -279,12 +279,12 @@ di_scope_close(DI_Scope *scope)
if(t->node != 0)
{
ins_atomic_u64_dec_eval(&t->node->touch_count);
os_condition_variable_broadcast(t->stripe->cv);
cond_var_broadcast(t->stripe->cv);
}
if(t->search_node != 0)
{
ins_atomic_u64_dec_eval(&t->search_node->scope_refcount);
os_condition_variable_broadcast(t->search_stripe->cv);
cond_var_broadcast(t->search_stripe->cv);
}
SLLStackPush(di_tctx->free_touch, t);
}
@@ -343,7 +343,7 @@ di_node_from_key_slot__stripe_mutex_r_guarded(DI_Slot *slot, DI_Key *key)
{
ProfBeginFunction();
DI_Node *node = 0;
StringMatchFlags match_flags = path_match_flags_from_os(operating_system_from_context());
StringMatchFlags match_flags = path_match_flags_from_os(OperatingSystem_CURRENT);
U64 most_recent_timestamp = max_U64;
for(DI_Node *n = slot->first; n != 0; n = n->next)
{
@@ -468,7 +468,7 @@ di_open(DI_Key *key)
DI_Slot *slot = &di_shared->slots[slot_idx];
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
log_infof("open_debug_info: {\"%S\", 0x%I64x}\n", key->path, key->min_timestamp);
OS_MutexScopeW(stripe->rw_mutex)
MutexScopeW(stripe->rw_mutex)
{
//- rjf: find existing node
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, key);
@@ -505,7 +505,7 @@ di_open(DI_Key *key)
{
di_u2p_enqueue_key(key, max_U64);
ins_atomic_u64_eval_assign(&node->is_working, 1);
DeferLoop(os_rw_mutex_drop_w(stripe->rw_mutex), os_rw_mutex_take_w(stripe->rw_mutex))
DeferLoop(rw_mutex_drop_w(stripe->rw_mutex), rw_mutex_take_w(stripe->rw_mutex))
{
async_push_work(di_parse_work);
}
@@ -529,7 +529,7 @@ di_close(DI_Key *key)
DI_Slot *slot = &di_shared->slots[slot_idx];
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
log_infof("close_debug_info: {\"%S\", 0x%I64x}\n", key->path, key->min_timestamp);
OS_MutexScopeW(stripe->rw_mutex)
MutexScopeW(stripe->rw_mutex)
{
//- rjf: find existing node
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, key);
@@ -566,7 +566,7 @@ di_close(DI_Key *key)
}
//- rjf: wait for touch count / working marker to go to 0
os_condition_variable_wait_rw_w(stripe->cv, stripe->rw_mutex, max_U64);
cond_var_wait_rw_w(stripe->cv, stripe->rw_mutex, max_U64);
}
}
}
@@ -591,7 +591,7 @@ di_rdi_from_key(DI_Scope *scope, DI_Key *key, B32 high_priority, U64 endt_us)
U64 stripe_idx = slot_idx%di_shared->stripes_count;
DI_Slot *slot = &di_shared->slots[slot_idx];
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
ProfScope("grab node") OS_MutexScopeR(stripe->rw_mutex) for(;;)
ProfScope("grab node") MutexScopeR(stripe->rw_mutex) for(;;)
{
//- rjf: find existing node
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, key);
@@ -625,7 +625,7 @@ di_rdi_from_key(DI_Scope *scope, DI_Key *key, B32 high_priority, U64 endt_us)
ProfScope("ask for parse")
{
ins_atomic_u64_eval_assign(&node->is_working, 1);
DeferLoop(os_rw_mutex_drop_r(stripe->rw_mutex), os_rw_mutex_take_r(stripe->rw_mutex))
DeferLoop(rw_mutex_drop_r(stripe->rw_mutex), rw_mutex_take_r(stripe->rw_mutex))
{
async_push_work(di_parse_work, .priority = high_priority ? ASYNC_Priority_High : ASYNC_Priority_Low);
}
@@ -640,7 +640,7 @@ di_rdi_from_key(DI_Scope *scope, DI_Key *key, B32 high_priority, U64 endt_us)
//- rjf: wait on this stripe
{
os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
cond_var_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
}
}
scratch_end(scratch);
@@ -662,7 +662,7 @@ di_search_items_from_key_params_query(DI_Scope *scope, U128 key, DI_SearchParams
U64 stripe_idx = slot_idx%di_shared->search_stripes_count;
DI_SearchSlot * slot = &di_shared->search_slots[slot_idx];
DI_SearchStripe * stripe = &di_shared->search_stripes[stripe_idx];
OS_MutexScopeW(stripe->rw_mutex) for(;;)
MutexScopeW(stripe->rw_mutex) for(;;)
{
// rjf: map key -> node
DI_SearchNode *node = 0;
@@ -740,7 +740,7 @@ di_search_items_from_key_params_query(DI_Scope *scope, U128 key, DI_SearchParams
}
// rjf: no results, but have time to wait -> wait
os_condition_variable_wait_rw_w(stripe->cv, stripe->rw_mutex, endt_us);
cond_var_wait_rw_w(stripe->cv, stripe->rw_mutex, endt_us);
}
}
return items;
@@ -753,7 +753,7 @@ internal B32
di_u2p_enqueue_key(DI_Key *key, U64 endt_us)
{
B32 sent = 0;
OS_MutexScope(di_shared->u2p_ring_mutex) for(;;)
MutexScope(di_shared->u2p_ring_mutex) for(;;)
{
U64 unconsumed_size = di_shared->u2p_ring_write_pos - di_shared->u2p_ring_read_pos;
U64 available_size = di_shared->u2p_ring_size - unconsumed_size;
@@ -770,11 +770,11 @@ di_u2p_enqueue_key(DI_Key *key, U64 endt_us)
{
break;
}
os_condition_variable_wait(di_shared->u2p_ring_cv, di_shared->u2p_ring_mutex, endt_us);
cond_var_wait(di_shared->u2p_ring_cv, di_shared->u2p_ring_mutex, endt_us);
}
if(sent)
{
os_condition_variable_broadcast(di_shared->u2p_ring_cv);
cond_var_broadcast(di_shared->u2p_ring_cv);
}
return sent;
}
@@ -782,7 +782,7 @@ di_u2p_enqueue_key(DI_Key *key, U64 endt_us)
internal void
di_u2p_dequeue_key(Arena *arena, DI_Key *out_key)
{
OS_MutexScope(di_shared->u2p_ring_mutex) for(;;)
MutexScope(di_shared->u2p_ring_mutex) for(;;)
{
U64 unconsumed_size = di_shared->u2p_ring_write_pos - di_shared->u2p_ring_read_pos;
if(unconsumed_size >= sizeof(out_key->path.size) + sizeof(out_key->min_timestamp))
@@ -793,15 +793,15 @@ di_u2p_dequeue_key(Arena *arena, DI_Key *out_key)
di_shared->u2p_ring_read_pos += ring_read(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_read_pos, out_key->path.str, out_key->path.size);
break;
}
os_condition_variable_wait(di_shared->u2p_ring_cv, di_shared->u2p_ring_mutex, max_U64);
cond_var_wait(di_shared->u2p_ring_cv, di_shared->u2p_ring_mutex, max_U64);
}
os_condition_variable_broadcast(di_shared->u2p_ring_cv);
cond_var_broadcast(di_shared->u2p_ring_cv);
}
internal void
di_p2u_push_event(DI_Event *event)
{
OS_MutexScope(di_shared->p2u_ring_mutex) for(;;)
MutexScope(di_shared->p2u_ring_mutex) for(;;)
{
U64 unconsumed_size = (di_shared->p2u_ring_write_pos-di_shared->p2u_ring_read_pos);
U64 available_size = di_shared->p2u_ring_size-unconsumed_size;
@@ -813,16 +813,16 @@ di_p2u_push_event(DI_Event *event)
di_shared->p2u_ring_write_pos += ring_write(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_write_pos, event->string.str, event->string.size);
break;
}
os_condition_variable_wait(di_shared->p2u_ring_cv, di_shared->p2u_ring_mutex, max_U64);
cond_var_wait(di_shared->p2u_ring_cv, di_shared->p2u_ring_mutex, max_U64);
}
os_condition_variable_broadcast(di_shared->p2u_ring_cv);
cond_var_broadcast(di_shared->p2u_ring_cv);
}
internal DI_EventList
di_p2u_pop_events(Arena *arena, U64 endt_us)
{
DI_EventList events = {0};
OS_MutexScope(di_shared->p2u_ring_mutex) for(;;)
MutexScope(di_shared->p2u_ring_mutex) for(;;)
{
U64 unconsumed_size = (di_shared->p2u_ring_write_pos-di_shared->p2u_ring_read_pos);
if(unconsumed_size >= sizeof(DI_EventKind) + sizeof(U64))
@@ -839,9 +839,9 @@ di_p2u_pop_events(Arena *arena, U64 endt_us)
{
break;
}
os_condition_variable_wait(di_shared->p2u_ring_cv, di_shared->p2u_ring_mutex, endt_us);
cond_var_wait(di_shared->p2u_ring_cv, di_shared->p2u_ring_mutex, endt_us);
}
os_condition_variable_broadcast(di_shared->p2u_ring_cv);
cond_var_broadcast(di_shared->p2u_ring_cv);
return events;
}
@@ -1040,7 +1040,7 @@ ASYNC_WORK_DEF(di_parse_work)
U64 start_wait_t = os_now_microseconds();
for(;;)
{
B32 wait_done = os_process_join(process, os_now_microseconds()+1000);
B32 wait_done = os_process_join(process, os_now_microseconds()+1000, 0);
if(wait_done)
{
rdi_file_is_up_to_date = 1;
@@ -1111,7 +1111,7 @@ ASYNC_WORK_DEF(di_parse_work)
////////////////////////////
//- rjf: commit parsed info to cache
//
OS_MutexScopeW(stripe->rw_mutex)
MutexScopeW(stripe->rw_mutex)
{
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key);
if(node != 0)
@@ -1136,7 +1136,7 @@ ASYNC_WORK_DEF(di_parse_work)
os_file_close(file);
}
}
os_condition_variable_broadcast(stripe->cv);
cond_var_broadcast(stripe->cv);
scratch_end(scratch);
ProfEnd();
@@ -1153,7 +1153,7 @@ di_u2s_enqueue_req(U128 key, U64 endt_us)
B32 result = 0;
U64 thread_idx = key.u64[0]%di_shared->search_threads_count;
DI_SearchThread *thread = &di_shared->search_threads[thread_idx];
OS_MutexScope(thread->ring_mutex) for(;;)
MutexScope(thread->ring_mutex) for(;;)
{
U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos;
U64 available_size = thread->ring_size - unconsumed_size;
@@ -1167,11 +1167,11 @@ di_u2s_enqueue_req(U128 key, U64 endt_us)
{
break;
}
os_condition_variable_wait(thread->ring_cv, thread->ring_mutex, endt_us);
cond_var_wait(thread->ring_cv, thread->ring_mutex, endt_us);
}
if(result)
{
os_condition_variable_broadcast(thread->ring_cv);
cond_var_broadcast(thread->ring_cv);
}
return result;
}
@@ -1181,7 +1181,7 @@ di_u2s_dequeue_req(U64 thread_idx)
{
U128 key = {0};
DI_SearchThread *thread = &di_shared->search_threads[thread_idx];
OS_MutexScope(thread->ring_mutex) for(;;)
MutexScope(thread->ring_mutex) for(;;)
{
U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos;
if(unconsumed_size >= sizeof(key))
@@ -1189,9 +1189,9 @@ di_u2s_dequeue_req(U64 thread_idx)
thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &key);
break;
}
os_condition_variable_wait(thread->ring_cv, thread->ring_mutex, max_U64);
cond_var_wait(thread->ring_cv, thread->ring_mutex, max_U64);
}
os_condition_variable_broadcast(thread->ring_cv);
cond_var_broadcast(thread->ring_cv);
return key;
}
@@ -1272,7 +1272,7 @@ ASYNC_WORK_DEF(di_search_work)
//- rjf: every so often, check the key's write gen - if it has been bumped, then cancel
if(idx%100 == 0)
{
OS_MutexScopeR(stripe->rw_mutex)
MutexScopeR(stripe->rw_mutex)
{
for(DI_SearchNode *n = slot->first; n != 0; n = n->next)
{
@@ -1385,7 +1385,7 @@ internal void
di_search_thread__entry_point(void *p)
{
U64 thread_idx = (U64)p;
ThreadNameF("[di] search thread #%I64u", thread_idx);
ThreadNameF("di_search_thread_%I64u", thread_idx);
for(;;)
{
Temp scratch = scratch_begin(0, 0);
@@ -1402,7 +1402,7 @@ di_search_thread__entry_point(void *p)
String8 query = {0};
DI_SearchParams params = {0};
U64 initial_bucket_write_gen = 0;
OS_MutexScopeW(stripe->rw_mutex)
MutexScopeW(stripe->rw_mutex)
{
for(DI_SearchNode *n = slot->first; n != 0; n = n->next)
{
@@ -1512,7 +1512,7 @@ di_search_thread__entry_point(void *p)
//- rjf: commit to cache - wait on scope touches
if(arena != 0)
{
OS_MutexScopeW(stripe->rw_mutex) for(;;)
MutexScopeW(stripe->rw_mutex) for(;;)
{
B32 found = 0;
B32 done = 0;
@@ -1541,7 +1541,7 @@ di_search_thread__entry_point(void *p)
}
if(found && !done)
{
os_condition_variable_wait_rw_w(stripe->cv, stripe->rw_mutex, os_now_microseconds()+1000);
cond_var_wait_rw_w(stripe->cv, stripe->rw_mutex, os_now_microseconds()+1000);
}
}
}
@@ -1553,7 +1553,7 @@ di_search_thread__entry_point(void *p)
internal void
di_search_evictor_thread__entry_point(void *p)
{
ThreadNameF("[di] search evictor thread");
ThreadNameF("di_search_evictor_thread");
for(;;)
{
for(U64 slot_idx = 0; slot_idx < di_shared->search_slots_count; slot_idx += 1)
@@ -1562,7 +1562,7 @@ di_search_evictor_thread__entry_point(void *p)
DI_SearchSlot *slot = &di_shared->search_slots[slot_idx];
DI_SearchStripe *stripe = &di_shared->search_stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
MutexScopeR(stripe->rw_mutex)
{
for(DI_SearchNode *n = slot->first; n != 0; n = n->next)
{
@@ -1573,7 +1573,7 @@ di_search_evictor_thread__entry_point(void *p)
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
if(slot_has_work) MutexScopeW(stripe->rw_mutex)
{
for(DI_SearchNode *n = slot->first, *next = 0; n != 0; n = next)
{
@@ -1610,17 +1610,17 @@ di_match_store_alloc(void)
store->gen_arenas[idx] = arena_alloc();
}
store->params_arena = arena_alloc();
store->params_rw_mutex = os_rw_mutex_alloc();
store->params_rw_mutex = rw_mutex_alloc();
store->match_name_slots_count = 4096;
store->match_name_slots = push_array(arena, DI_MatchNameSlot, store->match_name_slots_count);
store->match_rw_mutex = os_rw_mutex_alloc();
store->match_cv = os_condition_variable_alloc();
store->u2m_ring_cv = os_condition_variable_alloc();
store->u2m_ring_mutex = os_mutex_alloc();
store->match_rw_mutex = rw_mutex_alloc();
store->match_cv = cond_var_alloc();
store->u2m_ring_cv = cond_var_alloc();
store->u2m_ring_mutex = mutex_alloc();
store->u2m_ring_size = KB(2);
store->u2m_ring_base = push_array_no_zero(arena, U8, store->u2m_ring_size);
store->m2u_ring_cv = os_condition_variable_alloc();
store->m2u_ring_mutex = os_mutex_alloc();
store->m2u_ring_cv = cond_var_alloc();
store->m2u_ring_mutex = mutex_alloc();
store->m2u_ring_size = KB(2);
store->m2u_ring_base = push_array_no_zero(arena, U8, store->m2u_ring_size);
return store;
@@ -1642,7 +1642,7 @@ di_match_store_begin(DI_MatchStore *store, DI_KeyArray keys)
}
// rjf: store parameters if needed
if(store->params_hash != params_hash) OS_MutexScopeW(store->params_rw_mutex)
if(store->params_hash != params_hash) MutexScopeW(store->params_rw_mutex)
{
arena_clear(store->params_arena);
store->params_hash = params_hash;
@@ -1738,7 +1738,7 @@ di_match_from_name(DI_MatchStore *store, String8 name, U64 endt_us)
if(completed_params_hash != store->params_hash && node->req_count == ins_atomic_u64_eval(&node->cmp_count))
{
B32 sent = 0;
OS_MutexScope(store->u2m_ring_mutex) for(;;)
MutexScope(store->u2m_ring_mutex) for(;;)
{
U64 unconsumed_size = store->u2m_ring_write_pos - store->u2m_ring_read_pos;
U64 available_size = store->u2m_ring_size - unconsumed_size;
@@ -1756,11 +1756,11 @@ di_match_from_name(DI_MatchStore *store, String8 name, U64 endt_us)
{
break;
}
os_condition_variable_wait(store->u2m_ring_cv, store->u2m_ring_mutex, endt_us);
cond_var_wait(store->u2m_ring_cv, store->u2m_ring_mutex, endt_us);
}
if(sent)
{
os_condition_variable_broadcast(store->u2m_ring_cv);
cond_var_broadcast(store->u2m_ring_cv);
async_push_work(di_match_work, .input = store, .priority = ASYNC_Priority_Low, .completion_counter = &node->cmp_count);
node->req_params_hash = store->params_hash;
node->req_count += 1;
@@ -1770,7 +1770,7 @@ di_match_from_name(DI_MatchStore *store, String8 name, U64 endt_us)
// rjf: if this node's state is stale, wait for it if we need to
if(os_now_microseconds() < endt_us && node->req_params_hash != completed_params_hash)
{
OS_MutexScopeR(store->match_rw_mutex) for(;;)
MutexScopeR(store->match_rw_mutex) for(;;)
{
if(node->req_params_hash == ins_atomic_u64_eval(&node->cmp_params_hash))
{
@@ -1780,7 +1780,7 @@ di_match_from_name(DI_MatchStore *store, String8 name, U64 endt_us)
{
break;
}
os_condition_variable_wait_rw_r(store->match_cv, store->match_rw_mutex, endt_us);
cond_var_wait_rw_r(store->match_cv, store->match_rw_mutex, endt_us);
}
}
@@ -1805,7 +1805,7 @@ ASYNC_WORK_DEF(di_match_work)
DI_MatchNameNode *node = 0;
U64 alloc_gen = 0;
String8 name = {0};
ProfScope("get next name") OS_MutexScope(store->u2m_ring_mutex) for(;;)
ProfScope("get next name") MutexScope(store->u2m_ring_mutex) for(;;)
{
U64 unconsumed_size = store->u2m_ring_write_pos - store->u2m_ring_read_pos;
if(unconsumed_size >= sizeof(U64))
@@ -1817,14 +1817,14 @@ ASYNC_WORK_DEF(di_match_work)
store->u2m_ring_read_pos += ring_read(store->u2m_ring_base, store->u2m_ring_size, store->u2m_ring_read_pos, name.str, name.size);
break;
}
os_condition_variable_wait(store->u2m_ring_cv, store->u2m_ring_mutex, max_U64);
cond_var_wait(store->u2m_ring_cv, store->u2m_ring_mutex, max_U64);
}
os_condition_variable_broadcast(store->u2m_ring_cv);
cond_var_broadcast(store->u2m_ring_cv);
//- rjf: read parameters
U64 params_hash = 0;
DI_KeyArray params_keys = {0};
ProfScope("read parameters") OS_MutexScopeR(store->params_rw_mutex)
ProfScope("read parameters") MutexScopeR(store->params_rw_mutex)
{
params_keys = di_key_array_copy(scratch.arena, &store->params_keys);
params_hash = store->params_hash;
@@ -1873,7 +1873,7 @@ ASYNC_WORK_DEF(di_match_work)
// rjf: atomically update the node's primary match
ins_atomic_u64_eval_assign(&node->primary_match.dbgi_idx, dbgi_idx);
ins_atomic_u32_eval_assign(&node->primary_match.section, name_map_section_kinds[name_map_kind_idx]);
ins_atomic_u32_eval_assign(&node->primary_match.idx, run[0]);
ins_atomic_u32_eval_assign(&node->primary_match.idx, run[num-1]);
// rjf: gather all alternate matches
for(U32 match_idx = 1; match_idx < num; match_idx += 1)
+22 -22
View File
@@ -1,8 +1,8 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DBGI_H
#define DBGI_H
#ifndef DBG_INFO_H
#define DBG_INFO_H
////////////////////////////////
//~ rjf: Cache Key Type
@@ -121,8 +121,8 @@ struct DI_Stripe
Arena *arena;
DI_Node *free_node;
DI_StringChunkNode *free_string_chunks[8];
OS_Handle rw_mutex;
OS_Handle cv;
RWMutex rw_mutex;
CondVar cv;
};
////////////////////////////////
@@ -206,8 +206,8 @@ struct DI_SearchStripe
{
Arena *arena;
DI_SearchNode *free_node;
OS_Handle rw_mutex;
OS_Handle cv;
RWMutex rw_mutex;
CondVar cv;
};
////////////////////////////////
@@ -248,9 +248,9 @@ struct DI_TCTX
typedef struct DI_SearchThread DI_SearchThread;
struct DI_SearchThread
{
OS_Handle thread;
OS_Handle ring_mutex;
OS_Handle ring_cv;
Thread thread;
Mutex ring_mutex;
CondVar ring_cv;
U64 ring_size;
U8 *ring_base;
U64 ring_write_pos;
@@ -315,7 +315,7 @@ struct DI_MatchStore
// rjf: parameters
Arena *params_arena;
OS_Handle params_rw_mutex;
RWMutex params_rw_mutex;
U64 params_hash;
DI_KeyArray params_keys;
@@ -327,20 +327,20 @@ struct DI_MatchStore
DI_MatchNameNode *first_lru_match_name;
DI_MatchNameNode *last_lru_match_name;
U64 active_match_name_nodes_count;
OS_Handle match_rw_mutex;
OS_Handle match_cv;
RWMutex match_rw_mutex;
CondVar match_cv;
// rjf: user -> match work ring buffer
OS_Handle u2m_ring_cv;
OS_Handle u2m_ring_mutex;
CondVar u2m_ring_cv;
Mutex u2m_ring_mutex;
U64 u2m_ring_size;
U8 *u2m_ring_base;
U64 u2m_ring_write_pos;
U64 u2m_ring_read_pos;
// rjf: match -> user work ring buffer
OS_Handle m2u_ring_cv;
OS_Handle m2u_ring_mutex;
CondVar m2u_ring_cv;
Mutex m2u_ring_mutex;
U64 m2u_ring_size;
U8 *m2u_ring_base;
U64 m2u_ring_write_pos;
@@ -368,16 +368,16 @@ struct DI_Shared
DI_SearchStripe *search_stripes;
// rjf: user -> parse ring
OS_Handle u2p_ring_mutex;
OS_Handle u2p_ring_cv;
Mutex u2p_ring_mutex;
CondVar u2p_ring_cv;
U64 u2p_ring_size;
U8 *u2p_ring_base;
U64 u2p_ring_write_pos;
U64 u2p_ring_read_pos;
// rjf: parse -> user event ring
OS_Handle p2u_ring_mutex;
OS_Handle p2u_ring_cv;
Mutex p2u_ring_mutex;
CondVar p2u_ring_cv;
U64 p2u_ring_size;
U8 *p2u_ring_base;
U64 p2u_ring_write_pos;
@@ -386,7 +386,7 @@ struct DI_Shared
// rjf: search threads
U64 search_threads_count;
DI_SearchThread *search_threads;
OS_Handle search_evictor_thread;
Thread search_evictor_thread;
};
////////////////////////////////
@@ -486,4 +486,4 @@ internal void di_match_store_begin(DI_MatchStore *store, DI_KeyArray keys);
internal DI_Match di_match_from_name(DI_MatchStore *store, String8 name, U64 endt_us);
ASYNC_WORK_DEF(di_match_work);
#endif // DBGI_H
#endif // DBG_INFO_H
File diff suppressed because it is too large Load Diff
+281
View File
@@ -0,0 +1,281 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DBG_INFO2_H
#define DBG_INFO2_H
////////////////////////////////
//~ rjf: Unique Debug Info Key
typedef struct DI2_Key DI2_Key;
struct DI2_Key
{
U64 u64[2];
};
typedef struct DI2_KeyNode DI2_KeyNode;
struct DI2_KeyNode
{
DI2_KeyNode *next;
DI2_Key v;
};
typedef struct DI2_KeyList DI2_KeyList;
struct DI2_KeyList
{
DI2_KeyNode *first;
DI2_KeyNode *last;
U64 count;
};
typedef struct DI2_KeyArray DI2_KeyArray;
struct DI2_KeyArray
{
DI2_Key *v;
U64 count;
};
////////////////////////////////
//~ rjf: Debug Info Path / Timestamp => Key Cache Types
typedef struct DI2_KeyPathNode DI2_KeyPathNode;
struct DI2_KeyPathNode
{
DI2_KeyPathNode *next;
DI2_KeyPathNode *prev;
String8 path;
U64 min_timestamp;
DI2_Key key;
};
typedef struct DI2_KeySlot DI2_KeySlot;
struct DI2_KeySlot
{
DI2_KeyPathNode *first;
DI2_KeyPathNode *last;
};
////////////////////////////////
//~ rjf: Debug Info Cache Types
typedef struct DI2_Node DI2_Node;
struct DI2_Node
{
// rjf: links
DI2_Node *next;
DI2_Node *prev;
// rjf: key
DI2_Key key;
// rjf: value
OS_Handle file;
OS_Handle file_map;
void *file_base;
FileProperties file_props;
Arena *arena;
RDI_Parsed rdi;
// rjf: metadata
AccessPt access_pt;
U64 refcount;
U64 batch_request_counts[2];
U64 working_count;
U64 completion_count;
};
typedef struct DI2_Slot DI2_Slot;
struct DI2_Slot
{
DI2_Node *first;
DI2_Node *last;
};
////////////////////////////////
//~ rjf: Requests
typedef struct DI2_Request DI2_Request;
struct DI2_Request
{
DI2_Key key;
};
typedef struct DI2_RequestNode DI2_RequestNode;
struct DI2_RequestNode
{
DI2_RequestNode *next;
DI2_Request v;
};
typedef struct DI2_RequestBatch DI2_RequestBatch;
struct DI2_RequestBatch
{
Mutex mutex;
Arena *arena;
DI2_RequestNode *first;
DI2_RequestNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Load Tasks
typedef enum DI2_LoadTaskStatus
{
DI2_LoadTaskStatus_Null,
DI2_LoadTaskStatus_Active,
DI2_LoadTaskStatus_Done,
}
DI2_LoadTaskStatus;
typedef struct DI2_LoadTask DI2_LoadTask;
struct DI2_LoadTask
{
DI2_LoadTask *next;
DI2_LoadTask *prev;
DI2_Key key;
DI2_LoadTaskStatus status;
B32 og_analyzed;
B32 og_is_rdi;
U64 og_size;
B32 rdi_analyzed;
B32 rdi_is_stale;
U64 thread_count;
OS_Handle process;
};
////////////////////////////////
//~ rjf: Search Types
typedef struct DI2_SearchItem DI2_SearchItem;
struct DI2_SearchItem
{
U64 idx;
DI2_Key key;
U64 missed_size;
FuzzyMatchRangeList match_ranges;
};
typedef struct DI2_SearchItemChunk DI2_SearchItemChunk;
struct DI2_SearchItemChunk
{
DI2_SearchItemChunk *next;
U64 base_idx;
DI2_SearchItem *v;
U64 count;
U64 cap;
};
typedef struct DI2_SearchItemChunkList DI2_SearchItemChunkList;
struct DI2_SearchItemChunkList
{
DI2_SearchItemChunk *first;
DI2_SearchItemChunk *last;
U64 chunk_count;
U64 total_count;
};
typedef struct DI2_SearchItemArray DI2_SearchItemArray;
struct DI2_SearchItemArray
{
DI2_SearchItem *v;
U64 count;
};
////////////////////////////////
//~ rjf: Shared State
typedef struct DI2_Shared DI2_Shared;
struct DI2_Shared
{
Arena *arena;
U64 load_gen;
// rjf: key -> path cache
U64 key2path_slots_count;
DI2_KeySlot *key2path_slots;
StripeArray key2path_stripes;
// rjf: path -> key cache
U64 path2key_slots_count;
DI2_KeySlot *path2key_slots;
StripeArray path2key_stripes;
// rjf: debug info cache
U64 slots_count;
DI2_Slot *slots;
StripeArray stripes;
// rjf: requests
DI2_RequestBatch req_batches[2]; // [0] -> high priority, [1] -> low priority
// rjf: conversion tasks
DI2_LoadTask *first_load_task;
DI2_LoadTask *last_load_task;
DI2_LoadTask *free_load_task;
U64 conversion_process_count;
U64 conversion_thread_count;
// rjf: conversion completion receiving thread
String8 conversion_completion_signal_semaphore_name;
Semaphore conversion_completion_signal_semaphore;
Thread conversion_completion_signal_receiver_thread;
};
////////////////////////////////
//~ rjf: Globals
global DI2_Shared *di2_shared = 0;
////////////////////////////////
//~ rjf: Helpers
internal DI2_Key di2_key_zero(void);
internal B32 di2_key_match(DI2_Key a, DI2_Key b);
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void di2_init(CmdLine *cmdline);
////////////////////////////////
//~ rjf: Path * Timestamp Cache Submission & Lookup
internal DI2_Key di2_key_from_path_timestamp(String8 path, U64 min_timestamp);
////////////////////////////////
//~ rjf: Debug Info Opening / Closing
internal void di2_open(DI2_Key key);
internal void di2_close(DI2_Key key);
////////////////////////////////
//~ rjf: Debug Info Lookups
internal U64 di2_load_gen(void);
internal DI2_KeyArray di2_push_all_loaded_keys(Arena *arena);
internal RDI_Parsed *di2_rdi_from_key(Access *access, DI2_Key key, B32 high_priority, U64 endt_us);
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void di2_async_tick(void);
////////////////////////////////
//~ rjf: Conversion Completion Signal Receiver Thread
internal void di2_signal_completion(void);
internal void di2_conversion_completion_signal_receiver_thread_entry_point(void *p);
////////////////////////////////
//~ rjf: Search Artifact Cache Hooks / Lookups
internal AC_Artifact di2_search_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out);
internal void di2_search_artifact_destroy(AC_Artifact artifact);
internal DI2_SearchItemArray di2_search_item_array_from_target_query(Access *access, RDI_SectionKind target, String8 query, U64 endt_us);
#endif // DBG_INFO2_H
+5 -5
View File
@@ -905,7 +905,7 @@ dmn_init(void)
dmn_lnx_state->entities_arena = arena_alloc(.reserve_size = GB(32), .commit_size = KB(64), .flags = ArenaFlag_NoChain);
dmn_lnx_state->entities_base = push_array(dmn_lnx_state->entities_arena, DMN_LNX_Entity, 0);
dmn_lnx_entity_alloc(&dmn_lnx_nil_entity, DMN_LNX_EntityKind_Root);
dmn_lnx_state->access_mutex = os_mutex_alloc();
dmn_lnx_state->access_mutex = mutex_alloc();
}
////////////////////////////////
@@ -922,7 +922,7 @@ dmn_ctrl_begin(void)
internal void
dmn_ctrl_exclusive_access_begin(void)
{
OS_MutexScope(dmn_lnx_state->access_mutex)
MutexScope(dmn_lnx_state->access_mutex)
{
dmn_lnx_state->access_run_state = 1;
}
@@ -931,7 +931,7 @@ dmn_ctrl_exclusive_access_begin(void)
internal void
dmn_ctrl_exclusive_access_end(void)
{
OS_MutexScope(dmn_lnx_state->access_mutex)
MutexScope(dmn_lnx_state->access_mutex)
{
dmn_lnx_state->access_run_state = 0;
}
@@ -1649,7 +1649,7 @@ dmn_access_open(void)
}
else
{
os_mutex_take(dmn_lnx_state->access_mutex);
mutex_take(dmn_lnx_state->access_mutex);
result = !dmn_lnx_state->access_run_state;
}
return result;
@@ -1660,7 +1660,7 @@ dmn_access_close(void)
{
if(!dmn_lnx_ctrl_thread)
{
os_mutex_drop(dmn_lnx_state->access_mutex);
mutex_drop(dmn_lnx_state->access_mutex);
}
}
+26 -7
View File
@@ -690,7 +690,7 @@ dmn_w32_thread_read_reg_block(Arch arch, HANDLE thread, void *reg_block)
//- rjf: unpack info about available features
U32 feature_mask = GetEnabledXStateFeatures();
B32 xstate_enabled = (feature_mask & (XSTATE_MASK_AVX | XSTATE_MASK_AVX512)) != 0;
B32 xstate_enabled = (feature_mask & (XSTATE_MASK_AVX | XSTATE_MASK_AVX512 | XSTATE_MASK_CET_U)) != 0;
//- rjf: set up context
CONTEXT *ctx = 0;
@@ -709,7 +709,7 @@ dmn_w32_thread_read_reg_block(Arch arch, HANDLE thread, void *reg_block)
//- rjf: unpack features available on this context
if (xstate_enabled)
{
SetXStateFeaturesMask(ctx, XSTATE_MASK_AVX | XSTATE_MASK_AVX512);
SetXStateFeaturesMask(ctx, XSTATE_MASK_AVX | XSTATE_MASK_AVX512 | XSTATE_MASK_CET_U);
}
//- rjf: get thread context
@@ -867,6 +867,18 @@ dmn_w32_thread_read_reg_block(Arch arch, HANDLE thread, void *reg_block)
}
}
// CET / Shadow Stack
if(xstate_mask & XSTATE_MASK_CET_U)
{
DWORD cet_length = 0;
XSAVE_CET_U_FORMAT *cet = LocateXStateFeature(ctx, XSTATE_CET_U, &cet_length);
if (cet_length == sizeof(*cet))
{
dst->cetmsr.u64 = cet->Ia32CetUMsr;
dst->cetssp.u64 = cet->Ia32Pl3SspMsr;
}
}
scratch_end(scratch);
}break;
}
@@ -1142,7 +1154,7 @@ dmn_init(void)
Arena *arena = arena_alloc();
dmn_w32_shared = push_array(arena, DMN_W32_Shared, 1);
dmn_w32_shared->arena = arena;
dmn_w32_shared->access_mutex = os_mutex_alloc();
dmn_w32_shared->access_mutex = mutex_alloc();
dmn_w32_shared->detach_arena = arena_alloc();
dmn_w32_shared->entities_arena = arena_alloc(.reserve_size = GB(8), .commit_size = KB(64));
dmn_w32_shared->entities_base = dmn_w32_entity_alloc(&dmn_w32_entity_nil, DMN_W32_EntityKind_Root, 0);
@@ -1192,7 +1204,7 @@ dmn_ctrl_begin(void)
internal void
dmn_ctrl_exclusive_access_begin(void)
{
OS_MutexScope(dmn_w32_shared->access_mutex)
MutexScope(dmn_w32_shared->access_mutex)
{
dmn_w32_shared->access_run_state = 1;
}
@@ -1201,7 +1213,7 @@ dmn_ctrl_exclusive_access_begin(void)
internal void
dmn_ctrl_exclusive_access_end(void)
{
OS_MutexScope(dmn_w32_shared->access_mutex)
MutexScope(dmn_w32_shared->access_mutex)
{
dmn_w32_shared->access_run_state = 0;
}
@@ -2404,6 +2416,13 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
case DMN_W32_EXCEPTION_STACK_BUFFER_OVERRUN:
{
e->kind = DMN_EventKind_Trap;
if(exception->ExceptionInformation[0] == DMN_W32_FAST_FAIL_CONTROL_INVALID_RETURN_ADDRESS)
{
// TODO(rjf): this is a shadow stack violation - this can imply that the spoof was hit.
// need to handle this correctly in the ctrl layer when stepping w/ a spoof set.
//
// @shadow_stack_step
}
}break;
//- rjf: fill single-step event info
@@ -2977,7 +2996,7 @@ dmn_access_open(void)
}
else
{
os_mutex_take(dmn_w32_shared->access_mutex);
mutex_take(dmn_w32_shared->access_mutex);
result = !dmn_w32_shared->access_run_state;
}
return result;
@@ -2988,7 +3007,7 @@ dmn_access_close(void)
{
if(!dmn_w32_ctrl_thread)
{
os_mutex_drop(dmn_w32_shared->access_mutex);
mutex_drop(dmn_w32_shared->access_mutex);
}
}
+77 -1
View File
@@ -60,6 +60,82 @@
#define DMN_W32_EXCEPTION_RADDBG_SET_BREAKPOINT 0x00524145u
#define DMN_W32_EXCEPTION_RADDBG_SET_VADDR_RANGE_NOTE 0x00524156u
////////////////////////////////
//~ rjf: Win32 Exception ExceptionInformation Codes
//
// used as a subcode, apparently in all cases, for DMN_W32_EXCEPTION_STACK_BUFFER_OVERRUN.
// need to somehow pipe this through & interpret it correctly in outer layers... @fastfail
#define DMN_W32_FAST_FAIL_LEGACY_GS_VIOLATION 0
#define DMN_W32_FAST_FAIL_VTGUARD_CHECK_FAILURE 1
#define DMN_W32_FAST_FAIL_STACK_COOKIE_CHECK_FAILURE 2
#define DMN_W32_FAST_FAIL_CORRUPT_LIST_ENTRY 3
#define DMN_W32_FAST_FAIL_INCORRECT_STACK 4
#define DMN_W32_FAST_FAIL_INVALID_ARG 5
#define DMN_W32_FAST_FAIL_GS_COOKIE_INIT 6
#define DMN_W32_FAST_FAIL_FATAL_APP_EXIT 7
#define DMN_W32_FAST_FAIL_RANGE_CHECK_FAILURE 8
#define DMN_W32_FAST_FAIL_UNSAFE_REGISTRY_ACCESS 9
#define DMN_W32_FAST_FAIL_GUARD_ICALL_CHECK_FAILURE 10
#define DMN_W32_FAST_FAIL_GUARD_WRITE_CHECK_FAILURE 11
#define DMN_W32_FAST_FAIL_INVALID_FIBER_SWITCH 12
#define DMN_W32_FAST_FAIL_INVALID_SET_OF_CONTEXT 13
#define DMN_W32_FAST_FAIL_INVALID_REFERENCE_COUNT 14
#define DMN_W32_FAST_FAIL_INVALID_JUMP_BUFFER 18
#define DMN_W32_FAST_FAIL_MRDATA_MODIFIED 19
#define DMN_W32_FAST_FAIL_CERTIFICATION_FAILURE 20
#define DMN_W32_FAST_FAIL_INVALID_EXCEPTION_CHAIN 21
#define DMN_W32_FAST_FAIL_CRYPTO_LIBRARY 22
#define DMN_W32_FAST_FAIL_INVALID_CALL_IN_DLL_CALLOUT 23
#define DMN_W32_FAST_FAIL_INVALID_IMAGE_BASE 24
#define DMN_W32_FAST_FAIL_DLOAD_PROTECTION_FAILURE 25
#define DMN_W32_FAST_FAIL_UNSAFE_EXTENSION_CALL 26
#define DMN_W32_FAST_FAIL_DEPRECATED_SERVICE_INVOKED 27
#define DMN_W32_FAST_FAIL_INVALID_BUFFER_ACCESS 28
#define DMN_W32_FAST_FAIL_INVALID_BALANCED_TREE 29
#define DMN_W32_FAST_FAIL_INVALID_NEXT_THREAD 30
#define DMN_W32_FAST_FAIL_GUARD_ICALL_CHECK_SUPPRESSED 31 // Telemetry, nonfatal
#define DMN_W32_FAST_FAIL_APCS_DISABLED 32
#define DMN_W32_FAST_FAIL_INVALID_IDLE_STATE 33
#define DMN_W32_FAST_FAIL_MRDATA_PROTECTION_FAILURE 34
#define DMN_W32_FAST_FAIL_UNEXPECTED_HEAP_EXCEPTION 35
#define DMN_W32_FAST_FAIL_INVALID_LOCK_STATE 36
#define DMN_W32_FAST_FAIL_GUARD_JUMPTABLE 37 // Known to compiler, must retain value 37
#define DMN_W32_FAST_FAIL_INVALID_LONGJUMP_TARGET 38
#define DMN_W32_FAST_FAIL_INVALID_DISPATCH_CONTEXT 39
#define DMN_W32_FAST_FAIL_INVALID_THREAD 40
#define DMN_W32_FAST_FAIL_INVALID_SYSCALL_NUMBER 41 // Telemetry, nonfatal
#define DMN_W32_FAST_FAIL_INVALID_FILE_OPERATION 42 // Telemetry, nonfatal
#define DMN_W32_FAST_FAIL_LPAC_ACCESS_DENIED 43 // Telemetry, nonfatal
#define DMN_W32_FAST_FAIL_GUARD_SS_FAILURE 44
#define DMN_W32_FAST_FAIL_LOADER_CONTINUITY_FAILURE 45 // Telemetry, nonfatal
#define DMN_W32_FAST_FAIL_GUARD_EXPORT_SUPPRESSION_FAILURE 46
#define DMN_W32_FAST_FAIL_INVALID_CONTROL_STACK 47
#define DMN_W32_FAST_FAIL_SET_CONTEXT_DENIED 48
#define DMN_W32_FAST_FAIL_INVALID_IAT 49
#define DMN_W32_FAST_FAIL_HEAP_METADATA_CORRUPTION 50
#define DMN_W32_FAST_FAIL_PAYLOAD_RESTRICTION_VIOLATION 51
#define DMN_W32_FAST_FAIL_LOW_LABEL_ACCESS_DENIED 52 // Telemetry, nonfatal
#define DMN_W32_FAST_FAIL_ENCLAVE_CALL_FAILURE 53
#define DMN_W32_FAST_FAIL_UNHANDLED_LSS_EXCEPTON 54
#define DMN_W32_FAST_FAIL_ADMINLESS_ACCESS_DENIED 55 // Telemetry, nonfatal
#define DMN_W32_FAST_FAIL_UNEXPECTED_CALL 56
#define DMN_W32_FAST_FAIL_CONTROL_INVALID_RETURN_ADDRESS 57
#define DMN_W32_FAST_FAIL_UNEXPECTED_HOST_BEHAVIOR 58
#define DMN_W32_FAST_FAIL_FLAGS_CORRUPTION 59
#define DMN_W32_FAST_FAIL_VEH_CORRUPTION 60
#define DMN_W32_FAST_FAIL_ETW_CORRUPTION 61
#define DMN_W32_FAST_FAIL_RIO_ABORT 62
#define DMN_W32_FAST_FAIL_INVALID_PFN 63
#define DMN_W32_FAST_FAIL_GUARD_ICALL_CHECK_FAILURE_XFG 64
#define DMN_W32_FAST_FAIL_CAST_GUARD 65 // Known to compiler, must retain value 65
#define DMN_W32_FAST_FAIL_HOST_VISIBILITY_CHANGE 66
#define DMN_W32_FAST_FAIL_KERNEL_CET_SHADOW_STACK_ASSIST 67
#define DMN_W32_FAST_FAIL_PATCH_CALLBACK_FAILED 68
#define DMN_W32_FAST_FAIL_NTDLL_PATCH_FAILED 69
#define DMN_W32_FAST_FAIL_INVALID_FLS_DATA 70
#define DMN_W32_FAST_FAIL_INVALID_FAST_FAIL_CODE 0xFFFFFFFF
////////////////////////////////
//~ rjf: Win32 Register Codes
@@ -197,7 +273,7 @@ struct DMN_W32_Shared
String8List env_strings;
// rjf: access locking mechanism
OS_Handle access_mutex;
Mutex access_mutex;
B32 access_run_state;
// rjf: detaching info
+551
View File
@@ -0,0 +1,551 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#undef LAYER_COLOR
#define LAYER_COLOR 0xe34cd4ff
////////////////////////////////
//~ rjf: Instruction Decoding/Disassembling Type Functions
#if !defined(ZYDIS_H)
#include "third_party/zydis/zydis.h"
#include "third_party/zydis/zydis.c"
#endif
internal DASM_Inst
dasm_inst_from_code(Arena *arena, Arch arch, U64 vaddr, String8 code, DASM_Syntax syntax)
{
DASM_Inst inst = {0};
switch(arch)
{
default:{}break;
//- rjf: x86/x64 disassembly
case Arch_x86:
case Arch_x64:
{
// rjf: determine zydis formatter style
ZydisFormatterStyle style = ZYDIS_FORMATTER_STYLE_INTEL;
switch(syntax)
{
default:{}break;
case DASM_Syntax_Intel:{style = ZYDIS_FORMATTER_STYLE_INTEL;}break;
case DASM_Syntax_ATT: {style = ZYDIS_FORMATTER_STYLE_ATT;}break;
}
// rjf: disassemble one instruction
ZydisDisassembledInstruction zinst = {0};
ZyanStatus status = ZydisDisassemble(ZYDIS_MACHINE_MODE_LONG_64, vaddr, code.str, code.size, &zinst, style);
// rjf: analyze
DASM_InstFlags flags = 0;
U64 jump_dest_vaddr = 0;
{
ZydisDecodedOperand *first_visible_op = (zinst.info.operand_count_visible > 0 ? &zinst.operands[0] : 0);
ZydisDecodedOperand *first_op = (zinst.info.operand_count > 0 ? &zinst.operands[0] : 0);
ZydisDecodedOperand *second_op = (zinst.info.operand_count > 1 ? &zinst.operands[1] : 0);
if(first_visible_op != 0 &&
(first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM8 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM32 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM64 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16_32_64 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM32_32_64 ||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16_32_32))
{
ZydisCalcAbsoluteAddress(&zinst.info, first_visible_op, vaddr, &jump_dest_vaddr);
}
if(first_op != 0 && second_op != 0 && first_op->type == ZYDIS_OPERAND_TYPE_REGISTER &&
(first_op->reg.value == ZYDIS_REGISTER_RSP ||
first_op->reg.value == ZYDIS_REGISTER_ESP ||
first_op->reg.value == ZYDIS_REGISTER_SP))
{
flags |= DASM_InstFlag_ChangesStackPointer;
if(second_op->type != ZYDIS_OPERAND_TYPE_IMMEDIATE)
{
flags |= DASM_InstFlag_ChangesStackPointerVariably;
}
}
if(zinst.info.attributes & (ZYDIS_ATTRIB_HAS_REP|
ZYDIS_ATTRIB_HAS_REPE|
ZYDIS_ATTRIB_HAS_REPZ|
ZYDIS_ATTRIB_HAS_REPNZ|
ZYDIS_ATTRIB_HAS_REPNE))
{
flags |= DASM_InstFlag_Repeats;
}
switch(zinst.info.mnemonic)
{
case ZYDIS_MNEMONIC_CALL:
{
flags |= DASM_InstFlag_Call;
}break;
case ZYDIS_MNEMONIC_JB:
case ZYDIS_MNEMONIC_JBE:
case ZYDIS_MNEMONIC_JCXZ:
case ZYDIS_MNEMONIC_JECXZ:
case ZYDIS_MNEMONIC_JKNZD:
case ZYDIS_MNEMONIC_JKZD:
case ZYDIS_MNEMONIC_JL:
case ZYDIS_MNEMONIC_JLE:
case ZYDIS_MNEMONIC_JNB:
case ZYDIS_MNEMONIC_JNBE:
case ZYDIS_MNEMONIC_JNL:
case ZYDIS_MNEMONIC_JNLE:
case ZYDIS_MNEMONIC_JNO:
case ZYDIS_MNEMONIC_JNP:
case ZYDIS_MNEMONIC_JNS:
case ZYDIS_MNEMONIC_JNZ:
case ZYDIS_MNEMONIC_JO:
case ZYDIS_MNEMONIC_JP:
case ZYDIS_MNEMONIC_JRCXZ:
case ZYDIS_MNEMONIC_JS:
case ZYDIS_MNEMONIC_JZ:
case ZYDIS_MNEMONIC_LOOP:
case ZYDIS_MNEMONIC_LOOPE:
case ZYDIS_MNEMONIC_LOOPNE:
{
flags |= DASM_InstFlag_Branch;
}break;
case ZYDIS_MNEMONIC_JMP:
{
flags |= DASM_InstFlag_UnconditionalJump;
}break;
case ZYDIS_MNEMONIC_RET:
{
flags |= DASM_InstFlag_Return;
}break;
case ZYDIS_MNEMONIC_PUSH:
case ZYDIS_MNEMONIC_POP:
{
flags |= DASM_InstFlag_ChangesStackPointer;
}break;
default:
{
flags |= DASM_InstFlag_NonFlow;
}break;
}
}
// rjf: convert
{
inst.flags = flags;
inst.size = zinst.info.length;
inst.string = push_str8_copy(arena, str8_cstring(zinst.text));
inst.jump_dest_vaddr = jump_dest_vaddr;
}
}break;
}
return inst;
}
////////////////////////////////
//~ rjf: Control Flow Analysis
internal DASM_CtrlFlowInfo
dasm_ctrl_flow_info_from_arch_vaddr_code(Arena *arena, DASM_InstFlags exit_points_mask, Arch arch, U64 vaddr, String8 code)
{
Temp scratch = scratch_begin(&arena, 1);
DASM_CtrlFlowInfo info = {0};
for(U64 offset = 0; offset < code.size;)
{
DASM_Inst inst = dasm_inst_from_code(scratch.arena, arch, vaddr+offset, str8_skip(code, offset), DASM_Syntax_Intel);
U64 inst_vaddr = vaddr+offset;
offset += inst.size;
info.total_size += inst.size;
if(inst.flags & exit_points_mask)
{
DASM_CtrlFlowPoint point = {0};
point.inst_flags = inst.flags;
point.vaddr = inst_vaddr;
point.jump_dest_vaddr = inst.jump_dest_vaddr;
DASM_CtrlFlowPointNode *node = push_array(arena, DASM_CtrlFlowPointNode, 1);
node->v = point;
SLLQueuePush(info.exit_points.first, info.exit_points.last, node);
info.exit_points.count += 1;
}
}
scratch_end(scratch);
return info;
}
////////////////////////////////
//~ rjf: Parameter Type Functions
internal B32
dasm_params_match(DASM_Params *a, DASM_Params *b)
{
B32 result = (a->vaddr == b->vaddr &&
a->arch == b->arch &&
a->style_flags == b->style_flags &&
a->syntax == b->syntax &&
a->base_vaddr == b->base_vaddr &&
di_key_match(&a->dbgi_key, &b->dbgi_key));
return result;
}
////////////////////////////////
//~ rjf: Line Type Functions
internal void
dasm_line_chunk_list_push(Arena *arena, DASM_LineChunkList *list, U64 cap, DASM_Line *inst)
{
DASM_LineChunkNode *node = list->last;
if(node == 0 || node->count >= node->cap)
{
node = push_array(arena, DASM_LineChunkNode, 1);
node->v = push_array_no_zero(arena, DASM_Line, cap);
node->cap = cap;
SLLQueuePush(list->first, list->last, node);
list->node_count += 1;
}
MemoryCopyStruct(&node->v[node->count], inst);
node->count += 1;
list->line_count += 1;
}
internal DASM_LineArray
dasm_line_array_from_chunk_list(Arena *arena, DASM_LineChunkList *list)
{
DASM_LineArray array = {0};
array.count = list->line_count;
array.v = push_array_no_zero(arena, DASM_Line, array.count);
U64 idx = 0;
for(DASM_LineChunkNode *n = list->first; n != 0; n = n->next)
{
MemoryCopy(array.v+idx, n->v, sizeof(DASM_Line)*n->count);
idx += n->count;
}
return array;
}
internal U64
dasm_line_array_idx_from_code_off__linear_scan(DASM_LineArray *array, U64 off)
{
U64 result = 0;
for(U64 idx = 0; idx < array->count; idx += 1)
{
U64 next_off = (idx+1 < array->count ? array->v[idx+1].code_off : max_U64);
if(array->v[idx].code_off <= off && off < next_off)
{
result = idx;
if(!(array->v[idx].flags & DASM_LineFlag_Decorative))
{
break;
}
}
}
return result;
}
internal U64
dasm_line_array_code_off_from_idx(DASM_LineArray *array, U64 idx)
{
U64 off = 0;
if(idx < array->count)
{
off = array->v[idx].code_off;
}
return off;
}
////////////////////////////////
//~ rjf: Artifact Cache Hooks / Lookups
typedef struct DASM_Artifact DASM_Artifact;
struct DASM_Artifact
{
Arena *arena;
DASM_Info info;
U128 data_hash;
};
internal AC_Artifact
dasm_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out)
{
DASM_Artifact *artifact = 0;
if(lane_idx() == 0)
{
Temp scratch = scratch_begin(0, 0);
Access *access = access_open();
DI_Scope *di_scope = di_scope_open();
//- rjf: unpack key
U128 hash = {0};
DASM_Params params = {0};
U64 key_read_off = 0;
key_read_off += str8_deserial_read_struct(key, key_read_off, &hash);
key_read_off += str8_deserial_read_struct(key, key_read_off, &params);
params.dbgi_key.path.str = key.str + key_read_off;
String8 data = c_data_from_hash(access, hash);
//- rjf: get dbg info
B32 stale = 0;
RDI_Parsed *rdi = &rdi_parsed_nil;
if(params.dbgi_key.path.size != 0)
{
rdi = di_rdi_from_key(di_scope, &params.dbgi_key, 1, 0);
stale = (stale || (rdi == &rdi_parsed_nil));
}
//- rjf: data * arch * addr * dbg -> decode artifacts
DASM_LineChunkList line_list = {0};
String8List inst_strings = {0};
switch(params.arch)
{
default:{}break;
//- rjf: x86/x64 decoding
case Arch_x64:
case Arch_x86:
{
// rjf: disassemble
RDI_SourceFile *last_file = &rdi_nil_element_union.source_file;
RDI_Line *last_line = 0;
for(U64 off = 0; off < data.size;)
{
// rjf: disassemble one instruction
DASM_Inst inst = dasm_inst_from_code(scratch.arena, params.arch, params.vaddr+off, str8_skip(data, off), params.syntax);
if(inst.size == 0)
{
break;
}
// rjf: push strings derived from voff -> line info
if(params.style_flags & (DASM_StyleFlag_SourceFilesNames|DASM_StyleFlag_SourceLines) &&
rdi != &rdi_parsed_nil)
{
U64 voff = (params.vaddr+off) - params.base_vaddr;
U32 unit_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_UnitVMap, voff);
RDI_Unit *unit = rdi_element_from_name_idx(rdi, Units, unit_idx);
RDI_LineTable *line_table = rdi_element_from_name_idx(rdi, LineTables, unit->line_table_idx);
RDI_ParsedLineTable unit_line_info = {0};
rdi_parsed_from_line_table(rdi, line_table, &unit_line_info);
U64 line_info_idx = rdi_line_info_idx_from_voff(&unit_line_info, voff);
if(line_info_idx < unit_line_info.count)
{
RDI_Line *line = &unit_line_info.lines[line_info_idx];
RDI_SourceFile *file = rdi_element_from_name_idx(rdi, SourceFiles, line->file_idx);
String8 file_normalized_full_path = {0};
file_normalized_full_path.str = rdi_string_from_idx(rdi, file->normal_full_path_string_idx, &file_normalized_full_path.size);
if(file != last_file)
{
if(params.style_flags & DASM_StyleFlag_SourceFilesNames &&
file->normal_full_path_string_idx != 0 && file_normalized_full_path.size != 0)
{
String8 inst_string = push_str8f(scratch.arena, "> %S", file_normalized_full_path);
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
str8_list_push(scratch.arena, &inst_strings, inst_string);
}
if(params.style_flags & DASM_StyleFlag_SourceFilesNames && file->normal_full_path_string_idx == 0)
{
String8 inst_string = str8_lit(">");
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
str8_list_push(scratch.arena, &inst_strings, inst_string);
}
last_file = file;
}
if(line && line != last_line && file->normal_full_path_string_idx != 0 &&
params.style_flags & DASM_StyleFlag_SourceLines &&
file_normalized_full_path.size != 0)
{
FileProperties props = os_properties_from_file_path(file_normalized_full_path);
if(props.modified != 0)
{
// TODO(rjf): need redirection path - this may map to a different path on the local machine,
// need frontend to communicate path remapping info to this layer
C_Key key = fs_key_from_path_range(file_normalized_full_path, r1u64(0, max_U64), 0);
TXT_LangKind lang_kind = txt_lang_kind_from_extension(file_normalized_full_path);
U64 endt_us = max_U64;
U128 hash = {0};
TXT_TextInfo text_info = txt_text_info_from_key_lang(access, key, lang_kind, &hash);
stale = (stale || u128_match(hash, u128_zero()));
if(0 < line->line_num && line->line_num < text_info.lines_count)
{
String8 data = c_data_from_hash(access, hash);
String8 line_text = str8_skip_chop_whitespace(str8_substr(data, text_info.lines_ranges[line->line_num-1]));
if(line_text.size != 0)
{
String8 inst_string = push_str8f(scratch.arena, "> %S", line_text);
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
str8_list_push(scratch.arena, &inst_strings, inst_string);
}
}
}
last_line = line;
}
}
}
// rjf: push line
String8 addr_part = {0};
if(params.style_flags & DASM_StyleFlag_Addresses)
{
addr_part = push_str8f(scratch.arena, "%s0x%016I64x ", rdi != &rdi_parsed_nil ? " " : "", params.vaddr+off);
}
String8 code_bytes_part = {0};
if(params.style_flags & DASM_StyleFlag_CodeBytes)
{
String8List code_bytes_strings = {0};
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit("{"));
for(U64 byte_idx = 0; byte_idx < inst.size || byte_idx < 16; byte_idx += 1)
{
if(byte_idx < inst.size)
{
str8_list_pushf(scratch.arena, &code_bytes_strings, "%02x%s ", (U32)data.str[off+byte_idx], byte_idx == inst.size-1 ? "}" : "");
}
else if(byte_idx < 8)
{
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit(" "));
}
}
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit(" "));
code_bytes_part = str8_list_join(scratch.arena, &code_bytes_strings, 0);
}
String8 symbol_part = {0};
if(inst.jump_dest_vaddr != 0 && rdi != &rdi_parsed_nil && params.style_flags & DASM_StyleFlag_SymbolNames)
{
RDI_U32 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, inst.jump_dest_vaddr-params.base_vaddr);
if(scope_idx != 0)
{
RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx);
RDI_U32 procedure_idx = scope->proc_idx;
RDI_Procedure *procedure = rdi_element_from_name_idx(rdi, Procedures, procedure_idx);
String8 procedure_name = {0};
procedure_name.str = rdi_string_from_idx(rdi, procedure->name_string_idx, &procedure_name.size);
if(procedure_name.size != 0)
{
symbol_part = push_str8f(scratch.arena, " (%S)", procedure_name);
}
}
}
String8 inst_string = push_str8f(scratch.arena, "%S%S%S%S", addr_part, code_bytes_part, inst.string, symbol_part);
DASM_Line line = {u32_from_u64_saturate(off), 0, inst.jump_dest_vaddr, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &line);
str8_list_push(scratch.arena, &inst_strings, inst_string);
// rjf: increment
off += inst.size;
}
}break;
}
//- rjf: artifacts -> value bundle
Arena *info_arena = 0;
DASM_Info info = {0};
if(!stale)
{
//- rjf: produce joined text
Arena *text_arena = arena_alloc();
StringJoin text_join = {0};
text_join.sep = str8_lit("\n");
String8 text = str8_list_join(text_arena, &inst_strings, &text_join);
//- rjf: produce unique key for this disassembly's text
C_Key text_key = c_key_make(c_root_alloc(), c_id_make(0, 0));
//- rjf: submit text data to hash store
U128 text_hash = c_submit_data(text_key, &text_arena, text);
//- rjf: produce value bundle
info_arena = arena_alloc();
info.text_key = text_key;
info.lines = dasm_line_array_from_chunk_list(info_arena, &line_list);
}
//- rjf: if stale, retry
if(stale)
{
retry_out[0] = 1;
}
//- rjf: mark dependency on data hash
c_hash_downstream_inc(hash);
//- rjf: fill result
if(info_arena != 0)
{
artifact = push_array(info_arena, DASM_Artifact, 1);
artifact->arena = info_arena;
artifact->info = info;
artifact->data_hash = hash;
}
di_scope_close(di_scope);
access_close(access);
scratch_end(scratch);
}
lane_sync_u64(&artifact, 0);
AC_Artifact result = {0};
result.u64[0] = (U64)artifact;
return result;
}
internal void
dasm_artifact_destroy(AC_Artifact artifact)
{
DASM_Artifact *dasm_artifact = (DASM_Artifact *)artifact.u64[0];
if(dasm_artifact == 0) { return; }
c_close_key(dasm_artifact->info.text_key);
c_hash_downstream_dec(dasm_artifact->data_hash);
arena_release(dasm_artifact->arena);
}
internal DASM_Info
dasm_info_from_hash_params(Access *access, U128 hash, DASM_Params *params)
{
DASM_Info info = {0};
{
Temp scratch = scratch_begin(0, 0);
// rjf: form key
String8List key_parts = {0};
str8_list_push(scratch.arena, &key_parts, str8_struct(&hash));
str8_list_push(scratch.arena, &key_parts, str8_struct(params));
str8_list_push(scratch.arena, &key_parts, params->dbgi_key.path);
String8 key = str8_list_join(scratch.arena, &key_parts, 0);
// rjf: get info
AC_Artifact artifact = ac_artifact_from_key(access, key, dasm_artifact_create, dasm_artifact_destroy, 0, .gen = fs_change_gen(), .flags = AC_Flag_Wide);
DASM_Artifact *dasm_artifact = (DASM_Artifact *)artifact.u64[0];
if(dasm_artifact)
{
info = dasm_artifact->info;
}
scratch_end(scratch);
}
return info;
}
internal DASM_Info
dasm_info_from_key_params(Access *access, C_Key key, DASM_Params *params, U128 *hash_out)
{
DASM_Info result = {0};
for(U64 rewind_idx = 0; rewind_idx < C_KEY_HASH_HISTORY_COUNT; rewind_idx += 1)
{
U128 hash = c_hash_from_key(key, rewind_idx);
result = dasm_info_from_hash_params(access, hash, params);
if(result.lines.count != 0)
{
if(hash_out)
{
*hash_out = hash;
}
break;
}
}
return result;
}
@@ -1,8 +1,8 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DASM_CACHE_H
#define DASM_CACHE_H
#ifndef DISASM_H
#define DISASM_H
////////////////////////////////
//~ rjf: Disassembly Syntax Types
@@ -100,6 +100,24 @@ struct DASM_Params
DI_Key dbgi_key;
};
////////////////////////////////
//~ rjf: Disassembly Request Bundle
typedef struct DASM_Request DASM_Request;
struct DASM_Request
{
C_Root root;
U128 hash;
DASM_Params params;
};
typedef struct DASM_RequestNode DASM_RequestNode;
struct DASM_RequestNode
{
DASM_RequestNode *next;
DASM_Request v;
};
////////////////////////////////
//~ rjf: Disassembly Text Line Types
@@ -143,135 +161,16 @@ struct DASM_LineArray
U64 count;
};
////////////////////////////////
//~ rjf: Disassembly Result Bundle
typedef struct DASM_Result DASM_Result;
struct DASM_Result
{
String8 text;
DASM_LineArray lines;
};
////////////////////////////////
//~ rjf: Value Bundle Type
typedef struct DASM_Info DASM_Info;
struct DASM_Info
{
HS_Key text_key;
C_Key text_key;
DASM_LineArray lines;
};
////////////////////////////////
//~ rjf: Cache Types
typedef struct DASM_Node DASM_Node;
struct DASM_Node
{
// rjf: links
DASM_Node *next;
DASM_Node *prev;
// rjf: key
U128 hash;
DASM_Params params;
// rjf: root
HS_Root root;
// rjf: generations
U64 change_gen;
// rjf: value
Arena *info_arena;
DASM_Info info;
// rjf: metadata
U64 working_count;
U64 scope_ref_count;
U64 last_time_touched_us;
U64 last_user_clock_idx_touched;
U64 last_time_requested_us;
U64 last_user_clock_idx_requested;
};
typedef struct DASM_Slot DASM_Slot;
struct DASM_Slot
{
DASM_Node *first;
DASM_Node *last;
};
typedef struct DASM_Stripe DASM_Stripe;
struct DASM_Stripe
{
Arena *arena;
OS_Handle rw_mutex;
OS_Handle cv;
DASM_Node *free_node;
};
////////////////////////////////
//~ rjf: Scoped Access Types
typedef struct DASM_Touch DASM_Touch;
struct DASM_Touch
{
DASM_Touch *next;
U128 hash;
DASM_Params params;
};
typedef struct DASM_Scope DASM_Scope;
struct DASM_Scope
{
DASM_Scope *next;
DASM_Touch *top_touch;
U64 base_pos;
};
////////////////////////////////
//~ rjf: Thread Context
typedef struct DASM_TCTX DASM_TCTX;
struct DASM_TCTX
{
Arena *arena;
};
////////////////////////////////
//~ rjf: Shared State
typedef struct DASM_Shared DASM_Shared;
struct DASM_Shared
{
Arena *arena;
// rjf: cache
U64 slots_count;
U64 stripes_count;
DASM_Slot *slots;
DASM_Stripe *stripes;
// rjf: user -> parse thread
U64 u2p_ring_size;
U8 *u2p_ring_base;
U64 u2p_ring_write_pos;
U64 u2p_ring_read_pos;
OS_Handle u2p_ring_cv;
OS_Handle u2p_ring_mutex;
// rjf: evictor/detector thread
OS_Handle evictor_detector_thread;
};
////////////////////////////////
//~ rjf: Globals
thread_static DASM_TCTX *dasm_tctx = 0;
global DASM_Shared *dasm_shared = 0;
////////////////////////////////
//~ rjf: Instruction Decoding/Disassembling Type Functions
@@ -296,33 +195,11 @@ internal U64 dasm_line_array_idx_from_code_off__linear_scan(DASM_LineArray *arra
internal U64 dasm_line_array_code_off_from_idx(DASM_LineArray *array, U64 idx);
////////////////////////////////
//~ rjf: Main Layer Initialization
//~ rjf: Artifact Cache Hooks / Lookups
internal void dasm_init(void);
internal AC_Artifact dasm_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out);
internal void dasm_artifact_destroy(AC_Artifact artifact);
internal DASM_Info dasm_info_from_hash_params(Access *access, U128 hash, DASM_Params *params);
internal DASM_Info dasm_info_from_key_params(Access *access, C_Key key, DASM_Params *params, U128 *hash_out);
////////////////////////////////
//~ rjf: Scoped Access
internal DASM_Scope *dasm_scope_open(void);
internal void dasm_scope_close(DASM_Scope *scope);
internal void dasm_scope_touch_node__stripe_r_guarded(DASM_Scope *scope, DASM_Node *node);
////////////////////////////////
//~ rjf: Cache Lookups
internal DASM_Info dasm_info_from_hash_params(DASM_Scope *scope, U128 hash, DASM_Params *params);
internal DASM_Info dasm_info_from_key_params(DASM_Scope *scope, HS_Key key, DASM_Params *params, U128 *hash_out);
////////////////////////////////
//~ rjf: Parse Threads
internal B32 dasm_u2p_enqueue_req(HS_Root root, U128 hash, DASM_Params *params, U64 endt_us);
internal void dasm_u2p_dequeue_req(Arena *arena, HS_Root *root_out, U128 *hash_out, DASM_Params *params_out);
ASYNC_WORK_DEF(dasm_parse_work);
////////////////////////////////
//~ rjf: Evictor/Detector Thread
internal void dasm_evictor_detector_thread__entry_point(void *p);
#endif // DASM_CACHE_H
#endif // DISASM_H
-8
View File
@@ -165,7 +165,6 @@ dr_fuzzy_match_find_from_fstrs(Arena *arena, DR_FStrList *fstrs, String8 needle)
internal DR_FRunList
dr_fruns_from_fstrs(Arena *arena, F32 tab_size_px, DR_FStrList *strs)
{
ProfBeginFunction();
DR_FRunList run_list = {0};
F32 base_align_px = 0;
for(DR_FStrNode *n = strs->first; n != 0; n = n->next)
@@ -182,7 +181,6 @@ dr_fruns_from_fstrs(Arena *arena, F32 tab_size_px, DR_FStrList *strs)
run_list.dim.y = Max(run_list.dim.y, dst_n->v.run.dim.y);
base_align_px += dst_n->v.run.dim.x;
}
ProfEnd();
return run_list;
}
@@ -536,8 +534,6 @@ dr_sub_bucket(DR_Bucket *bucket)
internal void
dr_truncated_fancy_run_list(Vec2F32 p, DR_FRunList *list, F32 max_x, FNT_Run trailer_run)
{
ProfBeginFunction();
//- rjf: total advance > max? -> enable trailer
B32 trailer_enabled = (list->dim.x > max_x && trailer_run.dim.x < max_x);
@@ -632,8 +628,6 @@ dr_truncated_fancy_run_list(Vec2F32 p, DR_FRunList *list, F32 max_x, FNT_Run tra
advance += piece->advance;
}
}
ProfEnd();
}
internal void
@@ -691,7 +685,6 @@ dr_truncated_fancy_run_fuzzy_matches(Vec2F32 p, DR_FRunList *list, F32 max_x, Fu
internal void
dr_text_run(Vec2F32 p, Vec4F32 color, FNT_Run run)
{
ProfBeginFunction();
F32 advance = 0;
FNT_Piece *piece_first = run.pieces.v;
FNT_Piece *piece_opl = piece_first + run.pieces.count;
@@ -712,7 +705,6 @@ dr_text_run(Vec2F32 p, Vec4F32 color, FNT_Run run)
}
advance += piece->advance;
}
ProfEnd();
}
internal void
+46 -49
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal B32
@@ -32,7 +32,6 @@ dw_input_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin)
{
DW_Input result = {0};
B32 is_section_present[ArrayCount(result.sec)] = {0};
Temp scratch = scratch_begin(&arena, 1);
for(U64 section_idx = 1; section_idx < bin->shdrs.count; section_idx += 1)
{
ELF_Shdr64 *shdr = &bin->shdrs.v[section_idx];
@@ -49,69 +48,67 @@ dw_input_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin)
is_dwo = (section_kind != DW_Section_Null);
}
if(section_kind == DW_Section_Null) { continue; } // skip unknown sections
if(is_section_present[section_kind]) { continue; } // skip duplicate sections
//- rjf: decompress section data if needed
String8 section_data__uncompressed = {0};
if(section_kind != DW_Section_Null)
if(!(shdr->sh_flags & ELF_Shf_Compressed))
{
if(!(shdr->sh_flags & ELF_Shf_Compressed))
section_data__uncompressed = section_data__maybe_compressed;
}
else
{
// rjf: read compressed-section header
ELF_Chdr64 chdr64 = {0};
U64 chdr_size = 0;
if(ELF_HdrIs64Bit(bin->hdr.e_ident))
{
section_data__uncompressed = section_data__maybe_compressed;
chdr_size = str8_deserial_read_struct(section_data__maybe_compressed, 0, &chdr64);
}
else
else if(ELF_HdrIs32Bit(bin->hdr.e_ident))
{
// rjf: read compressed-section header
ELF_Chdr64 chdr64 = {0};
U64 chdr_size = 0;
if(ELF_HdrIs64Bit(bin->hdr.e_ident))
ELF_Chdr32 chdr32 = {0};
chdr_size = str8_deserial_read_struct(section_data__maybe_compressed, 0, &chdr32);
if(chdr_size == sizeof(chdr32))
{
chdr_size = str8_deserial_read_struct(section_data__maybe_compressed, 0, &chdr64);
chdr64 = elf_chdr64_from_chdr32(chdr32);
}
else if(ELF_HdrIs32Bit(bin->hdr.e_ident))
}
// rjf: decompress
{
String8 section_data__compressed_contents = str8_skip(section_data__maybe_compressed, chdr_size);
switch(chdr64.ch_type)
{
ELF_Chdr32 chdr32 = {0};
chdr_size = str8_deserial_read_struct(section_data__maybe_compressed, 0, &chdr32);
if(chdr_size == sizeof(chdr32))
case ELF_CompressType_None:
{
chdr64 = elf_chdr64_from_chdr32(chdr32);
}
}
// rjf: decompress
{
String8 section_data__compressed_contents = str8_skip(section_data__maybe_compressed, chdr_size);
switch(chdr64.ch_type)
section_data__uncompressed = section_data__compressed_contents;
}break;
case ELF_CompressType_ZLib:
{
default:
case ELF_CompressType_None:
{
section_data__uncompressed = section_data__compressed_contents;
}break;
case ELF_CompressType_ZLib:
{
U8 *section_data_uncompressed_buffer = push_array_no_zero_aligned(arena, U8, chdr64.ch_size, chdr64.ch_addr_align);
U64 section_data_uncompressed_size = 0;
section_data_uncompressed_size = zsinflate(section_data_uncompressed_buffer, chdr64.ch_size, section_data__compressed_contents.str, section_data__compressed_contents.size);
section_data__uncompressed = str8(section_data_uncompressed_buffer, section_data_uncompressed_size);
}break;
case ELF_CompressType_ZStd:
{
NotImplemented;
}break;
}
U8 *section_data_uncompressed_buffer = push_array_no_zero_aligned(arena, U8, chdr64.ch_size, chdr64.ch_addr_align);
U64 section_data_uncompressed_size = zsinflate(section_data_uncompressed_buffer, chdr64.ch_size, section_data__compressed_contents.str, section_data__compressed_contents.size);
section_data__uncompressed = str8(section_data_uncompressed_buffer, section_data_uncompressed_size);
}break;
case ELF_CompressType_ZStd:
{
NotImplemented;
}break;
default:
{
NotImplemented;
}break;
}
}
}
//- rjf: store
if(section_kind != DW_Section_Null)
{
is_section_present[section_kind] = 1;
DW_Section *d = &result.sec[section_kind];
d->name = push_str8_copy(arena, section_name);
d->data = section_data__uncompressed;
d->is_dwo = is_dwo;
}
is_section_present[section_kind] = 1;
DW_Section *d = &result.sec[section_kind];
d->name = push_str8_copy(arena, section_name);
d->data = section_data__uncompressed;
d->is_dwo = is_dwo;
}
scratch_end(scratch);
return result;
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DWARF_ELF_H
+2
View File
@@ -1999,6 +1999,8 @@ dw_u64_from_attrib(DW_Input *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind k
} else {
result = dw_interp_const_u64(attrib->form_kind, attrib->form);
}
} else if (attrib_class == DW_AttribClass_Address) {
result = dw_address_from_attrib(input, cu, attrib);
} else if (attrib_class == DW_AttribClass_Reference) {
NotImplemented;
} else if (attrib_class != DW_AttribClass_Null) {
+1
View File
@@ -132,6 +132,7 @@ typedef struct DW_Tag
DW_TagKind kind;
DW_AttribList attribs;
U64 info_off;
U8 v[1];
} DW_Tag;
typedef struct DW_TagNode
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
+3 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef ELF_H
@@ -971,7 +971,8 @@ typedef struct ELF_Chdr32
typedef struct ELF_Chdr64
{
U64 ch_type;
U32 ch_type;
U32 ch_reserved;
U64 ch_size;
U64 ch_addr_align;
} ELF_Chdr64;
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
//- rjf: top-level binary parsing
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef ELF_PARSE_H
+10 -9
View File
@@ -379,15 +379,16 @@ enum
{
E_TypeFlag_Const = (1<<0),
E_TypeFlag_Volatile = (1<<1),
E_TypeFlag_IsPlainText = (1<<2),
E_TypeFlag_IsCodeText = (1<<3),
E_TypeFlag_IsPathText = (1<<4),
E_TypeFlag_IsNotText = (1<<5),
E_TypeFlag_EditableChildren = (1<<6),
E_TypeFlag_InheritedByMembers = (1<<7),
E_TypeFlag_InheritedByElements = (1<<8),
E_TypeFlag_ArrayLikeExpansion = (1<<9),
E_TypeFlag_StubSingleLineExpansion = (1<<10),
E_TypeFlag_Restrict = (1<<2),
E_TypeFlag_IsPlainText = (1<<3),
E_TypeFlag_IsCodeText = (1<<4),
E_TypeFlag_IsPathText = (1<<5),
E_TypeFlag_IsNotText = (1<<6),
E_TypeFlag_EditableChildren = (1<<7),
E_TypeFlag_InheritedByMembers = (1<<8),
E_TypeFlag_InheritedByElements = (1<<9),
E_TypeFlag_ArrayLikeExpansion = (1<<10),
E_TypeFlag_StubSingleLineExpansion = (1<<11),
};
typedef struct E_Member E_Member;
+19 -6
View File
@@ -503,7 +503,7 @@ E_TYPE_ACCESS_FUNCTION_DEF(default)
E_TypeKey r_restype = e_type_key_unwrap(r.type_key, E_TypeUnwrapFlag_AllDecorative);
E_TypeKind l_restype_kind = e_type_kind_from_key(l_restype);
E_TypeKind r_restype_kind = e_type_kind_from_key(r_restype);
E_TypeKey direct_type = e_type_key_unwrap(l_restype, E_TypeUnwrapFlag_All);
E_TypeKey direct_type = e_type_key_unwrap(l_restype, E_TypeUnwrapFlag_All & ~(E_TypeUnwrapFlag_Enums|E_TypeUnwrapFlag_Aliases));
U64 direct_type_size = e_type_byte_size_from_key(direct_type);
// rjf: bad conditions? -> error if applicable, exit
@@ -752,9 +752,9 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I
E_Expr *r_expr = expr->first;
E_IRTreeAndType r_tree = e_push_irtree_and_type_from_expr(arena, parent, &e_default_identifier_resolution_rule, disallow_autohooks, 1, r_expr);
e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs);
E_TypeKey r_type = e_type_key_unwrap(r_tree.type_key, E_TypeUnwrapFlag_AllDecorative);
E_TypeKey r_type = e_type_key_unwrap(r_tree.type_key, E_TypeUnwrapFlag_AllDecorative & ~E_TypeUnwrapFlag_Enums);
E_TypeKind r_type_kind = e_type_kind_from_key(r_type);
E_TypeKey r_type_direct = e_type_key_unwrap(r_type, E_TypeUnwrapFlag_All);
E_TypeKey r_type_direct = e_type_key_unwrap(r_type, E_TypeUnwrapFlag_All & ~E_TypeUnwrapFlag_Enums);
U64 r_type_direct_size = e_type_byte_size_from_key(r_type_direct);
// rjf: bad conditions? -> error if applicable, exit
@@ -812,7 +812,7 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I
E_IRTreeAndType r_tree = e_push_irtree_and_type_from_expr(arena, parent, &e_default_identifier_resolution_rule, disallow_autohooks, 1, r_expr);
e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs);
E_TypeKey r_type = r_tree.type_key;
E_TypeKey r_type_unwrapped = e_type_key_unwrap(r_type, E_TypeUnwrapFlag_AllDecorative);
E_TypeKey r_type_unwrapped = e_type_key_unwrap(r_type, E_TypeUnwrapFlag_AllDecorative & (~E_TypeUnwrapFlag_Enums));
E_TypeKind r_type_unwrapped_kind = e_type_kind_from_key(r_type_unwrapped);
// rjf: bad conditions? -> error if applicable, exit
@@ -1184,6 +1184,7 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I
#define E_ArithPath_PtrAdd 1
#define E_ArithPath_PtrSub 2
#define E_ArithPath_PtrArrayCompare 3
#define E_ArithPath_TypeCompare 4
B32 ptr_arithmetic_mul_rptr = 0;
U32 arith_path = E_ArithPath_Normal;
if(kind == E_ExprKind_Add)
@@ -1216,7 +1217,7 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I
}
}
}
else if(kind == E_ExprKind_EqEq)
else if(kind == E_ExprKind_EqEq || kind == E_ExprKind_NtEq)
{
if(l_type_kind == E_TypeKind_Array && (r_type_kind == E_TypeKind_Ptr || r_is_decay))
{
@@ -1226,6 +1227,10 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I
{
arith_path = E_ArithPath_PtrArrayCompare;
}
if(l_tree.mode == E_Mode_Null && r_tree.mode == E_Mode_Null)
{
arith_path = E_ArithPath_TypeCompare;
}
}
// rjf: generate according to arithmetic path
@@ -1360,6 +1365,14 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I
result.type_key = e_type_key_basic(E_TypeKind_Bool);
result.mode = E_Mode_Value;
}break;
//- rjf: type comparison
case E_ArithPath_TypeCompare:
{
result.root = e_irtree_const_u(arena, !!e_type_match(l_type, r_type));
result.type_key = e_type_key_basic(E_TypeKind_Bool);
result.mode = E_Mode_Value;
}break;
}
}break;
@@ -2222,7 +2235,7 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I
{
E_IRTreeAndType direct_irtree = e_push_irtree_and_type_from_expr(arena, parent, &e_default_identifier_resolution_rule, disallow_autohooks, 1, expr->first);
result = direct_irtree;
E_TypeKey direct_type_key = result.type_key;
E_TypeKey direct_type_key = e_type_key_unwrap(result.type_key, E_TypeUnwrapFlag_AllDecorative);
E_TypeKind direct_type_kind = e_type_kind_from_key(direct_type_key);
if(e_type_kind_is_signed(direct_type_kind))
{
+50 -3
View File
@@ -859,10 +859,19 @@ e_push_parse_from_string_tokens__prec(Arena *arena, String8 text, E_TokenArray t
e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, token.range, "Missing `)`.");
}
// rjf: require type
if(type_parse.expr == &e_expr_nil)
{
e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, token.range, "Expected type in `cast(...)`.");
}
// rjf: fill prefix unary info
prefix_unary_kind = E_ExprKind_Cast;
prefix_unary_precedence = 2;
prefix_unary_cast_expr = type_parse.expr;
else
{
prefix_unary_kind = E_ExprKind_Cast;
prefix_unary_precedence = 2;
prefix_unary_cast_expr = type_parse.expr;
}
end_cast_parse:;
}
@@ -1303,6 +1312,33 @@ e_push_parse_from_string_tokens__prec(Arena *arena, String8 text, E_TokenArray t
}
}
// rjf: "as" style casts
if(token.kind == E_TokenKind_Identifier &&
str8_match(token_string, str8_lit("as"), 0))
{
it += 1;
// rjf: parse type expression
E_Parse type_parse = e_push_parse_from_string_tokens__prec(arena, text, e_token_array_make_first_opl(it, it_opl), e_max_precedence, 1);
e_msg_list_concat_in_place(&result.msgs, &type_parse.msgs);
it = type_parse.last_token;
// rjf: require type
if(type_parse.expr == &e_expr_nil)
{
e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, token.range, "Expected type following `as`.");
}
// rjf: build cast expr
else
{
E_Expr *rhs = atom;
atom = e_push_expr(arena, E_ExprKind_Cast, token.range);
e_expr_push_child(atom, type_parse.expr);
e_expr_push_child(atom, rhs);
}
}
// rjf: quit if this doesn't look like any patterns of postfix unary we know
if(!is_postfix_unary)
{
@@ -1310,6 +1346,17 @@ e_push_parse_from_string_tokens__prec(Arena *arena, String8 text, E_TokenArray t
}
}
////////////////////////////
//- rjf: no atom, just single `unsigned` prefix unary? -> unsigned int type expr
//
if(atom == &e_expr_nil &&
first_prefix_unary != 0 &&
first_prefix_unary->kind == E_ExprKind_Unsigned)
{
atom = e_push_expr(arena, E_ExprKind_LeafIdentifier, first_prefix_unary->cast_type_expr->range);
atom->string = str8_lit("int");
}
////////////////////////////
//- rjf: upgrade `atom` w/ previously parsed prefix unaries
//
+12 -3
View File
@@ -514,7 +514,7 @@ e_type_key_cons_base(Type *type)
if(type->flags & TypeFlag_IsPlainText){ flags |= E_TypeFlag_IsPlainText; }
if(type->flags & TypeFlag_IsCodeText) { flags |= E_TypeFlag_IsCodeText; }
if(type->flags & TypeFlag_IsPathText) { flags |= E_TypeFlag_IsPathText; }
result = e_type_key_cons_ptr(arch_from_context(), direct_type, 1, flags);
result = e_type_key_cons_ptr(Arch_CURRENT, direct_type, 1, flags);
}break;
case TypeKind_Array:
{
@@ -531,7 +531,7 @@ e_type_key_cons_base(Type *type)
e_member_list_push_new(scratch.arena, &members, .name = type->members[idx].name, .off = type->members[idx].value, .type_key = member_type_key);
}
E_MemberArray members_array = e_member_array_from_list(scratch.arena, &members);
result = e_type_key_cons(.arch = arch_from_context(),
result = e_type_key_cons(.arch = Arch_CURRENT,
.kind = E_TypeKind_Struct,
.name = type->name,
.members = members_array.v,
@@ -886,6 +886,10 @@ e_push_type_from_key(Arena *arena, E_TypeKey key)
{
flags |= E_TypeFlag_Volatile;
}
if(rdi_type->flags & RDI_TypeModifierFlag_Restrict)
{
flags |= E_TypeFlag_Restrict;
}
type = push_array(arena, E_Type, 1);
type->kind = kind;
type->direct_type_key = direct_type_key;
@@ -1679,6 +1683,10 @@ e_type_lhs_string_from_key(Arena *arena, E_TypeKey key, String8List *out, U32 pr
{
str8_list_push(arena, out, str8_lit("volatile "));
}
if(type->flags & E_TypeFlag_Restrict)
{
str8_list_push(arena, out, str8_lit("restrict "));
}
}break;
case E_TypeKind_Variadic:
@@ -1945,7 +1953,8 @@ e_default_expansion_type_from_key(E_TypeKey root_key)
// want to ignore them.
//
else if(kind == E_TypeKind_Lens ||
kind == E_TypeKind_Modifier)
kind == E_TypeKind_Modifier ||
kind == E_TypeKind_Alias)
{
done = 0;
}
@@ -1757,6 +1757,7 @@ ev_string_iter_next(Arena *arena, EV_StringIter *it, String8 *out_string)
//////////////////////////
//- rjf: modifiers / no-ops
//
case E_TypeKind_Alias:
case E_TypeKind_Modifier:
case E_TypeKind_MetaDescription:
case E_TypeKind_MetaDisplayName:
@@ -2068,8 +2069,7 @@ ev_string_iter_next(Arena *arena, EV_StringIter *it, String8 *out_string)
// rjf: [read only] if we did prefix content, do a parenthesized pointer value
if(!(params->flags & EV_StringFlag_DisableAddresses) && params->flags & EV_StringFlag_ReadOnlyDisplayRules &&
ptr_data->did_prefix_content &&
(!ptr_data->did_prefix_string || ptr_data->value_eval.value.u64 == 0))
ptr_data->did_prefix_content)
{
*out_string = push_str8f(arena, " (%S)", ptr_value_string);
}
+228 -334
View File
@@ -4,34 +4,6 @@
#undef LAYER_COLOR
#define LAYER_COLOR 0xfffa00ff
////////////////////////////////
//~ rjf: Basic Helpers
internal U64
fs_little_hash_from_string(String8 string)
{
U64 result = 5381;
for(U64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + string.str[i];
}
return result;
}
internal U128
fs_big_hash_from_string_range(String8 string, Rng1U64 range)
{
Temp scratch = scratch_begin(0, 0);
U64 buffer_size = string.size + sizeof(U64)*2;
U8 *buffer = push_array_no_zero(scratch.arena, U8, buffer_size);
MemoryCopy(buffer, string.str, string.size);
MemoryCopy(buffer + string.size, &range.min, sizeof(range.min));
MemoryCopy(buffer + string.size + sizeof(range.min), &range.max, sizeof(range.max));
U128 hash = hs_hash_from_data(str8(buffer, buffer_size));
scratch_end(scratch);
return hash;
}
////////////////////////////////
//~ rjf: Top-Level API
@@ -43,20 +15,8 @@ fs_init(void)
fs_shared->arena = arena;
fs_shared->change_gen = 1;
fs_shared->slots_count = 1024;
fs_shared->stripes_count = os_get_system_info()->logical_processor_count;
fs_shared->slots = push_array(arena, FS_Slot, fs_shared->slots_count);
fs_shared->stripes = push_array(arena, FS_Stripe, fs_shared->stripes_count);
for(U64 idx = 0; idx < fs_shared->stripes_count; idx += 1)
{
fs_shared->stripes[idx].arena = arena_alloc();
fs_shared->stripes[idx].cv = os_condition_variable_alloc();
fs_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
}
fs_shared->u2s_ring_size = KB(64);
fs_shared->u2s_ring_base = push_array_no_zero(arena, U8, fs_shared->u2s_ring_size);
fs_shared->u2s_ring_cv = os_condition_variable_alloc();
fs_shared->u2s_ring_mutex = os_mutex_alloc();
fs_shared->detector_thread = os_thread_launch(fs_detector_thread__entry_point, 0, 0);
fs_shared->stripes = stripe_array_alloc(arena);
}
////////////////////////////////
@@ -71,133 +31,236 @@ fs_change_gen(void)
////////////////////////////////
//~ rjf: Cache Interaction
internal HS_Key
fs_key_from_path_range(String8 path, Rng1U64 range, U64 endt_us)
internal AC_Artifact
fs_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
//- rjf: unpack args
path = path_normalized_from_string(scratch.arena, path);
U64 path_little_hash = fs_little_hash_from_string(path);
U64 path_slot_idx = path_little_hash%fs_shared->slots_count;
U64 path_stripe_idx = path_slot_idx%fs_shared->stripes_count;
FS_Slot *path_slot = &fs_shared->slots[path_slot_idx];
FS_Stripe *path_stripe = &fs_shared->stripes[path_stripe_idx];
//- rjf: get root for this path
HS_Root root = {0};
OS_MutexScopeR(path_stripe->rw_mutex)
//- rjf: unpack key
String8 path = {0};
Rng1U64 range = {0};
{
B32 node_found = 0;
for(FS_Node *n = path_slot->first; n != 0; n = n->next)
U64 key_read_off = 0;
key_read_off += str8_deserial_read_struct(key, key_read_off, &path.size);
path.str = push_array(scratch.arena, U8, path.size);
key_read_off += str8_deserial_read(key, key_read_off, path.str, path.size, 1);
key_read_off += str8_deserial_read_struct(key, key_read_off, &range);
}
//- rjf: measure file properties *before* read
B32 file_is_good = 0;
FileProperties pre_props = {0};
if(lane_idx() == 0)
{
pre_props = os_properties_from_file_path(path);
file_is_good = (pre_props.modified != 0);
}
lane_sync_u64(&file_is_good, 0);
//- rjf: setup output data
Arena *data_arena = 0;
U64 data_buffer_size = 0;
U8 *data_buffer = 0;
if(file_is_good)
{
if(lane_idx() == 0)
{
if(str8_match(n->path, path, 0))
{
node_found = 1;
root = n->root;
break;
}
U64 range_size = dim_1u64(range);
U64 read_size = Min(pre_props.size - range.min, range_size);
U64 data_arena_size = read_size+ARENA_HEADER_SIZE;
data_arena_size += KB(4)-1;
data_arena_size -= data_arena_size%KB(4);
data_arena = arena_alloc(.reserve_size = data_arena_size, .commit_size = data_arena_size);
data_buffer_size = read_size;
data_buffer = push_array_no_zero(data_arena, U8, data_buffer_size);
}
if(!node_found) OS_MutexScopeRWPromote(path_stripe->rw_mutex)
lane_sync_u64(&data_buffer, 0);
lane_sync_u64(&data_buffer_size, 0);
}
//- rjf: open file
OS_Handle file = {0};
if(file_is_good)
{
if(lane_idx() == 0)
{
B32 node_found = 0;
for(FS_Node *n = path_slot->first; n != 0; n = n->next)
{
if(str8_match(n->path, path, 0))
{
node_found = 1;
root = n->root;
break;
}
}
if(!node_found)
{
FS_Node *node = push_array(path_stripe->arena, FS_Node, 1);
SLLQueuePush(path_slot->first, path_slot->last, node);
node->path = push_str8_copy(path_stripe->arena, path);
node->root = hs_root_alloc();
node->slots_count = 64;
node->slots = push_array(path_stripe->arena, FS_RangeSlot, node->slots_count);
root = node->root;
}
file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, path);
}
lane_sync_u64(&file, 0);
}
B32 file_handle_is_valid = !os_handle_match(os_handle_zero(), file);
//- rjf: do read
U64 total_bytes_read = 0;
if(file_handle_is_valid)
{
U64 *total_bytes_read_ptr = 0;
if(lane_idx() == 0)
{
total_bytes_read_ptr = &total_bytes_read;
}
lane_sync_u64(&total_bytes_read_ptr, 0);
ProfScope("read \"%.*s\" [0x%I64x, 0x%I64x)", str8_varg(path), range.min, range.max)
{
Rng1U64 lane_read_range = lane_range(data_buffer_size);
U64 bytes_read = os_file_read(file, shift_1u64(lane_read_range, range.min), data_buffer + lane_read_range.min);
ins_atomic_u64_add_eval(total_bytes_read_ptr, bytes_read);
}
lane_sync();
lane_sync_u64(&total_bytes_read, 0);
}
//- rjf: close file
if(file_handle_is_valid)
{
if(lane_idx() == 0)
{
os_file_close(file);
}
}
//- rjf: build a key for this path/range combo
HS_Key key = hs_key_make(root, hs_id_make(range.min, range.max));
//- rjf: if the most recent hash for this key is zero, then try to submit a new
// request to pull it in.
if(u128_match(hs_hash_from_key(key, 0), u128_zero()))
//- rjf: measure file properties *after* read
FileProperties post_props = {0};
if(lane_idx() == 0)
{
// rjf: loop: request, check for results, return until we can't
OS_MutexScopeW(path_stripe->rw_mutex) for(;;)
post_props = os_properties_from_file_path(path);
}
//- rjf: form content key
C_Key content_key = {0};
{
content_key.id.u128[0] = u128_hash_from_str8(key);
}
//- rjf: abort if modification timestamps or sizes differ - we did not successfully read the file;
// otherwise submit data
B32 read_good = 0;
if(file_is_good)
{
if(lane_idx() == 0)
{
// rjf: path -> node
FS_Node *node = 0;
for(FS_Node *n = path_slot->first; n != 0; n = n->next)
read_good = (pre_props.modified == post_props.modified &&
pre_props.size == post_props.size &&
data_buffer_size == total_bytes_read &&
(file_handle_is_valid || pre_props.flags & FilePropertyFlag_IsFolder));
if(!read_good)
{
if(str8_match(path, n->path, 0))
retry_out[0] = 1;
ProfScope("abort")
{
arena_release(data_arena);
MemoryZeroStruct(&content_key);
}
}
else
{
ProfScope("submit")
{
c_submit_data(content_key, &data_arena, str8(data_buffer, data_buffer_size));
}
}
}
lane_sync();
}
//- rjf: if the read was good, record this path's timestamp in this layer's path info cache
U64 path_hash = u64_hash_from_str8(path);
if(lane_idx() == 0 && read_good)
{
U64 slot_idx = path_hash%fs_shared->slots_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&fs_shared->stripes, slot_idx);
RWMutexScope(stripe->rw_mutex, 1)
{
FS_Node *node = 0;
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->path, path, 0))
{
node = n;
break;
}
}
// rjf: no node? -> weird case, node should've been made at this point.
if(node == 0)
{
break;
}
// rjf: range -> node
U64 range_hash = fs_little_hash_from_string(str8_struct(&key.id));
U64 range_slot_idx = range_hash%node->slots_count;
FS_RangeSlot *range_slot = &node->slots[range_slot_idx];
FS_RangeNode *range_node = 0;
for(FS_RangeNode *n = range_slot->first; n != 0; n = n->next)
{
if(hs_id_match(n->id, key.id))
node = stripe->free;
if(node)
{
range_node = n;
break;
stripe->free = node->next;
}
}
// rjf: range node does not exist? create & store
if(range_node == 0)
{
range_node = push_array(path_stripe->arena, FS_RangeNode, 1);
SLLQueuePush(range_slot->first, range_slot->last, range_node);
range_node->id = key.id;
}
// rjf: try to send stream request
if(ins_atomic_u64_eval(&range_node->working_count) == 0 &&
fs_u2s_enqueue_req(key, range, path, endt_us))
{
ins_atomic_u64_inc_eval(&range_node->working_count);
DeferLoop(os_rw_mutex_drop_w(path_stripe->rw_mutex), os_rw_mutex_take_w(path_stripe->rw_mutex))
else
{
async_push_work(fs_stream_work, .working_counter = &range_node->working_count);
node = push_array_no_zero(stripe->arena, FS_Node, 1);
}
MemoryZeroStruct(node);
node->path = str8_copy(stripe->arena, path);
SLLQueuePush(slot->first, slot->last, node);
}
// rjf: have time to wait? -> wait on this stripe; otherwise exit
B32 have_results = !u128_match(hs_hash_from_key(key, 0), u128_zero());
if(!have_results && os_now_microseconds() < endt_us)
{
os_condition_variable_wait_rw_w(path_stripe->cv, path_stripe->rw_mutex, endt_us);
}
else
{
break;
}
node->last_modified_timestamp = pre_props.modified;
node->size = pre_props.size;
}
}
lane_sync();
//- rjf: bundle content key as artifact
AC_Artifact artifact = {0};
StaticAssert(sizeof(content_key) == sizeof(artifact), artifact_key_size_check);
MemoryCopyStruct(&artifact, &content_key);
scratch_end(scratch);
return key;
ProfEnd();
return artifact;
}
internal void
fs_artifact_destroy(AC_Artifact artifact)
{
C_Key key = {0};
MemoryCopyStruct(&key, &artifact);
c_close_key(key);
}
internal C_Key
fs_key_from_path_range(String8 path, Rng1U64 range, U64 endt_us)
{
C_Key result = {0};
Temp scratch = scratch_begin(0, 0);
Access *access = access_open();
{
String8List key_parts = {0};
str8_list_push(scratch.arena, &key_parts, str8_struct(&path.size));
str8_list_push(scratch.arena, &key_parts, path);
str8_list_push(scratch.arena, &key_parts, str8_struct(&range));
String8 key = str8_list_join(scratch.arena, &key_parts, 0);
//- rjf: find generation number for this key
U64 gen = 0;
{
U64 hash = u64_hash_from_str8(path);
U64 slot_idx = hash%fs_shared->slots_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&fs_shared->stripes, slot_idx);
RWMutexScope(stripe->rw_mutex, 0)
{
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(path, n->path, 0))
{
gen = n->gen;
break;
}
}
}
}
//- rjf: map to artifact
AC_Artifact artifact = ac_artifact_from_key(access, key, fs_artifact_create, fs_artifact_destroy, endt_us, .gen = gen, .flags = AC_Flag_Wide);
MemoryCopyStruct(&result, &artifact);
}
access_close(access);
scratch_end(scratch);
return result;
}
internal U128
@@ -205,10 +268,10 @@ fs_hash_from_path_range(String8 path, Rng1U64 range, U64 endt_us)
{
U128 hash = {0};
{
HS_Key key = fs_key_from_path_range(path, range, endt_us);
for EachIndex(rewind_idx, HS_KEY_HASH_HISTORY_COUNT)
C_Key key = fs_key_from_path_range(path, range, endt_us);
for EachIndex(rewind_idx, C_KEY_HASH_HISTORY_COUNT)
{
hash = hs_hash_from_key(key, rewind_idx);
hash = c_hash_from_key(key, rewind_idx);
if(!u128_match(hash, u128_zero()))
{
break;
@@ -218,216 +281,47 @@ fs_hash_from_path_range(String8 path, Rng1U64 range, U64 endt_us)
return hash;
}
internal FileProperties
fs_properties_from_path(String8 path)
{
Temp scratch = scratch_begin(0, 0);
FileProperties result = {0};
path = path_normalized_from_string(scratch.arena, path);
U64 path_hash = fs_little_hash_from_string(path);
U64 slot_idx = path_hash%fs_shared->slots_count;
U64 stripe_idx = slot_idx%fs_shared->stripes_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(path, n->path, 0))
{
result = n->props;
break;
}
}
}
scratch_end(scratch);
return result;
}
////////////////////////////////
//~ rjf: Streamer Threads
internal B32
fs_u2s_enqueue_req(HS_Key key, Rng1U64 range, String8 path, U64 endt_us)
{
B32 result = 0;
path.size = Min(path.size, fs_shared->u2s_ring_size);
OS_MutexScope(fs_shared->u2s_ring_mutex) for(;;)
{
U64 unconsumed_size = fs_shared->u2s_ring_write_pos - fs_shared->u2s_ring_read_pos;
U64 available_size = fs_shared->u2s_ring_size - unconsumed_size;
U64 needed_size = sizeof(key) + sizeof(range.min) + sizeof(range.max) + sizeof(path.size) + path.size;
if(available_size >= needed_size)
{
result = 1;
fs_shared->u2s_ring_write_pos += ring_write_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, &key);
fs_shared->u2s_ring_write_pos += ring_write_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, &range.min);
fs_shared->u2s_ring_write_pos += ring_write_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, &range.max);
fs_shared->u2s_ring_write_pos += ring_write_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, &path.size);
fs_shared->u2s_ring_write_pos += ring_write(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, path.str, path.size);
break;
}
os_condition_variable_wait(fs_shared->u2s_ring_cv, fs_shared->u2s_ring_mutex, endt_us);
}
if(result)
{
os_condition_variable_broadcast(fs_shared->u2s_ring_cv);
}
return result;
}
//~ rjf: Asynchronous Tick
internal void
fs_u2s_dequeue_req(Arena *arena, HS_Key *key_out, Rng1U64 *range_out, String8 *path_out)
{
OS_MutexScope(fs_shared->u2s_ring_mutex) for(;;)
{
U64 unconsumed_size = fs_shared->u2s_ring_write_pos - fs_shared->u2s_ring_read_pos;
if(unconsumed_size >= sizeof(*key_out) + sizeof(U64)*2 + sizeof(U64))
{
fs_shared->u2s_ring_read_pos += ring_read_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, key_out);
fs_shared->u2s_ring_read_pos += ring_read_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, &range_out->min);
fs_shared->u2s_ring_read_pos += ring_read_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, &range_out->max);
fs_shared->u2s_ring_read_pos += ring_read_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, &path_out->size);
path_out->str = push_array(arena, U8, path_out->size);
fs_shared->u2s_ring_read_pos += ring_read(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, path_out->str, path_out->size);
break;
}
os_condition_variable_wait(fs_shared->u2s_ring_cv, fs_shared->u2s_ring_mutex, max_U64);
}
os_condition_variable_broadcast(fs_shared->u2s_ring_cv);
}
ASYNC_WORK_DEF(fs_stream_work)
fs_async_tick(void)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
//- rjf: get next request
HS_Key key = {0};
Rng1U64 range = {0};
String8 path = {0};
fs_u2s_dequeue_req(scratch.arena, &key, &range, &path);
//- rjf: unpack request
U64 path_hash = fs_little_hash_from_string(path);
U64 path_slot_idx = path_hash%fs_shared->slots_count;
U64 path_stripe_idx = path_slot_idx%fs_shared->stripes_count;
FS_Slot *path_slot = &fs_shared->slots[path_slot_idx];
FS_Stripe *path_stripe = &fs_shared->stripes[path_stripe_idx];
//- rjf: load
ProfBegin("load \"%.*s\"", str8_varg(path));
FileProperties pre_props = os_properties_from_file_path(path);
U64 range_size = dim_1u64(range);
U64 read_size = Min(pre_props.size - range.min, range_size);
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, path);
B32 file_handle_is_valid = !os_handle_match(os_handle_zero(), file);
U64 data_arena_size = read_size+ARENA_HEADER_SIZE;
data_arena_size += KB(4)-1;
data_arena_size -= data_arena_size%KB(4);
ProfBegin("allocate");
Arena *data_arena = arena_alloc(.reserve_size = data_arena_size, .commit_size = data_arena_size);
ProfEnd();
ProfBegin("read");
String8 data = os_string_from_file_range(data_arena, file, r1u64(range.min, range.min+read_size));
ProfEnd();
os_file_close(file);
FileProperties post_props = os_properties_from_file_path(path);
//- rjf: abort if modification timestamps or sizes differ - we did not successfully read the file
B32 read_good = (pre_props.modified == post_props.modified &&
pre_props.size == post_props.size &&
read_size == data.size &&
(file_handle_is_valid || pre_props.flags & FilePropertyFlag_IsFolder));
if(!read_good)
//- rjf: detect changed timestamps for active paths
{
ProfScope("abort")
Rng1U64 range = lane_range(fs_shared->slots_count);
for EachInRange(slot_idx, range)
{
arena_release(data_arena);
MemoryZeroStruct(&data);
data_arena = 0;
}
}
//- rjf: submit
else
{
ProfScope("submit")
{
hs_submit_data(key, &data_arena, data);
}
}
//- rjf: commit info to cache
ProfScope("commit to cache") OS_MutexScopeW(path_stripe->rw_mutex)
{
FS_Node *node = 0;
for(FS_Node *n = path_slot->first; n != 0; n = n->next)
{
if(str8_match(n->path, path, 0))
FS_Slot *slot = &fs_shared->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&fs_shared->stripes, slot_idx);
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
{
node = n;
break;
}
}
if(node != 0 && read_good)
{
if(node->props.modified != 0)
{
ins_atomic_u64_inc_eval(&fs_shared->change_gen);
}
node->props = post_props;
}
}
os_condition_variable_broadcast(path_stripe->cv);
ProfEnd();
scratch_end(scratch);
ProfEnd();
return 0;
}
////////////////////////////////
//~ rjf: Change Detector Thread
internal void
fs_detector_thread__entry_point(void *p)
{
ThreadNameF("[fs] detector thread");
for(;;)
{
U64 slots_per_stripe = fs_shared->slots_count/fs_shared->stripes_count;
for(U64 stripe_idx = 0; stripe_idx < fs_shared->stripes_count; stripe_idx += 1)
{
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex) for(U64 slot_in_stripe_idx = 0; slot_in_stripe_idx < slots_per_stripe; slot_in_stripe_idx += 1)
{
U64 slot_idx = stripe_idx*slots_per_stripe + slot_in_stripe_idx;
FS_Slot *slot = &fs_shared->slots[slot_idx];
for(FS_Node *n = slot->first; n != 0; n = n->next)
B32 found_work = 0;
RWMutexScope(stripe->rw_mutex, write_mode)
{
FileProperties props = os_properties_from_file_path(n->path);
if(props.modified != n->props.modified)
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
for(U64 range_slot_idx = 0; range_slot_idx < n->slots_count; range_slot_idx += 1)
FileProperties props = os_properties_from_file_path(n->path);
if(props.modified != n->last_modified_timestamp)
{
for(FS_RangeNode *range_n = n->slots[range_slot_idx].first;
range_n != 0;
range_n = range_n->next)
found_work = 1;
if(write_mode)
{
HS_Key key = hs_key_make(n->root, range_n->id);
if(ins_atomic_u64_eval(&range_n->working_count) == 0 &&
fs_u2s_enqueue_req(key, r1u64(key.id.u128[0].u64[0], key.id.u128[0].u64[1]), n->path, os_now_microseconds()+100000))
{
ins_atomic_u64_inc_eval(&range_n->working_count);
async_push_work(fs_stream_work, .working_counter = &range_n->working_count);
}
n->gen += 1;
ins_atomic_u64_inc_eval(&fs_shared->change_gen);
}
}
}
}
if(!found_work)
{
break;
}
}
}
os_sleep_milliseconds(100);
}
ProfEnd();
}
+14 -67
View File
@@ -5,38 +5,16 @@
#define FILE_STREAM_H
////////////////////////////////
//~ rjf: Per-Path Info Cache Types
typedef struct FS_RangeNode FS_RangeNode;
struct FS_RangeNode
{
FS_RangeNode *next;
HS_ID id;
U64 working_count;
};
typedef struct FS_RangeSlot FS_RangeSlot;
struct FS_RangeSlot
{
FS_RangeNode *first;
FS_RangeNode *last;
};
//~ rjf: Path Cache
typedef struct FS_Node FS_Node;
struct FS_Node
{
FS_Node *next;
// rjf: file metadata
String8 path;
FileProperties props;
// rjf: hash store root
HS_Root root;
// rjf: sub-table of per-requested-file-range info
U64 slots_count;
FS_RangeSlot *slots;
U64 gen;
U64 last_modified_timestamp;
U64 size;
};
typedef struct FS_Slot FS_Slot;
@@ -46,14 +24,6 @@ struct FS_Slot
FS_Node *last;
};
typedef struct FS_Stripe FS_Stripe;
struct FS_Stripe
{
Arena *arena;
OS_Handle cv;
OS_Handle rw_mutex;
};
////////////////////////////////
//~ rjf: Shared State Bundle
@@ -62,23 +32,9 @@ struct FS_Shared
{
Arena *arena;
U64 change_gen;
// rjf: path info cache
U64 slots_count;
U64 stripes_count;
FS_Slot *slots;
FS_Stripe *stripes;
// rjf: user -> streamer ring buffer
U64 u2s_ring_size;
U8 *u2s_ring_base;
U64 u2s_ring_write_pos;
U64 u2s_ring_read_pos;
OS_Handle u2s_ring_cv;
OS_Handle u2s_ring_mutex;
// rjf: change detector threads
OS_Handle detector_thread;
StripeArray stripes;
};
////////////////////////////////
@@ -86,12 +42,6 @@ struct FS_Shared
global FS_Shared *fs_shared = 0;
////////////////////////////////
//~ rjf: Basic Helpers
internal U64 fs_little_hash_from_string(String8 string);
internal U128 fs_big_hash_from_string_range(String8 string, Rng1U64 range);
////////////////////////////////
//~ rjf: Top-Level API
@@ -103,22 +53,19 @@ internal void fs_init(void);
internal U64 fs_change_gen(void);
////////////////////////////////
//~ rjf: Cache Interaction
//~ rjf: Artifact Cache Hooks / Accessing API
internal HS_Key fs_key_from_path_range(String8 path, Rng1U64 range, U64 endt_us);
internal AC_Artifact fs_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out);
internal void fs_artifact_destroy(AC_Artifact artifact);
internal C_Key fs_key_from_path_range(String8 path, Rng1U64 range, U64 endt_us);
internal U128 fs_hash_from_path_range(String8 path, Rng1U64 range, U64 endt_us);
internal FileProperties fs_properties_from_path(String8 path);
#define fs_key_from_path(path, endt_us) fs_key_from_path_range((path), r1u64(0, max_U64), (endt_us))
#define fs_hash_from_path(path, endt_us) fs_hash_from_path_range((path), r1u64(0, max_U64), (endt_us))
////////////////////////////////
//~ rjf: Streaming Work
//~ rjf: Asynchronous Tick
internal B32 fs_u2s_enqueue_req(HS_Key key, Rng1U64 range, String8 path, U64 endt_us);
internal void fs_u2s_dequeue_req(Arena *arena, HS_Key *key_out, Rng1U64 *range_out, String8 *path_out);
ASYNC_WORK_DEF(fs_stream_work);
////////////////////////////////
//~ rjf: Change Detector Thread
internal void fs_detector_thread__entry_point(void *p);
internal void fs_async_tick(void);
#endif // FILE_STREAM_H
-21
View File
@@ -246,8 +246,6 @@ fnt_path_from_tag(FNT_Tag tag)
internal Rng2S16
fnt_atlas_region_alloc(Arena *arena, FNT_Atlas *atlas, Vec2S16 needed_size)
{
ProfBeginFunction();
//- rjf: find node with best-fit size
Vec2S16 region_p0 = {0};
Vec2S16 region_sz = {0};
@@ -356,15 +354,12 @@ fnt_atlas_region_alloc(Arena *arena, FNT_Atlas *atlas, Vec2S16 needed_size)
Rng2S16 result = {0};
result.p0 = region_p0;
result.p1 = add_2s16(region_p0, region_sz);
ProfEnd();
return result;
}
internal void
fnt_atlas_region_release(FNT_Atlas *atlas, Rng2S16 region)
{
ProfBeginFunction();
//- rjf: extract region size
Vec2S16 region_size = v2s16(region.x1 - region.x0, region.y1 - region.y0);
@@ -463,7 +458,6 @@ fnt_atlas_region_release(FNT_Atlas *atlas, Rng2S16 region)
}
}
}
ProfEnd();
}
////////////////////////////////
@@ -542,7 +536,6 @@ fnt_hash2style_from_tag_size_flags(FNT_Tag tag, F32 size, FNT_RasterFlags flags)
//- rjf: style hash -> style node
FNT_Hash2StyleRasterCacheNode *hash2style_node = 0;
{
ProfBegin("style hash -> style node");
U64 slot_idx = style_hash%fnt_state->hash2style_slots_count;
FNT_Hash2StyleRasterCacheSlot *slot = &fnt_state->hash2style_slots[slot_idx];
for(FNT_Hash2StyleRasterCacheNode *n = slot->first;
@@ -567,7 +560,6 @@ fnt_hash2style_from_tag_size_flags(FNT_Tag tag, F32 size, FNT_RasterFlags flags)
hash2style_node->hash2info_slots_count = 1024;
hash2style_node->hash2info_slots = push_array(fnt_state->raster_arena, FNT_Hash2InfoRasterCacheSlot, hash2style_node->hash2info_slots_count);
}
ProfEnd();
}
return hash2style_node;
@@ -576,8 +568,6 @@ fnt_hash2style_from_tag_size_flags(FNT_Tag tag, F32 size, FNT_RasterFlags flags)
internal FNT_Run
fnt_run_from_string(FNT_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, FNT_RasterFlags flags, String8 string)
{
ProfBeginFunction();
//- rjf: map tag/size to style node
FNT_Hash2StyleRasterCacheNode *hash2style_node = fnt_hash2style_from_tag_size_flags(tag, size, flags);
@@ -615,8 +605,6 @@ fnt_run_from_string(FNT_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, F
run = run_node->run;
}
else
ProfScope("no run node? -> cache miss")
ProfScope("compute & build & fill node for '%.*s'", str8_varg(string))
{
//- rjf: decode string & produce run pieces
FNT_PieceChunkList piece_chunks = {0};
@@ -700,7 +688,6 @@ fnt_run_from_string(FNT_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, F
//- rjf: no info found -> miss... fill this hash in the cache
if(info == 0)
{
ProfBegin("no info found -> miss... fill this hash in the cache");
Temp scratch = scratch_begin(0, 0);
// rjf: grab font handle for this tag if we don't have one already
@@ -822,7 +809,6 @@ fnt_run_from_string(FNT_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, F
}
scratch_end(scratch);
ProfEnd();
}
//- rjf: push piece for this raster portion
@@ -898,7 +884,6 @@ fnt_run_from_string(FNT_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, F
run_node->run = run;
}
ProfEnd();
return run;
}
@@ -1018,20 +1003,17 @@ fnt_wrapped_string_lines_from_font_size_string_max(Arena *arena, FNT_Tag font, F
internal Vec2F32
fnt_dim_from_tag_size_string(FNT_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
Vec2F32 result = {0};
FNT_Run run = fnt_run_from_string(tag, size, base_align_px, tab_size_px, 0, string);
result = run.dim;
scratch_end(scratch);
ProfEnd();
return result;
}
internal Vec2F32
fnt_dim_from_tag_size_string_list(FNT_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8List list)
{
ProfBeginFunction();
Vec2F32 sum = {0};
for(String8Node *n = list.first; n != 0; n = n->next)
{
@@ -1039,7 +1021,6 @@ fnt_dim_from_tag_size_string_list(FNT_Tag tag, F32 size, F32 base_align_px, F32
sum.x += str_dim.x;
sum.y = Max(sum.y, str_dim.y);
}
ProfEnd();
return sum;
}
@@ -1084,7 +1065,6 @@ fnt_char_pos_from_tag_size_string_p(FNT_Tag tag, F32 size, F32 base_align_px, F3
internal FNT_Metrics
fnt_metrics_from_tag_size(FNT_Tag tag, F32 size)
{
ProfBeginFunction();
FP_Metrics metrics = fnt_fp_metrics_from_tag(tag);
FNT_Metrics result = {0};
{
@@ -1093,7 +1073,6 @@ fnt_metrics_from_tag_size(FNT_Tag tag, F32 size)
result.line_gap = floor_f32(size) * metrics.line_gap / metrics.design_units_per_em;
result.capital_height = floor_f32(size) * metrics.capital_height / metrics.design_units_per_em;
}
ProfEnd();
return result;
}
-366
View File
@@ -1,366 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#undef LAYER_COLOR
#define LAYER_COLOR 0xe34cd4ff
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
geo_init(void)
{
Arena *arena = arena_alloc();
geo_shared = push_array(arena, GEO_Shared, 1);
geo_shared->arena = arena;
geo_shared->slots_count = 1024;
geo_shared->stripes_count = Min(geo_shared->slots_count, os_get_system_info()->logical_processor_count);
geo_shared->slots = push_array(arena, GEO_Slot, geo_shared->slots_count);
geo_shared->stripes = push_array(arena, GEO_Stripe, geo_shared->stripes_count);
geo_shared->stripes_free_nodes = push_array(arena, GEO_Node *, geo_shared->stripes_count);
for(U64 idx = 0; idx < geo_shared->stripes_count; idx += 1)
{
geo_shared->stripes[idx].arena = arena_alloc();
geo_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
geo_shared->stripes[idx].cv = os_condition_variable_alloc();
}
geo_shared->u2x_ring_size = KB(64);
geo_shared->u2x_ring_base = push_array_no_zero(arena, U8, geo_shared->u2x_ring_size);
geo_shared->u2x_ring_cv = os_condition_variable_alloc();
geo_shared->u2x_ring_mutex = os_mutex_alloc();
geo_shared->evictor_thread = os_thread_launch(geo_evictor_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Thread Context Initialization
internal void
geo_tctx_ensure_inited(void)
{
if(geo_tctx == 0)
{
Arena *arena = arena_alloc();
geo_tctx = push_array(arena, GEO_TCTX, 1);
geo_tctx->arena = arena;
}
}
////////////////////////////////
//~ rjf: Scoped Access
internal GEO_Scope *
geo_scope_open(void)
{
geo_tctx_ensure_inited();
GEO_Scope *scope = geo_tctx->free_scope;
if(scope)
{
SLLStackPop(geo_tctx->free_scope);
}
else
{
scope = push_array_no_zero(geo_tctx->arena, GEO_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
geo_scope_close(GEO_Scope *scope)
{
for(GEO_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next)
{
U128 hash = touch->hash;
next = touch->next;
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
SLLStackPush(geo_tctx->free_touch, touch);
}
SLLStackPush(geo_tctx->free_scope, scope);
}
internal void
geo_scope_touch_node__stripe_r_guarded(GEO_Scope *scope, GEO_Node *node)
{
GEO_Touch *touch = geo_tctx->free_touch;
ins_atomic_u64_inc_eval(&node->scope_ref_count);
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, update_tick_idx());
if(touch != 0)
{
SLLStackPop(geo_tctx->free_touch);
}
else
{
touch = push_array_no_zero(geo_tctx->arena, GEO_Touch, 1);
}
MemoryZeroStruct(touch);
touch->hash = node->hash;
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal R_Handle
geo_buffer_from_hash(GEO_Scope *scope, U128 hash)
{
R_Handle handle = {0};
if(!u128_match(hash, u128_zero()))
{
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
B32 found = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
handle = n->buffer;
found = !r_handle_match(r_handle_zero(), handle);
geo_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
B32 node_is_new = 0;
if(!found)
{
OS_MutexScopeW(stripe->rw_mutex)
{
GEO_Node *node = 0;
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
node = n;
break;
}
}
if(node == 0)
{
node = geo_shared->stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(geo_shared->stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array_no_zero(stripe->arena, GEO_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
node->hash = hash;
node_is_new = 1;
}
}
}
if(node_is_new)
{
geo_u2x_enqueue_req(hash, max_U64);
async_push_work(geo_xfer_work);
}
}
return handle;
}
internal R_Handle
geo_buffer_from_key(GEO_Scope *scope, HS_Key key)
{
R_Handle handle = {0};
for(U64 rewind_idx = 0; rewind_idx < HS_KEY_HASH_HISTORY_COUNT; rewind_idx += 1)
{
U128 hash = hs_hash_from_key(key, rewind_idx);
handle = geo_buffer_from_hash(scope, hash);
if(!r_handle_match(handle, r_handle_zero()))
{
break;
}
}
return handle;
}
////////////////////////////////
//~ rjf: Transfer Threads
internal B32
geo_u2x_enqueue_req(U128 hash, U64 endt_us)
{
B32 good = 0;
OS_MutexScope(geo_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = geo_shared->u2x_ring_write_pos-geo_shared->u2x_ring_read_pos;
U64 available_size = geo_shared->u2x_ring_size-unconsumed_size;
if(available_size >= sizeof(hash))
{
good = 1;
geo_shared->u2x_ring_write_pos += ring_write_struct(geo_shared->u2x_ring_base, geo_shared->u2x_ring_size, geo_shared->u2x_ring_write_pos, &hash);
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(geo_shared->u2x_ring_cv, geo_shared->u2x_ring_mutex, endt_us);
}
if(good)
{
os_condition_variable_broadcast(geo_shared->u2x_ring_cv);
}
return good;
}
internal void
geo_u2x_dequeue_req(U128 *hash_out)
{
OS_MutexScope(geo_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = geo_shared->u2x_ring_write_pos-geo_shared->u2x_ring_read_pos;
if(unconsumed_size >= sizeof(*hash_out))
{
geo_shared->u2x_ring_read_pos += ring_read_struct(geo_shared->u2x_ring_base, geo_shared->u2x_ring_size, geo_shared->u2x_ring_read_pos, hash_out);
break;
}
os_condition_variable_wait(geo_shared->u2x_ring_cv, geo_shared->u2x_ring_mutex, max_U64);
}
os_condition_variable_broadcast(geo_shared->u2x_ring_cv);
}
ASYNC_WORK_DEF(geo_xfer_work)
{
ProfBeginFunction();
HS_Scope *scope = hs_scope_open();
//- rjf: decode
U128 hash = {0};
geo_u2x_dequeue_req(&hash);
//- rjf: unpack hash
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
//- rjf: take task
B32 got_task = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0);
break;
}
}
}
//- rjf: hash -> data
String8 data = {0};
if(got_task)
{
data = hs_data_from_hash(scope, hash);
}
//- rjf: data -> buffer
R_Handle buffer = {0};
if(got_task && data.size != 0)
{
buffer = r_buffer_alloc(R_ResourceKind_Static, data.size, data.str);
}
//- rjf: commit results to cache
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
n->buffer = buffer;
ins_atomic_u32_eval_assign(&n->is_working, 0);
ins_atomic_u64_inc_eval(&n->load_count);
break;
}
}
}
hs_scope_close(scope);
ProfEnd();
return 0;
}
////////////////////////////////
//~ rjf: Evictor Threads
internal void
geo_evictor_thread__entry_point(void *p)
{
ThreadNameF("[geo] evictor thread");
for(;;)
{
U64 check_time_us = os_now_microseconds();
U64 check_time_user_clocks = update_tick_idx();
U64 evict_threshold_us = 10*1000000;
U64 evict_threshold_user_clocks = 10;
for(U64 slot_idx = 0; slot_idx < geo_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
DLLRemove(slot->first, slot->last, n);
if(!r_handle_match(n->buffer, r_handle_zero()))
{
r_buffer_release(n->buffer);
}
SLLStackPush(geo_shared->stripes_free_nodes[stripe_idx], n);
}
}
}
os_sleep_milliseconds(5);
}
os_sleep_milliseconds(1000);
}
}
-135
View File
@@ -1,135 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef GEO_CACHE_H
#define GEO_CACHE_H
////////////////////////////////
//~ rjf: Cache Types
typedef struct GEO_Node GEO_Node;
struct GEO_Node
{
GEO_Node *next;
GEO_Node *prev;
U128 hash;
R_Handle buffer;
B32 is_working;
U64 scope_ref_count;
U64 last_time_touched_us;
U64 last_user_clock_idx_touched;
U64 load_count;
};
typedef struct GEO_Slot GEO_Slot;
struct GEO_Slot
{
GEO_Node *first;
GEO_Node *last;
};
typedef struct GEO_Stripe GEO_Stripe;
struct GEO_Stripe
{
Arena *arena;
OS_Handle rw_mutex;
OS_Handle cv;
};
////////////////////////////////
//~ rjf: Scoped Access
typedef struct GEO_Touch GEO_Touch;
struct GEO_Touch
{
GEO_Touch *next;
U128 hash;
};
typedef struct GEO_Scope GEO_Scope;
struct GEO_Scope
{
GEO_Scope *next;
GEO_Touch *top_touch;
};
////////////////////////////////
//~ rjf: Thread Context
typedef struct GEO_TCTX GEO_TCTX;
struct GEO_TCTX
{
Arena *arena;
GEO_Scope *free_scope;
GEO_Touch *free_touch;
};
////////////////////////////////
//~ rjf: Shared State
typedef struct GEO_Shared GEO_Shared;
struct GEO_Shared
{
Arena *arena;
// rjf: cache
U64 slots_count;
U64 stripes_count;
GEO_Slot *slots;
GEO_Stripe *stripes;
GEO_Node **stripes_free_nodes;
// rjf: user -> xfer thread
U64 u2x_ring_size;
U8 *u2x_ring_base;
U64 u2x_ring_write_pos;
U64 u2x_ring_read_pos;
OS_Handle u2x_ring_cv;
OS_Handle u2x_ring_mutex;
// rjf: evictor thread
OS_Handle evictor_thread;
};
////////////////////////////////
//~ rjf: Globals
thread_static GEO_TCTX *geo_tctx = 0;
global GEO_Shared *geo_shared = 0;
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void geo_init(void);
////////////////////////////////
//~ rjf: Thread Context Initialization
internal void geo_tctx_ensure_inited(void);
////////////////////////////////
//~ rjf: Scoped Access
internal GEO_Scope *geo_scope_open(void);
internal void geo_scope_close(GEO_Scope *scope);
internal void geo_scope_touch_node__stripe_r_guarded(GEO_Scope *scope, GEO_Node *node);
////////////////////////////////
//~ rjf: Cache Lookups
internal R_Handle geo_buffer_from_hash(GEO_Scope *scope, U128 hash);
internal R_Handle geo_buffer_from_key(GEO_Scope *scope, HS_Key key);
////////////////////////////////
//~ rjf: Transfer Threads
internal B32 geo_u2x_enqueue_req(U128 hash, U64 endt_us);
internal void geo_u2x_dequeue_req(U128 *hash_out);
ASYNC_WORK_DEF(geo_xfer_work);
////////////////////////////////
//~ rjf: Evictor Threads
internal void geo_evictor_thread__entry_point(void *p);
#endif //GEO_CACHE_H
-596
View File
@@ -1,596 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#undef LAYER_COLOR
#define LAYER_COLOR 0x684123ff
////////////////////////////////
//~ rjf: Basic Helpers
#if !defined(XXH_IMPLEMENTATION)
# define XXH_IMPLEMENTATION
# define XXH_STATIC_LINKING_ONLY
# include "third_party/xxHash/xxhash.h"
#endif
internal U64
hs_little_hash_from_data(String8 data)
{
U64 result = XXH3_64bits(data.str, data.size);
return result;
}
internal U128
hs_hash_from_data(String8 data)
{
U128 u128 = {0};
XXH128_hash_t hash = XXH3_128bits(data.str, data.size);
MemoryCopy(&u128, &hash, sizeof(u128));
return u128;
}
internal HS_ID
hs_id_make(U64 u64_0, U64 u64_1)
{
HS_ID id;
id.u128[0].u64[0] = u64_0;
id.u128[0].u64[1] = u64_1;
return id;
}
internal B32
hs_id_match(HS_ID a, HS_ID b)
{
B32 result = MemoryMatchStruct(&a, &b);
return result;
}
internal HS_Key
hs_key_make(HS_Root root, HS_ID id)
{
HS_Key key = {root, 0, id};
return key;
}
internal B32
hs_key_match(HS_Key a, HS_Key b)
{
return (MemoryMatchStruct(&a.root, &b.root) && hs_id_match(a.id, b.id));
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
hs_init(void)
{
Arena *arena = arena_alloc();
hs_shared = push_array(arena, HS_Shared, 1);
hs_shared->arena = arena;
hs_shared->slots_count = 4096;
hs_shared->stripes_count = Min(hs_shared->slots_count, os_get_system_info()->logical_processor_count);
hs_shared->slots = push_array(arena, HS_Slot, hs_shared->slots_count);
hs_shared->stripes = push_array(arena, HS_Stripe, hs_shared->stripes_count);
hs_shared->stripes_free_nodes = push_array(arena, HS_Node *, hs_shared->stripes_count);
for(U64 idx = 0; idx < hs_shared->stripes_count; idx += 1)
{
HS_Stripe *stripe = &hs_shared->stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = os_rw_mutex_alloc();
stripe->cv = os_condition_variable_alloc();
}
hs_shared->key_slots_count = 4096;
hs_shared->key_stripes_count = Min(hs_shared->key_slots_count, os_get_system_info()->logical_processor_count);
hs_shared->key_slots = push_array(arena, HS_KeySlot, hs_shared->key_slots_count);
hs_shared->key_stripes = push_array(arena, HS_Stripe, hs_shared->key_stripes_count);
hs_shared->key_stripes_free_nodes = push_array(arena, HS_KeyNode *, hs_shared->key_stripes_count);
for(U64 idx = 0; idx < hs_shared->key_stripes_count; idx += 1)
{
HS_Stripe *stripe = &hs_shared->key_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = os_rw_mutex_alloc();
stripe->cv = os_condition_variable_alloc();
}
hs_shared->root_slots_count = 4096;
hs_shared->root_stripes_count = Min(hs_shared->root_slots_count, os_get_system_info()->logical_processor_count);
hs_shared->root_slots = push_array(arena, HS_RootSlot, hs_shared->root_slots_count);
hs_shared->root_stripes = push_array(arena, HS_Stripe, hs_shared->root_stripes_count);
hs_shared->root_stripes_free_nodes = push_array(arena, HS_RootNode *, hs_shared->root_stripes_count);
for(U64 idx = 0; idx < hs_shared->root_stripes_count; idx += 1)
{
HS_Stripe *stripe = &hs_shared->root_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = os_rw_mutex_alloc();
stripe->cv = os_condition_variable_alloc();
}
hs_shared->evictor_thread = os_thread_launch(hs_evictor_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Root Allocation/Deallocation
internal HS_Root
hs_root_alloc(void)
{
HS_Root root = {0};
root.u64[0] = ins_atomic_u64_inc_eval(&hs_shared->root_id_gen);
U64 slot_idx = root.u64[0]%hs_shared->root_slots_count;
U64 stripe_idx = slot_idx%hs_shared->root_stripes_count;
HS_RootSlot *slot = &hs_shared->root_slots[slot_idx];
HS_Stripe *stripe = &hs_shared->root_stripes[stripe_idx];
OS_MutexScopeW(stripe->rw_mutex)
{
HS_RootNode *node = hs_shared->root_stripes_free_nodes[stripe_idx];
if(node != 0)
{
SLLStackPop(hs_shared->root_stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array(stripe->arena, HS_RootNode, 1);
}
DLLPushBack(slot->first, slot->last, node);
node->root = root;
node->arena = arena_alloc();
}
return root;
}
internal void
hs_root_release(HS_Root root)
{
//- rjf: unpack root
U64 slot_idx = root.u64[0]%hs_shared->root_slots_count;
U64 stripe_idx = slot_idx%hs_shared->root_stripes_count;
HS_RootSlot *slot = &hs_shared->root_slots[slot_idx];
HS_Stripe *stripe = &hs_shared->root_stripes[stripe_idx];
//- rjf: release root node, grab its arena / ID list
Arena *root_arena = 0;
HS_RootIDChunkList root_ids = {0};
OS_MutexScopeW(stripe->rw_mutex)
{
for(HS_RootNode *n = slot->first; n != 0; n = n->next)
{
if(MemoryMatchStruct(&root, &n->root))
{
DLLRemove(slot->first, slot->last, n);
root_arena = n->arena;
root_ids = n->ids;
SLLStackPush(hs_shared->root_stripes_free_nodes[stripe_idx], n);
break;
}
}
}
//- rjf: release all IDs
for(HS_RootIDChunkNode *id_chunk_n = root_ids.first; id_chunk_n != 0; id_chunk_n = id_chunk_n->next)
{
for EachIndex(chunk_idx, id_chunk_n->count)
{
HS_ID id = id_chunk_n->v[chunk_idx];
HS_Key key = hs_key_make(root, id);
U64 key_hash = hs_little_hash_from_data(str8_struct(&key));
U64 key_slot_idx = key_hash%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
OS_MutexScopeW(key_stripe->rw_mutex)
{
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(hs_key_match(n->key, key))
{
// rjf: release reference to all hashes
for(U64 history_idx = 0; history_idx < HS_KEY_HASH_HISTORY_STRONG_REF_COUNT && history_idx < n->hash_history_gen; history_idx += 1)
{
U128 hash = n->hash_history[(n->hash_history_gen+history_idx)%ArrayCount(n->hash_history)];
U64 hash_slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 hash_stripe_idx = hash_slot_idx%hs_shared->stripes_count;
HS_Slot *hash_slot = &hs_shared->slots[hash_slot_idx];
HS_Stripe *hash_stripe = &hs_shared->stripes[hash_stripe_idx];
OS_MutexScopeR(hash_stripe->rw_mutex)
{
for(HS_Node *n = hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
// rjf: release key node
DLLRemove(key_slot->first, key_slot->last, n);
SLLStackPush(hs_shared->key_stripes_free_nodes[key_stripe_idx], n);
break;
}
}
}
}
}
}
////////////////////////////////
//~ rjf: Cache Submission
internal U128
hs_submit_data(HS_Key key, Arena **data_arena, String8 data)
{
U64 key_hash = hs_little_hash_from_data(str8_struct(&key));
U64 key_slot_idx = key_hash%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
U128 hash = hs_hash_from_data(data);
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
//- rjf: commit data to cache - if already there, just bump key refcount
ProfScope("commit data to cache - if already there, just bump key refcount") OS_MutexScopeW(stripe->rw_mutex)
{
HS_Node *existing_node = 0;
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
existing_node = n;
break;
}
}
if(existing_node == 0)
{
HS_Node *node = hs_shared->stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(hs_shared->stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array(stripe->arena, HS_Node, 1);
}
node->hash = hash;
if(data_arena != 0)
{
node->arena = *data_arena;
}
node->data = data;
node->scope_ref_count = 0;
node->key_ref_count = 1;
DLLPushBack(slot->first, slot->last, node);
}
else
{
existing_node->key_ref_count += 1;
if(data_arena != 0)
{
arena_release(*data_arena);
}
}
if(data_arena != 0)
{
*data_arena = 0;
}
}
//- rjf: commit this hash to key cache
U128 key_expired_hash = {0};
ProfScope("commit this hash to key cache") OS_MutexScopeW(key_stripe->rw_mutex)
{
// rjf: find existing key
B32 key_is_new = 0;
HS_KeyNode *key_node = 0;
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(hs_key_match(n->key, key))
{
key_node = n;
break;
}
}
// rjf: create key node if it doesn't exist
if(!key_node)
{
key_is_new = 1;
key_node = hs_shared->key_stripes_free_nodes[key_stripe_idx];
if(key_node)
{
SLLStackPop(hs_shared->key_stripes_free_nodes[key_stripe_idx]);
}
else
{
key_node = push_array(key_stripe->arena, HS_KeyNode, 1);
}
key_node->key = key;
DLLPushBack(key_slot->first, key_slot->last, key_node);
}
// rjf: push hash into key's history
if(key_node)
{
if(key_node->hash_history_gen >= HS_KEY_HASH_HISTORY_STRONG_REF_COUNT)
{
key_expired_hash = key_node->hash_history[(key_node->hash_history_gen-HS_KEY_HASH_HISTORY_STRONG_REF_COUNT)%ArrayCount(key_node->hash_history)];
}
key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)] = hash;
key_node->hash_history_gen += 1;
}
// rjf: key is new -> add this key to the associated root
if(key_is_new)
{
U64 root_hash = hs_little_hash_from_data(str8_struct(&key.root));
U64 root_slot_idx = root_hash%hs_shared->root_slots_count;
U64 root_stripe_idx = root_slot_idx%hs_shared->root_stripes_count;
HS_RootSlot *root_slot = &hs_shared->root_slots[root_slot_idx];
HS_Stripe *root_stripe = &hs_shared->root_stripes[root_stripe_idx];
OS_MutexScopeW(root_stripe->rw_mutex)
{
for(HS_RootNode *n = root_slot->first; n != 0; n = n->next)
{
if(MemoryMatchStruct(&n->root, &key.root))
{
HS_RootIDChunkNode *chunk = n->ids.last;
if(chunk == 0 || chunk->count >= chunk->cap)
{
chunk = push_array(n->arena, HS_RootIDChunkNode, 1);
SLLQueuePush(n->ids.first, n->ids.last, chunk);
n->ids.chunk_count += 1;
chunk->cap = 1024;
chunk->v = push_array_no_zero(n->arena, HS_ID, chunk->cap);
}
chunk->v[chunk->count] = key.id;
chunk->count += 1;
n->ids.total_count += 1;
break;
}
}
}
}
}
//- rjf: decrement key ref count of expired hash
ProfScope("decrement key ref count of expired hash")
if(!u128_match(key_expired_hash, u128_zero()))
{
U64 old_hash_slot_idx = key_expired_hash.u64[1]%hs_shared->slots_count;
U64 old_hash_stripe_idx = old_hash_slot_idx%hs_shared->stripes_count;
HS_Slot *old_hash_slot = &hs_shared->slots[old_hash_slot_idx];
HS_Stripe *old_hash_stripe = &hs_shared->stripes[old_hash_stripe_idx];
OS_MutexScopeR(old_hash_stripe->rw_mutex)
{
for(HS_Node *n = old_hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, key_expired_hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
return hash;
}
////////////////////////////////
//~ rjf: Scoped Access
internal HS_Scope *
hs_scope_open(void)
{
if(hs_tctx == 0)
{
Arena *arena = arena_alloc();
hs_tctx = push_array(arena, HS_TCTX, 1);
hs_tctx->arena = arena;
}
HS_Scope *scope = hs_tctx->free_scope;
if(scope)
{
SLLStackPop(hs_tctx->free_scope);
}
else
{
scope = push_array_no_zero(hs_tctx->arena, HS_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
hs_scope_close(HS_Scope *scope)
{
for(HS_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next)
{
U128 hash = touch->hash;
next = touch->next;
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
SLLStackPush(hs_tctx->free_touch, touch);
}
SLLStackPush(hs_tctx->free_scope, scope);
}
internal void
hs_scope_touch_node__stripe_r_guarded(HS_Scope *scope, HS_Node *node)
{
HS_Touch *touch = hs_tctx->free_touch;
ins_atomic_u64_inc_eval(&node->scope_ref_count);
if(touch != 0)
{
SLLStackPop(hs_tctx->free_touch);
}
else
{
touch = push_array_no_zero(hs_tctx->arena, HS_Touch, 1);
}
MemoryZeroStruct(touch);
touch->hash = node->hash;
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Downstream Accesses
internal void
hs_hash_downstream_inc(U128 hash)
{
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_inc_eval(&n->downstream_ref_count);
break;
}
}
}
}
internal void
hs_hash_downstream_dec(U128 hash)
{
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->downstream_ref_count);
break;
}
}
}
}
////////////////////////////////
//~ rjf: Cache Lookup
internal U128
hs_hash_from_key(HS_Key key, U64 rewind_count)
{
U128 result = {0};
U64 key_hash = hs_little_hash_from_data(str8_struct(&key));
U64 key_slot_idx = key_hash%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
OS_MutexScopeR(key_stripe->rw_mutex)
{
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(hs_key_match(n->key, key) && n->hash_history_gen > 0 && n->hash_history_gen-1 >= rewind_count)
{
result = n->hash_history[(n->hash_history_gen-1-rewind_count)%ArrayCount(n->hash_history)];
break;
}
}
}
return result;
}
internal String8
hs_data_from_hash(HS_Scope *scope, U128 hash)
{
ProfBeginFunction();
String8 result = {0};
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
result = n->data;
hs_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
ProfEnd();
return result;
}
////////////////////////////////
//~ rjf: Evictor Thread
internal void
hs_evictor_thread__entry_point(void *p)
{
ThreadNameF("[hs] evictor thread");
for(;;)
{
for(U64 slot_idx = 0; slot_idx < hs_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
U64 scope_ref_count = ins_atomic_u64_eval(&n->scope_ref_count);
U64 downstream_ref_count = ins_atomic_u64_eval(&n->downstream_ref_count);
if(key_ref_count == 0 && scope_ref_count == 0 && downstream_ref_count == 0)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(HS_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
U64 scope_ref_count = ins_atomic_u64_eval(&n->scope_ref_count);
U64 downstream_ref_count = ins_atomic_u64_eval(&n->downstream_ref_count);
if(key_ref_count == 0 && scope_ref_count == 0 && downstream_ref_count == 0)
{
DLLRemove(slot->first, slot->last, n);
SLLStackPush(hs_shared->stripes_free_nodes[stripe_idx], n);
if(n->arena != 0)
{
arena_release(n->arena);
}
}
}
}
}
os_sleep_milliseconds(1000);
}
}
-270
View File
@@ -1,270 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef HASH_STORE_H
#define HASH_STORE_H
////////////////////////////////
//~ NOTE(rjf): Hash Store Notes (2025/05/18)
//
// The hash store is a general-purpose data cache. It offers three layers of
// caching: (a) content (hash of data), (b) key (unique identity correllated
// with history of hashes), and (c) root (bucket for many keys, manually
// allocated / deallocated).
//
// (a) The "content" level of cache access is a simply hash(data) -> data
// mapping. This bypasses all identity/key/root mechanisms and provides a
// way to just talk about unique (and deduplicated) blobs of data.
//
// (b) The "key" level of cache access is used to encode a history of hashes
// for some unique "identity", where the "identity" is a concept managed
// by the user. One example of an identity would be a particular address
// range inside of some process to which the debugger is attached. Another
// might be a range inside of some file.
//
// (c) The "root" level is to provide a top-level allocation/deallocation
// mechanism for a large set of keys. It also provides an extra level of
// key uniqueness. For instance, each process to which the debugger is
// attached might have its own root, and each key might correspond to a
// particular address range within that process. This way, when the
// process ends, all of its keys can be easily destroyed using a single
// deallocation of the root.
//
// The way this might be generally used inside of the debugger would be that
// some evaluation - let's say it's some variable `x` - is mapped (via debug
// info) to some address range. If `x` is a `char[4096]`, then it might map
// to some address range [&x, &x + 4096). This, together with the process
// within which `x` is evaluated, forms both a `root` (for the process) and
// a `key` (for the address range). Some asynchronous memory streaming system
// can then, together with the root and key, read memory for that range, then
// submit that data to the hash store, correllating with the root and key
// combo.
////////////////////////////////
//~ rjf: Key Types
typedef struct HS_Root HS_Root;
struct HS_Root
{
U64 u64[1];
};
typedef struct HS_ID HS_ID;
struct HS_ID
{
U128 u128[1];
};
typedef struct HS_Key HS_Key;
struct HS_Key
{
HS_Root root;
U64 _padding_;
HS_ID id;
};
////////////////////////////////
//~ rjf: Cache Types
typedef struct HS_RootIDChunkNode HS_RootIDChunkNode;
struct HS_RootIDChunkNode
{
HS_RootIDChunkNode *next;
HS_ID *v;
U64 count;
U64 cap;
};
typedef struct HS_RootIDChunkList HS_RootIDChunkList;
struct HS_RootIDChunkList
{
HS_RootIDChunkNode *first;
HS_RootIDChunkNode *last;
U64 chunk_count;
U64 total_count;
};
typedef struct HS_RootNode HS_RootNode;
struct HS_RootNode
{
HS_RootNode *next;
HS_RootNode *prev;
Arena *arena;
HS_Root root;
HS_RootIDChunkList ids;
};
typedef struct HS_RootSlot HS_RootSlot;
struct HS_RootSlot
{
HS_RootNode *first;
HS_RootNode *last;
};
#define HS_KEY_HASH_HISTORY_COUNT 64
#define HS_KEY_HASH_HISTORY_STRONG_REF_COUNT 2
typedef struct HS_KeyNode HS_KeyNode;
struct HS_KeyNode
{
HS_KeyNode *next;
HS_KeyNode *prev;
HS_Key key;
U128 hash_history[HS_KEY_HASH_HISTORY_COUNT];
U64 hash_history_gen;
};
typedef struct HS_KeySlot HS_KeySlot;
struct HS_KeySlot
{
HS_KeyNode *first;
HS_KeyNode *last;
};
typedef struct HS_Node HS_Node;
struct HS_Node
{
HS_Node *next;
HS_Node *prev;
U128 hash;
Arena *arena;
String8 data;
U64 scope_ref_count;
U64 key_ref_count;
U64 downstream_ref_count;
};
typedef struct HS_Slot HS_Slot;
struct HS_Slot
{
HS_Node *first;
HS_Node *last;
};
typedef struct HS_Stripe HS_Stripe;
struct HS_Stripe
{
Arena *arena;
OS_Handle rw_mutex;
OS_Handle cv;
};
////////////////////////////////
//~ rjf: Scoped Access
typedef struct HS_Touch HS_Touch;
struct HS_Touch
{
HS_Touch *next;
U128 hash;
};
typedef struct HS_Scope HS_Scope;
struct HS_Scope
{
HS_Scope *next;
HS_Touch *top_touch;
};
////////////////////////////////
//~ rjf: Thread Context
typedef struct HS_TCTX HS_TCTX;
struct HS_TCTX
{
Arena *arena;
HS_Scope *free_scope;
HS_Touch *free_touch;
};
////////////////////////////////
//~ rjf: Shared State
typedef struct HS_Shared HS_Shared;
struct HS_Shared
{
Arena *arena;
// rjf: main data cache
U64 slots_count;
U64 stripes_count;
HS_Slot *slots;
HS_Stripe *stripes;
HS_Node **stripes_free_nodes;
// rjf: key cache
U64 key_slots_count;
U64 key_stripes_count;
HS_KeySlot *key_slots;
HS_Stripe *key_stripes;
HS_KeyNode **key_stripes_free_nodes;
// rjf: root cache
U64 root_slots_count;
U64 root_stripes_count;
HS_RootSlot *root_slots;
HS_Stripe *root_stripes;
HS_RootNode **root_stripes_free_nodes;
U64 root_id_gen;
// rjf: evictor thread
OS_Handle evictor_thread;
};
////////////////////////////////
//~ rjf: Globals
thread_static HS_TCTX *hs_tctx = 0;
global HS_Shared *hs_shared = 0;
////////////////////////////////
//~ rjf: Basic Helpers
internal U64 hs_little_hash_from_data(String8 data);
internal U128 hs_hash_from_data(String8 data);
internal HS_ID hs_id_make(U64 u64_0, U64 u64_1);
internal B32 hs_id_match(HS_ID a, HS_ID b);
internal HS_Key hs_key_make(HS_Root root, HS_ID id);
internal B32 hs_key_match(HS_Key a, HS_Key b);
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void hs_init(void);
////////////////////////////////
//~ rjf: Root Allocation/Deallocation
internal HS_Root hs_root_alloc(void);
internal void hs_root_release(HS_Root root);
////////////////////////////////
//~ rjf: Cache Submission
internal U128 hs_submit_data(HS_Key key, Arena **data_arena, String8 data);
////////////////////////////////
//~ rjf: Scoped Access
internal HS_Scope *hs_scope_open(void);
internal void hs_scope_close(HS_Scope *scope);
internal void hs_scope_touch_node__stripe_r_guarded(HS_Scope *scope, HS_Node *node);
////////////////////////////////
//~ rjf: Downstream Accesses
internal void hs_hash_downstream_inc(U128 hash);
internal void hs_hash_downstream_dec(U128 hash);
////////////////////////////////
//~ rjf: Cache Lookups
internal U128 hs_hash_from_key(HS_Key key, U64 rewind_count);
internal String8 hs_data_from_hash(HS_Scope *scope, U128 hash);
////////////////////////////////
//~ rjf: Evictor Thread
internal void hs_evictor_thread__entry_point(void *p);
#endif // HASH_STORE_H
+8 -48
View File
@@ -54,50 +54,6 @@ sizeof(RDI_NameMapNode),
sizeof(RDI_U8),
};
RDI_U8 rdi_section_is_required_table[40] =
{
0,
0,
1,
1,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
};
RDI_U16 rdi_eval_op_ctrlbits_table[52] =
{
RDI_EVAL_CTRLBITS(0, 0, 0),
@@ -177,11 +133,15 @@ struct {RDI_U8 *str; RDI_U64 size;} rdi_eval_conversion_kind_message_string_tabl
RDI_PROC RDI_U64
rdi_hash(RDI_U8 *ptr, RDI_U64 size)
{
RDI_U64 result = 5381;
RDI_U8 *opl = ptr + size;
for(;ptr < opl; ptr += 1)
RDI_U64 result = 0;
if(size != 0)
{
result = ((result << 5) + result) + *ptr;
result = 5381;
RDI_U8 *opl = ptr + size;
for(;ptr < opl; ptr += 1)
{
result = ((result << 5) + result) + *ptr;
}
}
return result;
}
+5 -6
View File
@@ -52,7 +52,7 @@ typedef int64_t RDI_S64;
// "raddbg\0\0"
#define RDI_MAGIC_CONSTANT 0x0000676264646172
#define RDI_ENCODING_VERSION 12
#define RDI_ENCODING_VERSION 13
////////////////////////////////////////////////////////////////
//~ Format Types & Functions
@@ -293,6 +293,8 @@ RDI_RegCodeX64_fds = 97,
RDI_RegCodeX64_fip = 98,
RDI_RegCodeX64_fdp = 99,
RDI_RegCodeX64_mxcsr_mask = 100,
RDI_RegCodeX64_cetmsr = 101,
RDI_RegCodeX64_cetssp = 102,
} RDI_RegCodeX64Enum;
typedef RDI_U32 RDI_BinarySectionFlags;
@@ -765,6 +767,8 @@ X(fds, 97)\
X(fip, 98)\
X(fdp, 99)\
X(mxcsr_mask, 100)\
X(cetmsr, 101)\
X(cetssp, 102)\
#define RDI_TopLevelInfo_XList \
X(RDI_Arch, arch)\
@@ -992,8 +996,6 @@ X(RDI_U32, voff_range_first)\
X(RDI_U32, voff_range_opl)\
X(RDI_U32, local_first)\
X(RDI_U32, local_count)\
X(RDI_U32, static_local_idx_run_first)\
X(RDI_U32, static_local_count)\
X(RDI_U32, inline_site_idx)\
#define RDI_InlineSite_XList \
@@ -1442,8 +1444,6 @@ RDI_U32 voff_range_first;
RDI_U32 voff_range_opl;
RDI_U32 local_first;
RDI_U32 local_count;
RDI_U32 static_local_idx_run_first;
RDI_U32 static_local_count;
RDI_U32 inline_site_idx;
};
@@ -1568,7 +1568,6 @@ RDI_PROC RDI_S32 rdi_eval_op_typegroup_are_compatible(RDI_EvalOp op, RDI_EvalTyp
RDI_PROC RDI_U8 *rdi_explanation_string_from_eval_conversion_kind(RDI_EvalConversionKind kind, RDI_U64 *size_out);
extern RDI_U16 rdi_section_element_size_table[40];
extern RDI_U8 rdi_section_is_required_table[40];
extern RDI_U16 rdi_eval_op_ctrlbits_table[52];
#endif // RDI_H
-21
View File
@@ -214,27 +214,6 @@ rdi_parse(RDI_U8 *data, RDI_U64 size, RDI_Parsed *out)
out->sections_count = dsec_count;
}
//////////////////////////////
//- rjf: validate results
//
if(result == RDI_ParseStatus_Good)
{
for(RDI_SectionKind k = (RDI_SectionKind)(RDI_SectionKind_NULL+1); k < RDI_SectionKind_COUNT; k = (RDI_SectionKind)(k+1))
{
if(rdi_section_is_required_table[k])
{
RDI_U64 data_size = 0;
RDI_SectionEncoding encoding = 0;
void *data = rdi_section_raw_data_from_kind(out, k, &encoding, &data_size);
if(data == 0 || data == &rdi_nil_element_union || data_size == 0)
{
result = RDI_ParseStatus_MissingRequiredSection;
break;
}
}
}
}
return result;
}
-1
View File
@@ -50,7 +50,6 @@ typedef enum RDI_ParseStatus
RDI_ParseStatus_HeaderDoesNotMatch = 1,
RDI_ParseStatus_UnsupportedVersionNumber = 2,
RDI_ParseStatus_InvalidDataSecionLayout = 3,
RDI_ParseStatus_MissingRequiredSection = 4,
}
RDI_ParseStatus;
+686 -2705
View File
File diff suppressed because it is too large Load Diff
+232 -150
View File
@@ -127,7 +127,7 @@ enum
// #define rdim_arena_alloc <name of your creation function - must be (void) -> Arena*>
// #define rdim_arena_release <name of your release function - must be (Arena*) -> void>
// #define rdim_arena_pos <name of your position function - must be (Arena*) -> U64>
// #define rdim_arena_push <name of your pushing function - must be (Arena*, U64 size) -> void*>
// #define rdim_arena_push <name of your pushing function - must be (Arena*, U64 size, U64 align, B32 zero) -> void*>
// #define rdim_arena_pop_to <name of your popping function - must be (Arena*, U64 pos) -> void>
#if !defined(RDIM_Arena)
@@ -318,6 +318,7 @@ X(Units, units)\
X(Procedures, procedures)\
X(GlobalVariables, global_variables)\
X(ThreadVariables, thread_variables)\
X(Constants, constants)\
X(Scopes, scopes)\
X(Locals, locals)\
X(Types, types)\
@@ -538,6 +539,7 @@ struct RDIM_SrcFile
RDIM_String8 path;
RDIM_SrcFileLineMapFragment *first_line_map_fragment;
RDIM_SrcFileLineMapFragment *last_line_map_fragment;
RDI_U64 total_line_count;
};
typedef struct RDIM_SrcFileChunkNode RDIM_SrcFileChunkNode;
@@ -655,7 +657,7 @@ struct RDIM_UnitChunkList
//~ rjf: Type System Node Types
typedef RDI_U32 RDIM_DataModel;
enum RDIM_DataModelEnum
typedef enum RDIM_DataModelEnum
{
RDIM_DataModel_Null,
RDIM_DataModel_ILP32,
@@ -663,7 +665,8 @@ enum RDIM_DataModelEnum
RDIM_DataModel_LP64,
RDIM_DataModel_ILP64,
RDIM_DataModel_SILP64
};
}
RDIM_DataModelEnum;
typedef struct RDIM_Type RDIM_Type;
struct RDIM_Type
@@ -718,6 +721,8 @@ struct RDIM_TypeChunkList
////////////////////////////////
//~ rjf: User-Defined-Type Info Types
//- rjf: UDT members
typedef struct RDIM_UDTMember RDIM_UDTMember;
struct RDIM_UDTMember
{
@@ -728,6 +733,8 @@ struct RDIM_UDTMember
RDI_U32 off;
};
//- rjf: UDT enum values
typedef struct RDIM_UDTEnumVal RDIM_UDTEnumVal;
struct RDIM_UDTEnumVal
{
@@ -736,6 +743,8 @@ struct RDIM_UDTEnumVal
RDI_U64 val;
};
//- rjf: UDTs
typedef struct RDIM_UDT RDIM_UDT;
struct RDIM_UDT
{
@@ -776,6 +785,8 @@ struct RDIM_UDTChunkList
////////////////////////////////
//~ rjf: Location Info Types
//- rjf: bytecode types
typedef struct RDIM_EvalBytecodeOp RDIM_EvalBytecodeOp;
struct RDIM_EvalBytecodeOp
{
@@ -794,8 +805,10 @@ struct RDIM_EvalBytecode
RDI_U32 encoded_size;
};
typedef struct RDIM_Location RDIM_Location;
struct RDIM_Location
//- rjf: location types
typedef struct RDIM_LocationInfo RDIM_LocationInfo;
struct RDIM_LocationInfo
{
RDI_LocationKind kind;
RDI_U8 reg_code;
@@ -803,20 +816,52 @@ struct RDIM_Location
RDIM_EvalBytecode bytecode;
};
typedef struct RDIM_Location RDIM_Location;
struct RDIM_Location
{
struct RDIM_LocationChunkNode *chunk;
RDIM_LocationInfo info;
RDI_U64 relative_encoding_off;
};
typedef struct RDIM_LocationChunkNode RDIM_LocationChunkNode;
struct RDIM_LocationChunkNode
{
RDIM_LocationChunkNode *next;
RDIM_Location *v;
RDI_U64 count;
RDI_U64 cap;
RDI_U64 base_idx;
RDI_U64 base_encoding_off;
RDI_U64 encoded_size;
};
typedef struct RDIM_LocationChunkList RDIM_LocationChunkList;
struct RDIM_LocationChunkList
{
RDIM_LocationChunkNode *first;
RDIM_LocationChunkNode *last;
RDI_U64 chunk_count;
RDI_U64 total_count;
RDI_U64 total_encoded_size;
};
//- rjf: location cases
typedef struct RDIM_LocationCase RDIM_LocationCase;
struct RDIM_LocationCase
{
RDIM_LocationCase *next;
RDIM_Rng1U64 voff_range;
RDIM_Location *location;
RDIM_Rng1U64 voff_range;
};
typedef struct RDIM_LocationSet RDIM_LocationSet;
struct RDIM_LocationSet
typedef struct RDIM_LocationCaseList RDIM_LocationCaseList;
struct RDIM_LocationCaseList
{
RDIM_LocationCase *first_location_case;
RDIM_LocationCase *last_location_case;
RDI_U64 location_case_count;
RDIM_LocationCase *first;
RDIM_LocationCase *last;
RDI_U64 count;
};
////////////////////////////////
@@ -834,7 +879,7 @@ struct RDIM_Symbol
RDIM_Symbol *container_symbol;
RDIM_Type *container_type;
struct RDIM_Scope *root_scope;
RDIM_LocationSet frame_base;
RDIM_LocationCaseList location_cases;
RDIM_String8 value_data;
};
@@ -900,7 +945,7 @@ struct RDIM_Local
RDI_LocalKind kind;
RDIM_String8 name;
RDIM_Type *type;
RDIM_LocationSet locset;
RDIM_LocationCaseList location_cases;
};
typedef struct RDIM_Scope RDIM_Scope;
@@ -938,7 +983,7 @@ struct RDIM_ScopeChunkList
RDI_U64 total_count;
RDI_U64 scope_voff_count;
RDI_U64 local_count;
RDI_U64 location_count;
RDI_U64 location_case_count;
};
////////////////////////////////
@@ -956,6 +1001,7 @@ struct RDIM_BakeParams
RDIM_UDTChunkList udts;
RDIM_SrcFileChunkList src_files;
RDIM_LineTableChunkList line_tables;
RDIM_LocationChunkList locations;
RDIM_SymbolChunkList global_variables;
RDIM_SymbolChunkList thread_variables;
RDIM_SymbolChunkList constants;
@@ -1049,27 +1095,51 @@ struct RDIM_BakeStringMapTight
//- rjf: index runs
typedef struct RDIM_BakeIdxRunNode RDIM_BakeIdxRunNode;
struct RDIM_BakeIdxRunNode
typedef struct RDIM_BakeIdxRun RDIM_BakeIdxRun;
struct RDIM_BakeIdxRun
{
RDIM_BakeIdxRunNode *hash_next;
RDIM_BakeIdxRunNode *order_next;
RDI_U32 *idx_run;
RDI_U64 hash;
RDI_U32 count;
RDI_U32 first_idx;
RDI_U64 count;
RDI_U32 *idxes;
};
typedef struct RDIM_BakeIdxRunChunkNode RDIM_BakeIdxRunChunkNode;
struct RDIM_BakeIdxRunChunkNode
{
RDIM_BakeIdxRunChunkNode *next;
RDIM_BakeIdxRun *v;
RDI_U64 count;
RDI_U64 cap;
};
typedef struct RDIM_BakeIdxRunChunkList RDIM_BakeIdxRunChunkList;
struct RDIM_BakeIdxRunChunkList
{
RDIM_BakeIdxRunChunkNode *first;
RDIM_BakeIdxRunChunkNode *last;
RDI_U64 chunk_count;
RDI_U64 total_count;
};
typedef struct RDIM_BakeIdxRunMapTopology RDIM_BakeIdxRunMapTopology;
struct RDIM_BakeIdxRunMapTopology
{
RDI_U64 slots_count;
};
typedef struct RDIM_BakeIdxRunMapLoose RDIM_BakeIdxRunMapLoose;
struct RDIM_BakeIdxRunMapLoose
{
RDIM_BakeIdxRunChunkList **slots;
RDI_U64 *slots_idx_counts;
};
typedef struct RDIM_BakeIdxRunMap RDIM_BakeIdxRunMap;
struct RDIM_BakeIdxRunMap
{
RDIM_BakeIdxRunNode *order_first;
RDIM_BakeIdxRunNode *order_last;
RDIM_BakeIdxRunNode **slots;
RDIM_BakeIdxRunChunkList *slots;
RDI_U64 *slots_base_idxs; // NOTE(rjf): [slots_count+1], [slots_count] holds total count
RDI_U64 slots_count;
RDI_U64 slot_collision_count;
RDI_U32 count;
RDI_U32 idx_count;
};
//- rjf: source info & path tree
@@ -1105,33 +1175,43 @@ struct RDIM_BakePathTree
//- rjf: name maps
typedef struct RDIM_BakeNameMapValNode RDIM_BakeNameMapValNode;
struct RDIM_BakeNameMapValNode
typedef struct RDIM_BakeName RDIM_BakeName;
struct RDIM_BakeName
{
RDIM_BakeNameMapValNode *next;
RDI_U32 val[6];
RDIM_String8 string;
RDI_U64 hash;
RDI_U64 idx;
};
typedef struct RDIM_BakeNameMapNode RDIM_BakeNameMapNode;
struct RDIM_BakeNameMapNode
typedef struct RDIM_BakeNameChunkNode RDIM_BakeNameChunkNode;
struct RDIM_BakeNameChunkNode
{
RDIM_BakeNameMapNode *slot_next;
RDIM_BakeNameMapNode *order_next;
RDIM_String8 string;
RDIM_BakeNameMapValNode *val_first;
RDIM_BakeNameMapValNode *val_last;
RDI_U64 val_count;
RDIM_BakeNameChunkNode *next;
RDIM_BakeName *v;
RDI_U64 count;
RDI_U64 cap;
RDI_U64 base_idx;
};
typedef struct RDIM_BakeNameChunkList RDIM_BakeNameChunkList;
struct RDIM_BakeNameChunkList
{
RDIM_BakeNameChunkNode *first;
RDIM_BakeNameChunkNode *last;
RDI_U64 chunk_count;
RDI_U64 total_count;
};
typedef struct RDIM_BakeNameMapTopology RDIM_BakeNameMapTopology;
struct RDIM_BakeNameMapTopology
{
RDI_U64 slots_count;
};
typedef struct RDIM_BakeNameMap RDIM_BakeNameMap;
struct RDIM_BakeNameMap
{
RDIM_BakeNameMapNode **slots;
RDI_U64 slots_count;
RDI_U64 slot_collision_count;
RDIM_BakeNameMapNode *first;
RDIM_BakeNameMapNode *last;
RDI_U64 name_count;
RDIM_BakeNameChunkList **slots;
};
//- rjf: vmaps
@@ -1150,12 +1230,49 @@ struct RDIM_VMapMarker
RDI_U32 begin_range;
};
//- rjf: source line maps
typedef struct RDIM_BakeSrcLineMapNode RDIM_BakeSrcLineMapNode;
struct RDIM_BakeSrcLineMapNode
{
RDIM_BakeSrcLineMapNode *next;
RDI_U32 line_num;
RDIM_Rng1U64List voff_ranges;
};
typedef struct RDIM_BakeSrcLineMapSlot RDIM_BakeSrcLineMapSlot;
struct RDIM_BakeSrcLineMapSlot
{
RDIM_BakeSrcLineMapNode *first;
RDIM_BakeSrcLineMapNode *last;
};
typedef struct RDIM_BakeSrcLineMap RDIM_BakeSrcLineMap;
struct RDIM_BakeSrcLineMap
{
RDIM_BakeSrcLineMapSlot *slots;
RDI_U64 slots_count;
RDI_U64 line_count;
RDI_U64 voff_range_count;
};
//- rjf: line table records
typedef struct RDIM_LineRec RDIM_LineRec;
struct RDIM_LineRec
{
RDI_U32 file_id;
RDI_U32 line_num;
RDI_U16 col_first;
RDI_U16 col_opl;
};
//- rjf: baking results
typedef struct RDIM_TopLevelInfoBakeResult RDIM_TopLevelInfoBakeResult;
struct RDIM_TopLevelInfoBakeResult
{
RDI_TopLevelInfo *top_level_info;
RDI_TopLevelInfo top_level_info;
};
typedef struct RDIM_BinarySectionBakeResult RDIM_BinarySectionBakeResult;
@@ -1224,6 +1341,20 @@ struct RDIM_UDTBakeResult
RDI_U64 enum_members_count;
};
typedef struct RDIM_LocationBakeResult RDIM_LocationBakeResult;
struct RDIM_LocationBakeResult
{
RDI_U8 *location_data;
RDI_U64 location_data_size;
};
typedef struct RDIM_LocationBlockBakeResult RDIM_LocationBlockBakeResult;
struct RDIM_LocationBlockBakeResult
{
RDI_LocationBlock *location_blocks;
RDI_U64 location_blocks_count;
};
typedef struct RDIM_GlobalVariableBakeResult RDIM_GlobalVariableBakeResult;
struct RDIM_GlobalVariableBakeResult
{
@@ -1349,6 +1480,8 @@ struct RDIM_BakeResults
RDIM_FilePathBakeResult file_paths;
RDIM_StringBakeResult strings;
RDIM_IndexRunBakeResult idx_runs;
RDIM_LocationBakeResult locations;
RDIM_LocationBlockBakeResult location_blocks2;
RDIM_String8 location_blocks;
RDIM_String8 location_data;
};
@@ -1393,8 +1526,8 @@ RDI_PROC RDI_U64 rdim_arena_pos_fallback(RDIM_Arena *arena);
RDI_PROC void *rdim_arena_push_fallback(RDIM_Arena *arena, RDI_U64 align, RDI_U64 size);
RDI_PROC void rdim_arena_pop_to_fallback(RDIM_Arena *arena, RDI_U64 pos);
#endif
#define rdim_push_array_no_zero(a,T,c) (T*)rdim_arena_push((a), sizeof(T)*(c), RDIM_AlignOf(T))
#define rdim_push_array(a,T,c) (T*)rdim_memzero(rdim_push_array_no_zero(a,T,c), sizeof(T)*(c))
#define rdim_push_array_no_zero(a,T,c) (T*)rdim_arena_push((a), sizeof(T)*(c), RDIM_AlignOf(T), (0))
#define rdim_push_array(a,T,c) (T*)rdim_arena_push((a), sizeof(T)*(c), RDIM_AlignOf(T), (1))
//- rjf: thread-local scratch arenas
#if !defined (RDIM_SCRATCH_OVERRIDE)
@@ -1427,7 +1560,7 @@ RDI_PROC void rdim_rng1u64_list_push(RDIM_Arena *arena, RDIM_Rng1U64List *list,
RDI_PROC void rdim_rng1u64_chunk_list_push(RDIM_Arena *arena, RDIM_Rng1U64ChunkList *list, RDI_U64 chunk_cap, RDIM_Rng1U64 r);
////////////////////////////////
//~ Data Model
//~ rjf: [Building] Data Model
RDI_PROC RDI_TypeKind rdim_short_type_kind_from_data_model(RDIM_DataModel data_model);
RDI_PROC RDI_TypeKind rdim_unsigned_short_type_kind_from_data_model(RDIM_DataModel data_model);
@@ -1470,9 +1603,12 @@ RDI_PROC void rdim_unit_chunk_list_concat_in_place(RDIM_UnitChunkList *dst, RDIM
////////////////////////////////
//~ rjf: [Building] Type Info & UDT Building
//- rjf: type nodes
RDI_PROC RDIM_Type *rdim_type_chunk_list_push(RDIM_Arena *arena, RDIM_TypeChunkList *list, RDI_U64 cap);
RDI_PROC RDI_U64 rdim_idx_from_type(RDIM_Type *type);
RDI_PROC void rdim_type_chunk_list_concat_in_place(RDIM_TypeChunkList *dst, RDIM_TypeChunkList *to_push);
//- rjf: UDTs
RDI_PROC RDIM_UDT *rdim_udt_chunk_list_push(RDIM_Arena *arena, RDIM_UDTChunkList *list, RDI_U64 cap);
RDI_PROC RDI_U64 rdim_idx_from_udt(RDIM_UDT *udt);
RDI_PROC void rdim_udt_chunk_list_concat_in_place(RDIM_UDTChunkList *dst, RDIM_UDTChunkList *to_push);
@@ -1495,14 +1631,7 @@ RDI_PROC RDI_U64 rdim_idx_from_inline_site(RDIM_InlineSite *inline_site);
RDI_PROC void rdim_inline_site_chunk_list_concat_in_place(RDIM_InlineSiteChunkList *dst, RDIM_InlineSiteChunkList *to_push);
////////////////////////////////
//~ rjf: [Building] Scope Info Building
//- rjf: scopes
RDI_PROC RDIM_Scope *rdim_scope_chunk_list_push(RDIM_Arena *arena, RDIM_ScopeChunkList *list, RDI_U64 cap);
RDI_PROC RDI_U64 rdim_idx_from_scope(RDIM_Scope *scope);
RDI_PROC void rdim_scope_chunk_list_concat_in_place(RDIM_ScopeChunkList *dst, RDIM_ScopeChunkList *to_push);
RDI_PROC void rdim_scope_push_voff_range(RDIM_Arena *arena, RDIM_ScopeChunkList *list, RDIM_Scope *scope, RDIM_Rng1U64 range);
RDI_PROC RDIM_Local *rdim_scope_push_local(RDIM_Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *scope);
//~ rjf: [Building] Location Info Building
//- rjf: bytecode
RDI_PROC void rdim_bytecode_push_op(RDIM_Arena *arena, RDIM_EvalBytecode *bytecode, RDI_EvalOp op, RDI_U64 p);
@@ -1510,56 +1639,75 @@ RDI_PROC void rdim_bytecode_push_uconst(RDIM_Arena *arena, RDIM_EvalBytecode *by
RDI_PROC void rdim_bytecode_push_sconst(RDIM_Arena *arena, RDIM_EvalBytecode *bytecode, RDI_S64 x);
RDI_PROC void rdim_bytecode_concat_in_place(RDIM_EvalBytecode *left_dst, RDIM_EvalBytecode *right_destroyed);
//- rjf: individual locations
RDI_PROC RDIM_Location *rdim_push_location_addr_bytecode_stream(RDIM_Arena *arena, RDIM_EvalBytecode *bytecode);
RDI_PROC RDIM_Location *rdim_push_location_val_bytecode_stream(RDIM_Arena *arena, RDIM_EvalBytecode *bytecode);
RDI_PROC RDIM_Location *rdim_push_location_addr_reg_plus_u16(RDIM_Arena *arena, RDI_U8 reg_code, RDI_U16 offset);
RDI_PROC RDIM_Location *rdim_push_location_addr_addr_reg_plus_u16(RDIM_Arena *arena, RDI_U8 reg_code, RDI_U16 offset);
RDI_PROC RDIM_Location *rdim_push_location_val_reg(RDIM_Arena *arena, RDI_U8 reg_code);
//- rjf: location sets
RDI_PROC void rdim_location_set_push_case(RDIM_Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_LocationSet *locset, RDIM_Rng1U64 voff_range, RDIM_Location *location);
//- rjf: location block chunk list
RDI_PROC RDI_LocationBlock *rdim_location_block_chunk_list_push_array(RDIM_Arena *arena, RDIM_String8List *list, RDI_U32 count);
RDI_PROC RDI_U32 rdim_count_from_location_block_chunk_list(RDIM_String8List *list);
//- rjf: locations
RDI_PROC RDI_U64 rdim_encoded_size_from_location_info(RDIM_LocationInfo *info);
RDI_PROC RDIM_Location *rdim_location_chunk_list_push_new(RDIM_Arena *arena, RDIM_LocationChunkList *list, RDI_U64 cap, RDIM_LocationInfo *info);
RDI_PROC RDI_U64 rdim_off_from_location(RDIM_Location *location);
RDI_PROC void rdim_location_chunk_list_concat_in_place(RDIM_LocationChunkList *dst, RDIM_LocationChunkList *to_push);
////////////////////////////////
//~ rjf: [Baking Helpers] Baked VMap Building
//~ rjf: [Building] Scope Info Building
RDI_PROC RDIM_BakeVMap rdim_bake_vmap_from_markers(RDIM_Arena *arena, RDIM_VMapMarker *markers, RDIM_SortKey *keys, RDI_U64 marker_count);
RDI_PROC RDIM_Scope *rdim_scope_chunk_list_push(RDIM_Arena *arena, RDIM_ScopeChunkList *list, RDI_U64 cap);
RDI_PROC RDI_U64 rdim_idx_from_scope(RDIM_Scope *scope);
RDI_PROC void rdim_scope_chunk_list_concat_in_place(RDIM_ScopeChunkList *dst, RDIM_ScopeChunkList *to_push);
RDI_PROC void rdim_scope_push_voff_range(RDIM_Arena *arena, RDIM_ScopeChunkList *list, RDIM_Scope *scope, RDIM_Rng1U64 range);
RDI_PROC RDIM_Local *rdim_scope_push_local(RDIM_Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *scope);
RDI_PROC RDIM_LocationCase *rdim_local_push_location_case(RDIM_Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Local *local, RDIM_Location *location, RDIM_Rng1U64 voff_range);
////////////////////////////////
//~ rjf: [Baking Helpers] Interned / Deduplicated Blob Data Structure Helpers
//~ rjf: [Baking Helpers] Deduplicated String Baking Map
//- rjf: bake string chunk lists
//- rjf: chunk lists
RDI_PROC RDIM_BakeString *rdim_bake_string_chunk_list_push(RDIM_Arena *arena, RDIM_BakeStringChunkList *list, RDI_U64 cap);
RDI_PROC void rdim_bake_string_chunk_list_concat_in_place(RDIM_BakeStringChunkList *dst, RDIM_BakeStringChunkList *to_push);
RDI_PROC RDIM_BakeStringChunkList rdim_bake_string_chunk_list_sorted_from_unsorted(RDIM_Arena *arena, RDIM_BakeStringChunkList *src);
//- rjf: bake string chunk list maps
//- rjf: loose map
RDI_PROC RDIM_BakeStringMapLoose *rdim_bake_string_map_loose_make(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top);
RDI_PROC void rdim_bake_string_map_loose_insert(RDIM_Arena *arena, RDIM_BakeStringMapTopology *map_topology, RDIM_BakeStringMapLoose *map, RDI_U64 chunk_cap, RDIM_String8 string);
RDI_PROC void rdim_bake_string_map_loose_join_in_place(RDIM_BakeStringMapTopology *map_topology, RDIM_BakeStringMapLoose *dst, RDIM_BakeStringMapLoose *src);
RDI_PROC RDIM_BakeStringMapBaseIndices rdim_bake_string_map_base_indices_from_map_loose(RDIM_Arena *arena, RDIM_BakeStringMapTopology *map_topology, RDIM_BakeStringMapLoose *map);
//- rjf: finalized bake string map
RDI_PROC RDIM_BakeStringMapTight rdim_bake_string_map_tight_from_loose(RDIM_Arena *arena, RDIM_BakeStringMapTopology *map_topology, RDIM_BakeStringMapBaseIndices *map_base_indices, RDIM_BakeStringMapLoose *map);
//- rjf: finalized / tight map
RDI_PROC RDI_U32 rdim_bake_idx_from_string(RDIM_BakeStringMapTight *map, RDIM_String8 string);
////////////////////////////////
//~ rjf: [Baking Helpers] Deduplicated Index Run Baking Map
//- rjf: bake idx run map reading/writing
RDI_PROC RDI_U64 rdim_hash_from_idx_run(RDI_U32 *idx_run, RDI_U32 count);
RDI_PROC RDI_U32 rdim_bake_idx_from_idx_run(RDIM_BakeIdxRunMap *map, RDI_U32 *idx_run, RDI_U32 count);
RDI_PROC RDI_U32 rdim_bake_idx_run_map_insert(RDIM_Arena *arena, RDIM_BakeIdxRunMap *map, RDI_U32 *idx_run, RDI_U32 count);
//- rjf: bake path tree reading/writing
//- rjf: chunk lists
RDI_PROC RDIM_BakeIdxRun *rdim_bake_idx_run_chunk_list_push(RDIM_Arena *arena, RDIM_BakeIdxRunChunkList *list, RDI_U64 cap);
RDI_PROC void rdim_bake_idx_run_chunk_list_concat_in_place(RDIM_BakeIdxRunChunkList *dst, RDIM_BakeIdxRunChunkList *to_push);
RDI_PROC RDIM_BakeIdxRunChunkList rdim_bake_idx_run_chunk_list_sorted_from_unsorted(RDIM_Arena *arena, RDIM_BakeIdxRunChunkList *src);
//- rjf: loose map
RDI_PROC RDIM_BakeIdxRunMapLoose *rdim_bake_idx_run_map_loose_make(RDIM_Arena *arena, RDIM_BakeIdxRunMapTopology *top);
RDI_PROC void rdim_bake_idx_run_map_loose_insert(RDIM_Arena *arena, RDIM_BakeIdxRunMapTopology *map_topology, RDIM_BakeIdxRunMapLoose *map, RDI_U64 chunk_cap, RDI_U32 *idxes, RDI_U32 count);
//- rjf: finalized / tight map
RDI_PROC RDI_U32 rdim_bake_idx_from_idx_run(RDIM_BakeIdxRunMap *map, RDI_U32 *idxes, RDI_U32 count);
////////////////////////////////
//~ rjf: [Baking Helpers] Deduplicated Name Map Baking Map
//- rjf: chunk lists
RDI_PROC RDIM_BakeName *rdim_bake_name_chunk_list_push(RDIM_Arena *arena, RDIM_BakeNameChunkList *list, RDI_U64 cap);
RDI_PROC void rdim_bake_name_chunk_list_concat_in_place(RDIM_BakeNameChunkList *dst, RDIM_BakeNameChunkList *to_push);
RDI_PROC RDIM_BakeNameChunkList rdim_bake_name_chunk_list_sorted_from_unsorted(RDIM_Arena *arena, RDIM_BakeNameChunkList *src);
//- rjf: bake name chunk list maps
RDI_PROC RDIM_BakeNameMap *rdim_bake_name_map_make(RDIM_Arena *arena, RDIM_BakeNameMapTopology *top);
RDI_PROC void rdim_bake_name_map_insert(RDIM_Arena *arena, RDIM_BakeNameMapTopology *map_topology, RDIM_BakeNameMap *map, RDI_U64 chunk_cap, RDIM_String8 string, RDI_U64 idx);
////////////////////////////////
//~ rjf: [Baking Helpers] Deduplicated Path Baking Tree
RDI_PROC RDIM_BakePathNode *rdim_bake_path_node_from_string(RDIM_BakePathTree *tree, RDIM_String8 string);
RDI_PROC RDI_U32 rdim_bake_path_node_idx_from_string(RDIM_BakePathTree *tree, RDIM_String8 string);
RDI_PROC RDIM_BakePathNode *rdim_bake_path_tree_insert(RDIM_Arena *arena, RDIM_BakePathTree *tree, RDIM_String8 string);
//- rjf: bake name maps writing
RDI_PROC void rdim_bake_name_map_push(RDIM_Arena *arena, RDIM_BakeNameMap *map, RDIM_String8 string, RDI_U32 idx);
////////////////////////////////
//~ rjf: [Baking Helpers] Data Section List Building Helpers
@@ -1567,72 +1715,6 @@ RDI_PROC RDIM_BakeSection *rdim_bake_section_list_push(RDIM_Arena *arena, RDIM_B
RDI_PROC RDIM_BakeSection *rdim_bake_section_list_push_new_unpacked(RDIM_Arena *arena, RDIM_BakeSectionList *list, void *data, RDI_U64 size, RDI_SectionKind tag, RDI_U64 tag_idx);
RDI_PROC void rdim_bake_section_list_concat_in_place(RDIM_BakeSectionList *dst, RDIM_BakeSectionList *to_push);
////////////////////////////////
//~ rjf: [Baking] Build Artifacts -> Interned/Deduplicated Data Structures
//- rjf: basic bake string gathering passes
RDI_PROC void rdim_bake_string_map_loose_push_top_level_info(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_TopLevelInfo *tli);
RDI_PROC void rdim_bake_string_map_loose_push_binary_sections(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_BinarySectionList *secs);
RDI_PROC void rdim_bake_string_map_loose_push_path_tree(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_BakePathTree *path_tree);
//- rjf: slice-granularity bake string gathering passes
RDI_PROC void rdim_bake_string_map_loose_push_src_file_slice(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_SrcFile *v, RDI_U64 count);
RDI_PROC void rdim_bake_string_map_loose_push_unit_slice(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_Unit *v, RDI_U64 count);
RDI_PROC void rdim_bake_string_map_loose_push_type_slice(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_Type *v, RDI_U64 count);
RDI_PROC void rdim_bake_string_map_loose_push_udt_slice(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_UDT *v, RDI_U64 count);
RDI_PROC void rdim_bake_string_map_loose_push_symbol_slice(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_Symbol *v, RDI_U64 count);
RDI_PROC void rdim_bake_string_map_loose_push_inline_site_slice(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_InlineSite *v, RDI_U64 count);
RDI_PROC void rdim_bake_string_map_loose_push_scope_slice(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_Scope *v, RDI_U64 count);
//- rjf: list-granularity bake string gathering passes
RDI_PROC void rdim_bake_string_map_loose_push_src_files(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_SrcFileChunkList *list);
RDI_PROC void rdim_bake_string_map_loose_push_units(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_UnitChunkList *list);
RDI_PROC void rdim_bake_string_map_loose_push_types(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_TypeChunkList *list);
RDI_PROC void rdim_bake_string_map_loose_push_udts(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_UDTChunkList *list);
RDI_PROC void rdim_bake_string_map_loose_push_symbols(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_SymbolChunkList *list);
RDI_PROC void rdim_bake_string_map_loose_push_scopes(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_ScopeChunkList *list);
//- rjf: bake name map building
RDI_PROC RDIM_BakeNameMap *rdim_bake_name_map_from_kind_params(RDIM_Arena *arena, RDI_NameMapKind kind, RDIM_BakeParams *params);
//- rjf: bake idx run map building
RDI_PROC RDIM_BakeIdxRunMap *rdim_bake_idx_run_map_from_params(RDIM_Arena *arena, RDIM_BakeNameMap *name_maps[RDI_NameMapKind_COUNT], RDIM_BakeParams *params);
//- rjf: bake path tree building
RDI_PROC RDIM_BakePathTree *rdim_bake_path_tree_from_params(RDIM_Arena *arena, RDIM_BakeParams *params);
////////////////////////////////
//~ rjf: [Baking] Build Artifacts -> Baked Versions
//- rjf: partial/joinable baking functions
RDI_PROC RDIM_NameMapBakeResult rdim_bake_name_map(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakeIdxRunMap *idx_runs, RDIM_BakeNameMap *src);
//- rjf: partial bakes -> final bake functions
RDI_PROC RDIM_NameMapBakeResult rdim_name_map_bake_results_combine(RDIM_Arena *arena, RDIM_NameMapBakeResult *results, RDI_U64 results_count);
//- rjf: independent (top-level, global) baking functions
RDI_PROC RDIM_TopLevelInfoBakeResult rdim_bake_top_level_info(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_TopLevelInfo *src);
RDI_PROC RDIM_BinarySectionBakeResult rdim_bake_binary_sections(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BinarySectionList *src);
RDI_PROC RDIM_UnitBakeResult rdim_bake_units(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakePathTree *path_tree, RDIM_UnitChunkList *src);
RDI_PROC RDIM_UnitVMapBakeResult rdim_bake_unit_vmap(RDIM_Arena *arena, RDIM_UnitChunkList *units);
RDI_PROC RDIM_SrcFileBakeResult rdim_bake_src_files(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakePathTree *path_tree, RDIM_SrcFileChunkList *src);
RDI_PROC RDIM_LineTableBakeResult rdim_bake_line_tables(RDIM_Arena *arena, RDIM_LineTableChunkList *src);
RDI_PROC RDIM_TypeNodeBakeResult rdim_bake_types(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakeIdxRunMap *idx_runs, RDIM_TypeChunkList *src);
RDI_PROC RDIM_UDTBakeResult rdim_bake_udts(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_UDTChunkList *src);
RDI_PROC RDIM_GlobalVariableBakeResult rdim_bake_global_variables(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_SymbolChunkList *src);
RDI_PROC RDIM_GlobalVMapBakeResult rdim_bake_global_vmap(RDIM_Arena *arena, RDIM_SymbolChunkList *src);
RDI_PROC RDIM_ThreadVariableBakeResult rdim_bake_thread_variables(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_SymbolChunkList *src);
RDI_PROC RDIM_ConstantsBakeResult rdim_bake_constants(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_SymbolChunkList *src);
RDI_PROC RDIM_ProcedureBakeResult rdim_bake_procedures(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_String8List *location_blocks, RDIM_String8List *location_data_blobs, RDIM_SymbolChunkList *src);
RDI_PROC RDIM_ScopeBakeResult rdim_bake_scopes(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_String8List *location_blocks, RDIM_String8List *location_data_blobs, RDIM_ScopeChunkList *src);
RDI_PROC RDIM_ScopeVMapBakeResult rdim_bake_scope_vmap(RDIM_Arena *arena, RDIM_ScopeChunkList *src);
RDI_PROC RDIM_InlineSiteBakeResult rdim_bake_inline_sites(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_InlineSiteChunkList *src);
RDI_PROC RDIM_TopLevelNameMapBakeResult rdim_bake_name_maps_top_level(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakeIdxRunMap *idx_runs, RDIM_BakeNameMap *name_maps[RDI_NameMapKind_COUNT]);
RDI_PROC RDIM_FilePathBakeResult rdim_bake_file_paths(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakePathTree *path_tree);
RDI_PROC RDIM_StringBakeResult rdim_bake_strings(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings);
RDI_PROC RDIM_IndexRunBakeResult rdim_bake_index_runs(RDIM_Arena *arena, RDIM_BakeIdxRunMap *idx_runs);
////////////////////////////////
//~ rjf: [Serializing] Bake Results -> String Blobs
+1 -9
View File
@@ -1,14 +1,6 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U32 *
push_u32(Arena *arena, U32 value)
{
U32 *result = push_array_no_zero(arena, U32, 1);
*result = value;
return result;
}
internal U64 *
push_u64(Arena *arena, U64 value)
{
+3 -5
View File
@@ -1,14 +1,12 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal U32 * push_u32(Arena *arena, U32 value);
internal U64 * push_u64(Arena *arena, U64 value);
internal U64 * push_u64 (Arena *arena, U64 value);
internal U32 * push_array_copy_u32(Arena *arena, U32 *v, U64 count);
internal U64 * push_array_copy_u64(Arena *arena, U64 *v, U64 count);
internal U64 ** push_matrix_u64(Arena *arena, U64 rows, U64 columns);
internal String8 push_cstr(Arena *arena, String8 str);
internal U64 ** push_matrix_u64 (Arena *arena, U64 rows, U64 columns);
internal Arena ** alloc_fixed_size_arena_array(Arena *arena, U64 count, U64 res, U64 cmt);
internal void release_arena_array(Arena **arr);
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U64
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U32Array
+10 -10
View File
@@ -1,18 +1,18 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal U32Array bit_array_init32(Arena *arena, U64 word_count);
internal U64 bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state);
internal U64 bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state);
internal U32Array bit_array_init32 (Arena *arena, U64 word_count);
internal U64 bit_array_scan_left_to_right32 (U32Array bit_array, U64 lo, U64 hi, B32 state);
internal U64 bit_array_scan_right_to_left32 (U32Array bit_array, U64 lo, U64 hi, B32 state);
internal Rng1U64 bit_array_scan_left_to_right32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count);
internal Rng1U64 bit_array_scan_right_to_left32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count);
internal B32 byte_scan_right_to_left(U8 *start, U8 *opl, U8 byte, U64 *offset_out);
internal U64 bit_array_find_next_unset_bit32(U32Array bit_array);
internal U64 bit_array_find_next_set_bit32(U32Array bit_array);
internal void bit_array_set_bit32(U32Array bit_array, U64 idx, B32 state);
internal void bit_array_set_bit_range32(U32Array bit_array, Rng1U64 range, B32 state);
internal U32 bit_array_get_bit32(U32Array bit_array, U64 idx);
internal B32 byte_scan_right_to_left (U8 *start, U8 *opl, U8 byte, U64 *offset_out);
internal U64 bit_array_find_next_unset_bit32 (U32Array bit_array);
internal U64 bit_array_find_next_set_bit32 (U32Array bit_array);
internal void bit_array_set_bit32 (U32Array bit_array, U64 idx, B32 state);
internal void bit_array_set_bit_range32 (U32Array bit_array, Rng1U64 range, B32 state);
internal U32 bit_array_get_bit32 (U32Array bit_array, U64 idx);
+2 -1
View File
@@ -1,9 +1,10 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmacro-redefined"
#pragma clang diagnostic ignored "-Wsign-conversion"
#elif defined(_MSC_VER)
#pragma warning (push, 0)
#endif
+4 -1
View File
@@ -1,8 +1,11 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#define BLAKE3_API static
#define BLAKE3_PRIVATE static
#if defined(__clang__) && defined(__x86_64__)
# if defined(__IMMINTRIN_H)
# error "include this header before immintrin.h / x86intrin.h / intrin.h"
-13
View File
@@ -1,13 +0,0 @@
// Copyright (c) 2025 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "../third_party_ext/blake3/blake3_portable.c"
#if defined(__aarch64__) || defined(_M_ARM64)
#include "../third_party_ext/blake3/blake3_neon.c"
#endif
#include "../third_party_ext/blake3/blake3_dispatch.c"
#include "../third_party_ext/blake3/blake3.c"
#pragma comment (lib, "blake3")
-18
View File
@@ -1,18 +0,0 @@
// Copyright (c) 2025 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_BLAKE3_H
#define BASE_BLAKE3_H
#include "../third_party_ext/blake3/blake3.h"
static void
blake3(void* out, size_t outlen, void* in, size_t inlen)
{
blake3_hasher hasher;
blake3_hasher_init(&hasher);
blake3_hasher_update(&hasher, in, inlen);
blake3_hasher_finalize(&hasher, (uint8_t*)out, outlen);
}
#endif // BASE_BLAKE3_H
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U16
+1 -5
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
@@ -110,10 +110,6 @@
////////////////////////////////
#define MemoryIsZeroStruct(p) memory_is_zero(p, sizeof(*p))
////////////////////////////////
typedef struct
{
U64 major;
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U32
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "base_core.c"
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal MD5Hash
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
+33 -50
View File
@@ -1,52 +1,10 @@
// Copyright (c) 2025 Epic Games Tools
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal String8Node *
str8_list_push_raw(Arena *arena, String8List *list, void *data_ptr, U64 data_size)
internal B32
str8_starts_with(String8 string, String8 expected_prefix)
{
String8 data = str8((U8 *)data_ptr, data_size);
String8Node *node = str8_list_push(arena, list, data);
return node;
}
internal U64
str8_list_push_pad(Arena *arena, String8List *list, U64 offset, U64 align)
{
U64 pad_size = AlignPow2(offset, align) - offset;
U8 *pad = push_array(arena, U8, pad_size);
MemorySet(pad, 0, pad_size);
str8_list_push(arena, list, str8(pad, pad_size));
return pad_size;
}
internal U64
str8_list_push_pad_front(Arena *arena, String8List *list, U64 offset, U64 align)
{
U64 pad_size = AlignPow2(offset, align) - offset;
U8 *pad = push_array(arena, U8, pad_size);
MemorySet(pad, 0, pad_size);
str8_list_push_front(arena, list, str8(pad, pad_size));
return pad_size;
}
internal String8List
str8_list_arr_concat(String8List *v, U64 count)
{
String8List result = {0};
for (U64 i = 0; i < count; i += 1) {
str8_list_concat_in_place(&result, &v[i]);
}
return result;
}
internal String8Node *
str8_list_push_many(Arena *arena, String8List *list, U64 count)
{
String8Node *arr = push_array(arena, String8Node, count);
for (U64 i = 0; i < count; ++i) {
str8_list_push_node(list, arr + i);
}
return arr;
return str8_match(str8_prefix(string, expected_prefix.size), expected_prefix, 0);
}
internal String8Node *
@@ -64,9 +22,34 @@ str8_list_pop_front(String8List *list)
}
internal U64
hash_from_str8(String8 string)
str8_array_bsearch(String8Array arr, String8 value)
{
XXH64_hash_t hash64 = XXH3_64bits(string.str, string.size);
return hash64;
}
if (arr.count > 1) {
int lo_compar = str8_compar_case_sensitive(&value, &arr.v[0]);
if (lo_compar == 0) {
return 0;
}
int hi_compar = str8_compar_case_sensitive(&value, &arr.v[arr.count-1]);
if (hi_compar == 0){
return arr.count-1;
}
if (lo_compar > 0 && hi_compar < 0) {
for (U64 l = 0, r = arr.count -1; l <= r; ) {
U64 m = l + (r- l) / 2;
int cmp = str8_compar_case_sensitive(&arr.v[m], &value);
if (cmp == 0) {
return m;
} else if (cmp < 0) {
l = m + 1;
} else {
r = m - 1;
}
}
}
} else if (arr.count == 1 && str8_match(arr.v[0], value, 0)) {
return 0;
}
return max_U64;
}

Some files were not shown because too many files have changed in this diff Show More