diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index a1a3dd9a..f9263756 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..c818c936 --- /dev/null +++ b/CHANGELOG.md @@ -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. + + diff --git a/LICENSE b/LICENSE index ef60e20b..81025cbb 100644 --- a/LICENSE +++ b/LICENSE @@ -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 diff --git a/README.md b/README.md index 8d8396ec..84191fd3 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/build.bat b/build.bat index aab7c76e..81427182 100644 --- a/build.bat +++ b/build.bat @@ -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 %* +) diff --git a/project.4coder b/project.4coder index 6fc96e05..9dc98bf1 100644 --- a/project.4coder +++ b/project.4coder @@ -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, }, diff --git a/src/artifact_cache/artifact_cache.c b/src/artifact_cache/artifact_cache.c new file mode 100644 index 00000000..514463b5 --- /dev/null +++ b/src/artifact_cache/artifact_cache.c @@ -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(¶ms->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); +} diff --git a/src/artifact_cache/artifact_cache.h b/src/artifact_cache/artifact_cache.h new file mode 100644 index 00000000..6d14a48e --- /dev/null +++ b/src/artifact_cache/artifact_cache.h @@ -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 diff --git a/src/async/async.c b/src/async/async.c index 85ff743b..eeef9054 100644 --- a/src/async/async.c +++ b/src/async/async.c @@ -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(¶ms->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(;;) { diff --git a/src/async/async.h b/src/async/async.h index a25492cb..cf905e5a 100644 --- a/src/async/async.h +++ b/src/async/async.h @@ -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; }; diff --git a/src/base/base_arena.c b/src/base/base_arena.c index 314fcd9e..22778571 100644 --- a/src/base/base_arena.c +++ b/src/base/base_arena.c @@ -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) diff --git a/src/base/base_arena.h b/src/base/base_arena.h index 47c5238d..99fd33bb 100644 --- a/src/base/base_arena.h +++ b/src/base/base_arena.h @@ -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))) diff --git a/src/base/base_command_line.c b/src/base/base_command_line.c index 7792afde..1150bcc4 100644 --- a/src/base/base_command_line.c +++ b/src/base/base_command_line.c @@ -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); diff --git a/src/base/base_context_cracking.h b/src/base/base_context_cracking.h index 7edbd59b..50cb84a5 100644 --- a/src/base/base_context_cracking.h +++ b/src/base/base_context_cracking.h @@ -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) diff --git a/src/base/base_core.c b/src/base/base_core.c index e982b684..97515ce6 100644 --- a/src/base/base_core.c +++ b/src/base/base_core.c @@ -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, +}; diff --git a/src/base/base_core.h b/src/base/base_core.h index 319837ba..e9a166f2 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -13,6 +13,13 @@ #include #include +//////////////////////////////// +//~ 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 # 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 diff --git a/src/base/base_entry_point.c b/src/base/base_entry_point.c index feaa45e1..7411e32c 100644 --- a/src/base/base_entry_point.c +++ b/src/base/base_entry_point.c @@ -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; + } + } +} diff --git a/src/base/base_entry_point.h b/src/base/base_entry_point.h index 346df5df..91cffd99 100644 --- a/src/base/base_entry_point.h +++ b/src/base/base_entry_point.h @@ -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 diff --git a/src/base/base_inc.c b/src/base/base_inc.c index 45e1fe84..7e9462f5 100644 --- a/src/base/base_inc.c +++ b/src/base/base_inc.c @@ -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" diff --git a/src/base/base_inc.h b/src/base/base_inc.h index e6adf0d3..4bf367da 100644 --- a/src/base/base_inc.h +++ b/src/base/base_inc.h @@ -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" diff --git a/src/base/base_math.c b/src/base/base_math.c index a5d00232..16e3ad28 100644 --- a/src/base/base_math.c +++ b/src/base/base_math.c @@ -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; +} diff --git a/src/base/base_math.h b/src/base/base_math.h index dbabea0b..416dfeeb 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -1,694 +1,694 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_MATH_H -#define BASE_MATH_H - -//////////////////////////////// -//~ rjf: Vector Types - -//- rjf: 2-vectors - -typedef union Vec2F32 Vec2F32; -union Vec2F32 -{ - struct - { - F32 x; - F32 y; - }; - F32 v[2]; -}; - -typedef union Vec2S64 Vec2S64; -union Vec2S64 -{ - struct - { - S64 x; - S64 y; - }; - S64 v[2]; -}; - -typedef union Vec2S32 Vec2S32; -union Vec2S32 -{ - struct - { - S32 x; - S32 y; - }; - S32 v[2]; -}; - -typedef union Vec2S16 Vec2S16; -union Vec2S16 -{ - struct - { - S16 x; - S16 y; - }; - S16 v[2]; -}; - -//- rjf: 3-vectors - -typedef union Vec3F32 Vec3F32; -union Vec3F32 -{ - struct - { - F32 x; - F32 y; - F32 z; - }; - struct - { - Vec2F32 xy; - F32 _z0; - }; - struct - { - F32 _x0; - Vec2F32 yz; - }; - F32 v[3]; -}; - -typedef union Vec3S32 Vec3S32; -union Vec3S32 -{ - struct - { - S32 x; - S32 y; - S32 z; - }; - struct - { - Vec2S32 xy; - S32 _z0; - }; - struct - { - S32 _x0; - Vec2S32 yz; - }; - S32 v[3]; -}; - -//- rjf: 4-vectors - -typedef union Vec4F32 Vec4F32; -union Vec4F32 -{ - struct - { - F32 x; - F32 y; - F32 z; - F32 w; - }; - struct - { - Vec2F32 xy; - Vec2F32 zw; - }; - struct - { - Vec3F32 xyz; - F32 _z0; - }; - struct - { - F32 _x0; - Vec3F32 yzw; - }; - F32 v[4]; -}; - -typedef union Vec4S32 Vec4S32; -union Vec4S32 -{ - struct - { - S32 x; - S32 y; - S32 z; - S32 w; - }; - struct - { - Vec2S32 xy; - Vec2S32 zw; - }; - struct - { - Vec3S32 xyz; - S32 _z0; - }; - struct - { - S32 _x0; - Vec3S32 yzw; - }; - S32 v[4]; -}; - -//////////////////////////////// -//~ rjf: Matrix Types - -typedef struct Mat3x3F32 Mat3x3F32; -struct Mat3x3F32 -{ - F32 v[3][3]; -}; - -typedef struct Mat4x4F32 Mat4x4F32; -struct Mat4x4F32 -{ - F32 v[4][4]; -}; - -//////////////////////////////// -//~ rjf: Range Types - -//- rjf: 1-range - -typedef union Rng1U32 Rng1U32; -union Rng1U32 -{ - struct - { - U32 min; - U32 max; - }; - U32 v[2]; -}; - -typedef union Rng1S32 Rng1S32; -union Rng1S32 -{ - struct - { - S32 min; - S32 max; - }; - S32 v[2]; -}; - -typedef union Rng1U64 Rng1U64; -union Rng1U64 -{ - struct - { - U64 min; - U64 max; - }; - U64 v[2]; -}; - -typedef union Rng1S64 Rng1S64; -union Rng1S64 -{ - struct - { - S64 min; - S64 max; - }; - S64 v[2]; -}; - -typedef union Rng1F32 Rng1F32; -union Rng1F32 -{ - struct - { - F32 min; - F32 max; - }; - F32 v[2]; -}; - -//- rjf: 2-range (rectangles) - -typedef union Rng2S16 Rng2S16; -union Rng2S16 -{ - struct - { - Vec2S16 min; - Vec2S16 max; - }; - struct - { - Vec2S16 p0; - Vec2S16 p1; - }; - struct - { - S16 x0; - S16 y0; - S16 x1; - S16 y1; - }; - Vec2S16 v[2]; -}; - -typedef union Rng2S32 Rng2S32; -union Rng2S32 -{ - struct - { - Vec2S32 min; - Vec2S32 max; - }; - struct - { - Vec2S32 p0; - Vec2S32 p1; - }; - struct - { - S32 x0; - S32 y0; - S32 x1; - S32 y1; - }; - Vec2S32 v[2]; -}; - -typedef union Rng2F32 Rng2F32; -union Rng2F32 -{ - struct - { - Vec2F32 min; - Vec2F32 max; - }; - struct - { - Vec2F32 p0; - Vec2F32 p1; - }; - struct - { - F32 x0; - F32 y0; - F32 x1; - F32 y1; - }; - Vec2F32 v[2]; -}; - -typedef union Rng2S64 Rng2S64; -union Rng2S64 -{ - struct - { - Vec2S64 min; - Vec2S64 max; - }; - struct - { - Vec2S64 p0; - Vec2S64 p1; - }; - struct - { - S64 x0; - S64 y0; - S64 x1; - S64 y1; - }; - Vec2S64 v[2]; -}; - -//////////////////////////////// -//~ rjf: List Types - -typedef struct Rng1U64Node Rng1U64Node; -struct Rng1U64Node -{ - Rng1U64Node *next; - Rng1U64 v; -}; - -typedef struct Rng1U64List Rng1U64List; -struct Rng1U64List -{ - U64 count; - Rng1U64Node *first; - Rng1U64Node *last; -}; - -typedef struct Rng1U64Array Rng1U64Array; -struct Rng1U64Array -{ - Rng1U64 *v; - U64 count; -}; - -typedef struct Rng1S64Node Rng1S64Node; -struct Rng1S64Node -{ - Rng1S64Node *next; - Rng1S64 v; -}; - -typedef struct Rng1S64List Rng1S64List; -struct Rng1S64List -{ - Rng1S64Node *first; - Rng1S64Node *last; - U64 count; -}; - -typedef struct Rng1S64Array Rng1S64Array; -struct Rng1S64Array -{ - Rng1S64 *v; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Scalar Ops - -#define abs_s64(v) (S64)llabs(v) - -#define sqrt_f32(v) sqrtf(v) -#define cbrt_f32(v) cbrtf(v) -#define mod_f32(a, b) fmodf((a), (b)) -#define pow_f32(b, e) powf((b), (e)) -#define ceil_f32(v) ceilf(v) -#define floor_f32(v) floorf(v) -#define round_f32(v) roundf(v) -#define abs_f32(v) fabsf(v) -#define radians_from_turns_f32(v) ((v)*(2*3.1415926535897f)) -#define turns_from_radians_f32(v) ((v)/(2*3.1415926535897f)) -#define degrees_from_turns_f32(v) ((v)*360.f) -#define turns_from_degrees_f32(v) ((v)/360.f) -#define degrees_from_radians_f32(v) (degrees_from_turns_f32(turns_from_radians_f32(v))) -#define radians_from_degrees_f32(v) (radians_from_turns_f32(turns_from_degrees_f32(v))) -#define sin_f32(v) sinf(radians_from_turns_f32(v)) -#define cos_f32(v) cosf(radians_from_turns_f32(v)) -#define tan_f32(v) tanf(radians_from_turns_f32(v)) - -#define sqrt_f64(v) sqrt(v) -#define cbrt_f64(v) cbrt(v) -#define mod_f64(a, b) fmod((a), (b)) -#define pow_f64(b, e) pow((b), (e)) -#define ceil_f64(v) ceil(v) -#define floor_f64(v) floor(v) -#define round_f64(v) round(v) -#define abs_f64(v) fabs(v) -#define radians_from_turns_f64(v) ((v)*(2*3.1415926535897)) -#define turns_from_radians_f64(v) ((v)/(2*3.1415926535897)) -#define degrees_from_turns_f64(v) ((v)*360.0) -#define turns_from_degrees_f64(v) ((v)/360.0) -#define degrees_from_radians_f64(v) (degrees_from_turns_f64(turns_from_radians_f64(v))) -#define radians_from_degrees_f64(v) (radians_from_turns_f64(turns_from_degrees_f64(v))) -#define sin_f64(v) sin(radians_from_turns_f64(v)) -#define cos_f64(v) cos(radians_from_turns_f64(v)) -#define tan_f64(v) tan(radians_from_turns_f64(v)) - -internal F32 mix_1f32(F32 a, F32 b, F32 t); -internal F64 mix_1f64(F64 a, F64 b, F64 t); - -//////////////////////////////// -//~ rjf: Vector Ops - -#define v2f32(x, y) vec_2f32((x), (y)) -internal Vec2F32 vec_2f32(F32 x, F32 y); -internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 scale_2f32(Vec2F32 v, F32 s); -internal F32 dot_2f32(Vec2F32 a, Vec2F32 b); -internal F32 length_squared_2f32(Vec2F32 v); -internal F32 length_2f32(Vec2F32 v); -internal Vec2F32 normalize_2f32(Vec2F32 v); -internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t); - -#define v2s64(x, y) vec_2s64((x), (y)) -internal Vec2S64 vec_2s64(S64 x, S64 y); -internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 scale_2s64(Vec2S64 v, S64 s); -internal S64 dot_2s64(Vec2S64 a, Vec2S64 b); -internal S64 length_squared_2s64(Vec2S64 v); -internal S64 length_2s64(Vec2S64 v); -internal Vec2S64 normalize_2s64(Vec2S64 v); -internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t); - -#define v2s32(x, y) vec_2s32((x), (y)) -internal Vec2S32 vec_2s32(S32 x, S32 y); -internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 scale_2s32(Vec2S32 v, S32 s); -internal S32 dot_2s32(Vec2S32 a, Vec2S32 b); -internal S32 length_squared_2s32(Vec2S32 v); -internal S32 length_2s32(Vec2S32 v); -internal Vec2S32 normalize_2s32(Vec2S32 v); -internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t); - -#define v2s16(x, y) vec_2s16((x), (y)) -internal Vec2S16 vec_2s16(S16 x, S16 y); -internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 scale_2s16(Vec2S16 v, S16 s); -internal S16 dot_2s16(Vec2S16 a, Vec2S16 b); -internal S16 length_squared_2s16(Vec2S16 v); -internal S16 length_2s16(Vec2S16 v); -internal Vec2S16 normalize_2s16(Vec2S16 v); -internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t); - -#define v3f32(x, y, z) vec_3f32((x), (y), (z)) -internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z); -internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 scale_3f32(Vec3F32 v, F32 s); -internal F32 dot_3f32(Vec3F32 a, Vec3F32 b); -internal F32 length_squared_3f32(Vec3F32 v); -internal F32 length_3f32(Vec3F32 v); -internal Vec3F32 normalize_3f32(Vec3F32 v); -internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t); -internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 xform_3f32(Vec3F32 v, Mat3x3F32 m); - -#define v3s32(x, y, z) vec_3s32((x), (y), (z)) -internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z); -internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 scale_3s32(Vec3S32 v, S32 s); -internal S32 dot_3s32(Vec3S32 a, Vec3S32 b); -internal S32 length_squared_3s32(Vec3S32 v); -internal S32 length_3s32(Vec3S32 v); -internal Vec3S32 normalize_3s32(Vec3S32 v); -internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t); -internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b); - -#define v4f32(x, y, z, w) vec_4f32((x), (y), (z), (w)) -internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w); -internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 scale_4f32(Vec4F32 v, F32 s); -internal F32 dot_4f32(Vec4F32 a, Vec4F32 b); -internal F32 length_squared_4f32(Vec4F32 v); -internal F32 length_4f32(Vec4F32 v); -internal Vec4F32 normalize_4f32(Vec4F32 v); -internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t); - -#define v4s32(x, y, z, w) vec_4s32((x), (y), (z), (w)) -internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w); -internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 scale_4s32(Vec4S32 v, S32 s); -internal S32 dot_4s32(Vec4S32 a, Vec4S32 b); -internal S32 length_squared_4s32(Vec4S32 v); -internal S32 length_4s32(Vec4S32 v); -internal Vec4S32 normalize_4s32(Vec4S32 v); -internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t); - -//////////////////////////////// -//~ rjf: Matrix Ops - -internal Mat3x3F32 mat_3x3f32(F32 diagonal); -internal Mat3x3F32 make_translate_3x3f32(Vec2F32 delta); -internal Mat3x3F32 make_scale_3x3f32(Vec2F32 scale); -internal Mat3x3F32 mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b); - -internal Mat4x4F32 mat_4x4f32(F32 diagonal); -internal Mat4x4F32 make_translate_4x4f32(Vec3F32 delta); -internal Mat4x4F32 make_scale_4x4f32(Vec3F32 scale); -internal Mat4x4F32 make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z); -internal Mat4x4F32 make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z); -internal Mat4x4F32 make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up); -internal Mat4x4F32 make_rotate_4x4f32(Vec3F32 axis, F32 turns); -internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b); -internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale); -internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m); -internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat); -internal Mat4x4F32 transpose_4x4f32(Mat4x4F32 mat); - -//////////////////////////////// -//~ rjf: Range Ops - -#define r1u32(min, max) rng_1u32((min), (max)) -internal Rng1U32 rng_1u32(U32 min, U32 max); -internal Rng1U32 shift_1u32(Rng1U32 r, U32 x); -internal Rng1U32 pad_1u32(Rng1U32 r, U32 x); -internal U32 center_1u32(Rng1U32 r); -internal B32 contains_1u32(Rng1U32 r, U32 x); -internal U32 dim_1u32(Rng1U32 r); -internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b); -internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b); -internal U32 clamp_1u32(Rng1U32 r, U32 v); - -#define r1s32(min, max) rng_1s32((min), (max)) -internal Rng1S32 rng_1s32(S32 min, S32 max); -internal Rng1S32 shift_1s32(Rng1S32 r, S32 x); -internal Rng1S32 pad_1s32(Rng1S32 r, S32 x); -internal S32 center_1s32(Rng1S32 r); -internal B32 contains_1s32(Rng1S32 r, S32 x); -internal S32 dim_1s32(Rng1S32 r); -internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b); -internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b); -internal S32 clamp_1s32(Rng1S32 r, S32 v); - -#define r1u64(min, max) rng_1u64((min), (max)) -internal Rng1U64 rng_1u64(U64 min, U64 max); -internal Rng1U64 shift_1u64(Rng1U64 r, U64 x); -internal Rng1U64 pad_1u64(Rng1U64 r, U64 x); -internal U64 center_1u64(Rng1U64 r); -internal B32 contains_1u64(Rng1U64 r, U64 x); -internal U64 dim_1u64(Rng1U64 r); -internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b); -internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b); -internal U64 clamp_1u64(Rng1U64 r, U64 v); - -#define r1s64(min, max) rng_1s64((min), (max)) -internal Rng1S64 rng_1s64(S64 min, S64 max); -internal Rng1S64 shift_1s64(Rng1S64 r, S64 x); -internal Rng1S64 pad_1s64(Rng1S64 r, S64 x); -internal S64 center_1s64(Rng1S64 r); -internal B32 contains_1s64(Rng1S64 r, S64 x); -internal S64 dim_1s64(Rng1S64 r); -internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b); -internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b); -internal S64 clamp_1s64(Rng1S64 r, S64 v); - -#define r1f32(min, max) rng_1f32((min), (max)) -internal Rng1F32 rng_1f32(F32 min, F32 max); -internal Rng1F32 shift_1f32(Rng1F32 r, F32 x); -internal Rng1F32 pad_1f32(Rng1F32 r, F32 x); -internal F32 center_1f32(Rng1F32 r); -internal B32 contains_1f32(Rng1F32 r, F32 x); -internal F32 dim_1f32(Rng1F32 r); -internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b); -internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b); -internal F32 clamp_1f32(Rng1F32 r, F32 v); - -#define r2s16(min, max) rng_2s16((min), (max)) -#define r2s16p(x, y, z, w) r2s16(v2s16((x), (y)), v2s16((z), (w))) -internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max); -internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x); -internal Rng2S16 pad_2s16(Rng2S16 r, S16 x); -internal Vec2S16 center_2s16(Rng2S16 r); -internal B32 contains_2s16(Rng2S16 r, Vec2S16 x); -internal Vec2S16 dim_2s16(Rng2S16 r); -internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b); -internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b); -internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v); - -#define r2s32(min, max) rng_2s32((min), (max)) -#define r2s32p(x, y, z, w) r2s32(v2s32((x), (y)), v2s32((z), (w))) -internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max); -internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x); -internal Rng2S32 pad_2s32(Rng2S32 r, S32 x); -internal Vec2S32 center_2s32(Rng2S32 r); -internal B32 contains_2s32(Rng2S32 r, Vec2S32 x); -internal Vec2S32 dim_2s32(Rng2S32 r); -internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b); -internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b); -internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v); - -#define r2s64(min, max) rng_2s64((min), (max)) -#define r2s64p(x, y, z, w) r2s64(v2s64((x), (y)), v2s64((z), (w))) -internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max); -internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x); -internal Rng2S64 pad_2s64(Rng2S64 r, S64 x); -internal Vec2S64 center_2s64(Rng2S64 r); -internal B32 contains_2s64(Rng2S64 r, Vec2S64 x); -internal Vec2S64 dim_2s64(Rng2S64 r); -internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b); -internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b); -internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v); - -#define r2f32(min, max) rng_2f32((min), (max)) -#define r2f32p(x, y, z, w) r2f32(v2f32((x), (y)), v2f32((z), (w))) -internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max); -internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x); -internal Rng2F32 pad_2f32(Rng2F32 r, F32 x); -internal Vec2F32 center_2f32(Rng2F32 r); -internal B32 contains_2f32(Rng2F32 r, Vec2F32 x); -internal Vec2F32 dim_2f32(Rng2F32 r); -internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b); -internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b); -internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v); - -//////////////////////////////// -//~ rjf: Color Operations - -//- rjf: hsv <-> rgb -internal Vec3F32 hsv_from_rgb(Vec3F32 rgb); -internal Vec3F32 rgb_from_hsv(Vec3F32 hsv); -internal Vec4F32 hsva_from_rgba(Vec4F32 rgba); -internal Vec4F32 rgba_from_hsva(Vec4F32 hsva); - -//- rjf: srgb <-> linear -internal Vec3F32 linear_from_srgb(Vec3F32 srgb); -internal Vec3F32 srgb_from_linear(Vec3F32 linear); -internal Vec4F32 linear_from_srgba(Vec4F32 srgba); -internal Vec4F32 srgba_from_linear(Vec4F32 linear); - -//- rjf: oklab <-> linear -internal Vec3F32 oklab_from_linear(Vec3F32 linear); -internal Vec3F32 linear_from_oklab(Vec3F32 oklab); -internal Vec4F32 oklab_from_lineara(Vec4F32 lineara); -internal Vec4F32 lineara_from_oklab(Vec4F32 oklab); - -//- rjf: rgba <-> u32 -internal U32 u32_from_rgba(Vec4F32 rgba); -internal Vec4F32 rgba_from_u32(U32 hex); -#define rgba_from_u32_lit_comp(h) { (((h)&0xff000000)>>24)/255.f, (((h)&0x00ff0000)>>16)/255.f, (((h)&0x0000ff00)>> 8)/255.f, (((h)&0x000000ff)>> 0)/255.f } - -//////////////////////////////// -//~ rjf: List Type Functions - -internal void rng1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 rng); -internal void rng1u64_list_concat(Rng1U64List *list, Rng1U64List *to_concat); -internal Rng1U64Array rng1u64_array_from_list(Arena *arena, Rng1U64List *list); -internal U64 rng_1u64_array_bsearch(Rng1U64Array arr, U64 value); - -internal void rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng); -internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list); - -#endif // BASE_MATH_H +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_MATH_H +#define BASE_MATH_H + +//////////////////////////////// +//~ rjf: Vector Types + +//- rjf: 2-vectors + +typedef union Vec2F32 Vec2F32; +union Vec2F32 +{ + struct + { + F32 x; + F32 y; + }; + F32 v[2]; +}; + +typedef union Vec2S64 Vec2S64; +union Vec2S64 +{ + struct + { + S64 x; + S64 y; + }; + S64 v[2]; +}; + +typedef union Vec2S32 Vec2S32; +union Vec2S32 +{ + struct + { + S32 x; + S32 y; + }; + S32 v[2]; +}; + +typedef union Vec2S16 Vec2S16; +union Vec2S16 +{ + struct + { + S16 x; + S16 y; + }; + S16 v[2]; +}; + +//- rjf: 3-vectors + +typedef union Vec3F32 Vec3F32; +union Vec3F32 +{ + struct + { + F32 x; + F32 y; + F32 z; + }; + struct + { + Vec2F32 xy; + F32 _z0; + }; + struct + { + F32 _x0; + Vec2F32 yz; + }; + F32 v[3]; +}; + +typedef union Vec3S32 Vec3S32; +union Vec3S32 +{ + struct + { + S32 x; + S32 y; + S32 z; + }; + struct + { + Vec2S32 xy; + S32 _z0; + }; + struct + { + S32 _x0; + Vec2S32 yz; + }; + S32 v[3]; +}; + +//- rjf: 4-vectors + +typedef union Vec4F32 Vec4F32; +union Vec4F32 +{ + struct + { + F32 x; + F32 y; + F32 z; + F32 w; + }; + struct + { + Vec2F32 xy; + Vec2F32 zw; + }; + struct + { + Vec3F32 xyz; + F32 _z0; + }; + struct + { + F32 _x0; + Vec3F32 yzw; + }; + F32 v[4]; +}; + +typedef union Vec4S32 Vec4S32; +union Vec4S32 +{ + struct + { + S32 x; + S32 y; + S32 z; + S32 w; + }; + struct + { + Vec2S32 xy; + Vec2S32 zw; + }; + struct + { + Vec3S32 xyz; + S32 _z0; + }; + struct + { + S32 _x0; + Vec3S32 yzw; + }; + S32 v[4]; +}; + +//////////////////////////////// +//~ rjf: Matrix Types + +typedef struct Mat3x3F32 Mat3x3F32; +struct Mat3x3F32 +{ + F32 v[3][3]; +}; + +typedef struct Mat4x4F32 Mat4x4F32; +struct Mat4x4F32 +{ + F32 v[4][4]; +}; + +//////////////////////////////// +//~ rjf: Range Types + +//- rjf: 1-range + +typedef union Rng1U32 Rng1U32; +union Rng1U32 +{ + struct + { + U32 min; + U32 max; + }; + U32 v[2]; +}; + +typedef union Rng1S32 Rng1S32; +union Rng1S32 +{ + struct + { + S32 min; + S32 max; + }; + S32 v[2]; +}; + +typedef union Rng1U64 Rng1U64; +union Rng1U64 +{ + struct + { + U64 min; + U64 max; + }; + U64 v[2]; +}; + +typedef union Rng1S64 Rng1S64; +union Rng1S64 +{ + struct + { + S64 min; + S64 max; + }; + S64 v[2]; +}; + +typedef union Rng1F32 Rng1F32; +union Rng1F32 +{ + struct + { + F32 min; + F32 max; + }; + F32 v[2]; +}; + +//- rjf: 2-range (rectangles) + +typedef union Rng2S16 Rng2S16; +union Rng2S16 +{ + struct + { + Vec2S16 min; + Vec2S16 max; + }; + struct + { + Vec2S16 p0; + Vec2S16 p1; + }; + struct + { + S16 x0; + S16 y0; + S16 x1; + S16 y1; + }; + Vec2S16 v[2]; +}; + +typedef union Rng2S32 Rng2S32; +union Rng2S32 +{ + struct + { + Vec2S32 min; + Vec2S32 max; + }; + struct + { + Vec2S32 p0; + Vec2S32 p1; + }; + struct + { + S32 x0; + S32 y0; + S32 x1; + S32 y1; + }; + Vec2S32 v[2]; +}; + +typedef union Rng2F32 Rng2F32; +union Rng2F32 +{ + struct + { + Vec2F32 min; + Vec2F32 max; + }; + struct + { + Vec2F32 p0; + Vec2F32 p1; + }; + struct + { + F32 x0; + F32 y0; + F32 x1; + F32 y1; + }; + Vec2F32 v[2]; +}; + +typedef union Rng2S64 Rng2S64; +union Rng2S64 +{ + struct + { + Vec2S64 min; + Vec2S64 max; + }; + struct + { + Vec2S64 p0; + Vec2S64 p1; + }; + struct + { + S64 x0; + S64 y0; + S64 x1; + S64 y1; + }; + Vec2S64 v[2]; +}; + +//////////////////////////////// +//~ rjf: Range List Types + +typedef struct Rng1U64Node Rng1U64Node; +struct Rng1U64Node +{ + Rng1U64Node *next; + Rng1U64 v; +}; + +typedef struct Rng1U64List Rng1U64List; +struct Rng1U64List +{ + U64 count; + Rng1U64Node *first; + Rng1U64Node *last; +}; + +typedef struct Rng1U64Array Rng1U64Array; +struct Rng1U64Array +{ + Rng1U64 *v; + U64 count; +}; + +typedef struct Rng1S64Node Rng1S64Node; +struct Rng1S64Node +{ + Rng1S64Node *next; + Rng1S64 v; +}; + +typedef struct Rng1S64List Rng1S64List; +struct Rng1S64List +{ + Rng1S64Node *first; + Rng1S64Node *last; + U64 count; +}; + +typedef struct Rng1S64Array Rng1S64Array; +struct Rng1S64Array +{ + Rng1S64 *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Scalar Math Ops + +#define abs_s64(v) (S64)llabs(v) + +#define sqrt_f32(v) sqrtf(v) +#define cbrt_f32(v) cbrtf(v) +#define mod_f32(a, b) fmodf((a), (b)) +#define pow_f32(b, e) powf((b), (e)) +#define ceil_f32(v) ceilf(v) +#define floor_f32(v) floorf(v) +#define round_f32(v) roundf(v) +#define abs_f32(v) fabsf(v) +#define radians_from_turns_f32(v) ((v)*(2*3.1415926535897f)) +#define turns_from_radians_f32(v) ((v)/(2*3.1415926535897f)) +#define degrees_from_turns_f32(v) ((v)*360.f) +#define turns_from_degrees_f32(v) ((v)/360.f) +#define degrees_from_radians_f32(v) (degrees_from_turns_f32(turns_from_radians_f32(v))) +#define radians_from_degrees_f32(v) (radians_from_turns_f32(turns_from_degrees_f32(v))) +#define sin_f32(v) sinf(radians_from_turns_f32(v)) +#define cos_f32(v) cosf(radians_from_turns_f32(v)) +#define tan_f32(v) tanf(radians_from_turns_f32(v)) + +#define sqrt_f64(v) sqrt(v) +#define cbrt_f64(v) cbrt(v) +#define mod_f64(a, b) fmod((a), (b)) +#define pow_f64(b, e) pow((b), (e)) +#define ceil_f64(v) ceil(v) +#define floor_f64(v) floor(v) +#define round_f64(v) round(v) +#define abs_f64(v) fabs(v) +#define radians_from_turns_f64(v) ((v)*(2*3.1415926535897)) +#define turns_from_radians_f64(v) ((v)/(2*3.1415926535897)) +#define degrees_from_turns_f64(v) ((v)*360.0) +#define turns_from_degrees_f64(v) ((v)/360.0) +#define degrees_from_radians_f64(v) (degrees_from_turns_f64(turns_from_radians_f64(v))) +#define radians_from_degrees_f64(v) (radians_from_turns_f64(turns_from_degrees_f64(v))) +#define sin_f64(v) sin(radians_from_turns_f64(v)) +#define cos_f64(v) cos(radians_from_turns_f64(v)) +#define tan_f64(v) tan(radians_from_turns_f64(v)) + +internal F32 mix_1f32(F32 a, F32 b, F32 t); +internal F64 mix_1f64(F64 a, F64 b, F64 t); + +//////////////////////////////// +//~ rjf: Vector Ops + +#define v2f32(x, y) vec_2f32((x), (y)) +internal Vec2F32 vec_2f32(F32 x, F32 y); +internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 scale_2f32(Vec2F32 v, F32 s); +internal F32 dot_2f32(Vec2F32 a, Vec2F32 b); +internal F32 length_squared_2f32(Vec2F32 v); +internal F32 length_2f32(Vec2F32 v); +internal Vec2F32 normalize_2f32(Vec2F32 v); +internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t); + +#define v2s64(x, y) vec_2s64((x), (y)) +internal Vec2S64 vec_2s64(S64 x, S64 y); +internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 scale_2s64(Vec2S64 v, S64 s); +internal S64 dot_2s64(Vec2S64 a, Vec2S64 b); +internal S64 length_squared_2s64(Vec2S64 v); +internal S64 length_2s64(Vec2S64 v); +internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t); + +#define v2s32(x, y) vec_2s32((x), (y)) +internal Vec2S32 vec_2s32(S32 x, S32 y); +internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 scale_2s32(Vec2S32 v, S32 s); +internal S32 dot_2s32(Vec2S32 a, Vec2S32 b); +internal S32 length_squared_2s32(Vec2S32 v); +internal S32 length_2s32(Vec2S32 v); +internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t); + +#define v2s16(x, y) vec_2s16((x), (y)) +internal Vec2S16 vec_2s16(S16 x, S16 y); +internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 scale_2s16(Vec2S16 v, S16 s); +internal S16 dot_2s16(Vec2S16 a, Vec2S16 b); +internal S16 length_squared_2s16(Vec2S16 v); +internal S16 length_2s16(Vec2S16 v); +internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t); + +#define v3f32(x, y, z) vec_3f32((x), (y), (z)) +internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z); +internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 scale_3f32(Vec3F32 v, F32 s); +internal F32 dot_3f32(Vec3F32 a, Vec3F32 b); +internal F32 length_squared_3f32(Vec3F32 v); +internal F32 length_3f32(Vec3F32 v); +internal Vec3F32 normalize_3f32(Vec3F32 v); +internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t); +internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 xform_3f32(Vec3F32 v, Mat3x3F32 m); + +#define v3s32(x, y, z) vec_3s32((x), (y), (z)) +internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z); +internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 scale_3s32(Vec3S32 v, S32 s); +internal S32 dot_3s32(Vec3S32 a, Vec3S32 b); +internal S32 length_squared_3s32(Vec3S32 v); +internal S32 length_3s32(Vec3S32 v); +internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t); +internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b); + +#define v4f32(x, y, z, w) vec_4f32((x), (y), (z), (w)) +internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w); +internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 scale_4f32(Vec4F32 v, F32 s); +internal F32 dot_4f32(Vec4F32 a, Vec4F32 b); +internal F32 length_squared_4f32(Vec4F32 v); +internal F32 length_4f32(Vec4F32 v); +internal Vec4F32 normalize_4f32(Vec4F32 v); +internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t); + +#define v4s32(x, y, z, w) vec_4s32((x), (y), (z), (w)) +internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w); +internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 scale_4s32(Vec4S32 v, S32 s); +internal S32 dot_4s32(Vec4S32 a, Vec4S32 b); +internal S32 length_squared_4s32(Vec4S32 v); +internal S32 length_4s32(Vec4S32 v); +internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t); + +//////////////////////////////// +//~ rjf: Matrix Ops + +internal Mat3x3F32 mat_3x3f32(F32 diagonal); +internal Mat3x3F32 make_translate_3x3f32(Vec2F32 delta); +internal Mat3x3F32 make_scale_3x3f32(Vec2F32 scale); +internal Mat3x3F32 mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b); + +internal Mat4x4F32 mat_4x4f32(F32 diagonal); +internal Mat4x4F32 make_translate_4x4f32(Vec3F32 delta); +internal Mat4x4F32 make_scale_4x4f32(Vec3F32 scale); +internal Mat4x4F32 make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z); +internal Mat4x4F32 make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z); +internal Mat4x4F32 make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up); +internal Mat4x4F32 make_rotate_4x4f32(Vec3F32 axis, F32 turns); +internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b); +internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale); +internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m); +internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat); +internal Mat4x4F32 transpose_4x4f32(Mat4x4F32 mat); + +//////////////////////////////// +//~ rjf: Range Ops + +#define r1u32(min, max) rng_1u32((min), (max)) +internal Rng1U32 rng_1u32(U32 min, U32 max); +internal Rng1U32 shift_1u32(Rng1U32 r, U32 x); +internal Rng1U32 pad_1u32(Rng1U32 r, U32 x); +internal U32 center_1u32(Rng1U32 r); +internal B32 contains_1u32(Rng1U32 r, U32 x); +internal U32 dim_1u32(Rng1U32 r); +internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b); +internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b); +internal U32 clamp_1u32(Rng1U32 r, U32 v); + +#define r1s32(min, max) rng_1s32((min), (max)) +internal Rng1S32 rng_1s32(S32 min, S32 max); +internal Rng1S32 shift_1s32(Rng1S32 r, S32 x); +internal Rng1S32 pad_1s32(Rng1S32 r, S32 x); +internal S32 center_1s32(Rng1S32 r); +internal B32 contains_1s32(Rng1S32 r, S32 x); +internal S32 dim_1s32(Rng1S32 r); +internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b); +internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b); +internal S32 clamp_1s32(Rng1S32 r, S32 v); + +#define r1u64(min, max) rng_1u64((min), (max)) +internal Rng1U64 rng_1u64(U64 min, U64 max); +internal Rng1U64 shift_1u64(Rng1U64 r, U64 x); +internal Rng1U64 pad_1u64(Rng1U64 r, U64 x); +internal U64 center_1u64(Rng1U64 r); +internal B32 contains_1u64(Rng1U64 r, U64 x); +internal U64 dim_1u64(Rng1U64 r); +internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b); +internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b); +internal U64 clamp_1u64(Rng1U64 r, U64 v); + +#define r1s64(min, max) rng_1s64((min), (max)) +internal Rng1S64 rng_1s64(S64 min, S64 max); +internal Rng1S64 shift_1s64(Rng1S64 r, S64 x); +internal Rng1S64 pad_1s64(Rng1S64 r, S64 x); +internal S64 center_1s64(Rng1S64 r); +internal B32 contains_1s64(Rng1S64 r, S64 x); +internal S64 dim_1s64(Rng1S64 r); +internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b); +internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b); +internal S64 clamp_1s64(Rng1S64 r, S64 v); + +#define r1f32(min, max) rng_1f32((min), (max)) +internal Rng1F32 rng_1f32(F32 min, F32 max); +internal Rng1F32 shift_1f32(Rng1F32 r, F32 x); +internal Rng1F32 pad_1f32(Rng1F32 r, F32 x); +internal F32 center_1f32(Rng1F32 r); +internal B32 contains_1f32(Rng1F32 r, F32 x); +internal F32 dim_1f32(Rng1F32 r); +internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b); +internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b); +internal F32 clamp_1f32(Rng1F32 r, F32 v); + +#define r2s16(min, max) rng_2s16((min), (max)) +#define r2s16p(x, y, z, w) r2s16(v2s16((x), (y)), v2s16((z), (w))) +internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max); +internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x); +internal Rng2S16 pad_2s16(Rng2S16 r, S16 x); +internal Vec2S16 center_2s16(Rng2S16 r); +internal B32 contains_2s16(Rng2S16 r, Vec2S16 x); +internal Vec2S16 dim_2s16(Rng2S16 r); +internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b); +internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b); +internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v); + +#define r2s32(min, max) rng_2s32((min), (max)) +#define r2s32p(x, y, z, w) r2s32(v2s32((x), (y)), v2s32((z), (w))) +internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max); +internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x); +internal Rng2S32 pad_2s32(Rng2S32 r, S32 x); +internal Vec2S32 center_2s32(Rng2S32 r); +internal B32 contains_2s32(Rng2S32 r, Vec2S32 x); +internal Vec2S32 dim_2s32(Rng2S32 r); +internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b); +internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b); +internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v); + +#define r2s64(min, max) rng_2s64((min), (max)) +#define r2s64p(x, y, z, w) r2s64(v2s64((x), (y)), v2s64((z), (w))) +internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max); +internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x); +internal Rng2S64 pad_2s64(Rng2S64 r, S64 x); +internal Vec2S64 center_2s64(Rng2S64 r); +internal B32 contains_2s64(Rng2S64 r, Vec2S64 x); +internal Vec2S64 dim_2s64(Rng2S64 r); +internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b); +internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b); +internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v); + +#define r2f32(min, max) rng_2f32((min), (max)) +#define r2f32p(x, y, z, w) r2f32(v2f32((x), (y)), v2f32((z), (w))) +internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max); +internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x); +internal Rng2F32 pad_2f32(Rng2F32 r, F32 x); +internal Vec2F32 center_2f32(Rng2F32 r); +internal B32 contains_2f32(Rng2F32 r, Vec2F32 x); +internal Vec2F32 dim_2f32(Rng2F32 r); +internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b); +internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b); +internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v); + +//////////////////////////////// +//~ rjf: Color Operations + +//- rjf: hsv <-> rgb +internal Vec3F32 hsv_from_rgb(Vec3F32 rgb); +internal Vec3F32 rgb_from_hsv(Vec3F32 hsv); +internal Vec4F32 hsva_from_rgba(Vec4F32 rgba); +internal Vec4F32 rgba_from_hsva(Vec4F32 hsva); + +//- rjf: srgb <-> linear +internal Vec3F32 linear_from_srgb(Vec3F32 srgb); +internal Vec3F32 srgb_from_linear(Vec3F32 linear); +internal Vec4F32 linear_from_srgba(Vec4F32 srgba); +internal Vec4F32 srgba_from_linear(Vec4F32 linear); + +//- rjf: oklab <-> linear +internal Vec3F32 oklab_from_linear(Vec3F32 linear); +internal Vec3F32 linear_from_oklab(Vec3F32 oklab); +internal Vec4F32 oklab_from_lineara(Vec4F32 lineara); +internal Vec4F32 lineara_from_oklab(Vec4F32 oklab); + +//- rjf: rgba <-> u32 +internal U32 u32_from_rgba(Vec4F32 rgba); +internal Vec4F32 rgba_from_u32(U32 hex); +#define rgba_from_u32_lit_comp(h) { (((h)&0xff000000)>>24)/255.f, (((h)&0x00ff0000)>>16)/255.f, (((h)&0x0000ff00)>> 8)/255.f, (((h)&0x000000ff)>> 0)/255.f } + +//////////////////////////////// +//~ rjf: List Type Functions + +internal void rng1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 rng); +internal void rng1u64_list_concat(Rng1U64List *list, Rng1U64List *to_concat); +internal Rng1U64Array rng1u64_array_from_list(Arena *arena, Rng1U64List *list); +internal U64 rng_1u64_array_bsearch(Rng1U64Array arr, U64 value); + +internal void rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng); +internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list); + +//////////////////////////////// +//~ rjf: N -> M Element Subdivision + +internal Rng1U64 m_range_from_n_idx_m_count(U64 n_idx, U64 n_count, U64 m_count); + +#endif //BASE_MATH_H diff --git a/src/base/base_profile.h b/src/base/base_profile.h index 3a97d4f3..6ef4a046 100644 --- a/src/base/base_profile.h +++ b/src/base/base_profile.h @@ -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__); \ diff --git a/src/base/base_strings.c b/src/base/base_strings.c index 84f19d77..84e3f448 100644 --- a/src/base/base_strings.c +++ b/src/base/base_strings.c @@ -1,215 +1,196 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -//////////////////////////////// -//~ rjf: Third Party Includes - -#if !BUILD_SUPPLEMENTARY_UNIT -# define STB_SPRINTF_IMPLEMENTATION -# define STB_SPRINTF_STATIC -# include "third_party/stb/stb_sprintf.h" -#endif - -//////////////////////////////// -//~ NOTE(allen): 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(allen): 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, -}; - //////////////////////////////// //~ rjf: Character Classification & Conversion Functions internal B32 -char_is_space(U8 c){ - return(c == ' ' || c == '\n' || c == '\t' || c == '\r' || c == '\f' || c == '\v'); +char_is_space(U8 c) +{ + return (c == ' ' || c == '\n' || c == '\t' || c == '\r' || c == '\f' || c == '\v'); } internal B32 -char_is_upper(U8 c){ - return('A' <= c && c <= 'Z'); +char_is_upper(U8 c) +{ + return ('A' <= c && c <= 'Z'); } internal B32 -char_is_lower(U8 c){ - return('a' <= c && c <= 'z'); +char_is_lower(U8 c) +{ + return ('a' <= c && c <= 'z'); } internal B32 -char_is_alpha(U8 c){ - return(char_is_upper(c) || char_is_lower(c)); +char_is_alpha(U8 c) +{ + return (char_is_upper(c) || char_is_lower(c)); } internal B32 -char_is_slash(U8 c){ - return(c == '/' || c == '\\'); +char_is_slash(U8 c) +{ + return (c == '/' || c == '\\'); } internal B32 -char_is_digit(U8 c, U32 base){ +char_is_digit(U8 c, U32 base) +{ B32 result = 0; - if (0 < base && base <= 16){ + if(0 < base && base <= 16) + { U8 val = integer_symbol_reverse[c]; - if (val < base){ + if(val < base) + { result = 1; } } - return(result); + return result; } internal U8 -char_to_lower(U8 c){ - if (char_is_upper(c)){ +lower_from_char(U8 c) +{ + if(char_is_upper(c)) + { c += ('a' - 'A'); } - return(c); + return c; } internal U8 -char_to_upper(U8 c){ - if (char_is_lower(c)){ +upper_from_char(U8 c) +{ + if(char_is_lower(c)) + { c += ('A' - 'a'); } - return(c); + return c; } internal U8 -char_to_correct_slash(U8 c){ - if(char_is_slash(c)){ +correct_slash_from_char(U8 c) +{ + if(char_is_slash(c)) + { c = '/'; } - return(c); + return c; } //////////////////////////////// //~ rjf: C-String Measurement internal U64 -cstring8_length(U8 *c){ +cstring8_length(U8 *c) +{ U8 *p = c; for (;*p != 0; p += 1); - return(p - c); + return (p - c); } internal U64 -cstring16_length(U16 *c){ +cstring16_length(U16 *c) +{ U16 *p = c; for (;*p != 0; p += 1); - return(p - c); + return (p - c); } internal U64 -cstring32_length(U32 *c){ +cstring32_length(U32 *c) +{ U32 *p = c; for (;*p != 0; p += 1); - return(p - c); + return (p - c); } //////////////////////////////// //~ rjf: String Constructors internal String8 -str8(U8 *str, U64 size){ +str8(U8 *str, U64 size) +{ String8 result = {str, size}; - return(result); + return result; } internal String8 -str8_range(U8 *first, U8 *one_past_last){ +str8_range(U8 *first, U8 *one_past_last) +{ String8 result = {first, (U64)(one_past_last - first)}; - return(result); + return result; } internal String8 -str8_zero(void){ +str8_zero(void) +{ String8 result = {0}; - return(result); + return result; } internal String16 -str16(U16 *str, U64 size){ +str16(U16 *str, U64 size) +{ String16 result = {str, size}; - return(result); + return result; } internal String16 -str16_range(U16 *first, U16 *one_past_last){ +str16_range(U16 *first, U16 *one_past_last) +{ String16 result = {first, (U64)(one_past_last - first)}; - return(result); + return result; } internal String16 -str16_zero(void){ +str16_zero(void) +{ String16 result = {0}; - return(result); + return result; } internal String32 -str32(U32 *str, U64 size){ +str32(U32 *str, U64 size) +{ String32 result = {str, size}; - return(result); + return result; } internal String32 -str32_range(U32 *first, U32 *one_past_last){ +str32_range(U32 *first, U32 *one_past_last) +{ String32 result = {first, (U64)(one_past_last - first)}; - return(result); + return result; } internal String32 -str32_zero(void){ +str32_zero(void) +{ String32 result = {0}; - return(result); + return result; } internal String8 -str8_cstring(char *c){ +str8_cstring(char *c) +{ String8 result = {(U8*)c, cstring8_length((U8*)c)}; - return(result); + return result; } internal String16 -str16_cstring(U16 *c){ +str16_cstring(U16 *c) +{ String16 result = {(U16*)c, cstring16_length((U16*)c)}; - return(result); + return result; } internal String32 -str32_cstring(U32 *c){ +str32_cstring(U32 *c) +{ String32 result = {(U32*)c, cstring32_length((U32*)c)}; - return(result); + return result; } internal String8 @@ -242,7 +223,6 @@ str8_cstring_capped_reverse(void *raw_start, void *raw_cap) for(; ptr > start; ) { ptr -= 1; - if (*ptr == '\0') { break; @@ -262,7 +242,7 @@ upper_from_str8(Arena *arena, String8 string) string = push_str8_copy(arena, string); for(U64 idx = 0; idx < string.size; idx += 1) { - string.str[idx] = char_to_upper(string.str[idx]); + string.str[idx] = upper_from_char(string.str[idx]); } return string; } @@ -273,7 +253,7 @@ lower_from_str8(Arena *arena, String8 string) string = push_str8_copy(arena, string); for(U64 idx = 0; idx < string.size; idx += 1) { - string.str[idx] = char_to_lower(string.str[idx]); + string.str[idx] = lower_from_char(string.str[idx]); } return string; } @@ -312,13 +292,13 @@ str8_match(String8 a, String8 b, StringMatchFlags flags) U8 bt = b.str[i]; if(case_insensitive) { - at = char_to_upper(at); - bt = char_to_upper(bt); + at = upper_from_char(at); + bt = upper_from_char(bt); } if(slash_insensitive) { - at = char_to_correct_slash(at); - bt = char_to_correct_slash(bt); + at = correct_slash_from_char(at); + bt = correct_slash_from_char(bt); } if(at != bt) { @@ -331,35 +311,43 @@ str8_match(String8 a, String8 b, StringMatchFlags flags) } internal U64 -str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags){ +str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags) +{ U8 *p = string.str + start_pos; U64 stop_offset = Max(string.size + 1, needle.size) - needle.size; U8 *stop_p = string.str + stop_offset; - if (needle.size > 0){ + if(needle.size > 0) + { U8 *string_opl = string.str + string.size; String8 needle_tail = str8_skip(needle, 1); StringMatchFlags adjusted_flags = flags | StringMatchFlag_RightSideSloppy; U8 needle_first_char_adjusted = needle.str[0]; - if(adjusted_flags & StringMatchFlag_CaseInsensitive){ - needle_first_char_adjusted = char_to_upper(needle_first_char_adjusted); + if(adjusted_flags & StringMatchFlag_CaseInsensitive) + { + needle_first_char_adjusted = upper_from_char(needle_first_char_adjusted); } - for (;p < stop_p; p += 1){ + for(;p < stop_p; p += 1) + { U8 haystack_char_adjusted = *p; - if(adjusted_flags & StringMatchFlag_CaseInsensitive){ - haystack_char_adjusted = char_to_upper(haystack_char_adjusted); + if(adjusted_flags & StringMatchFlag_CaseInsensitive) + { + haystack_char_adjusted = upper_from_char(haystack_char_adjusted); } - if (haystack_char_adjusted == needle_first_char_adjusted){ - if (str8_match(str8_range(p + 1, string_opl), needle_tail, adjusted_flags)){ + if(haystack_char_adjusted == needle_first_char_adjusted) + { + if(str8_match(str8_range(p + 1, string_opl), needle_tail, adjusted_flags)) + { break; } } } } U64 result = string.size; - if (p < stop_p){ + if(p < stop_p) + { result = (U64)(p - string.str); } - return(result); + return result; } internal U64 @@ -379,51 +367,76 @@ str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMa } internal B32 -str8_ends_with(String8 string, String8 end, StringMatchFlags flags){ - String8 postfix = str8_postfix(string, end.size); - B32 is_match = str8_match(end, postfix, flags); - return is_match; +str8_is_before(String8 a, String8 b) +{ + B32 result = 0; + { + U64 common_size = Min(a.size, b.size); + for(U64 off = 0; off < common_size; off += 1) + { + if(a.str[off] < b.str[off]) + { + result = 1; + break; + } + else if(a.str[off] > b.str[off]) + { + result = 0; + break; + } + else if(off+1 == common_size) + { + result = (a.size < b.size); + } + } + } + return result; } //////////////////////////////// //~ rjf: String Slicing internal String8 -str8_substr(String8 str, Rng1U64 range){ +str8_substr(String8 str, Rng1U64 range) +{ range.min = ClampTop(range.min, str.size); range.max = ClampTop(range.max, str.size); str.str += range.min; str.size = dim_1u64(range); - return(str); + return str; } internal String8 -str8_prefix(String8 str, U64 size){ +str8_prefix(String8 str, U64 size) +{ str.size = ClampTop(size, str.size); - return(str); + return str; } internal String8 -str8_skip(String8 str, U64 amt){ +str8_skip(String8 str, U64 amt) +{ amt = ClampTop(amt, str.size); str.str += amt; str.size -= amt; - return(str); + return str; } internal String8 -str8_postfix(String8 str, U64 size){ +str8_postfix(String8 str, U64 size) +{ size = ClampTop(size, str.size); str.str = (str.str + str.size) - size; str.size = size; - return(str); + return str; } internal String8 -str8_chop(String8 str, U64 amt){ +str8_chop(String8 str, U64 amt) +{ amt = ClampTop(amt, str.size); str.size -= amt; - return(str); + return str; } internal String8 @@ -542,11 +555,13 @@ push_cstr(Arena *arena, String8 str) //- rjf: string -> integer internal S64 -sign_from_str8(String8 string, String8 *string_tail){ +sign_from_str8(String8 string, String8 *string_tail) +{ // count negative signs U64 neg_count = 0; U64 i = 0; - for (; i < string.size; i += 1){ + for(; i < string.size; i += 1) + { if (string.str[i] == '-'){ neg_count += 1; } @@ -560,44 +575,53 @@ sign_from_str8(String8 string, String8 *string_tail){ // output integer sign S64 sign = (neg_count & 1)?-1:+1; - return(sign); + return sign; } internal B32 -str8_is_integer(String8 string, U32 radix){ +str8_is_integer(String8 string, U32 radix) +{ B32 result = 0; - if (string.size > 0){ - if (1 < radix && radix <= 16){ + if(string.size > 0) + { + if(1 < radix && radix <= 16) + { result = 1; - for (U64 i = 0; i < string.size; i += 1){ + for(U64 i = 0; i < string.size; i += 1) + { U8 c = string.str[i]; - if (!(c < 0x80) || integer_symbol_reverse[c] >= radix){ + if(!(c < 0x80) || integer_symbol_reverse[c] >= radix) + { result = 0; break; } } } } - return(result); + return result; } internal U64 -u64_from_str8(String8 string, U32 radix){ +u64_from_str8(String8 string, U32 radix) +{ U64 x = 0; - if (1 < radix && radix <= 16){ - for (U64 i = 0; i < string.size; i += 1){ + if(1 < radix && radix <= 16) + { + for(U64 i = 0; i < string.size; i += 1) + { x *= radix; x += integer_symbol_reverse[string.str[i]&0x7F]; } } - return(x); + return x; } internal S64 -s64_from_str8(String8 string, U32 radix){ +s64_from_str8(String8 string, U32 radix) +{ S64 sign = sign_from_str8(string, &string); S64 x = (S64)u64_from_str8(string, radix) * sign; - return(x); + return x; } internal U32 @@ -619,28 +643,33 @@ s32_from_str8(String8 string, U32 radix) internal B32 try_u64_from_str8_c_rules(String8 string, U64 *x) { - U64 radix, prefix_size; - // hex - if(str8_match(str8_prefix(string, 2), str8_lit("0x"), StringMatchFlag_CaseInsensitive)) + // rjf: unpack radix / prefix size based on string prefix + U64 radix = 0; + U64 prefix_size = 0; { - radix = 0x10, prefix_size = 2; - } - // binary - else if(str8_match(str8_prefix(string, 2), str8_lit("0b"), StringMatchFlag_CaseInsensitive)) - { - radix = 2, prefix_size = 2; - } - // octal - else if(str8_match(str8_prefix(string, 1), str8_lit("0"), StringMatchFlag_CaseInsensitive) && string.size > 1) - { - radix = 010, prefix_size = 1; - } - // decimal - else - { - radix = 10, prefix_size = 0; + // hex + if(str8_match(str8_prefix(string, 2), str8_lit("0x"), StringMatchFlag_CaseInsensitive)) + { + radix = 0x10, prefix_size = 2; + } + // binary + else if(str8_match(str8_prefix(string, 2), str8_lit("0b"), StringMatchFlag_CaseInsensitive)) + { + radix = 2, prefix_size = 2; + } + // octal + else if(str8_match(str8_prefix(string, 1), str8_lit("0"), StringMatchFlag_CaseInsensitive) && string.size > 1) + { + radix = 010, prefix_size = 1; + } + // decimal + else + { + radix = 10, prefix_size = 0; + } } + // rjf: convert if we can String8 integer = str8_skip(string, prefix_size); B32 is_integer = str8_is_integer(integer, radix); if(is_integer) @@ -667,78 +696,78 @@ try_s64_from_str8_c_rules(String8 string, S64 *x) internal String8 str8_from_memory_size(Arena *arena, U64 size) { - String8 result; - - if(size < KB(1)) + String8 result = {0}; { - result = push_str8f(arena, "%llu Bytes", size); + if(size < KB(1)) + { + result = push_str8f(arena, "%llu Bytes", size); + } + else if(size < MB(1)) + { + result = push_str8f(arena, "%llu.%02llu KiB", size / KB(1), ((size * 100) / KB(1)) % 100); + } + else if(size < GB(1)) + { + result = push_str8f(arena, "%llu.%02llu MiB", size / MB(1), ((size * 100) / MB(1)) % 100); + } + else if(size < TB(1)) + { + result = push_str8f(arena, "%llu.%02llu GiB", size / GB(1), ((size * 100) / GB(1)) % 100); + } + else + { + result = push_str8f(arena, "%llu.%02llu TiB", size / TB(1), ((size * 100) / TB(1)) % 100); + } } - else if(size < MB(1)) - { - result = push_str8f(arena, "%llu.%02llu KiB", size / KB(1), ((size * 100) / KB(1)) % 100); - } - else if(size < GB(1)) - { - result = push_str8f(arena, "%llu.%02llu MiB", size / MB(1), ((size * 100) / MB(1)) % 100); - } - else if(size < TB(1)) - { - result = push_str8f(arena, "%llu.%02llu GiB", size / GB(1), ((size * 100) / GB(1)) % 100); - } - else - { - result = push_str8f(arena, "%llu.%02llu TiB", size / TB(1), ((size * 100) / TB(1)) % 100); - } - return result; } internal String8 str8_from_count(Arena *arena, U64 count) { - String8 result; - - if(count < 1 * 1000) + String8 result = {0}; { - result = push_str8f(arena, "%llu", count); - } - else if(count < 1000000) - { - U64 frac = ((count * 100) / 1000) % 100; - if(frac > 0) + if(count < 1 * 1000) { - result = push_str8f(arena, "%llu.%02lluK", count / 1000, frac); + result = push_str8f(arena, "%llu", count); + } + else if(count < 1000000) + { + U64 frac = ((count * 100) / 1000) % 100; + if(frac > 0) + { + result = push_str8f(arena, "%llu.%02lluK", count / 1000, frac); + } + else + { + result = push_str8f(arena, "%lluK", count / 1000); + } + } + else if(count < 1000000000) + { + U64 frac = ((count * 100) / 1000000) % 100; + if(frac > 0) + { + result = push_str8f(arena, "%llu.%02lluM", count / 1000000, frac); + } + else + { + result = push_str8f(arena, "%lluM", count / 1000000); + } } else { - result = push_str8f(arena, "%lluK", count / 1000); + U64 frac = ((count * 100) * 1000000000) % 100; + if(frac > 0) + { + result = push_str8f(arena, "%llu.%02lluB", count / 1000000000, frac); + } + else + { + result = push_str8f(arena, "%lluB", count / 1000000000, frac); + } } } - else if(count < 1000000000) - { - U64 frac = ((count * 100) / 1000000) % 100; - if(frac > 0) - { - result = push_str8f(arena, "%llu.%02lluM", count / 1000000, frac); - } - else - { - result = push_str8f(arena, "%lluM", count / 1000000); - } - } - else - { - U64 frac = ((count * 100) * 1000000000) % 100; - if(frac > 0) - { - result = push_str8f(arena, "%llu.%02lluB", count / 1000000000, frac); - } - else - { - result = push_str8f(arena, "%lluB", count / 1000000000, frac); - } - } - return result; } @@ -851,7 +880,7 @@ str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_se } else { - result.str[result.size - idx - 1] = char_to_lower(integer_symbols[u64_reduce%radix]); + result.str[result.size - idx - 1] = lower_from_char(integer_symbols[u64_reduce%radix]); u64_reduce /= radix; } digits_until_separator -= 1; @@ -943,73 +972,74 @@ f64_from_str8(String8 string) //////////////////////////////// //~ rjf: String List Construction Functions -internal String8Node* -str8_list_push_node(String8List *list, String8Node *node){ - SLLQueuePush(list->first, list->last, node); - list->node_count += 1; - list->total_size += node->string.size; - return(node); -} - -internal String8Node* -str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string){ - SLLQueuePush(list->first, list->last, node); - list->node_count += 1; - list->total_size += string.size; - node->string = string; - return(node); -} - -internal String8Node* -str8_list_push_node_front(String8List *list, String8Node *node){ - SLLQueuePushFront(list->first, list->last, node); - list->node_count += 1; - list->total_size += node->string.size; - return(node); -} - -internal String8Node* -str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string){ - SLLQueuePushFront(list->first, list->last, node); - list->node_count += 1; - list->total_size += string.size; - node->string = string; - return(node); -} - -internal String8Node* -str8_list_push(Arena *arena, String8List *list, String8 string){ - String8Node *node = push_array_no_zero(arena, String8Node, 1); - str8_list_push_node_set_string(list, node, string); - return(node); -} - internal String8Node * -str8_list_push_cstr(Arena *arena, String8List *list, String8 string) +str8_list_push_node(String8List *list, String8Node *node) { - String8Node *node = str8_list_push(arena, list, string); - local_persist String8 null = str8_lit_comp("\0"); - str8_list_push(arena, list, null); + SLLQueuePush(list->first, list->last, node); + list->node_count += 1; + list->total_size += node->string.size; return node; } -internal String8Node* -str8_list_push_front(Arena *arena, String8List *list, String8 string){ +internal String8Node * +str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string) +{ + SLLQueuePush(list->first, list->last, node); + list->node_count += 1; + list->total_size += string.size; + node->string = string; + return node; +} + +internal String8Node * +str8_list_push_node_front(String8List *list, String8Node *node) +{ + SLLQueuePushFront(list->first, list->last, node); + list->node_count += 1; + list->total_size += node->string.size; + return node; +} + +internal String8Node * +str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string) +{ + SLLQueuePushFront(list->first, list->last, node); + list->node_count += 1; + list->total_size += string.size; + node->string = string; + return node; +} + +internal String8Node * +str8_list_push(Arena *arena, String8List *list, String8 string) +{ + String8Node *node = push_array_no_zero(arena, String8Node, 1); + str8_list_push_node_set_string(list, node, string); + return node; +} + +internal String8Node * +str8_list_push_front(Arena *arena, String8List *list, String8 string) +{ String8Node *node = push_array_no_zero(arena, String8Node, 1); str8_list_push_node_front_set_string(list, node, string); - return(node); + return node; } internal void -str8_list_concat_in_place(String8List *list, String8List *to_push){ - if(to_push->node_count != 0){ - if (list->last){ +str8_list_concat_in_place(String8List *list, String8List *to_push) +{ + if(to_push->node_count != 0) + { + if(list->last) + { list->node_count += to_push->node_count; list->total_size += to_push->total_size; list->last->next = to_push->first; list->last = to_push->last; } - else{ + else + { *list = *to_push; } MemoryZeroStruct(to_push); @@ -1040,133 +1070,121 @@ str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align){ } internal String8Node* -str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...){ +str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...) +{ va_list args; va_start(args, fmt); String8 string = push_str8fv(arena, fmt, args); String8Node *result = str8_list_push(arena, list, string); va_end(args); - return(result); + return result; } internal String8Node* -str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...){ +str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...) +{ va_list args; va_start(args, fmt); String8 string = push_str8fv(arena, fmt, args); String8Node *result = str8_list_push_front(arena, list, string); va_end(args); - return(result); + return result; } internal String8List -str8_list_copy(Arena *arena, String8List *list){ +str8_list_copy(Arena *arena, String8List *list) +{ String8List result = {0}; - for (String8Node *node = list->first; - node != 0; - node = node->next){ + for(String8Node *node = list->first; node != 0; node = node->next) + { String8Node *new_node = push_array_no_zero(arena, String8Node, 1); String8 new_string = push_str8_copy(arena, node->string); str8_list_push_node_set_string(&result, new_node, new_string); } - return(result); + return result; } +//////////////////////////////// +//~ rjf: String Splitting & Joining + internal String8List -str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags){ +str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags) +{ String8List list = {0}; - B32 keep_empties = (flags & StringSplitFlag_KeepEmpties); - U8 *ptr = string.str; U8 *opl = string.str + string.size; - for (;ptr < opl;){ + for(;ptr < opl;) + { U8 *first = ptr; - for (;ptr < opl; ptr += 1){ + for(;ptr < opl; ptr += 1) + { U8 c = *ptr; B32 is_split = 0; - for (U64 i = 0; i < split_char_count; i += 1){ - if (split_chars[i] == c){ + for(U64 i = 0; i < split_char_count; i += 1) + { + if(split_chars[i] == c) + { is_split = 1; break; } } - if (is_split){ + if(is_split) + { break; } } - String8 string = str8_range(first, ptr); - if (keep_empties || string.size > 0){ + if(keep_empties || string.size > 0) + { str8_list_push(arena, &list, string); } ptr += 1; } - - return(list); -} - -internal String8List -str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags){ - String8List list = str8_split(arena, string, split_chars.str, split_chars.size, flags); return list; } internal String8List -str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags){ - String8List result = {0}; - for (String8Node *node = list.first; node != 0; node = node->next){ - String8List split = str8_split_by_string_chars(arena, node->string, split_chars, flags); - str8_list_concat_in_place(&result, &split); - } - return result; +str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags) +{ + String8List list = str8_split(arena, string, split_chars.str, split_chars.size, flags); + return list; } internal String8 -str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params){ +str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params) +{ StringJoin join = {0}; - if (optional_params != 0){ + if(optional_params != 0) + { MemoryCopyStruct(&join, optional_params); } - U64 sep_count = 0; - if (list->node_count > 0){ + if(list->node_count > 0) + { sep_count = list->node_count - 1; } - String8 result; result.size = join.pre.size + join.post.size + sep_count*join.sep.size + list->total_size; U8 *ptr = result.str = push_array_no_zero(arena, U8, result.size + 1); - MemoryCopy(ptr, join.pre.str, join.pre.size); ptr += join.pre.size; - for (String8Node *node = list->first; - node != 0; - node = node->next){ + for(String8Node *node = list->first; + node != 0; + node = node->next) + { MemoryCopy(ptr, node->string.str, node->string.size); ptr += node->string.size; - if (node->next != 0){ + if(node->next != 0) + { MemoryCopy(ptr, join.sep.str, join.sep.size); ptr += join.sep.size; } } MemoryCopy(ptr, join.post.str, join.post.size); ptr += join.post.size; - *ptr = 0; - - return(result); -} - -internal void -str8_list_from_flags(Arena *arena, String8List *list, - U32 flags, String8 *flag_string_table, U32 flag_string_count){ - for (U32 i = 0; i < flag_string_count; i += 1){ - U32 flag = (1 << i); - if (flags & flag){ - str8_list_push(arena, list, flag_string_table[i]); - } - } + return result; } //////////////////////////////// @@ -1273,7 +1291,7 @@ str8_from_version(Arena *arena, U64 version) U64 version_major = MajorFromVersion(version); U64 version_minor = MinorFromVersion(version); U64 version_patch = PatchFromVersion(version); - String8 result = push_str8f(arena, "%I64d.%I64d.%I64d", version_major, version_minor, version_patch); + String8 result = str8f(arena, "%I64d.%I64d.%I64d", version_major, version_minor, version_patch); return result; } @@ -1393,6 +1411,12 @@ internal void str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style) { Temp scratch = scratch_begin(0, 0); + typedef struct String8MetaNode String8MetaNode; + struct String8MetaNode + { + String8MetaNode *next; + String8Node *node; + }; String8MetaNode *stack = 0; String8MetaNode *free_meta_node = 0; String8Node *first = path->first; @@ -1568,7 +1592,7 @@ path_relative_dst_from_absolute_dst_src(Arena *arena, String8 dst, String8 src) src_n != 0 && bp_n != 0; src_n = src_n->next, bp_n = bp_n->next) { - if(str8_match(src_n->string, bp_n->string, path_match_flags_from_os(operating_system_from_context()))) + if(str8_match(src_n->string, bp_n->string, path_match_flags_from_os(OperatingSystem_CURRENT))) { num_backtracks -= 1; } @@ -1602,7 +1626,7 @@ path_relative_dst_from_absolute_dst_src(Arena *arena, String8 dst, String8 src) bp_n != 0; bp_n = bp_n->next) { - if(!unique_from_src && (src_n == 0 || !str8_match(src_n->string, bp_n->string, path_match_flags_from_os(operating_system_from_context())))) + if(!unique_from_src && (src_n == 0 || !str8_match(src_n->string, bp_n->string, path_match_flags_from_os(OperatingSystem_CURRENT)))) { unique_from_src = 1; } @@ -1765,23 +1789,25 @@ internal String8 path_replace_file_extension(Arena *arena, String8 file_name, String8 ext) { String8 file_name_no_ext = str8_chop_last_dot(file_name); - String8 result = push_str8f(arena, "%S.%S", file_name_no_ext, ext); + String8 result = str8f(arena, "%S.%S", file_name_no_ext, ext); return result; } //////////////////////////////// //~ rjf: UTF-8 & UTF-16 Decoding/Encoding -read_only global U8 utf8_class[32] = { +read_only global U8 utf8_class[32] = +{ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2,3,3,4,5, }; internal UnicodeDecode -utf8_decode(U8 *str, U64 max){ +utf8_decode(U8 *str, U64 max) +{ UnicodeDecode result = {1, max_U32}; U8 byte = str[0]; U8 byte_class = utf8_class[byte >> 3]; - switch (byte_class) + switch(byte_class) { case 1: { @@ -1789,10 +1815,10 @@ utf8_decode(U8 *str, U64 max){ }break; case 2: { - if (1 < max) + if(1 < max) { U8 cont_byte = str[1]; - if (utf8_class[cont_byte >> 3] == 0) + if(utf8_class[cont_byte >> 3] == 0) { result.codepoint = (byte & bitmask5) << 6; result.codepoint |= (cont_byte & bitmask6); @@ -1802,11 +1828,11 @@ utf8_decode(U8 *str, U64 max){ }break; case 3: { - if (2 < max) + if(2 < max) { U8 cont_byte[2] = {str[1], str[2]}; - if (utf8_class[cont_byte[0] >> 3] == 0 && - utf8_class[cont_byte[1] >> 3] == 0) + if(utf8_class[cont_byte[0] >> 3] == 0 && + utf8_class[cont_byte[1] >> 3] == 0) { result.codepoint = (byte & bitmask4) << 12; result.codepoint |= ((cont_byte[0] & bitmask6) << 6); @@ -1817,12 +1843,12 @@ utf8_decode(U8 *str, U64 max){ }break; case 4: { - if (3 < max) + if(3 < max) { U8 cont_byte[3] = {str[1], str[2], str[3]}; - if (utf8_class[cont_byte[0] >> 3] == 0 && - utf8_class[cont_byte[1] >> 3] == 0 && - utf8_class[cont_byte[2] >> 3] == 0) + if(utf8_class[cont_byte[0] >> 3] == 0 && + utf8_class[cont_byte[1] >> 3] == 0 && + utf8_class[cont_byte[2] >> 3] == 0) { result.codepoint = (byte & bitmask3) << 18; result.codepoint |= ((cont_byte[0] & bitmask6) << 12); @@ -1833,74 +1859,81 @@ utf8_decode(U8 *str, U64 max){ } } } - return(result); + return result; } internal UnicodeDecode -utf16_decode(U16 *str, U64 max){ +utf16_decode(U16 *str, U64 max) +{ UnicodeDecode result = {1, max_U32}; result.codepoint = str[0]; result.inc = 1; - if (max > 1 && 0xD800 <= str[0] && str[0] < 0xDC00 && 0xDC00 <= str[1] && str[1] < 0xE000){ + if(max > 1 && 0xD800 <= str[0] && str[0] < 0xDC00 && 0xDC00 <= str[1] && str[1] < 0xE000) + { result.codepoint = ((str[0] - 0xD800) << 10) | ((str[1] - 0xDC00) + 0x10000); result.inc = 2; } - return(result); + return result; } internal U32 -utf8_encode(U8 *str, U32 codepoint){ +utf8_encode(U8 *str, U32 codepoint) +{ U32 inc = 0; - if (codepoint <= 0x7F){ + if(codepoint <= 0x7F) + { str[0] = (U8)codepoint; inc = 1; } - else if (codepoint <= 0x7FF){ + else if(codepoint <= 0x7FF) + { str[0] = (bitmask2 << 6) | ((codepoint >> 6) & bitmask5); str[1] = bit8 | (codepoint & bitmask6); inc = 2; } - else if (codepoint <= 0xFFFF){ + else if(codepoint <= 0xFFFF) + { str[0] = (bitmask3 << 5) | ((codepoint >> 12) & bitmask4); str[1] = bit8 | ((codepoint >> 6) & bitmask6); str[2] = bit8 | ( codepoint & bitmask6); inc = 3; } - else if (codepoint <= 0x10FFFF){ + else if(codepoint <= 0x10FFFF) + { str[0] = (bitmask4 << 4) | ((codepoint >> 18) & bitmask3); str[1] = bit8 | ((codepoint >> 12) & bitmask6); str[2] = bit8 | ((codepoint >> 6) & bitmask6); str[3] = bit8 | ( codepoint & bitmask6); inc = 4; } - else{ + else + { str[0] = '?'; inc = 1; } - return(inc); + return inc; } internal U32 -utf16_encode(U16 *str, U32 codepoint){ +utf16_encode(U16 *str, U32 codepoint) +{ U32 inc = 1; - if (codepoint == max_U32){ + if(codepoint == max_U32) + { str[0] = (U16)'?'; } - else if (codepoint < 0x10000){ + else if(codepoint < 0x10000) + { str[0] = (U16)codepoint; } - else{ + else + { U32 v = codepoint - 0x10000; str[0] = safe_cast_u16(0xD800 + (v >> 10)); str[1] = safe_cast_u16(0xDC00 + (v & bitmask10)); inc = 2; } - return(inc); -} - -internal U32 -utf8_from_utf32_single(U8 *buffer, U32 character){ - return(utf8_encode(buffer, character)); + return inc; } //////////////////////////////// @@ -2002,7 +2035,7 @@ str32_from_8(Arena *arena, String8 in) } //////////////////////////////// -//~ String -> Enum Conversions +//~ rjf: Basic Types & Space Enum -> String Conversions read_only global struct { @@ -2020,45 +2053,48 @@ StaticAssert(ArrayCount(g_os_enum_map) == OperatingSystem_COUNT, g_os_enum_map_c internal OperatingSystem operating_system_from_string(String8 string) { - for(U64 i = 0; i < ArrayCount(g_os_enum_map); ++i) + for EachElement(idx, g_os_enum_map) { - if(str8_match(g_os_enum_map[i].string, string, StringMatchFlag_CaseInsensitive)) + if(str8_match(g_os_enum_map[idx].string, string, StringMatchFlag_CaseInsensitive)) { - return g_os_enum_map[i].os; + return g_os_enum_map[idx].os; } } return OperatingSystem_Null; } -//////////////////////////////// -//~ rjf: Basic Types & Space Enum -> String Conversions - internal String8 -string_from_dimension(Dimension dimension){ - local_persist String8 strings[] = { +string_from_dimension(Dimension dimension) +{ + read_only local_persist String8 strings[] = + { str8_lit_comp("X"), str8_lit_comp("Y"), str8_lit_comp("Z"), str8_lit_comp("W"), }; String8 result = str8_lit("error"); - if ((U32)dimension < 4){ + if((U32)dimension < 4) + { result = strings[dimension]; } - return(result); + return result; } internal String8 -string_from_side(Side side){ - local_persist String8 strings[] = { +string_from_side(Side side) +{ + local_persist String8 strings[] = + { str8_lit_comp("Min"), str8_lit_comp("Max"), }; String8 result = str8_lit("error"); - if ((U32)side < 2){ + if((U32)side < 2) + { result = strings[side]; } - return(result); + return result; } internal String8 @@ -2073,27 +2109,27 @@ string_from_operating_system(OperatingSystem os) } internal String8 -string_from_arch(Arch arch){ - local_persist String8 strings[] = { - str8_lit_comp("Null"), - str8_lit_comp("x64"), - str8_lit_comp("x86"), - str8_lit_comp("arm64"), - str8_lit_comp("arm32"), - }; - String8 result = str8_lit("error"); - if (arch < Arch_COUNT){ - result = strings[arch]; +string_from_arch(Arch arch) +{ + String8 result = {0}; + switch(arch) + { + case Arch_Null: {result = str8_lit("Null");}break; + case Arch_x64: {result = str8_lit("x64");}break; + case Arch_x86: {result = str8_lit("x86");}break; + case Arch_arm64: {result = str8_lit("arm64");}break; + case Arch_arm32: {result = str8_lit("arm32");}break; + case Arch_COUNT: + {result = str8_lit("Invalid");}break; } - return(result); + return result; } -//////////////////////////////// -//~ rjf: Time Types -> String - internal String8 -string_from_week_day(WeekDay week_day){ - local_persist String8 strings[] = { +string_from_week_day(WeekDay week_day) +{ + read_only local_persist String8 strings[] = + { str8_lit_comp("Sun"), str8_lit_comp("Mon"), str8_lit_comp("Tue"), @@ -2103,15 +2139,18 @@ string_from_week_day(WeekDay week_day){ str8_lit_comp("Sat"), }; String8 result = str8_lit("Err"); - if ((U32)week_day < WeekDay_COUNT){ + if((U32)week_day < WeekDay_COUNT) + { result = strings[week_day]; } - return(result); + return result; } internal String8 -string_from_month(Month month){ - local_persist String8 strings[] = { +string_from_month(Month month) +{ + read_only local_persist String8 strings[] = + { str8_lit_comp("Jan"), str8_lit_comp("Feb"), str8_lit_comp("Mar"), @@ -2126,77 +2165,88 @@ string_from_month(Month month){ str8_lit_comp("Dec"), }; String8 result = str8_lit("Err"); - if ((U32)month < Month_COUNT){ + if((U32)month < Month_COUNT) + { result = strings[month]; } - return(result); + return result; } internal String8 -push_date_time_string(Arena *arena, DateTime *date_time){ +string_from_date_time(Arena *arena, DateTime *date_time) +{ char *mon_str = (char*)string_from_month(date_time->month).str; U32 adjusted_hour = date_time->hour%12; - if (adjusted_hour == 0){ + if(adjusted_hour == 0) + { adjusted_hour = 12; } char *ampm = "am"; - if (date_time->hour >= 12){ + if(date_time->hour >= 12) + { ampm = "pm"; } String8 result = push_str8f(arena, "%d %s %d, %02d:%02d:%02d %s", date_time->day, mon_str, date_time->year, adjusted_hour, date_time->min, date_time->sec, ampm); - return(result); + return result; } internal String8 -push_file_name_date_time_string(Arena *arena, DateTime *date_time){ +string_from_date_time__file_name(Arena *arena, DateTime *date_time) +{ char *mon_str = (char*)string_from_month(date_time->month).str; - String8 result = push_str8f(arena, "%d-%s-%0d--%02d-%02d-%02d", - date_time->year, mon_str, date_time->day, - date_time->hour, date_time->min, date_time->sec); - return(result); + String8 result = str8f(arena, "%d-%s-%0d--%02d-%02d-%02d", + date_time->year, mon_str, date_time->day, + date_time->hour, date_time->min, date_time->sec); + return result; } internal String8 -string_from_elapsed_time(Arena *arena, DateTime dt){ +string_from_elapsed_time(Arena *arena, DateTime dt) +{ Temp scratch = scratch_begin(&arena, 1); String8List list = {0}; - if (dt.year){ + if(dt.year) + { str8_list_pushf(scratch.arena, &list, "%dy", dt.year); str8_list_pushf(scratch.arena, &list, "%um", dt.mon); str8_list_pushf(scratch.arena, &list, "%ud", dt.day); - } else if (dt.mon){ + } + else if(dt.mon) + { str8_list_pushf(scratch.arena, &list, "%um", dt.mon); str8_list_pushf(scratch.arena, &list, "%ud", dt.day); - } else if (dt.day){ + } + else if (dt.day) + { str8_list_pushf(scratch.arena, &list, "%ud", dt.day); } str8_list_pushf(scratch.arena, &list, "%u:%u:%u:%u ms", dt.hour, dt.min, dt.sec, dt.msec); StringJoin join = { str8_lit_comp(""), str8_lit_comp(" "), str8_lit_comp("") }; String8 result = str8_list_join(arena, &list, &join); scratch_end(scratch); - return(result); + return result; } //////////////////////////////// -//~ Globally UNique Ids +//~ rjf: String <-> Globally Unique IDs internal String8 string_from_guid(Arena *arena, Guid guid) { - String8 result = push_str8f(arena, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", - guid.data1, - guid.data2, - guid.data3, - guid.data4[0], - guid.data4[1], - guid.data4[2], - guid.data4[3], - guid.data4[4], - guid.data4[5], - guid.data4[6], - guid.data4[7]); + String8 result = str8f(arena, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + guid.data1, + guid.data2, + guid.data3, + guid.data4[0], + guid.data4[1], + guid.data4[2], + guid.data4[3], + guid.data4[4], + guid.data4[5], + guid.data4[6], + guid.data4[7]); return result; } @@ -2450,7 +2500,7 @@ wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width internal String8 hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba) { - String8 hex_string = push_str8f(arena, "%02x%02x%02x%02x", (U8)(rgba.x*255.f), (U8)(rgba.y*255.f), (U8)(rgba.z*255.f), (U8)(rgba.w*255.f)); + String8 hex_string = str8f(arena, "%02x%02x%02x%02x", (U8)(rgba.x*255.f), (U8)(rgba.y*255.f), (U8)(rgba.z*255.f), (U8)(rgba.w*255.f)); return hex_string; } @@ -2463,7 +2513,7 @@ rgba_from_hex_string_4f32(String8 hex_string) { if(char_is_digit(hex_string.str[idx], 16)) { - byte_text[byte_text_idx] = char_to_lower(hex_string.str[idx]); + byte_text[byte_text_idx] = lower_from_char(hex_string.str[idx]); byte_text_idx += 1; } } @@ -2766,8 +2816,8 @@ str8_compar(String8 a, String8 b, B32 ignore_case) U64 size = Min(a.size, b.size); if (ignore_case) { for (U64 i = 0; i < size; ++i) { - U8 la = char_to_lower(a.str[i]); - U8 lb = char_to_lower(b.str[i]); + U8 la = lower_from_char(a.str[i]); + U8 lb = lower_from_char(b.str[i]); if (la < lb) { cmp = -1; break; @@ -2800,8 +2850,7 @@ str8_compar(String8 a, String8 b, B32 ignore_case) return cmp; } -internal int -str8_compar_ignore_case(const void *a, const void *b) +internal int str8_compar_ignore_case(const void *a, const void *b) { return str8_compar(*(String8*)a, *(String8*)b, 1); } @@ -2819,9 +2868,11 @@ str8_is_before_case_sensitive(const void *a, const void *b) return cmp < 0; } +//////////////////////////////// //~ rjf: Basic String Hashes #if !defined(XXH_IMPLEMENTATION) +# define XXH_INLINE_ALL # define XXH_IMPLEMENTATION # define XXH_STATIC_LINKING_ONLY # include "third_party/xxHash/xxhash.h" @@ -2840,3 +2891,19 @@ u64_hash_from_str8(String8 string) U64 result = u64_hash_from_seed_str8(5381, string); return result; } + +internal U128 +u128_hash_from_seed_str8(U64 seed, String8 string) +{ + U128 result = {0}; + XXH128_hash_t hash = XXH3_128bits_withSeed(string.str, string.size, seed); + MemoryCopy(&result, &hash, sizeof(result)); + return result; +} + +internal U128 +u128_hash_from_str8(String8 string) +{ + U128 result = u128_hash_from_seed_str8(5381, string); + return result; +} diff --git a/src/base/base_strings.h b/src/base/base_strings.h index 1c3a5d63..5a14e3dc 100644 --- a/src/base/base_strings.h +++ b/src/base/base_strings.h @@ -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 diff --git a/src/base/base_thread_context.c b/src/base/base_thread_context.c index 43bf16f3..3e12a8b9 100644 --- a/src/base/base_thread_context.c +++ b/src/base/base_thread_context.c @@ -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; +} diff --git a/src/base/base_thread_context.h b/src/base/base_thread_context.h index 44ff7ab2..5b1726be 100644 --- a/src/base/base_thread_context.h +++ b/src/base/base_thread_context.h @@ -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 diff --git a/src/base/base_threads.c b/src/base/base_threads.c new file mode 100644 index 00000000..50c6e370 --- /dev/null +++ b/src/base/base_threads.c @@ -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; +} diff --git a/src/base/base_threads.h b/src/base/base_threads.h new file mode 100644 index 00000000..330efb00 --- /dev/null +++ b/src/base/base_threads.h @@ -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 diff --git a/src/coff/coff.c b/src/coff/coff.c index 08bc3123..eef1c129 100644 --- a/src/coff/coff.c +++ b/src/coff/coff.c @@ -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[] = { diff --git a/src/coff/coff.h b/src/coff/coff.h index 68038f0c..4adff448 100644 --- a/src/coff/coff.h +++ b/src/coff/coff.h @@ -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); diff --git a/src/coff/coff_lib_writer.c b/src/coff/coff_lib_writer.c index ad7fa781..aeb9a3d2 100644 --- a/src/coff/coff_lib_writer.c +++ b/src/coff/coff_lib_writer.c @@ -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; } diff --git a/src/coff/coff_lib_writer.h b/src/coff/coff_lib_writer.h index 76b283c0..ab9dfc99 100644 --- a/src/coff/coff_lib_writer.h +++ b/src/coff/coff_lib_writer.h @@ -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 diff --git a/src/coff/coff_obj_writer.c b/src/coff/coff_obj_writer.c index 33da32fb..ccfc4f47 100644 --- a/src/coff/coff_obj_writer.c +++ b/src/coff/coff_obj_writer.c @@ -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 = §_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 = §ab[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 = §_n->v; sect->name = name; sect->flags = flags; - + if (data.size) { str8_list_push(obj_writer->arena, §->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); } diff --git a/src/coff/coff_obj_writer.h b/src/coff/coff_obj_writer.h index 9068822a..1419396a 100644 --- a/src/coff/coff_obj_writer.h +++ b/src/coff/coff_obj_writer.h @@ -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); diff --git a/src/coff/coff_parse.c b/src/coff/coff_parse.c index f8b2a3a4..3f9e81ca 100644 --- a/src/coff/coff_parse.c +++ b/src/coff/coff_parse.c @@ -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) { diff --git a/src/coff/coff_parse.h b/src/coff/coff_parse.h index 92106867..4156b6c6 100644 --- a/src/coff/coff_parse.h +++ b/src/coff/coff_parse.h @@ -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); diff --git a/src/content/content.c b/src/content/content.c new file mode 100644 index 00000000..8e554b94 --- /dev/null +++ b/src/content/content.c @@ -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(); +} diff --git a/src/content/content.h b/src/content/content.h new file mode 100644 index 00000000..e314f0f6 --- /dev/null +++ b/src/content/content.h @@ -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 diff --git a/src/ctrl/ctrl.mdesk b/src/ctrl/ctrl.mdesk index 9684baeb..9f5d3fa6 100644 --- a/src/ctrl/ctrl.mdesk +++ b/src/ctrl/ctrl.mdesk @@ -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 } +} diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index 26b59be8..8a0b1b2d 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -188,6 +188,22 @@ ctrl_handle_list_copy(Arena *arena, CTRL_HandleList *src) return dst; } +internal CTRL_HandleArray +ctrl_handle_array_from_list(Arena *arena, CTRL_HandleList *src) +{ + CTRL_HandleArray array = {0}; + array.count = src->count; + array.v = push_array_no_zero(arena, CTRL_Handle, array.count); + { + U64 idx = 0; + for(CTRL_HandleNode *n = src->first; n != 0; n = n->next, idx += 1) + { + array.v[idx] = n->v; + } + } + return array; +} + internal String8 ctrl_string_from_handle(Arena *arena, CTRL_Handle handle) { @@ -859,7 +875,7 @@ ctrl_entity_ctx_rw_store_alloc(void) store->ctx.hash_slots_count = 1024; store->ctx.hash_slots = push_array(arena, CTRL_EntityHashSlot, store->ctx.hash_slots_count); CTRL_Entity *root = store->ctx.root = ctrl_entity_alloc(store, &ctrl_entity_nil, CTRL_EntityKind_Root, Arch_Null, ctrl_handle_zero(), 0); - CTRL_Entity *local_machine = ctrl_entity_alloc(store, root, CTRL_EntityKind_Machine, arch_from_context(), ctrl_handle_make(CTRL_MachineID_Local, dmn_handle_zero()), 0); + CTRL_Entity *local_machine = ctrl_entity_alloc(store, root, CTRL_EntityKind_Machine, Arch_CURRENT, ctrl_handle_make(CTRL_MachineID_Local, dmn_handle_zero()), 0); Temp scratch = scratch_begin(0, 0); String8 local_machine_name = push_str8f(scratch.arena, "This PC (%S)", os_get_system_info()->machine_name); ctrl_entity_equip_string(store, local_machine, local_machine_name); @@ -1416,62 +1432,6 @@ ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *lis } } -//////////////////////////////// -//~ rjf: Cache Accessing Scopes - -internal CTRL_Scope * -ctrl_scope_open(void) -{ - if(ctrl_tctx == 0) - { - Arena *arena = arena_alloc(); - ctrl_tctx = push_array(arena, CTRL_TCTX, 1); - ctrl_tctx->arena = arena; - } - CTRL_Scope *scope = ctrl_tctx->free_scope; - if(scope != 0) - { - SLLStackPop(ctrl_tctx->free_scope); - } - else - { - scope = push_array_no_zero(ctrl_tctx->arena, CTRL_Scope, 1); - } - MemoryZeroStruct(scope); - return scope; -} - -internal void -ctrl_scope_close(CTRL_Scope *scope) -{ - for(CTRL_ScopeCallStackTouch *t = scope->first_call_stack_touch, *next = 0; t != 0; t = next) - { - next = t->next; - ins_atomic_u64_dec_eval(&t->node->scope_touch_count); - os_condition_variable_broadcast(t->stripe->cv); - SLLStackPush(ctrl_tctx->free_call_stack_touch, t); - } - SLLStackPush(ctrl_tctx->free_scope, scope); -} - -internal void -ctrl_scope_touch_call_stack_node__stripe_r_guarded(CTRL_Scope *scope, CTRL_CallStackCacheStripe *stripe, CTRL_CallStackCacheNode *node) -{ - ins_atomic_u64_inc_eval(&node->scope_touch_count); - CTRL_ScopeCallStackTouch *touch = ctrl_tctx->free_call_stack_touch; - if(touch != 0) - { - SLLStackPop(ctrl_tctx->free_call_stack_touch); - } - else - { - touch = push_array(ctrl_tctx->arena, CTRL_ScopeCallStackTouch, 1); - } - SLLQueuePush(scope->first_call_stack_touch, scope->last_call_stack_touch, touch); - touch->stripe = stripe; - touch->node = node; -} - //////////////////////////////// //~ rjf: Main Layer Initialization @@ -1498,15 +1458,6 @@ ctrl_init(void) e_string2num_map_insert(ctrl_state->arena, &ctrl_state->arch_string2alias_tables[arch], alias_names[idx], idx); } } - ctrl_state->process_memory_cache.slots_count = 256; - ctrl_state->process_memory_cache.slots = push_array(arena, CTRL_ProcessMemoryCacheSlot, ctrl_state->process_memory_cache.slots_count); - ctrl_state->process_memory_cache.stripes_count = os_get_system_info()->logical_processor_count; - ctrl_state->process_memory_cache.stripes = push_array(arena, CTRL_ProcessMemoryCacheStripe, ctrl_state->process_memory_cache.stripes_count); - for(U64 idx = 0; idx < ctrl_state->process_memory_cache.stripes_count; idx += 1) - { - ctrl_state->process_memory_cache.stripes[idx].rw_mutex = os_rw_mutex_alloc(); - ctrl_state->process_memory_cache.stripes[idx].cv = os_condition_variable_alloc(); - } ctrl_state->thread_reg_cache.slots_count = 1024; ctrl_state->thread_reg_cache.slots = push_array(arena, CTRL_ThreadRegCacheSlot, ctrl_state->thread_reg_cache.slots_count); ctrl_state->thread_reg_cache.stripes_count = os_get_system_info()->logical_processor_count; @@ -1514,17 +1465,7 @@ ctrl_init(void) for(U64 idx = 0; idx < ctrl_state->thread_reg_cache.stripes_count; idx += 1) { ctrl_state->thread_reg_cache.stripes[idx].arena = arena_alloc(); - ctrl_state->thread_reg_cache.stripes[idx].rw_mutex = os_rw_mutex_alloc(); - } - ctrl_state->call_stack_cache.slots_count = 1024; - ctrl_state->call_stack_cache.slots = push_array(arena, CTRL_CallStackCacheSlot, ctrl_state->call_stack_cache.slots_count); - ctrl_state->call_stack_cache.stripes_count = os_get_system_info()->logical_processor_count; - ctrl_state->call_stack_cache.stripes = push_array(arena, CTRL_CallStackCacheStripe, ctrl_state->call_stack_cache.stripes_count); - for(U64 idx = 0; idx < ctrl_state->call_stack_cache.stripes_count; idx += 1) - { - ctrl_state->call_stack_cache.stripes[idx].arena = arena_alloc(); - ctrl_state->call_stack_cache.stripes[idx].rw_mutex = os_rw_mutex_alloc(); - ctrl_state->call_stack_cache.stripes[idx].cv = os_condition_variable_alloc(); + ctrl_state->thread_reg_cache.stripes[idx].rw_mutex = rw_mutex_alloc(); } ctrl_state->module_image_info_cache.slots_count = 1024; ctrl_state->module_image_info_cache.slots = push_array(arena, CTRL_ModuleImageInfoCacheSlot, ctrl_state->module_image_info_cache.slots_count); @@ -1533,17 +1474,17 @@ ctrl_init(void) for(U64 idx = 0; idx < ctrl_state->module_image_info_cache.stripes_count; idx += 1) { ctrl_state->module_image_info_cache.stripes[idx].arena = arena_alloc(); - ctrl_state->module_image_info_cache.stripes[idx].rw_mutex = os_rw_mutex_alloc(); + ctrl_state->module_image_info_cache.stripes[idx].rw_mutex = rw_mutex_alloc(); } ctrl_state->u2c_ring_size = KB(64); ctrl_state->u2c_ring_base = push_array_no_zero(arena, U8, ctrl_state->u2c_ring_size); - ctrl_state->u2c_ring_mutex = os_mutex_alloc(); - ctrl_state->u2c_ring_cv = os_condition_variable_alloc(); + ctrl_state->u2c_ring_mutex = mutex_alloc(); + ctrl_state->u2c_ring_cv = cond_var_alloc(); ctrl_state->c2u_ring_size = KB(64); ctrl_state->c2u_ring_max_string_size = ctrl_state->c2u_ring_size/2; ctrl_state->c2u_ring_base = push_array_no_zero(arena, U8, ctrl_state->c2u_ring_size); - ctrl_state->c2u_ring_mutex = os_mutex_alloc(); - ctrl_state->c2u_ring_cv = os_condition_variable_alloc(); + ctrl_state->c2u_ring_mutex = mutex_alloc(); + ctrl_state->c2u_ring_cv = cond_var_alloc(); { Temp scratch = scratch_begin(0, 0); String8 user_program_data_path = os_get_process_info()->user_program_data_path; @@ -1553,7 +1494,7 @@ ctrl_init(void) os_write_data_to_file_path(ctrl_state->ctrl_thread_log_path, str8_zero()); scratch_end(scratch); } - ctrl_state->ctrl_thread_entity_ctx_rw_mutex = os_rw_mutex_alloc(); + ctrl_state->ctrl_thread_entity_ctx_rw_mutex = rw_mutex_alloc(); ctrl_state->ctrl_thread_entity_store = ctrl_entity_ctx_rw_store_alloc(); ctrl_state->ctrl_thread_eval_cache = e_cache_alloc(); ctrl_state->ctrl_thread_msg_process_arena = arena_alloc(); @@ -1567,16 +1508,8 @@ ctrl_init(void) ctrl_state->exception_code_filters[k/64] |= 1ull<<(k%64); } } - ctrl_state->u2ms_ring_size = KB(64); - ctrl_state->u2ms_ring_base = push_array(arena, U8, ctrl_state->u2ms_ring_size); - ctrl_state->u2ms_ring_mutex = os_mutex_alloc(); - ctrl_state->u2ms_ring_cv = os_condition_variable_alloc(); - ctrl_state->u2csb_ring_size = KB(64); - ctrl_state->u2csb_ring_base = push_array(arena, U8, ctrl_state->u2csb_ring_size); - ctrl_state->u2csb_ring_mutex = os_mutex_alloc(); - ctrl_state->u2csb_ring_cv = os_condition_variable_alloc(); ctrl_state->ctrl_thread_log = log_alloc(); - ctrl_state->ctrl_thread = os_thread_launch(ctrl_thread__entry_point, 0, 0); + ctrl_state->ctrl_thread = thread_launch(ctrl_thread__entry_point, 0); } //////////////////////////////// @@ -1588,470 +1521,6 @@ ctrl_set_wakeup_hook(CTRL_WakeupFunctionType *wakeup_hook) ctrl_state->wakeup_hook = 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) -{ - CTRL_ProcessMemoryCache *cache = &ctrl_state->process_memory_cache; - - //- rjf: unpack process key - U64 process_hash = ctrl_hash_from_handle(process); - U64 process_slot_idx = process_hash%cache->slots_count; - U64 process_stripe_idx = process_slot_idx%cache->stripes_count; - CTRL_ProcessMemoryCacheSlot *process_slot = &cache->slots[process_slot_idx]; - CTRL_ProcessMemoryCacheStripe *process_stripe = &cache->stripes[process_stripe_idx]; - - //- rjf: get the hash store root for this process; construct process node if it - // doesn't exist - HS_Root root = {0}; - { - B32 node_found = 0; - OS_MutexScopeR(process_stripe->rw_mutex) - { - for(CTRL_ProcessMemoryCacheNode *n = process_slot->first; n != 0; n = n->next) - { - if(ctrl_handle_match(n->handle, process)) - { - node_found = 1; - root = n->root; - break; - } - } - } - if(!node_found) OS_MutexScopeW(process_stripe->rw_mutex) - { - for(CTRL_ProcessMemoryCacheNode *n = process_slot->first; n != 0; n = n->next) - { - if(ctrl_handle_match(n->handle, process)) - { - node_found = 1; - root = n->root; - break; - } - } - if(!node_found) - { - Arena *node_arena = arena_alloc(); - CTRL_ProcessMemoryCacheNode *node = push_array(node_arena, CTRL_ProcessMemoryCacheNode, 1); - DLLPushBack(process_slot->first, process_slot->last, node); - node->arena = node_arena; - node->handle = process; - node->root = hs_root_alloc(); - node->range_hash_slots_count = 1024; - node->range_hash_slots = push_array(node_arena, CTRL_ProcessMemoryRangeHashSlot, node->range_hash_slots_count); - root = node->root; - } - } - } - - //- rjf: form ID for this process memory query - HS_ID id = {0}; - { - id.u128[0].u64[0] = vaddr_range.min & 0x00ffffffffffffffull; - id.u128[0].u64[1] = vaddr_range.max & 0x00ffffffffffffffull; - if(zero_terminated) - { - id.u128[0].u64[0] |= (1ull << 63); - } - } - U64 range_hash = hs_little_hash_from_data(str8_struct(&id)); - - //- rjf: form full key - HS_Key key = hs_key_make(root, id); - - //- rjf: loop: try to look for current results, request if not there, wait if we can, repeat until we can't - U64 mem_gen = ctrl_mem_gen(); - B32 key_is_stale = 0; - for(;;) - { - //- rjf: step 1: [read-only] try to look for current results for key's ID; wait if working & retry - B32 id_exists = 0; - B32 id_stale = 0; - B32 id_working = 0; - OS_MutexScopeR(process_stripe->rw_mutex) for(;;) - { - for(CTRL_ProcessMemoryCacheNode *process_n = process_slot->first; process_n != 0; process_n = process_n->next) - { - if(ctrl_handle_match(process_n->handle, process)) - { - U64 range_slot_idx = range_hash%process_n->range_hash_slots_count; - CTRL_ProcessMemoryRangeHashSlot *range_slot = &process_n->range_hash_slots[range_slot_idx]; - for(CTRL_ProcessMemoryRangeHashNode *n = range_slot->first; n != 0; n = n->next) - { - if(hs_id_match(n->id, id)) - { - id_exists = 1; - id_stale = (n->mem_gen < mem_gen); - id_working = (n->working_count != 0); - goto end_fast_lookup; - } - } - } - } - end_fast_lookup:; - if(!id_stale || !id_working || os_now_microseconds() >= endt_us) - { - break; - } - else - { - os_condition_variable_wait_rw_r(process_stripe->cv, process_stripe->rw_mutex, endt_us); - } - } - key_is_stale = id_stale; - - //- rjf: step 2: if the ID exists and is not stale, then we're done; - // the hash store contains the most up-to-date representation of the - // process memory for this key. - if(id_exists && !id_stale) - { - break; - } - - //- rjf: step 3: if the ID does not exist in the process' cache, then we - // need to build a node for it. if that, or if the ID is stale, then also - // request that that range is streamed & wait for its result (for as long - // as we have.) - B32 requested = 0; - if(!id_exists || (id_exists && id_stale && !id_working)) - { - B32 node_needs_stream = 0; - OS_MutexScopeW(process_stripe->rw_mutex) - { - for(CTRL_ProcessMemoryCacheNode *process_n = process_slot->first; process_n != 0; process_n = process_n->next) - { - if(ctrl_handle_match(process_n->handle, process)) - { - U64 range_slot_idx = range_hash%process_n->range_hash_slots_count; - CTRL_ProcessMemoryRangeHashSlot *range_slot = &process_n->range_hash_slots[range_slot_idx]; - CTRL_ProcessMemoryRangeHashNode *range_n = 0; - for(CTRL_ProcessMemoryRangeHashNode *n = range_slot->first; n != 0; n = n->next) - { - if(hs_id_match(n->id, id)) - { - range_n = n; - break; - } - } - if(range_n == 0) - { - range_n = push_array(process_n->arena, CTRL_ProcessMemoryRangeHashNode, 1); - SLLQueuePush(range_slot->first, range_slot->last, range_n); - range_n->vaddr_range = vaddr_range; - range_n->zero_terminated = zero_terminated; - range_n->id = id; - node_needs_stream = 1; - } - else - { - node_needs_stream = (range_n->mem_gen < mem_gen && range_n->working_count == 0); - } - if(node_needs_stream) - { - range_n->working_count += 1; - } - break; - } - } - } - if(node_needs_stream) - { - if(ctrl_u2ms_enqueue_req(key, process, vaddr_range, zero_terminated, endt_us)) - { - // NOTE(rjf): debugging -#if 0 - raddbg_log("[0x%I64x, 0x%I64x) push: (gen: %I64u)\n", vaddr_range.min, vaddr_range.max, mem_gen); -#endif - async_push_work(ctrl_mem_stream_work); - requested = 1; - } - else OS_MutexScopeW(process_stripe->rw_mutex) - { - for(CTRL_ProcessMemoryCacheNode *process_n = process_slot->first; process_n != 0; process_n = process_n->next) - { - if(ctrl_handle_match(process_n->handle, process)) - { - U64 range_slot_idx = range_hash%process_n->range_hash_slots_count; - CTRL_ProcessMemoryRangeHashSlot *range_slot = &process_n->range_hash_slots[range_slot_idx]; - CTRL_ProcessMemoryRangeHashNode *range_n = 0; - for(CTRL_ProcessMemoryRangeHashNode *n = range_slot->first; n != 0; n = n->next) - { - if(hs_id_match(n->id, id)) - { - n->working_count -= 1; - break; - } - } - } - } - } - } - } - - //- rjf: step 4: if we didn't request, and if we aren't working, then exit - if(!requested) - { - break; - } - - //- rjf: step 5: exit if out of time - if(os_now_microseconds() >= endt_us) - { - break; - } - } - if(out_is_stale) - { - *out_is_stale = key_is_stale; - } - return key; -} - -//- 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) -{ - ProfBeginFunction(); - CTRL_ProcessMemorySlice result = {0}; - if(range.max > range.min && - dim_1u64(range) <= MB(256) && - range.min <= 0x000FFFFFFFFFFFFFull && - range.max <= 0x000FFFFFFFFFFFFFull) - { - Temp scratch = scratch_begin(&arena, 1); - HS_Scope *scope = hs_scope_open(); - CTRL_ProcessMemoryCache *cache = &ctrl_state->process_memory_cache; - - //- rjf: unpack address range, prepare per-touched-page info - U64 page_size = KB(4); - Rng1U64 page_range = r1u64(AlignDownPow2(range.min, page_size), AlignPow2(range.max, page_size)); - U64 page_count = dim_1u64(page_range)/page_size; - U128 *page_hashes = push_array(scratch.arena, U128, page_count); - U128 *page_last_hashes = push_array(scratch.arena, U128, page_count); - - //- rjf: gather hashes & last-hashes for each page - ProfScope("gather hashes & last-hashes for each page") - { - for(U64 page_idx = 0; page_idx < page_count; page_idx += 1) - { - U64 page_base_vaddr = page_range.min + page_idx*page_size; - B32 page_is_stale = 0; - HS_Key page_key = ctrl_key_from_process_vaddr_range(process, r1u64(page_base_vaddr, page_base_vaddr+page_size), 0, endt_us, &page_is_stale); - U128 page_hash = hs_hash_from_key(page_key, 0); - U128 page_last_hash = hs_hash_from_key(page_key, 1); - result.stale = (result.stale || page_is_stale); - page_hashes[page_idx] = page_hash; - page_last_hashes[page_idx] = page_last_hash; - } - } - - //- rjf: setup output buffers - void *read_out = push_array(arena, U8, dim_1u64(range)); - U64 *byte_bad_flags = push_array(arena, U64, (dim_1u64(range)+63)/64); - U64 *byte_changed_flags = push_array(arena, U64, (dim_1u64(range)+63)/64); - - //- rjf: iterate pages, fill output - ProfScope("iterate pages, fill output") - { - U64 write_off = 0; - for(U64 page_idx = 0; page_idx < page_count; page_idx += 1) - { - // rjf: read data for this page - String8 data = hs_data_from_hash(scope, page_hashes[page_idx]); - Rng1U64 data_vaddr_range = r1u64(page_range.min + page_idx*page_size, page_range.min + page_idx*page_size+data.size); - - // rjf: skip/chop bytes which are irrelevant for the actual requested read - String8 in_range_data = data; - if(page_idx == page_count-1 && data_vaddr_range.max > range.max) - { - in_range_data = str8_chop(in_range_data, data_vaddr_range.max-range.max); - } - if(page_idx == 0 && range.min > data_vaddr_range.min) - { - in_range_data = str8_skip(in_range_data, range.min-data_vaddr_range.min); - } - - // rjf: write this chunk - MemoryCopy((U8*)read_out+write_off, in_range_data.str, in_range_data.size); - - // rjf; if this page's data doesn't fill the entire range, mark - // missing bytes as bad - if(data.size < page_size) ProfScope("mark missing bytes as bad") - { - Rng1U64 invalid_range = r1u64(data_vaddr_range.min+data.size, data_vaddr_range.min + page_size); - Rng1U64 in_range_invalid_range = intersect_1u64(invalid_range, range); - for(U64 invalid_vaddr = in_range_invalid_range.min; - invalid_vaddr < in_range_invalid_range.max; - invalid_vaddr += 1) - { - U64 idx_in_range = invalid_vaddr - range.min; - byte_bad_flags[idx_in_range/64] |= (1ull<<(idx_in_range%64)); - } - } - - // rjf: if this page's hash & last_hash don't match, diff each byte & - // fill out changed flags - if(!u128_match(page_hashes[page_idx], page_last_hashes[page_idx])) ProfScope("hashes don't match; diff each byte") - { - String8 last_data = hs_data_from_hash(scope, page_last_hashes[page_idx]); - String8 in_range_last_data = last_data; - if(page_idx == page_count-1 && data_vaddr_range.max > range.max) - { - in_range_last_data = str8_chop(in_range_last_data, data_vaddr_range.max-range.max); - } - if(page_idx == 0 && range.min > data_vaddr_range.min) - { - in_range_last_data = str8_skip(in_range_last_data, range.min-data_vaddr_range.min); - } - for(U64 idx = 0; idx < in_range_data.size; idx += 1) - { - U8 last_byte = idx < in_range_last_data.size ? in_range_last_data.str[idx] : 0; - U8 now_byte = idx < in_range_data.size ? in_range_data.str[idx] : 0; - if(last_byte != now_byte) - { - U64 idx_in_read_out = write_off+idx; - byte_changed_flags[idx_in_read_out/64] |= (1ull<<(idx_in_read_out%64)); - } - } - } - - // rjf: increment past this chunk - U64 bytes_to_skip = page_size; - if(page_idx == 0 && range.min > data_vaddr_range.min) - { - bytes_to_skip -= (range.min-data_vaddr_range.min); - } - write_off += bytes_to_skip; - } - } - - //- rjf: fill result - result.data.str = (U8*)read_out; - result.data.size = dim_1u64(range); - result.byte_bad_flags = byte_bad_flags; - result.byte_changed_flags = byte_changed_flags; - if(byte_bad_flags != 0) - { - for(U64 idx = 0; idx < (dim_1u64(range)+63)/64; idx += 1) - { - result.any_byte_bad = result.any_byte_bad || !!result.byte_bad_flags[idx]; - } - } - if(byte_changed_flags != 0) - { - for(U64 idx = 0; idx < (dim_1u64(range)+63)/64; idx += 1) - { - result.any_byte_changed = result.any_byte_changed || !!result.byte_changed_flags[idx]; - } - } - - hs_scope_close(scope); - scratch_end(scratch); - } - ProfEnd(); - return result; -} - -internal B32 -ctrl_process_memory_read(CTRL_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us) -{ - Temp scratch = scratch_begin(0, 0); - U64 needed_size = dim_1u64(range); - CTRL_ProcessMemorySlice slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process, range, endt_us); - B32 good = (slice.data.size >= needed_size && !slice.any_byte_bad); - if(good) - { - MemoryCopy(out, slice.data.str, needed_size); - } - if(slice.stale && is_stale_out) - { - *is_stale_out = 1; - } - scratch_end(scratch); - return good; -} - -//- rjf: process memory writing - -internal B32 -ctrl_process_write(CTRL_Handle process, Rng1U64 range, void *src) -{ - ProfBeginFunction(); - B32 result = dmn_process_write(process.dmn_handle, range, src); - - //- rjf: success -> bump generation - if(result) - { - ins_atomic_u64_inc_eval(&ctrl_state->mem_gen); - } - - //- rjf: success -> wait for cache updates, for small regions - prefer relatively seamless - // writes within calling frame's "view" of the memory, at the expense of a small amount of - // time. - if(result) - { - Temp scratch = scratch_begin(0, 0); - U64 endt_us = os_now_microseconds()+5000; - - //- rjf: gather tasks for all affected cached regions - typedef struct Task Task; - struct Task - { - Task *next; - CTRL_Handle process; - Rng1U64 range; - }; - Task *first_task = 0; - Task *last_task = 0; - CTRL_ProcessMemoryCache *cache = &ctrl_state->process_memory_cache; - for(U64 slot_idx = 0; slot_idx < cache->slots_count; slot_idx += 1) - { - U64 stripe_idx = slot_idx%cache->stripes_count; - CTRL_ProcessMemoryCacheSlot *slot = &cache->slots[slot_idx]; - CTRL_ProcessMemoryCacheStripe *stripe = &cache->stripes[stripe_idx]; - OS_MutexScopeW(stripe->rw_mutex) - { - for(CTRL_ProcessMemoryCacheNode *proc_n = slot->first; proc_n != 0; proc_n = proc_n->next) - { - for(U64 range_hash_idx = 0; range_hash_idx < proc_n->range_hash_slots_count; range_hash_idx += 1) - { - CTRL_ProcessMemoryRangeHashSlot *range_slot = &proc_n->range_hash_slots[range_hash_idx]; - for(CTRL_ProcessMemoryRangeHashNode *n = range_slot->first; n != 0; n = n->next) - { - Rng1U64 intersection_w_range = intersect_1u64(range, n->vaddr_range); - if(dim_1u64(intersection_w_range) != 0 && dim_1u64(n->vaddr_range) <= KB(64)) - { - Task *task = push_array(scratch.arena, Task, 1); - task->process = proc_n->handle; - task->range = n->vaddr_range; - SLLQueuePush(first_task, last_task, task); - } - } - } - } - } - } - - //- rjf: for all tasks, wait for up-to-date results - for(Task *task = first_task; task != 0; task = task->next) - { - Temp temp = temp_begin(scratch.arena); - ctrl_process_memory_slice_from_vaddr_range(temp.arena, task->process, task->range, endt_us); - temp_end(temp); - } - - scratch_end(scratch); - } - - ProfEnd(); - return result; -} - //////////////////////////////// //~ rjf: Thread Register Functions @@ -2070,7 +1539,7 @@ ctrl_reg_block_from_thread(Arena *arena, CTRL_EntityCtx *ctx, CTRL_Handle handle CTRL_ThreadRegCacheSlot *slot = &cache->slots[slot_idx]; CTRL_ThreadRegCacheStripe *stripe = &cache->stripes[stripe_idx]; void *result = push_array(arena, U8, reg_block_size); - OS_MutexScopeW(stripe->rw_mutex) + MutexScopeW(stripe->rw_mutex) { // rjf: find existing node CTRL_ThreadRegCacheNode *node = 0; @@ -2176,7 +1645,7 @@ ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_Handle module_handle, U64 v U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count; CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx]; CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx]; - OS_MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) + MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) { if(ctrl_handle_match(n->module, module_handle)) { @@ -2242,7 +1711,7 @@ ctrl_entry_point_voff_from_module(CTRL_Handle module_handle) U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count; CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx]; CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx]; - OS_MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) + MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) { if(ctrl_handle_match(n->module, module_handle)) { @@ -2262,7 +1731,7 @@ ctrl_tls_vaddr_range_from_module(CTRL_Handle module_handle) U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count; CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx]; CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx]; - OS_MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) + MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) { if(ctrl_handle_match(n->module, module_handle)) { @@ -2282,7 +1751,7 @@ ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count; CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx]; CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx]; - OS_MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) + MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) { if(ctrl_handle_match(n->module, module_handle)) { @@ -2302,7 +1771,7 @@ ctrl_raddbg_data_from_module(Arena *arena, CTRL_Handle module_handle) U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count; CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx]; CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx]; - OS_MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) + MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) { if(ctrl_handle_match(n->module, module_handle)) { @@ -3403,117 +2872,6 @@ ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U return f; } -//////////////////////////////// -//~ 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) -{ - CTRL_CallStack call_stack = {0}; - CTRL_CallStackCache *cache = &ctrl_state->call_stack_cache; - - ////////////////////////////// - //- rjf: unpack thread - // - CTRL_Handle handle = thread->handle; - U64 hash = ctrl_hash_from_handle(handle); - U64 slot_idx = hash%cache->slots_count; - U64 stripe_idx = slot_idx%cache->stripes_count; - CTRL_CallStackCacheSlot *slot = &cache->slots[slot_idx]; - CTRL_CallStackCacheStripe *stripe = &cache->stripes[stripe_idx]; - U64 reg_gen = ctrl_reg_gen(); - U64 mem_gen = ctrl_mem_gen(); - - ////////////////////////////// - //- rjf: loop: try to grab cached call stack; request; wait - // - B32 can_request = !ins_atomic_u64_eval(&ctrl_state->ctrl_thread_run_state); - for(U64 retry_idx = 0;; retry_idx += 1) - { - //- rjf: [read-only] try to look for current call stack; wait if working - B32 node_exists = 0; - B32 node_stale = 1; - B32 node_working = 0; - OS_MutexScopeR(stripe->rw_mutex) for(;;) - { - CTRL_CallStackCacheNode *node = 0; - for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) - { - if(ctrl_handle_match(n->thread, handle)) - { - node = n; - node_exists = 1; - node_stale = (reg_gen > n->reg_gen || mem_gen > n->mem_gen); - node_working = (n->working_count > 0); - break; - } - } - if(node_exists && (!can_request || !node_stale || os_now_microseconds() >= endt_us)) - { - call_stack = node->call_stack; - ctrl_scope_touch_call_stack_node__stripe_r_guarded(scope, stripe, node); - break; - } - else if(node_working) - { - os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us); - } - else - { - break; - } - } - - //- rjf: out of time => exit - if(retry_idx > 0 && os_now_microseconds() >= endt_us) - { - break; - } - - //- rjf: [write] node does not exist => create; request if new or stale - B32 need_request = (!node_exists || node_stale); - CTRL_CallStackCacheNode *node_to_request = 0; - if(can_request && need_request) OS_MutexScopeW(stripe->rw_mutex) - { - CTRL_CallStackCacheNode *node = 0; - for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) - { - if(ctrl_handle_match(n->thread, handle)) - { - node = n; - break; - } - } - if(node == 0) - { - node = push_array(stripe->arena, CTRL_CallStackCacheNode, 1); - DLLPushBack(slot->first, slot->last, node); - node->thread = thread->handle; - } - if(node->working_count == 0) - { - node->working_count += 1; - node_to_request = node; - } - } - - //- rjf: request if needed - if(node_to_request != 0) - { - if(ctrl_u2csb_enqueue_req(thread->handle, endt_us)) - { - async_push_work(ctrl_call_stack_build_work, .priority = high_priority ? ASYNC_Priority_High : ASYNC_Priority_Low); - } - else OS_MutexScopeW(stripe->rw_mutex) - { - node_to_request->working_count -= 1; - } - } - } - - return call_stack; -} - //////////////////////////////// //~ rjf: Halting All Attached Processes @@ -3574,7 +2932,7 @@ ctrl_u2c_push_msgs(CTRL_MsgList *msgs, U64 endt_us) Temp scratch = scratch_begin(0, 0); String8 msgs_srlzed_baked = ctrl_serialized_string_from_msg_list(scratch.arena, msgs); B32 good = 0; - OS_MutexScope(ctrl_state->u2c_ring_mutex) for(;;) + MutexScope(ctrl_state->u2c_ring_mutex) for(;;) { U64 unconsumed_size = (ctrl_state->u2c_ring_write_pos-ctrl_state->u2c_ring_read_pos); U64 available_size = ctrl_state->u2c_ring_size-unconsumed_size; @@ -3590,11 +2948,11 @@ ctrl_u2c_push_msgs(CTRL_MsgList *msgs, U64 endt_us) { break; } - os_condition_variable_wait(ctrl_state->u2c_ring_cv, ctrl_state->u2c_ring_mutex, endt_us); + cond_var_wait(ctrl_state->u2c_ring_cv, ctrl_state->u2c_ring_mutex, endt_us); } if(good) { - os_condition_variable_broadcast(ctrl_state->u2c_ring_cv); + cond_var_broadcast(ctrl_state->u2c_ring_cv); } scratch_end(scratch); return good; @@ -3605,7 +2963,7 @@ ctrl_u2c_pop_msgs(Arena *arena) { Temp scratch = scratch_begin(&arena, 1); String8 msgs_srlzed_baked = {0}; - OS_MutexScope(ctrl_state->u2c_ring_mutex) for(;;) + MutexScope(ctrl_state->u2c_ring_mutex) for(;;) { U64 unconsumed_size = (ctrl_state->u2c_ring_write_pos-ctrl_state->u2c_ring_read_pos); if(unconsumed_size >= sizeof(U64)) @@ -3617,9 +2975,9 @@ ctrl_u2c_pop_msgs(Arena *arena) ctrl_state->u2c_ring_read_pos += ring_read(ctrl_state->u2c_ring_base, ctrl_state->u2c_ring_size, ctrl_state->u2c_ring_read_pos, msgs_srlzed_baked.str, size_to_decode); break; } - os_condition_variable_wait(ctrl_state->u2c_ring_cv, ctrl_state->u2c_ring_mutex, max_U64); + cond_var_wait(ctrl_state->u2c_ring_cv, ctrl_state->u2c_ring_mutex, max_U64); } - os_condition_variable_broadcast(ctrl_state->u2c_ring_cv); + cond_var_broadcast(ctrl_state->u2c_ring_cv); CTRL_MsgList msgs = ctrl_msg_list_from_serialized_string(arena, msgs_srlzed_baked); scratch_end(scratch); return msgs; @@ -3632,7 +2990,7 @@ ctrl_c2u_push_events(CTRL_EventList *events) { if(events->count != 0) ProfScope("ctrl_c2u_push_events") { - OS_MutexScopeW(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) + MutexScopeW(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) { ctrl_entity_store_apply_events(ctrl_state->ctrl_thread_entity_store, events); } @@ -3640,7 +2998,7 @@ ctrl_c2u_push_events(CTRL_EventList *events) { Temp scratch = scratch_begin(0, 0); String8 event_srlzed = ctrl_serialized_string_from_event(scratch.arena, &n->v, ctrl_state->c2u_ring_size-sizeof(U64)); - OS_MutexScope(ctrl_state->c2u_ring_mutex) for(;;) + MutexScope(ctrl_state->c2u_ring_mutex) for(;;) { U64 unconsumed_size = (ctrl_state->c2u_ring_write_pos-ctrl_state->c2u_ring_read_pos); U64 available_size = ctrl_state->c2u_ring_size-unconsumed_size; @@ -3651,9 +3009,9 @@ ctrl_c2u_push_events(CTRL_EventList *events) ctrl_state->c2u_ring_write_pos += ring_write(ctrl_state->c2u_ring_base, ctrl_state->c2u_ring_size, ctrl_state->c2u_ring_write_pos, event_srlzed.str, event_srlzed.size); break; } - os_condition_variable_wait(ctrl_state->c2u_ring_cv, ctrl_state->c2u_ring_mutex, os_now_microseconds()+100); + cond_var_wait(ctrl_state->c2u_ring_cv, ctrl_state->c2u_ring_mutex, os_now_microseconds()+100); } - os_condition_variable_broadcast(ctrl_state->c2u_ring_cv); + cond_var_broadcast(ctrl_state->c2u_ring_cv); if(ctrl_state->wakeup_hook != 0) { ctrl_state->wakeup_hook(); @@ -3669,7 +3027,7 @@ ctrl_c2u_pop_events(Arena *arena) ProfBeginFunction(); Temp scratch = scratch_begin(&arena, 1); CTRL_EventList events = {0}; - OS_MutexScope(ctrl_state->c2u_ring_mutex) for(;;) + MutexScope(ctrl_state->c2u_ring_mutex) for(;;) { U64 unconsumed_size = (ctrl_state->c2u_ring_write_pos-ctrl_state->c2u_ring_read_pos); if(unconsumed_size >= sizeof(U64)) @@ -3688,7 +3046,7 @@ ctrl_c2u_pop_events(Arena *arena) break; } } - os_condition_variable_broadcast(ctrl_state->c2u_ring_cv); + cond_var_broadcast(ctrl_state->c2u_ring_cv); scratch_end(scratch); ProfEnd(); return events; @@ -3699,7 +3057,7 @@ ctrl_c2u_pop_events(Arena *arena) internal void ctrl_thread__entry_point(void *p) { - ThreadNameF("[ctrl] thread"); + ThreadNameF("ctrl_thread"); ProfBeginFunction(); DMN_CtrlCtx *ctrl_ctx = dmn_ctrl_begin(); log_select(ctrl_state->ctrl_thread_log); @@ -3829,7 +3187,7 @@ ctrl_thread__entry_point(void *p) CTRL_Entity *debug_info_path = ctrl_entity_child_from_kind(module, CTRL_EntityKind_DebugInfoPath); DI_Key old_dbgi_key = {debug_info_path->string, debug_info_path->timestamp}; di_close(&old_dbgi_key); - OS_MutexScopeW(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) + MutexScopeW(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) { ctrl_entity_equip_string(ctrl_state->ctrl_thread_entity_store, debug_info_path, path_normalized_from_string(scratch.arena, path)); } @@ -3924,8 +3282,8 @@ ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_EvalScope * String8 filename_normalized = push_str8_copy(scratch.arena, filename); for(U64 idx = 0; idx < filename_normalized.size; idx += 1) { - filename_normalized.str[idx] = char_to_lower(filename_normalized.str[idx]); - filename_normalized.str[idx] = char_to_correct_slash(filename_normalized.str[idx]); + filename_normalized.str[idx] = lower_from_char(filename_normalized.str[idx]); + filename_normalized.str[idx] = correct_slash_from_char(filename_normalized.str[idx]); } // rjf: filename -> src_id @@ -4319,7 +3677,7 @@ ctrl_thread__module_open(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_ U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count; CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx]; CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx]; - OS_MutexScopeW(stripe->rw_mutex) + MutexScopeW(stripe->rw_mutex) { CTRL_ModuleImageInfoCacheNode *node = 0; for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) @@ -4360,7 +3718,7 @@ ctrl_thread__module_close(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count; CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx]; CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx]; - OS_MutexScopeW(stripe->rw_mutex) + MutexScopeW(stripe->rw_mutex) { CTRL_ModuleImageInfoCacheNode *node = 0; for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) @@ -4992,27 +4350,6 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, } } - //- rjf: clear process memory cache, if we've just started a lone process - if(event->kind == DMN_EventKind_CreateProcess && ctrl_state->process_counter == 1) - { - CTRL_ProcessMemoryCache *cache = &ctrl_state->process_memory_cache; - for(U64 slot_idx = 0; slot_idx < cache->slots_count; slot_idx += 1) - { - U64 stripe_idx = slot_idx%cache->stripes_count; - CTRL_ProcessMemoryCacheSlot *slot = &cache->slots[slot_idx]; - CTRL_ProcessMemoryCacheStripe *stripe = &cache->stripes[stripe_idx]; - OS_MutexScopeW(stripe->rw_mutex) - { - for(CTRL_ProcessMemoryCacheNode *n = slot->first, *next = 0; n != 0; n = next) - { - next = n->next; - arena_clear(n->arena); - } - } - MemoryZeroStruct(slot); - } - } - //- rjf: out of queued up demon events -> clear event arena if(ctrl_state->first_dmn_event_node == 0) { @@ -5364,7 +4701,7 @@ ctrl_thread__launch(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) //- rjf: record (id -> entry points), so that we know custom entry points for this PID CTRL_EntityCtxRWStore *entity_ctx_rw_store = ctrl_state->ctrl_thread_entity_store; - OS_MutexScopeW(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) + MutexScopeW(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) { for(String8Node *n = msg->entry_points.first; n != 0; n = n->next) { @@ -5863,7 +5200,7 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) B32 spoof_mode = 0; CTRL_Spoof spoof = {0}; DMN_TrapChunkList entry_traps = {0}; - for(;;) + for(U64 run_loop_idx = 0;; run_loop_idx += 1) { ////////////////////////// //- rjf: choose low level traps @@ -5887,7 +5224,10 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) //- rjf: setup run controls // DMN_RunCtrls run_ctrls = {0}; - run_ctrls.priority_thread = target_thread.dmn_handle; + if(run_loop_idx == 0) + { + run_ctrls.priority_thread = target_thread.dmn_handle; + } run_ctrls.ignore_previous_exception = 1; run_ctrls.run_entity_count = frozen_threads.count; run_ctrls.run_entities = push_array(scratch.arena, DMN_Handle, run_ctrls.run_entity_count); @@ -6791,473 +6131,713 @@ 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 AC_Artifact +ctrl_memory_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out) { - B32 good = 0; - OS_MutexScope(ctrl_state->u2ms_ring_mutex) for(;;) + AC_Artifact artifact = {0}; { - U64 unconsumed_size = ctrl_state->u2ms_ring_write_pos-ctrl_state->u2ms_ring_read_pos; - U64 available_size = ctrl_state->u2ms_ring_size-unconsumed_size; - if(available_size >= sizeof(key)+sizeof(process)+sizeof(vaddr_range)+sizeof(zero_terminated)) + //- rjf: unpack key + CTRL_Handle process = {0}; + Rng1U64 vaddr_range = {0}; + B32 zero_terminated = 0; { - good = 1; - ctrl_state->u2ms_ring_write_pos += ring_write_struct(ctrl_state->u2ms_ring_base, ctrl_state->u2ms_ring_size, ctrl_state->u2ms_ring_write_pos, &key); - ctrl_state->u2ms_ring_write_pos += ring_write_struct(ctrl_state->u2ms_ring_base, ctrl_state->u2ms_ring_size, ctrl_state->u2ms_ring_write_pos, &process); - ctrl_state->u2ms_ring_write_pos += ring_write_struct(ctrl_state->u2ms_ring_base, ctrl_state->u2ms_ring_size, ctrl_state->u2ms_ring_write_pos, &vaddr_range); - ctrl_state->u2ms_ring_write_pos += ring_write_struct(ctrl_state->u2ms_ring_base, ctrl_state->u2ms_ring_size, ctrl_state->u2ms_ring_write_pos, &zero_terminated); - break; + U64 key_read_off = 0; + key_read_off += str8_deserial_read_struct(key, key_read_off, &process); + key_read_off += str8_deserial_read_struct(key, key_read_off, &vaddr_range); + key_read_off += str8_deserial_read_struct(key, key_read_off, &zero_terminated); } - if(os_now_microseconds() >= endt_us) {break;} - os_condition_variable_wait(ctrl_state->u2ms_ring_cv, ctrl_state->u2ms_ring_mutex, endt_us); - } - os_condition_variable_broadcast(ctrl_state->u2ms_ring_cv); - return good; -} - -internal void -ctrl_u2ms_dequeue_req(HS_Key *out_key, CTRL_Handle *out_process, Rng1U64 *out_vaddr_range, B32 *out_zero_terminated) -{ - OS_MutexScope(ctrl_state->u2ms_ring_mutex) for(;;) - { - U64 unconsumed_size = ctrl_state->u2ms_ring_write_pos-ctrl_state->u2ms_ring_read_pos; - if(unconsumed_size >= sizeof(*out_key)+sizeof(*out_process)+sizeof(*out_vaddr_range)+sizeof(*out_zero_terminated)) + + //- rjf: clamp vaddr range + Rng1U64 vaddr_range_clamped = vaddr_range; { - ctrl_state->u2ms_ring_read_pos += ring_read_struct(ctrl_state->u2ms_ring_base, ctrl_state->u2ms_ring_size, ctrl_state->u2ms_ring_read_pos, out_key); - ctrl_state->u2ms_ring_read_pos += ring_read_struct(ctrl_state->u2ms_ring_base, ctrl_state->u2ms_ring_size, ctrl_state->u2ms_ring_read_pos, out_process); - ctrl_state->u2ms_ring_read_pos += ring_read_struct(ctrl_state->u2ms_ring_base, ctrl_state->u2ms_ring_size, ctrl_state->u2ms_ring_read_pos, out_vaddr_range); - ctrl_state->u2ms_ring_read_pos += ring_read_struct(ctrl_state->u2ms_ring_base, ctrl_state->u2ms_ring_size, ctrl_state->u2ms_ring_read_pos, out_zero_terminated); - break; + vaddr_range_clamped.max = Max(vaddr_range_clamped.max, vaddr_range_clamped.min); + U64 max_size_cap = Min(max_U64-vaddr_range_clamped.min, GB(1)); + vaddr_range_clamped.max = Min(vaddr_range_clamped.max, vaddr_range_clamped.min+max_size_cap); } - os_condition_variable_wait(ctrl_state->u2ms_ring_cv, ctrl_state->u2ms_ring_mutex, max_U64); - } - os_condition_variable_broadcast(ctrl_state->u2ms_ring_cv); -} - -//- rjf: entry point - -ASYNC_WORK_DEF(ctrl_mem_stream_work) -{ -#define CTRL_MEM_STREAM_WORK_DEBUG 0 - ProfBeginFunction(); - CTRL_ProcessMemoryCache *cache = &ctrl_state->process_memory_cache; - - //- rjf: unpack next request - HS_Key key = {0}; - CTRL_Handle process = {0}; - Rng1U64 vaddr_range = {0}; - B32 zero_terminated = 0; - ctrl_u2ms_dequeue_req(&key, &process, &vaddr_range, &zero_terminated); - ProfBegin("memory stream request"); - - //- rjf: unpack process key - U64 process_hash = ctrl_hash_from_handle(process); - U64 process_slot_idx = process_hash%cache->slots_count; - U64 process_stripe_idx = process_slot_idx%cache->stripes_count; - CTRL_ProcessMemoryCacheSlot *process_slot = &cache->slots[process_slot_idx]; - CTRL_ProcessMemoryCacheStripe *process_stripe = &cache->stripes[process_stripe_idx]; - - //- rjf: unpack address range hash cache key - U64 range_hash = hs_little_hash_from_data(str8_struct(&key.id)); - - //- rjf: clamp vaddr range - Rng1U64 vaddr_range_clamped = vaddr_range; - { - vaddr_range_clamped.max = Max(vaddr_range_clamped.max, vaddr_range_clamped.min); - U64 max_size_cap = Min(max_U64-vaddr_range_clamped.min, GB(1)); - vaddr_range_clamped.max = Min(vaddr_range_clamped.max, vaddr_range_clamped.min+max_size_cap); - } - - //- rjf: task was taken -> read memory - U64 range_size = 0; - Arena *range_arena = 0; - void *range_base = 0; - U64 zero_terminated_size = 0; - U64 pre_read_mem_gen = ctrl_mem_gen(); - B32 pre_run_state = ins_atomic_u64_eval(&ctrl_state->ctrl_thread_run_state); -#if CTRL_MEM_STREAM_WORK_DEBUG - Log *log = log_alloc(); - log_select(log); - log_scope_begin(); -#endif - { - range_size = dim_1u64(vaddr_range_clamped); - U64 page_size = os_get_system_info()->page_size; - U64 arena_size = AlignPow2(range_size + ARENA_HEADER_SIZE, page_size); - range_arena = arena_alloc(.reserve_size = range_size+ARENA_HEADER_SIZE, .commit_size = range_size+ARENA_HEADER_SIZE); - if(range_arena == 0) + + //- rjf: do read + U64 range_size = 0; + Arena *range_arena = 0; + void *range_base = 0; + U64 zero_terminated_size = 0; + U64 pre_read_mem_gen = ctrl_mem_gen(); + B32 pre_run_state = ins_atomic_u64_eval(&ctrl_state->ctrl_thread_run_state); { - range_size = 0; - } - else - { - range_base = push_array_no_zero(range_arena, U8, range_size); - U64 bytes_read = 0; - U64 retry_count = 0; - U64 retry_limit = range_size > page_size ? 64 : 0; - for(Rng1U64 vaddr_range_clamped_retry = vaddr_range_clamped; - retry_count <= retry_limit; - retry_count += 1) + range_size = dim_1u64(vaddr_range_clamped); + U64 page_size = os_get_system_info()->page_size; // TODO(rjf): @page_size_from_process + U64 arena_size = AlignPow2(range_size + ARENA_HEADER_SIZE, page_size); + range_arena = arena_alloc(.reserve_size = range_size+ARENA_HEADER_SIZE, .commit_size = range_size+ARENA_HEADER_SIZE); + if(range_arena == 0) { - bytes_read = dmn_process_read(process.dmn_handle, vaddr_range_clamped_retry, range_base); - if(bytes_read == 0 && vaddr_range_clamped_retry.max > vaddr_range_clamped_retry.min) - { - U64 diff = (vaddr_range_clamped_retry.max-vaddr_range_clamped_retry.min)/2; - vaddr_range_clamped_retry.max -= diff; - vaddr_range_clamped_retry.max = AlignDownPow2(vaddr_range_clamped_retry.max, page_size); - if(diff == 0) - { - break; - } - } - else - { - break; - } - } - if(bytes_read == 0) - { - arena_release(range_arena); - range_base = 0; range_size = 0; - range_arena = 0; } - else if(bytes_read < range_size) + else { - MemoryZero((U8 *)range_base + bytes_read, range_size-bytes_read); - } - zero_terminated_size = range_size; - if(zero_terminated) - { - for(U64 idx = 0; idx < bytes_read; idx += 1) + range_base = push_array_no_zero(range_arena, U8, range_size); + U64 bytes_read = 0; + U64 retry_count = 0; + U64 retry_limit = range_size > page_size ? 64 : 0; + for(Rng1U64 vaddr_range_clamped_retry = vaddr_range_clamped; + retry_count <= retry_limit; + retry_count += 1) { - if(((U8 *)range_base)[idx] == 0) + bytes_read = dmn_process_read(process.dmn_handle, vaddr_range_clamped_retry, range_base); + if(bytes_read == 0 && vaddr_range_clamped_retry.max > vaddr_range_clamped_retry.min) { - zero_terminated_size = idx; - break; - } - } - } - } - } - U64 post_read_mem_gen = ctrl_mem_gen(); - B32 post_run_state = ins_atomic_u64_eval(&ctrl_state->ctrl_thread_run_state); - // NOTE(rjf): debugging -#if CTRL_MEM_STREAM_WORK_DEBUG - { - Temp scratch = scratch_begin(0, 0); - String8 sample_data_str = str8_lit("no data"); - if(range_base != 0) - { - String8 sample_data = str8((U8*)range_base + 0x100, 16); - String8List sample_data_strs = numeric_str8_list_from_data(scratch.arena, 16, sample_data, 1); - sample_data_str = str8_list_join(scratch.arena, &sample_data_strs, &(StringJoin){.sep = str8_lit(", ")}); - } - LogScopeResult log = log_scope_end(scratch.arena); - raddbg_log("[0x%I64x, 0x%I64x) { pre_gen: %I64u, post_gen: %I64u, pre_run: %i, post_run: %i, bytes: [%S], info:```%S``` }\n", vaddr_range.min, vaddr_range.max, pre_read_mem_gen, post_read_mem_gen, pre_run_state, post_run_state, sample_data_str, log.strings[LogMsgKind_Info]); - scratch_end(scratch); - } -#endif - - //- rjf: read successful -> submit to hash store - U128 hash = {0}; - if(range_base != 0 && pre_read_mem_gen == post_read_mem_gen) - { - hash = hs_submit_data(key, &range_arena, str8((U8*)range_base, zero_terminated_size)); - } - else if(range_arena != 0) - { - arena_release(range_arena); - } - - //- rjf: commit new info to cache - OS_MutexScopeW(process_stripe->rw_mutex) - { - for(CTRL_ProcessMemoryCacheNode *n = process_slot->first; n != 0; n = n->next) - { - if(ctrl_handle_match(n->handle, process)) - { - U64 range_slot_idx = range_hash%n->range_hash_slots_count; - CTRL_ProcessMemoryRangeHashSlot *range_slot = &n->range_hash_slots[range_slot_idx]; - for(CTRL_ProcessMemoryRangeHashNode *range_n = range_slot->first; range_n != 0; range_n = range_n->next) - { - if(hs_id_match(range_n->id, key.id)) - { - if(pre_read_mem_gen == post_read_mem_gen) + U64 diff = (vaddr_range_clamped_retry.max-vaddr_range_clamped_retry.min)/2; + vaddr_range_clamped_retry.max -= diff; + vaddr_range_clamped_retry.max = AlignDownPow2(vaddr_range_clamped_retry.max, page_size); + if(diff == 0) { - range_n->mem_gen = post_read_mem_gen; - } - range_n->working_count -= 1; - goto commit__break_all; - } - } - } - } - commit__break_all:; - } - - //- rjf: broadcast changes - os_condition_variable_broadcast(process_stripe->cv); - if(!u128_match(u128_zero(), hash)) - { - if(ctrl_state->wakeup_hook != 0) - { - ctrl_state->wakeup_hook(); - } - } - ProfEnd(); - ProfEnd(); - return 0; -} - -//////////////////////////////// -//~ rjf: Asynchronous Unwinding Functions - -//- rjf: user -> memory stream communication - -internal B32 -ctrl_u2csb_enqueue_req(CTRL_Handle thread, U64 endt_us) -{ - B32 good = 0; - OS_MutexScope(ctrl_state->u2csb_ring_mutex) for(;;) - { - U64 unconsumed_size = ctrl_state->u2csb_ring_write_pos - ctrl_state->u2csb_ring_read_pos; - U64 available_size = ctrl_state->u2csb_ring_size - unconsumed_size; - if(available_size >= sizeof(thread)) - { - good = 1; - ctrl_state->u2csb_ring_write_pos += ring_write_struct(ctrl_state->u2csb_ring_base, ctrl_state->u2csb_ring_size, ctrl_state->u2csb_ring_write_pos, &thread); - break; - } - if(os_now_microseconds() >= endt_us) - { - break; - } - os_condition_variable_wait(ctrl_state->u2csb_ring_cv, ctrl_state->u2csb_ring_mutex, endt_us); - } - if(good) - { - os_condition_variable_broadcast(ctrl_state->u2csb_ring_cv); - } - return good; -} - -internal void -ctrl_u2csb_dequeue_req(CTRL_Handle *out_thread) -{ - OS_MutexScope(ctrl_state->u2csb_ring_mutex) for(;;) - { - U64 unconsumed_size = ctrl_state->u2csb_ring_write_pos - ctrl_state->u2csb_ring_read_pos; - if(unconsumed_size >= sizeof(*out_thread)) - { - ctrl_state->u2csb_ring_read_pos += ring_read_struct(ctrl_state->u2csb_ring_base, ctrl_state->u2csb_ring_size, ctrl_state->u2csb_ring_read_pos, out_thread); - break; - } - os_condition_variable_wait(ctrl_state->u2csb_ring_cv, ctrl_state->u2csb_ring_mutex, max_U64); - } - os_condition_variable_broadcast(ctrl_state->u2csb_ring_cv); -} - -//- rjf: entry point - -ASYNC_WORK_DEF(ctrl_call_stack_build_work) -{ - Temp scratch = scratch_begin(0, 0); - CTRL_CallStackCache *cache = &ctrl_state->call_stack_cache; - - //- rjf: get next request & unpack - CTRL_Handle thread_handle = {0}; - ctrl_u2csb_dequeue_req(&thread_handle); - U64 hash = ctrl_hash_from_handle(thread_handle); - U64 slot_idx = hash%cache->slots_count; - U64 stripe_idx = hash%cache->stripes_count; - CTRL_CallStackCacheSlot *slot = &cache->slots[slot_idx]; - CTRL_CallStackCacheStripe *stripe = &cache->stripes[stripe_idx]; - - //- rjf: produce mini entity context for just this process - CTRL_EntityCtx *entity_ctx = push_array(scratch.arena, CTRL_EntityCtx, 1); - OS_MutexScopeR(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) - { - CTRL_EntityCtx *src_ctx = &ctrl_state->ctrl_thread_entity_store->ctx; - CTRL_EntityCtx *dst_ctx = entity_ctx; - { - dst_ctx->root = &ctrl_entity_nil; - dst_ctx->hash_slots_count = 1024; - dst_ctx->hash_slots = push_array(scratch.arena, CTRL_EntityHashSlot, dst_ctx->hash_slots_count); - MemoryCopyArray(dst_ctx->entity_kind_counts, src_ctx->entity_kind_counts); - MemoryCopyArray(dst_ctx->entity_kind_alloc_gens, src_ctx->entity_kind_alloc_gens); - } - CTRL_Entity *src_thread = ctrl_entity_from_handle(src_ctx, thread_handle); - CTRL_Entity *src_process = ctrl_process_from_entity(src_thread); - { - CTRL_EntityRec rec = {0}; - CTRL_Entity *dst_parent = &ctrl_entity_nil; - for(CTRL_Entity *src_e = src_process; src_e != &ctrl_entity_nil; src_e = rec.next) - { - rec = ctrl_entity_rec_depth_first_pre(src_e, src_process); - - // rjf: copy this entity - CTRL_Entity *dst_e = push_array(scratch.arena, CTRL_Entity, 1); - { - dst_e->first = dst_e->last = dst_e->next = dst_e->prev = &ctrl_entity_nil; - dst_e->parent = dst_parent; - dst_e->kind = src_e->kind; - dst_e->arch = src_e->arch; - dst_e->is_frozen = src_e->is_frozen; - dst_e->is_soloed = src_e->is_soloed; - dst_e->rgba = src_e->rgba; - dst_e->handle = src_e->handle; - dst_e->id = src_e->id; - dst_e->vaddr_range = src_e->vaddr_range; - dst_e->stack_base = src_e->stack_base; - dst_e->timestamp = src_e->timestamp; - dst_e->bp_flags = src_e->bp_flags; - dst_e->string = push_str8_copy(scratch.arena, src_e->string); - } - if(dst_parent == &ctrl_entity_nil) - { - dst_ctx->root = dst_e; - } - else - { - DLLPushBack_NPZ(&ctrl_entity_nil, dst_parent->first, dst_parent->last, dst_e, next, prev); - } - - // rjf: insert into hash map - { - U64 hash = ctrl_hash_from_handle(dst_e->handle); - U64 slot_idx = hash%dst_ctx->hash_slots_count; - CTRL_EntityHashSlot *slot = &dst_ctx->hash_slots[slot_idx]; - CTRL_EntityHashNode *node = 0; - for(CTRL_EntityHashNode *n = slot->first; n != 0; n = n->next) - { - if(ctrl_handle_match(n->entity->handle, dst_e->handle)) - { - node = n; break; } } - if(node == 0) + else { - node = push_array(scratch.arena, CTRL_EntityHashNode, 1); - MemoryZeroStruct(node); - DLLPushBack(slot->first, slot->last, node); - node->entity = dst_e; + break; + } + } + if(bytes_read == 0) + { + arena_release(range_arena); + range_base = 0; + range_size = 0; + range_arena = 0; + } + else if(bytes_read < range_size) + { + MemoryZero((U8 *)range_base + bytes_read, range_size-bytes_read); + } + zero_terminated_size = range_size; + if(zero_terminated) + { + for(U64 idx = 0; idx < bytes_read; idx += 1) + { + if(((U8 *)range_base)[idx] == 0) + { + zero_terminated_size = idx; + break; + } + } + U64 bytes_overkill = (bytes_read - zero_terminated_size); + arena_pop(range_arena, bytes_overkill); + } + } + } + U64 post_read_mem_gen = ctrl_mem_gen(); + B32 post_run_state = ins_atomic_u64_eval(&ctrl_state->ctrl_thread_run_state); + + //- rjf: form content key + C_Key content_key = {0}; + { + content_key.id.u128[0] = u128_hash_from_str8(key); + } + + //- rjf: read successful -> submit to hash store + U128 hash = {0}; + if(range_base != 0 && pre_read_mem_gen == post_read_mem_gen) + { + hash = c_submit_data(content_key, &range_arena, str8((U8*)range_base, zero_terminated_size)); + } + else if(range_arena != 0) + { + arena_release(range_arena); + retry_out[0] = 1; + } + + //- rjf: wakeup on new reads + if(!u128_match(u128_zero(), hash)) + { + if(ctrl_state->wakeup_hook != 0) + { + ctrl_state->wakeup_hook(); + } + } + + //- rjf: bundle content key as artifact + StaticAssert(sizeof(content_key) == sizeof(artifact), artifact_key_size_check); + MemoryCopyStruct(&artifact, &content_key); + } + return artifact; +} + +internal void +ctrl_memory_artifact_destroy(AC_Artifact artifact) +{ + C_Key key = {0}; + MemoryCopyStruct(&key, &artifact); + c_close_key(key); +} + +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) +{ + ProfBeginFunction(); + struct + { + CTRL_Handle process; + Rng1U64 vaddr_range; + B32 zero_terminated; + B32 _padding_; + } key_data = {process, vaddr_range, zero_terminated}; + String8 key = str8_struct(&key_data); + Access *access = access_open(); + AC_Artifact artifact = ac_artifact_from_key(access, key, ctrl_memory_artifact_create, ctrl_memory_artifact_destroy, endt_us, + .flags = AC_Flag_HighPriority, + .gen = ctrl_mem_gen(), + .slots_count = 2048, + .stale_out = out_is_stale, + .evict_threshold_us = 10000000); + C_Key content_key = {0}; + MemoryCopyStruct(&content_key, &artifact); + access_close(access); + ProfEnd(); + return content_key; +} + +//- 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) +{ + ProfBeginFunction(); + CTRL_ProcessMemorySlice result = {0}; + if(range.max > range.min && + dim_1u64(range) <= MB(256) && + range.min <= 0x000FFFFFFFFFFFFFull && + range.max <= 0x000FFFFFFFFFFFFFull) + { + Temp scratch = scratch_begin(&arena, 1); + Access *access = access_open(); + + //- rjf: unpack address range, prepare per-touched-page info + U64 page_size = KB(4); + Rng1U64 page_range = r1u64(AlignDownPow2(range.min, page_size), AlignPow2(range.max, page_size)); + U64 page_count = dim_1u64(page_range)/page_size; + U128 *page_hashes = push_array(scratch.arena, U128, page_count); + U128 *page_last_hashes = push_array(scratch.arena, U128, page_count); + + //- rjf: gather hashes & last-hashes for each page + ProfScope("gather hashes & last-hashes for each page") + { + for(U64 page_idx = 0; page_idx < page_count; page_idx += 1) + { + U64 page_base_vaddr = page_range.min + page_idx*page_size; + B32 page_is_stale = 0; + C_Key page_key = ctrl_key_from_process_vaddr_range(process, r1u64(page_base_vaddr, page_base_vaddr+page_size), 0, endt_us, &page_is_stale); + U128 page_hash = c_hash_from_key(page_key, 0); + U128 page_last_hash = c_hash_from_key(page_key, 1); + result.stale = (result.stale || page_is_stale); + page_hashes[page_idx] = page_hash; + page_last_hashes[page_idx] = page_last_hash; + } + } + + //- rjf: setup output buffers + void *read_out = push_array(arena, U8, dim_1u64(range)); + U64 *byte_bad_flags = push_array(arena, U64, (dim_1u64(range)+63)/64); + U64 *byte_changed_flags = push_array(arena, U64, (dim_1u64(range)+63)/64); + + //- rjf: iterate pages, fill output + ProfScope("iterate pages, fill output") + { + U64 write_off = 0; + for(U64 page_idx = 0; page_idx < page_count; page_idx += 1) + { + // rjf: read data for this page + String8 data = c_data_from_hash(access, page_hashes[page_idx]); + Rng1U64 data_vaddr_range = r1u64(page_range.min + page_idx*page_size, page_range.min + page_idx*page_size+data.size); + + // rjf: skip/chop bytes which are irrelevant for the actual requested read + String8 in_range_data = data; + if(page_idx == page_count-1 && data_vaddr_range.max > range.max) + { + in_range_data = str8_chop(in_range_data, data_vaddr_range.max-range.max); + } + if(page_idx == 0 && range.min > data_vaddr_range.min) + { + in_range_data = str8_skip(in_range_data, range.min-data_vaddr_range.min); + } + + // rjf: write this chunk + MemoryCopy((U8*)read_out+write_off, in_range_data.str, in_range_data.size); + + // rjf; if this page's data doesn't fill the entire range, mark + // missing bytes as bad + if(data.size < page_size) ProfScope("mark missing bytes as bad") + { + Rng1U64 invalid_range = r1u64(data_vaddr_range.min+data.size, data_vaddr_range.min + page_size); + Rng1U64 in_range_invalid_range = intersect_1u64(invalid_range, range); + for(U64 invalid_vaddr = in_range_invalid_range.min; + invalid_vaddr < in_range_invalid_range.max; + invalid_vaddr += 1) + { + U64 idx_in_range = invalid_vaddr - range.min; + byte_bad_flags[idx_in_range/64] |= (1ull<<(idx_in_range%64)); } } - // rjf: push/pop - if(rec.push_count) + // rjf: if this page's hash & last_hash don't match, diff each byte & + // fill out changed flags + if(!u128_match(page_hashes[page_idx], page_last_hashes[page_idx])) ProfScope("hashes don't match; diff each byte") { - dst_parent = dst_e; + String8 last_data = c_data_from_hash(access, page_last_hashes[page_idx]); + String8 in_range_last_data = last_data; + if(page_idx == page_count-1 && data_vaddr_range.max > range.max) + { + in_range_last_data = str8_chop(in_range_last_data, data_vaddr_range.max-range.max); + } + if(page_idx == 0 && range.min > data_vaddr_range.min) + { + in_range_last_data = str8_skip(in_range_last_data, range.min-data_vaddr_range.min); + } + for(U64 idx = 0; idx < in_range_data.size; idx += 1) + { + U8 last_byte = idx < in_range_last_data.size ? in_range_last_data.str[idx] : 0; + U8 now_byte = idx < in_range_data.size ? in_range_data.str[idx] : 0; + if(last_byte != now_byte) + { + U64 idx_in_read_out = write_off+idx; + byte_changed_flags[idx_in_read_out/64] |= (1ull<<(idx_in_read_out%64)); + } + } } - else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1) + + // rjf: increment past this chunk + U64 bytes_to_skip = page_size; + if(page_idx == 0 && range.min > data_vaddr_range.min) { - dst_parent = dst_parent->parent; + bytes_to_skip -= (range.min-data_vaddr_range.min); } + write_off += bytes_to_skip; + } + } + + //- rjf: fill result + result.data.str = (U8*)read_out; + result.data.size = dim_1u64(range); + result.byte_bad_flags = byte_bad_flags; + result.byte_changed_flags = byte_changed_flags; + if(byte_bad_flags != 0) + { + for(U64 idx = 0; idx < (dim_1u64(range)+63)/64; idx += 1) + { + result.any_byte_bad = result.any_byte_bad || !!result.byte_bad_flags[idx]; + } + } + if(byte_changed_flags != 0) + { + for(U64 idx = 0; idx < (dim_1u64(range)+63)/64; idx += 1) + { + result.any_byte_changed = result.any_byte_changed || !!result.byte_changed_flags[idx]; + } + } + + access_close(access); + scratch_end(scratch); + } + ProfEnd(); + return result; +} + +internal B32 +ctrl_process_memory_read(CTRL_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us) +{ + Temp scratch = scratch_begin(0, 0); + U64 needed_size = dim_1u64(range); + CTRL_ProcessMemorySlice slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process, range, endt_us); + B32 good = (slice.data.size >= needed_size && !slice.any_byte_bad); + if(good) + { + MemoryCopy(out, slice.data.str, needed_size); + } + if(slice.stale && is_stale_out) + { + *is_stale_out = 1; + } + scratch_end(scratch); + return good; +} + +//- rjf: process memory writing + +internal B32 +ctrl_process_write(CTRL_Handle process, Rng1U64 range, void *src) +{ + ProfBeginFunction(); + B32 result = dmn_process_write(process.dmn_handle, range, src); + + //- rjf: success -> bump generation + if(result) + { + ins_atomic_u64_inc_eval(&ctrl_state->mem_gen); + } + + //- rjf: success -> wait for cache updates, for small regions - prefer relatively seamless + // writes within calling frame's "view" of the memory, at the expense of a small amount of + // time. + if(result) + { + U64 endt_us = os_now_microseconds()+5000; + U64 page_size = os_get_system_info()->page_size; // TODO(rjf): @page_size_from_process + Rng1U64 page_range = r1u64(range.min/page_size, range.max/page_size); + for EachInRange(page_idx, page_range) + { + Temp scratch = scratch_begin(0, 0); + ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process, r1u64(page_idx*page_size, (page_idx+1)*page_size), endt_us); + scratch_end(scratch); + if(os_now_microseconds() >= endt_us) + { + break; } } } - //- rjf: do task + ProfEnd(); + return result; +} + +//////////////////////////////// +//~ rjf: Call Stack Artifact Cache Hooks / Lookups + +internal AC_Artifact +ctrl_call_stack_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out) +{ + AC_Artifact artifact = {0}; { - CTRL_Entity *thread = ctrl_entity_from_handle(entity_ctx, thread_handle); - CTRL_Entity *process = ctrl_process_from_entity(thread); + Temp scratch = scratch_begin(0, 0); - //- rjf: compute unwind to find list of all concrete frames, then - // call stack, to determine list of all concrete & inline frames - Arena *arena = arena_alloc(); - U64 pre_reg_gen = 0; - U64 post_reg_gen = 0; - U64 pre_mem_gen = 0; - U64 post_mem_gen = 0; - CTRL_Unwind unwind = {0}; - CTRL_CallStack call_stack = {0}; - { - pre_reg_gen = ctrl_reg_gen(); - pre_mem_gen = ctrl_mem_gen(); - unwind = ctrl_unwind_from_thread(arena, entity_ctx, thread_handle, os_now_microseconds()+5000); - call_stack = ctrl_call_stack_from_unwind(arena, process, &unwind); - post_reg_gen = ctrl_reg_gen(); - post_mem_gen = ctrl_mem_gen(); - } + //- rjf: unpack key + CTRL_Handle thread_handle = {0}; + str8_deserial_read_struct(key, 0, &thread_handle); - //- rjf: store new results in cache - Arena *last_arena = arena; - if(pre_reg_gen == post_reg_gen && - pre_mem_gen == post_mem_gen) + //- rjf: produce mini entity context for just this call stack build + CTRL_EntityCtx *entity_ctx = push_array(scratch.arena, CTRL_EntityCtx, 1); + MutexScopeR(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) { - B32 found = 0; - B32 committed = 0; - OS_MutexScopeW(stripe->rw_mutex) for(;;) + CTRL_EntityCtx *src_ctx = &ctrl_state->ctrl_thread_entity_store->ctx; + CTRL_EntityCtx *dst_ctx = entity_ctx; { - // rjf: try to find node & commit - for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) + dst_ctx->root = &ctrl_entity_nil; + dst_ctx->hash_slots_count = 1024; + dst_ctx->hash_slots = push_array(scratch.arena, CTRL_EntityHashSlot, dst_ctx->hash_slots_count); + MemoryCopyArray(dst_ctx->entity_kind_counts, src_ctx->entity_kind_counts); + MemoryCopyArray(dst_ctx->entity_kind_alloc_gens, src_ctx->entity_kind_alloc_gens); + } + CTRL_Entity *src_thread = ctrl_entity_from_handle(src_ctx, thread_handle); + CTRL_Entity *src_process = ctrl_process_from_entity(src_thread); + { + CTRL_EntityRec rec = {0}; + CTRL_Entity *dst_parent = &ctrl_entity_nil; + for(CTRL_Entity *src_e = src_process; src_e != &ctrl_entity_nil; src_e = rec.next) { - if(ctrl_handle_match(n->thread, thread_handle)) + rec = ctrl_entity_rec_depth_first_pre(src_e, src_process); + + // rjf: determine if we need this entity + B32 need_this_entity = (ctrl_handle_match(thread_handle, src_e->handle) || src_e->kind == CTRL_EntityKind_Module || src_e->kind == CTRL_EntityKind_Process); + + // rjf: copy this entity + CTRL_Entity *dst_e = &ctrl_entity_nil; + if(need_this_entity) { - found = 1; - if(n->scope_touch_count == 0) + dst_e = push_array(scratch.arena, CTRL_Entity, 1); { - committed = 1; - if(unwind.flags == 0 || call_stack.frames_count >= n->call_stack.frames_count) + dst_e->first = dst_e->last = dst_e->next = dst_e->prev = &ctrl_entity_nil; + dst_e->parent = dst_parent; + dst_e->kind = src_e->kind; + dst_e->arch = src_e->arch; + dst_e->is_frozen = src_e->is_frozen; + dst_e->is_soloed = src_e->is_soloed; + dst_e->rgba = src_e->rgba; + dst_e->handle = src_e->handle; + dst_e->id = src_e->id; + dst_e->vaddr_range = src_e->vaddr_range; + dst_e->stack_base = src_e->stack_base; + dst_e->timestamp = src_e->timestamp; + dst_e->bp_flags = src_e->bp_flags; + dst_e->string = push_str8_copy(scratch.arena, src_e->string); + } + if(dst_parent == &ctrl_entity_nil) + { + dst_ctx->root = dst_e; + } + else + { + DLLPushBack_NPZ(&ctrl_entity_nil, dst_parent->first, dst_parent->last, dst_e, next, prev); + } + } + + // rjf: insert into hash map + if(dst_e != &ctrl_entity_nil) + { + U64 hash = ctrl_hash_from_handle(dst_e->handle); + U64 slot_idx = hash%dst_ctx->hash_slots_count; + CTRL_EntityHashSlot *slot = &dst_ctx->hash_slots[slot_idx]; + CTRL_EntityHashNode *node = 0; + for(CTRL_EntityHashNode *n = slot->first; n != 0; n = n->next) + { + if(ctrl_handle_match(n->entity->handle, dst_e->handle)) { - last_arena = n->arena; - n->arena = arena; - n->call_stack = call_stack; - } - if(unwind.flags == 0) - { - n->reg_gen = pre_reg_gen; - n->mem_gen = pre_mem_gen; + node = n; + break; } } - break; + if(node == 0) + { + node = push_array(scratch.arena, CTRL_EntityHashNode, 1); + MemoryZeroStruct(node); + DLLPushBack(slot->first, slot->last, node); + node->entity = dst_e; + } + } + + // rjf: push/pop + if(rec.push_count) + { + dst_parent = dst_e; + } + else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1) + { + dst_parent = dst_parent->parent; } - } - - // rjf: not found, or committed? -> abort - if(!found || committed) - { - break; - } - - // rjf: found, not committed? -> wait & retry - if(found && !committed) - { - os_condition_variable_wait_rw_w(stripe->cv, stripe->rw_mutex, os_now_microseconds()+10); } } } - //- rjf: release last results - if(last_arena != 0) + //- rjf: compute call stack + CTRL_Entity *thread = ctrl_entity_from_handle(entity_ctx, thread_handle); + B32 good = 1; + Arena *arena = 0; + CTRL_CallStack *call_stack = 0; + if(thread != &ctrl_entity_nil) { - arena_release(last_arena); - } - - //- rjf: mark work as done - OS_MutexScopeW(stripe->rw_mutex) for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) - { - if(ctrl_handle_match(n->thread, thread_handle)) + good = 0; + arena = arena_alloc(); + call_stack = push_array(arena, CTRL_CallStack, 1); + CTRL_Entity *process = ctrl_process_from_entity(thread); + U64 pre_reg_gen = 0; + U64 post_reg_gen = 0; + U64 pre_mem_gen = 0; + U64 post_mem_gen = 0; + CTRL_Unwind unwind = {0}; { - ins_atomic_u64_dec_eval(&n->working_count); - break; + pre_reg_gen = ctrl_reg_gen(); + pre_mem_gen = ctrl_mem_gen(); + unwind = ctrl_unwind_from_thread(arena, entity_ctx, thread_handle, os_now_microseconds()+5000); + if(unwind.flags == 0) + { + good = 1; + call_stack[0] = ctrl_call_stack_from_unwind(arena, process, &unwind); + } + post_reg_gen = ctrl_reg_gen(); + post_mem_gen = ctrl_mem_gen(); + } + if(pre_reg_gen != post_reg_gen || pre_mem_gen != post_mem_gen) + { + good = 0; + } + if(!good) + { + arena_release(arena); } } //- rjf: broadcast update - os_condition_variable_broadcast(stripe->cv); - if(ctrl_state->wakeup_hook != 0) + if(good && ctrl_state->wakeup_hook != 0) { ctrl_state->wakeup_hook(); } + + //- rjf: bundle call stack as artifact + if(good) + { + artifact.u64[0] = (U64)arena; + artifact.u64[1] = (U64)call_stack; + } + + //- rjf: retry on bad + if(!good) + { + retry_out[0] = 1; + } + + scratch_end(scratch); + } + return artifact; +} + +internal void +ctrl_call_stack_artifact_destroy(AC_Artifact artifact) +{ + Arena *arena = (Arena *)artifact.u64[0]; + if(arena != 0) + { + arena_release(arena); + } +} + +internal CTRL_CallStack +ctrl_call_stack_from_thread(Access *access, CTRL_Handle thread_handle, B32 high_priority, U64 endt_us) +{ + CTRL_CallStack result = {0}; + { + AC_Artifact artifact = ac_artifact_from_key(access, str8_struct(&thread_handle), ctrl_call_stack_artifact_create, ctrl_call_stack_artifact_destroy, endt_us, + .gen = ctrl_mem_gen() + ctrl_reg_gen(), + .evict_threshold_us = 10000000, + .flags = high_priority ? AC_Flag_HighPriority : 0); + if(artifact.u64[1] != 0) + { + MemoryCopyStruct(&result, (CTRL_CallStack *)artifact.u64[1]); + } + } + return result; +} + +//////////////////////////////// +//~ 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) +{ + Temp scratch = scratch_begin(0, 0); + Access *access = access_open(); + + //- rjf: gather list of all thread handles + U64 threads_count = 0; + CTRL_Handle *threads = 0; + CTRL_Handle *threads_processes = 0; + Arch *threads_arches = 0; + MutexScopeR(ctrl_state->ctrl_thread_entity_ctx_rw_mutex) + { + CTRL_EntityCtx *ctx = &ctrl_state->ctrl_thread_entity_store->ctx; + CTRL_EntityArray thread_entities = ctrl_entity_array_from_kind(ctx, CTRL_EntityKind_Thread); + threads_count = thread_entities.count; + threads = push_array(scratch.arena, CTRL_Handle, threads_count); + threads_processes = push_array(scratch.arena, CTRL_Handle, threads_count); + threads_arches = push_array(scratch.arena, Arch, threads_count); + for EachIndex(idx, threads_count) + { + threads[idx] = thread_entities.v[idx]->handle; + threads_processes[idx] = thread_entities.v[idx]->parent->handle; + threads_arches[idx] = thread_entities.v[idx]->arch; + } } + //- rjf: gather all callstacks + B32 stale = 0; + U64 pre_mem_gen = ctrl_mem_gen(); + U64 pre_reg_gen = ctrl_reg_gen(); + CTRL_CallStack *call_stacks = push_array(scratch.arena, CTRL_CallStack, threads_count); + { + for EachIndex(idx, threads_count) + { + call_stacks[idx] = ctrl_call_stack_from_thread(access, threads[idx], 0, 0); + if(call_stacks[idx].concrete_frames_count == 0) + { + stale = 1; + break; + } + } + } + U64 post_mem_gen = ctrl_mem_gen(); + U64 post_reg_gen = ctrl_reg_gen(); + stale = (stale || pre_mem_gen != post_mem_gen || pre_reg_gen != post_reg_gen); + + //- rjf: build call stack tree + Arena *arena = 0; + CTRL_CallStackTree *tree = 0; + if(!stale) + { + U64 id_gen = 0; + arena = arena_alloc(); + tree = push_array(arena, CTRL_CallStackTree, 1); + tree->root = push_array(arena, CTRL_CallStackTreeNode, 1); + MemoryCopyStruct(tree->root, &ctrl_call_stack_tree_node_nil); + tree->root->id = id_gen; + tree->slots_count = Max(1, threads_count); + tree->slots = push_array(arena, CTRL_CallStackTreeNode *, tree->slots_count); + id_gen += 1; + for EachIndex(thread_idx, threads_count) + { + CTRL_Handle thread = threads[thread_idx]; + CTRL_Handle process = threads_processes[thread_idx]; + Arch arch = threads_arches[thread_idx]; + CTRL_CallStack call_stack = call_stacks[thread_idx]; + CTRL_CallStackTreeNode *thread_node = tree->root; + for EachIndex(frame_idx, call_stack.frames_count) + { + U64 vaddr = regs_rip_from_arch_block(arch, call_stack.frames[frame_idx].regs); + U64 depth = call_stack.frames[frame_idx].inline_depth; + CTRL_CallStackTreeNode *next_node = &ctrl_call_stack_tree_node_nil; + for(CTRL_CallStackTreeNode *child = thread_node->first; child != &ctrl_call_stack_tree_node_nil; child = child->next) + { + if(ctrl_handle_match(child->process, process) && child->vaddr == vaddr && child->depth == depth) + { + next_node = child; + break; + } + } + if(next_node == &ctrl_call_stack_tree_node_nil) + { + next_node = push_array(arena, CTRL_CallStackTreeNode, 1); + MemoryCopyStruct(next_node, &ctrl_call_stack_tree_node_nil); + next_node->id = id_gen; + SLLStackPush_N(tree->slots[next_node->id%tree->slots_count], next_node, hash_next); + id_gen += 1; + SLLQueuePush_NZ(&ctrl_call_stack_tree_node_nil, thread_node->first, thread_node->last, next_node, next); + next_node->parent = thread_node; + thread_node->child_count += 1; + } + thread_node = next_node; + } + ctrl_handle_list_push(arena, &thread_node->threads, &thread); + for(CTRL_CallStackTreeNode *n = thread_node; n != &ctrl_call_stack_tree_node_nil; n = n->parent) + { + n->all_descendant_threads_count += 1; + } + } + } + + //- rjf: produce artifact + AC_Artifact artifact = {0}; + { + artifact.u64[0] = (U64)arena; + artifact.u64[1] = (U64)tree; + } + + //- rjf: retry on stale + if(stale) + { + retry_out[0] = 1; + } + + access_close(access); scratch_end(scratch); - return 0; + return artifact; +} + +internal void +ctrl_call_stack_tree_artifact_destroy(AC_Artifact artifact) +{ + Arena *arena = (Arena *)artifact.u64[0]; + if(arena != 0) + { + arena_release(arena); + } +} + +internal CTRL_CallStackTree +ctrl_call_stack_tree(Access *access, U64 endt_us) +{ + CTRL_CallStackTree result = {&ctrl_call_stack_tree_node_nil}; + { + AC_Artifact artifact = ac_artifact_from_key(access, str8_zero(), ctrl_call_stack_tree_artifact_create, ctrl_call_stack_tree_artifact_destroy, endt_us); + if(artifact.u64[1] != 0) + { + MemoryCopyStruct(&result, (CTRL_CallStackTree *)artifact.u64[1]); + } + } + return result; } diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index d31a5e1b..56762142 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -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 diff --git a/src/dasm_cache/dasm_cache.c b/src/dasm_cache/dasm_cache.c deleted file mode 100644 index 39cb7711..00000000 --- a/src/dasm_cache/dasm_cache.c +++ /dev/null @@ -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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms); - 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, ¶ms.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, ¶ms)) - { - 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); - } -} diff --git a/src/dbg_engine/dbg_engine_core.c b/src/dbg_engine/dbg_engine_core.c index ad61e676..d5e2ad00 100644 --- a/src/dbg_engine/dbg_engine_core.c +++ b/src/dbg_engine/dbg_engine_core.c @@ -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) diff --git a/src/dbg_engine/dbg_engine_core.h b/src/dbg_engine/dbg_engine_core.h index e6e535db..9b7aeafc 100644 --- a/src/dbg_engine/dbg_engine_core.h +++ b/src/dbg_engine/dbg_engine_core.h @@ -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; diff --git a/src/dbgi/dbgi.c b/src/dbg_info/dbg_info.c similarity index 89% rename from src/dbgi/dbgi.c rename to src/dbg_info/dbg_info.c index 4fd6798d..07a10bd5 100644 --- a/src/dbgi/dbgi.c +++ b/src/dbg_info/dbg_info.c @@ -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) diff --git a/src/dbgi/dbgi.h b/src/dbg_info/dbg_info.h similarity index 91% rename from src/dbgi/dbgi.h rename to src/dbg_info/dbg_info.h index 20699773..22e4d8eb 100644 --- a/src/dbgi/dbgi.h +++ b/src/dbg_info/dbg_info.h @@ -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 diff --git a/src/dbg_info/dbg_info2.c b/src/dbg_info/dbg_info2.c new file mode 100644 index 00000000..720ddce0 --- /dev/null +++ b/src/dbg_info/dbg_info2.c @@ -0,0 +1,1138 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Helpers + +internal DI2_Key +di2_key_zero(void) +{ + DI2_Key key = {0}; + return key; +} + +internal B32 +di2_key_match(DI2_Key a, DI2_Key b) +{ + B32 result = MemoryMatchStruct(&a, &b); + return result; +} + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void +di2_init(CmdLine *cmdline) +{ + Arena *arena = arena_alloc(); + di2_shared = push_array(arena, DI2_Shared, 1); + di2_shared->arena = arena; + di2_shared->key2path_slots_count = 4096; + di2_shared->key2path_slots = push_array(arena, DI2_KeySlot, di2_shared->key2path_slots_count); + di2_shared->key2path_stripes = stripe_array_alloc(arena); + di2_shared->path2key_slots_count = 4096; + di2_shared->path2key_slots = push_array(arena, DI2_KeySlot, di2_shared->path2key_slots_count); + di2_shared->path2key_stripes = stripe_array_alloc(arena); + di2_shared->slots_count = 4096; + di2_shared->slots = push_array(arena, DI2_Slot, di2_shared->slots_count); + di2_shared->stripes = stripe_array_alloc(arena); + for EachElement(idx, di2_shared->req_batches) + { + di2_shared->req_batches[idx].mutex = mutex_alloc(); + di2_shared->req_batches[idx].arena = arena_alloc(); + } + U64 signal_pid = 0; + String8 signal_pid_string = cmd_line_string(cmdline, str8_lit("signal_pid")); + B32 has_parent = 1; + if(!try_u64_from_str8_c_rules(signal_pid_string, &signal_pid)) + { + has_parent = 0; + signal_pid = os_get_process_info()->pid; + } + di2_shared->conversion_completion_signal_semaphore_name = str8f(arena, "conversion_completion_signal_pid_%I64u", signal_pid); + if(has_parent) + { + di2_shared->conversion_completion_signal_semaphore = semaphore_open(di2_shared->conversion_completion_signal_semaphore_name); + } + else + { + di2_shared->conversion_completion_signal_semaphore = semaphore_alloc(0, 65536, di2_shared->conversion_completion_signal_semaphore_name); + di2_shared->conversion_completion_signal_receiver_thread = thread_launch(di2_conversion_completion_signal_receiver_thread_entry_point, 0); + } +} + +//////////////////////////////// +//~ rjf: Path * Timestamp Cache Submission & Lookup + +internal DI2_Key +di2_key_from_path_timestamp(String8 path, U64 min_timestamp) +{ + //- rjf: unpack key + U64 hash = u64_hash_from_str8(path); + U64 slot_idx = hash%di2_shared->path2key_slots_count; + DI2_KeySlot *slot = &di2_shared->path2key_slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&di2_shared->path2key_stripes, slot_idx); + + //- rjf: look up key, create if needed + DI2_Key key = {0}; + for(B32 write_mode = 0; write_mode <= 1; write_mode += 1) + { + // rjf: look up node, with this write mode, to find existing key computation + B32 found = 0; + RWMutexScope(stripe->rw_mutex, write_mode) + { + DI2_KeyPathNode *node = 0; + for(DI2_KeyPathNode *n = slot->first; n != 0; n = n->next) + { + if(str8_match(n->path, path, 0) && min_timestamp <= n->min_timestamp) + { + found = 1; + node = n; + key = node->key; + break; + } + } + if(!found && write_mode) + { + node = stripe->free; + if(node) + { + stripe->free = node->next; + } + else + { + node = push_array(stripe->arena, DI2_KeyPathNode, 1); + } + node->path = str8_copy(stripe->arena, path); + node->min_timestamp = min_timestamp; + node->key = key; + DLLPushBack(slot->first, slot->last, node); + } + } + + // rjf: found the key? abort + if(found) + { + break; + } + + // rjf: didn't find the key on our read lookup? compute the key before entering + // write mode + if(!found && !write_mode) + { + B32 made_key = 0; + + //- rjf: try to make key from file's contents + if(!made_key) + { + OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, path); + FileProperties props = os_properties_from_file(file); + if(min_timestamp <= props.modified) + { + //- rjf: PDB magic => use GUID for key + if(!made_key) + { + B32 is_pdb = 0; + if(!is_pdb) + { + read_only local_persist char msf_msf20_magic[] = "Microsoft C/C++ program database 2.00\r\n\x1aJG\0\0"; + U8 msf20_magic_maybe[sizeof(msf_msf20_magic)] = {0}; + os_file_read(file, r1u64(0, sizeof(msf20_magic_maybe)), msf20_magic_maybe); + if(MemoryMatch(msf20_magic_maybe, msf_msf20_magic, sizeof(msf20_magic_maybe))) + { + is_pdb = 1; + } + } + if(!is_pdb) + { + read_only local_persist char msf_msf70_magic[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0"; + U8 msf70_magic_maybe[sizeof(msf_msf70_magic)] = {0}; + os_file_read(file, r1u64(0, sizeof(msf70_magic_maybe)), msf70_magic_maybe); + if(MemoryMatch(msf70_magic_maybe, msf_msf70_magic, sizeof(msf70_magic_maybe))) + { + is_pdb = 1; + } + } + if(is_pdb) + { + // TODO(rjf) + } + } + } + os_file_close(file); + } + + //- rjf: fallback: hash from path/timestamp + if(!made_key) + { + made_key = 1; + U128 hash = u128_hash_from_seed_str8(min_timestamp, path); + MemoryCopy(&key, &hash, Min(sizeof(hash), sizeof(key))); + } + + //- rjf: made key -> store in (key -> path/timestamp) table + if(made_key) + { + U64 key_hash = u64_hash_from_str8(str8_struct(&key)); + U64 key_slot_idx = key_hash%di2_shared->key2path_slots_count; + DI2_KeySlot *key_slot = &di2_shared->key2path_slots[key_slot_idx]; + Stripe *key_stripe = stripe_from_slot_idx(&di2_shared->key2path_stripes, key_slot_idx); + RWMutexScope(key_stripe->rw_mutex, 1) + { + DI2_KeyPathNode *node = 0; + for EachNode(n, DI2_KeyPathNode, key_slot->first) + { + if(di2_key_match(n->key, key)) + { + node = n; + break; + } + } + if(node == 0) + { + node = key_stripe->free; + if(node != 0) + { + key_stripe->free = node->next; + } + else + { + node = push_array(key_stripe->arena, DI2_KeyPathNode, 1); + } + DLLPushBack(key_slot->first, key_slot->last, node); + node->path = str8_copy(key_stripe->arena, path); + node->min_timestamp = min_timestamp; + node->key = key; + } + } + } + } + } + + return key; +} + +//////////////////////////////// +//~ rjf: Debug Info Opening / Closing + +internal void +di2_open(DI2_Key key) +{ + //- rjf: unpack key + U64 hash = u64_hash_from_str8(str8_struct(&key)); + U64 slot_idx = hash%di2_shared->slots_count; + DI2_Slot *slot = &di2_shared->slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&di2_shared->stripes, slot_idx); + + //- rjf: bump this key's node's refcount; create if needed + B32 node_is_new = 0; + RWMutexScope(stripe->rw_mutex, 1) + { + DI2_Node *node = 0; + for(DI2_Node *n = slot->first; n != 0; n = n->next) + { + if(di2_key_match(n->key, key) && ins_atomic_u64_eval(&n->completion_count) > 0) + { + node = n; + break; + } + } + if(node == 0) + { + node_is_new = 1; + node = stripe->free; + if(node) + { + stripe->free = node->next; + } + else + { + node = push_array_no_zero(stripe->arena, DI2_Node, 1); + } + MemoryZeroStruct(node); + DLLPushBack(slot->first, slot->last, node); + node->key = key; + node->batch_request_counts[1] = 1; + } + node->refcount += 1; + } + + //- rjf: if new, submit low-priority request to load this key + if(node_is_new) + { + DI2_RequestBatch *batch = &di2_shared->req_batches[1]; + MutexScope(batch->mutex) + { + DI2_RequestNode *n = push_array(batch->arena, DI2_RequestNode, 1); + SLLQueuePush(batch->first, batch->last, n); + n->v.key = key; + batch->count += 1; + } + cond_var_broadcast(async_tick_start_cond_var); + } +} + +internal void +di2_close(DI2_Key key) +{ + //- rjf: unpack key + U64 hash = u64_hash_from_str8(str8_struct(&key)); + U64 slot_idx = hash%di2_shared->slots_count; + DI2_Slot *slot = &di2_shared->slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&di2_shared->stripes, slot_idx); + + //- rjf: decrement this key's node's refcount; remove if needed + B32 node_released = 0; + OS_Handle file = {0}; + OS_Handle file_map = {0}; + FileProperties file_props = {0}; + void *file_base = 0; + Arena *arena = 0; + RWMutexScope(stripe->rw_mutex, 1) + { + DI2_Node *node = 0; + for(DI2_Node *n = slot->first; n != 0; n = n->next) + { + if(di2_key_match(n->key, key) && ins_atomic_u64_eval(&n->completion_count) > 0) + { + node = n; + break; + } + } + if(node) + { + node->refcount -= 1; + if(node->refcount == 0) + { + for(;;) + { + if(access_pt_is_expired(&node->access_pt, .time = 0, .update_idxs = 0)) + { + node_released = 1; + DLLRemove(slot->first, slot->last, node); + node->next = stripe->free; + stripe->free = node; + file = node->file; + file_map = node->file_map; + file_props = node->file_props; + arena = node->arena; + break; + } + cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, max_U64); + } + } + } + } + + //- rjf: release node's resources if needed + if(node_released) + { + arena_release(arena); + os_file_map_view_close(file_map, file_base, r1u64(0, file_props.size)); + os_file_map_close(file_map); + os_file_close(file); + } +} + +//////////////////////////////// +//~ rjf: Debug Info Lookups + +internal U64 +di2_load_gen(void) +{ + U64 result = ins_atomic_u64_eval(&di2_shared->load_gen); + return result; +} + +internal DI2_KeyArray +di2_push_all_loaded_keys(Arena *arena) +{ + Temp scratch = scratch_begin(&arena, 1); + DI2_KeyList list = {0}; + { + for EachIndex(slot_idx, di2_shared->key2path_slots_count) + { + DI2_KeySlot *slot = &di2_shared->key2path_slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&di2_shared->key2path_stripes, slot_idx); + RWMutexScope(stripe->rw_mutex, 0) + { + for(DI2_KeyPathNode *n = slot->first; n != 0; n = n->next) + { + DI2_KeyNode *dst_n = push_array(scratch.arena, DI2_KeyNode, 1); + SLLQueuePush(list.first, list.last, dst_n); + list.count += 1; + dst_n->v = n->key; + } + } + } + } + DI2_KeyArray array = {0}; + array.count = list.count; + array.v = push_array(arena, DI2_Key, array.count); + { + U64 idx = 0; + for EachNode(n, DI2_KeyNode, list.first) + { + array.v[idx] = n->v; + idx += 1; + } + } + scratch_end(scratch); + return array; +} + +internal RDI_Parsed * +di2_rdi_from_key(Access *access, DI2_Key key, B32 high_priority, U64 endt_us) +{ + RDI_Parsed *rdi = &rdi_parsed_nil; + { + U64 hash = u64_hash_from_str8(str8_struct(&key)); + U64 slot_idx = hash%di2_shared->slots_count; + DI2_Slot *slot = &di2_shared->slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&di2_shared->stripes, slot_idx); + RWMutexScope(stripe->rw_mutex, 0) for(;;) + { + // rjf: try to grab current results + B32 found = 0; + B32 need_hi_request = 0; + B32 grabbed = 0; + for(DI2_Node *n = slot->first; n != 0; n = n->next) + { + if(di2_key_match(n->key, key)) + { + found = 1; + if(high_priority && ins_atomic_u64_eval_cond_assign(&n->batch_request_counts[0], 1, 0) == 0) + { + need_hi_request = 1; + } + if(ins_atomic_u64_eval(&n->completion_count) > 0) + { + grabbed = 1; + rdi = &n->rdi; + access_touch(access, &n->access_pt, stripe->cv); + } + break; + } + } + + // rjf: push high-priority request if needed + if(need_hi_request) + { + DI2_RequestBatch *batch = &di2_shared->req_batches[0]; + MutexScope(batch->mutex) + { + DI2_RequestNode *n = push_array(batch->arena, DI2_RequestNode, 1); + SLLQueuePush(batch->first, batch->last, n); + n->v.key = key; + batch->count += 1; + } + cond_var_broadcast(async_tick_start_cond_var); + } + + // rjf: found current results, or out-of-time? abort + if(grabbed || os_now_microseconds() >= endt_us) + { + break; + } + + // rjf: wait on stripe change + cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 0, endt_us); + } + } + return rdi; +} + +//////////////////////////////// +//~ rjf: Asynchronous Tick + +internal void +di2_async_tick(void) +{ + Temp scratch = scratch_begin(0, 0); + + ////////////////////////////// + //- rjf: do single-lane update: pop requests, update tasks, gather RDI paths to parse wide + // + typedef struct ParseTask ParseTask; + struct ParseTask + { + DI2_Key key; + String8 rdi_path; + }; + ParseTask *parse_tasks = 0; + U64 parse_tasks_count = 0; + if(lane_idx() == 0) + { + typedef struct ParseTaskNode ParseTaskNode; + struct ParseTaskNode + { + ParseTaskNode *next; + ParseTask v; + }; + ParseTaskNode *first_parse_task = 0; + ParseTaskNode *last_parse_task = 0; + + //////////////////////////// + //- rjf: pop all requests, high priority first + // + DI2_RequestNode *first_req = 0; + DI2_RequestNode *last_req = 0; + for EachElement(idx, di2_shared->req_batches) + { + DI2_RequestBatch *b = &di2_shared->req_batches[idx]; + MutexScope(b->mutex) + { + for EachNode(n, DI2_RequestNode, b->first) + { + DI2_RequestNode *n_copy = push_array(scratch.arena, DI2_RequestNode, 1); + MemoryCopyStruct(&n_copy->v, &n->v); + SLLQueuePush(first_req, last_req, n_copy); + } + } + arena_clear(b->arena); + b->first = b->last = 0; + b->count = 0; + } + + //////////////////////////// + //- rjf: generate load tasks for all unique requests + // + for EachNode(n, DI2_RequestNode, first_req) + { + // rjf: unpack request + DI2_Key key = n->v.key; + + // rjf: determine if this request is a duplicate + B32 request_is_duplicate = 1; + { + U64 hash = u64_hash_from_str8(str8_struct(&key)); + U64 slot_idx = hash%di2_shared->slots_count; + DI2_Slot *slot = &di2_shared->slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&di2_shared->stripes, slot_idx); + RWMutexScope(stripe->rw_mutex, 0) + { + for(DI2_Node *n = slot->first; n != 0; n = n->next) + { + if(di2_key_match(n->key, key)) + { + request_is_duplicate = (ins_atomic_u64_eval_cond_assign(&n->working_count, 1, 0) != 0); + break; + } + } + } + } + + // rjf: if not a duplicate, create new task + if(!request_is_duplicate) + { + DI2_LoadTask *t = di2_shared->free_load_task; + if(t) + { + SLLStackPop(di2_shared->free_load_task); + } + else + { + t = push_array_no_zero(di2_shared->arena, DI2_LoadTask, 1); + } + MemoryZeroStruct(t); + DLLPushBack(di2_shared->first_load_task, di2_shared->last_load_task, t); + t->key = key; + } + } + + //////////////////////////// + //- rjf: update tasks: configure, launch if we can, & retire if we can + // + for(DI2_LoadTask *t = di2_shared->first_load_task, *next = 0; t != 0; t = next) + { + next = t->next; + + //- rjf: unpack key + DI2_Key key = t->key; + U64 key_hash = u64_hash_from_str8(str8_struct(&key)); + U64 key_slot_idx = key_hash%di2_shared->key2path_slots_count; + DI2_KeySlot *key_slot = &di2_shared->key2path_slots[key_slot_idx]; + Stripe *key_stripe = stripe_from_slot_idx(&di2_shared->key2path_stripes, key_slot_idx); + + //- rjf: get key's O.G. path + String8 og_path = {0}; + U64 og_min_timestamp = 0; + RWMutexScope(key_stripe->rw_mutex, 0) + { + for(DI2_KeyPathNode *n = key_slot->first; n != 0; n = n->next) + { + if(di2_key_match(n->key, key)) + { + og_path = str8_copy(scratch.arena, n->path); + og_min_timestamp = n->min_timestamp; + break; + } + } + } + + //- rjf: analyze O.G. debug info + if(!t->og_analyzed) + { + t->og_analyzed = 1; + OS_Handle file = os_file_open(OS_AccessFlag_ShareRead|OS_AccessFlag_Read, og_path); + FileProperties props = os_properties_from_file(file); + t->og_size = props.size; + U64 rdi_magic_maybe = 0; + if(os_file_read_struct(file, 0, &rdi_magic_maybe) == 8 && + rdi_magic_maybe == RDI_MAGIC_CONSTANT) + { + t->og_is_rdi = 1; + } + os_file_close(file); + } + U64 og_size = t->og_size; + B32 og_is_rdi = t->og_is_rdi; + + //- rjf: compute key's RDI path + String8 rdi_path = {0}; + { + if(og_is_rdi) + { + rdi_path = og_path; + } + else + { + rdi_path = str8f(scratch.arena, "%S.rdi", str8_chop_last_dot(og_path)); + } + } + + //- rjf: determine if RDI is stale + if(!t->rdi_analyzed) + { + t->rdi_analyzed = 1; + OS_Handle file = os_file_open(OS_AccessFlag_ShareRead|OS_AccessFlag_Read, rdi_path); + FileProperties props = os_properties_from_file(file); + if(props.modified < og_min_timestamp) + { + t->rdi_is_stale = 1; + } + else + { + t->rdi_is_stale = 1; + RDI_Header header = {0}; + if(os_file_read_struct(file, 0, &header) == sizeof(header)) + { + t->rdi_is_stale = (header.encoding_version != RDI_ENCODING_VERSION); + } + } + os_file_close(file); + } + B32 rdi_is_stale = t->rdi_is_stale; + + //- rjf: calculate thread counts for conversion processes + if(!og_is_rdi && rdi_is_stale && t->thread_count == 0) + { + U64 thread_count = 1; + U64 max_thread_count = os_get_system_info()->logical_processor_count; + { + if(0){} + else if(og_size <= MB(4)) {thread_count = 1;} + else if(og_size <= MB(256)) {thread_count = max_thread_count/4;} + else if(og_size <= MB(512)) {thread_count = max_thread_count/3;} + else if(og_size <= GB(1)) {thread_count = max_thread_count/2;} + else {thread_count = max_thread_count;} + } + thread_count = Max(1, thread_count); + t->thread_count = thread_count; + } + + //- rjf: determine if there are threads available + B32 threads_available = 0; + { + U64 max_threads = os_get_system_info()->logical_processor_count*8; + U64 current_threads = di2_shared->conversion_thread_count; + U64 needed_threads = (current_threads + t->thread_count); + threads_available = (max_threads >= needed_threads); + } + + //- rjf: launch conversion processes + if(threads_available && !og_is_rdi && rdi_is_stale && t->thread_count != 0 && t->status != DI2_LoadTaskStatus_Active) + { + B32 should_compress = 0; + OS_ProcessLaunchParams params = {0}; + params.path = os_get_process_info()->binary_path; + params.inherit_env = 1; + params.consoleless = 1; + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "raddbg"); + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--bin"); + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--quiet"); + if(should_compress) + { + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--compress"); + } + // str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--capture"); + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--rdi"); + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--out:%S", rdi_path); + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--thread_count:%I64u", t->thread_count); + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--signal_pid:%I64u", (U64)os_get_process_info()->pid); + str8_list_pushf(scratch.arena, ¶ms.cmd_line, "%S", og_path); + t->process = os_process_launch(¶ms); + t->status = DI2_LoadTaskStatus_Active; + di2_shared->conversion_process_count += 1; + di2_shared->conversion_thread_count += t->thread_count; + } + + //- rjf: if active & process has completed, mark as done + { + U64 exit_code = 0; + if(t->status == DI2_LoadTaskStatus_Active && os_process_join(t->process, 0, &exit_code)) + { + t->status = DI2_LoadTaskStatus_Done; + di2_shared->conversion_process_count -= 1; + di2_shared->conversion_thread_count -= t->thread_count; + } + } + + //- rjf: if the RDI for this task is not stale, then we're already done - mark this + // task as done & prepped for storing into the cache + if(!rdi_is_stale) + { + t->status = DI2_LoadTaskStatus_Done; + } + + //- rjf: if task is done, retire & recycle task; gather path to load + if(t->status == DI2_LoadTaskStatus_Done) + { + DLLRemove(di2_shared->first_load_task, di2_shared->last_load_task, t); + SLLStackPush(di2_shared->free_load_task, t); + ParseTaskNode *n = push_array(scratch.arena, ParseTaskNode, 1); + n->v.key = key; + n->v.rdi_path = rdi_path; + SLLQueuePush(first_parse_task, last_parse_task, n); + parse_tasks_count += 1; + } + } + + //////////////////////////// + //- rjf: join all parse tasks + // + parse_tasks = push_array(scratch.arena, ParseTask, parse_tasks_count); + { + U64 idx = 0; + for EachNode(n, ParseTaskNode, first_parse_task) + { + parse_tasks[idx] = n->v; + idx += 1; + } + } + } + lane_sync_u64(&parse_tasks, 0); + lane_sync_u64(&parse_tasks_count, 0); + lane_sync(); + + ////////////////////////////// + //- rjf: do wide load of all prepped RDIs + // + U64 parse_task_take_counter = 0; + U64 *parse_task_take_counter_ptr = 0; + if(lane_idx() == 0) + { + parse_task_take_counter_ptr = &parse_task_take_counter; + } + lane_sync_u64(&parse_task_take_counter_ptr, 0); + { + for(;;) + { + //- rjf: take next task + U64 parse_task_idx = ins_atomic_u64_inc_eval(parse_task_take_counter_ptr) - 1; + if(parse_task_idx >= parse_tasks_count) + { + break; + } + + //- rjf: unpack task + DI2_Key key = parse_tasks[parse_task_idx].key; + String8 rdi_path = parse_tasks[parse_task_idx].rdi_path; + ProfBegin("parse %.*s", str8_varg(rdi_path)); + + //- rjf: open file + OS_Handle file = {0}; + OS_Handle file_map = {0}; + FileProperties file_props = {0}; + void *file_base = 0; + { + file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, rdi_path); + file_map = os_file_map_open(OS_AccessFlag_Read, file); + file_props = os_properties_from_file(file); + file_base = os_file_map_view_open(file_map, OS_AccessFlag_Read, r1u64(0, file_props.size)); + } + + //- rjf: do initial parse of rdi + RDI_Parsed rdi_parsed_maybe_compressed = rdi_parsed_nil; + { + RDI_ParseStatus parse_status = rdi_parse((U8 *)file_base, file_props.size, &rdi_parsed_maybe_compressed); + (void)parse_status; + } + + //- rjf: decompress & re-parse, if necessary + Arena *rdi_parsed_arena = 0; + RDI_Parsed rdi_parsed = rdi_parsed_maybe_compressed; + { + U64 decompressed_size = rdi_decompressed_size_from_parsed(&rdi_parsed_maybe_compressed); + if(decompressed_size > file_props.size) + { + rdi_parsed_arena = arena_alloc(); + U8 *decompressed_data = push_array_no_zero(rdi_parsed_arena, U8, decompressed_size); + rdi_decompress_parsed(decompressed_data, decompressed_size, &rdi_parsed_maybe_compressed); + RDI_ParseStatus parse_status = rdi_parse(decompressed_data, decompressed_size, &rdi_parsed); + (void)parse_status; + } + } + + //- rjf: commit parsed info to cache + { + U64 hash = u64_hash_from_str8(str8_struct(&key)); + U64 slot_idx = hash%di2_shared->slots_count; + DI2_Slot *slot = &di2_shared->slots[slot_idx]; + Stripe *stripe = stripe_from_slot_idx(&di2_shared->stripes, slot_idx); + RWMutexScope(stripe->rw_mutex, 1) + { + DI2_Node *node = 0; + for(DI2_Node *n = slot->first; n != 0; n = n->next) + { + if(di2_key_match(n->key, key)) + { + node = n; + break; + } + } + if(node) + { + node->file = file; + node->file_map = file_map; + node->file_props = file_props; + node->file_base = file_base; + node->arena = rdi_parsed_arena; + MemoryCopyStruct(&node->rdi, &rdi_parsed); + node->completion_count += 1; + node->working_count -= 1; + ins_atomic_u64_inc_eval(&di2_shared->load_gen); + } + else + { + if(rdi_parsed_arena != 0) + { + arena_release(rdi_parsed_arena); + } + os_file_map_view_close(file_map, file_base, r1u64(0, file_props.size)); + os_file_map_close(file_map); + os_file_close(file); + } + } + cond_var_broadcast(stripe->cv); + } + + ProfEnd(); + } + } + lane_sync(); + + scratch_end(scratch); +} + +//////////////////////////////// +//~ rjf: Conversion Completion Signal Receiver Thread + +internal void +di2_signal_completion(void) +{ + semaphore_drop(di2_shared->conversion_completion_signal_semaphore); +} + +internal void +di2_conversion_completion_signal_receiver_thread_entry_point(void *p) +{ + ThreadNameF("di2_conversion_completion_signal_receiver_thread"); + for(;;) + { + if(semaphore_take(di2_shared->conversion_completion_signal_semaphore, max_U64)) + { + cond_var_broadcast(async_tick_start_cond_var); + } + } +} + +//////////////////////////////// +//~ rjf: Search Artifact Cache Hooks / Lookups + +internal AC_Artifact +di2_search_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out) +{ + ProfBeginFunction(); + Access *access = access_open(); + Temp scratch = scratch_begin(0, 0); + AC_Artifact artifact = {0}; + { + //- rjf: unpack key + RDI_SectionKind section_kind = RDI_SectionKind_NULL; + String8 query = {0}; + { + U64 key_read_off = 0; + key_read_off += str8_deserial_read_struct(key, key_read_off, §ion_kind); + key_read_off += str8_deserial_read_struct(key, key_read_off, &query.size); + query.str = push_array(scratch.arena, U8, query.size); + key_read_off += str8_deserial_read(key, key_read_off, query.str, query.size, 1); + } + + //- rjf: gather all debug info keys we'll search on + DI2_KeyArray keys = {0}; + ProfScope("gather all debug info keys we'll search on") + { + if(lane_idx() == 0) + { + keys = di2_push_all_loaded_keys(scratch.arena); + } + lane_sync_u64(&keys.v, 0); + lane_sync_u64(&keys.count, 0); + } + + //- rjf: map all debug info keys -> RDIs + RDI_Parsed **rdis = 0; + ProfScope("map all debug info keys -> RDIs") + { + if(lane_idx() == 0) + { + rdis = push_array(scratch.arena, RDI_Parsed *, keys.count); + } + lane_sync_u64(&rdis, 0); + { + Rng1U64 range = lane_range(keys.count); + for EachInRange(idx, range) + { + rdis[idx] = di2_rdi_from_key(access, keys.v[idx], 0, 0); + } + } + } + lane_sync(); + + //- rjf: do wide search on all lanes + Arena *arena = arena_alloc(); + DI2_SearchItemChunkList *lanes_items = 0; + ProfScope("do wide search on all lanes") + { + if(lane_idx() == 0) + { + lanes_items = push_array(scratch.arena, DI2_SearchItemChunkList, lane_count()); + } + lane_sync_u64(&lanes_items, 0); + { + DI2_SearchItemChunkList *lane_items = &lanes_items[lane_idx()]; + for EachIndex(rdi_idx, keys.count) + { + DI2_Key key = keys.v[rdi_idx]; + RDI_Parsed *rdi = rdis[rdi_idx]; + + // rjf: unpack table info + U64 element_count = 0; + void *table_base = rdi_section_raw_table_from_kind(rdi, section_kind, &element_count); + U64 element_size = rdi_section_element_size_table[section_kind]; + + // rjf: determine name string index offset, depending on table kind + U64 element_name_idx_off = 0; + switch(section_kind) + { + default:{}break; + case RDI_SectionKind_Procedures: + { + element_name_idx_off = OffsetOf(RDI_Procedure, name_string_idx); + }break; + case RDI_SectionKind_GlobalVariables: + { + element_name_idx_off = OffsetOf(RDI_GlobalVariable, name_string_idx); + }break; + case RDI_SectionKind_ThreadVariables: + { + element_name_idx_off = OffsetOf(RDI_ThreadVariable, name_string_idx); + }break; + case RDI_SectionKind_UDTs: + { + // NOTE(rjf): name must be determined from self_type_idx + }break; + case RDI_SectionKind_SourceFiles: + { + // NOTE(rjf): name must be determined from file path node chain + }break; + } + + Rng1U64 range = lane_range(element_count); + for EachInRange(idx, range) + { + //- rjf: every so often, check if we need to cancel, and cancel + { + // TODO(rjf) + } + + //- rjf: get element, map to string; if empty, continue to next element + void *element = (U8 *)table_base + element_size*idx; + String8 name = {0}; + switch(section_kind) + { + case RDI_SectionKind_UDTs: + { + RDI_UDT *udt = (RDI_UDT *)element; + RDI_TypeNode *type_node = rdi_element_from_name_idx(rdi, TypeNodes, udt->self_type_idx); + name.str = rdi_string_from_idx(rdi, type_node->user_defined.name_string_idx, &name.size); + name = str8_copy(arena, name); + }break; + case RDI_SectionKind_SourceFiles: + { + Temp scratch = scratch_begin(&arena, 1); + RDI_SourceFile *file = (RDI_SourceFile *)element; + String8List path_parts = {0}; + for(RDI_FilePathNode *fpn = rdi_element_from_name_idx(rdi, FilePathNodes, file->file_path_node_idx); + fpn != rdi_element_from_name_idx(rdi, FilePathNodes, 0); + fpn = rdi_element_from_name_idx(rdi, FilePathNodes, fpn->parent_path_node)) + { + String8 path_part = {0}; + path_part.str = rdi_string_from_idx(rdi, fpn->name_string_idx, &path_part.size); + str8_list_push_front(scratch.arena, &path_parts, path_part); + } + StringJoin join = {0}; + join.sep = str8_lit("/"); + name = str8_list_join(arena, &path_parts, &join); + scratch_end(scratch); + }break; + default: + { + U32 name_idx = *(U32 *)((U8 *)element + element_name_idx_off); + U64 name_size = 0; + U8 *name_base = rdi_string_from_idx(rdi, name_idx, &name_size); + name = str8(name_base, name_size); + }break; + } + if(name.size == 0) { continue; } + + //- rjf: fuzzy match against query + FuzzyMatchRangeList matches = fuzzy_match_find(arena, query, name); + + //- rjf: collect + if(matches.count == matches.needle_part_count) + { + DI2_SearchItemChunk *chunk = lane_items->last; + if(chunk == 0 || chunk->count >= chunk->cap) + { + chunk = push_array(scratch.arena, DI2_SearchItemChunk, 1); + chunk->base_idx = lane_items->total_count; + chunk->cap = 1024; + chunk->count = 0; + chunk->v = push_array_no_zero(scratch.arena, DI2_SearchItem, chunk->cap); + SLLQueuePush(lane_items->first, lane_items->last, chunk); + lane_items->chunk_count += 1; + } + chunk->v[chunk->count].idx = idx; + chunk->v[chunk->count].key = key; + chunk->v[chunk->count].match_ranges = matches; + chunk->v[chunk->count].missed_size = (name.size > matches.total_dim) ? (name.size-matches.total_dim) : 0; + chunk->count += 1; + lane_items->total_count += 1; + } + } + } + } + } + lane_sync(); + + //- rjf: join all lane chunk lists + DI2_SearchItemChunkList *all_items = &lanes_items[0]; + if(lane_idx() == 0) ProfScope("join all lane chunk lists") + { + for(U64 lidx = 1; lidx < lane_count(); lidx += 1) + { + DI2_SearchItemChunkList *dst = all_items; + DI2_SearchItemChunkList *to_push = &lanes_items[lidx]; + for EachNode(n, DI2_SearchItemChunk, to_push->first) + { + n->base_idx += dst->total_count; + } + if(dst->first && to_push->first) + { + dst->last->next = to_push->first; + dst->last = to_push->last; + dst->chunk_count += to_push->chunk_count; + dst->total_count += to_push->total_count; + } + else if(dst->first == 0) + { + MemoryCopyStruct(dst, to_push); + } + MemoryZeroStruct(to_push); + } + } + lane_sync(); + + //- rjf: flatten into array + DI2_SearchItemArray items = {0}; + ProfScope("flatten into array") + { + if(lane_idx() == 0) + { + items.count = all_items->total_count; + items.v = push_array(arena, DI2_SearchItem, items.count); + } + lane_sync_u64(&items.count, 0); + lane_sync_u64(&items.v, 0); + for EachNode(n, DI2_SearchItemChunk, all_items->first) + { + Rng1U64 range = lane_range(n->count); + U64 dst_idx = n->base_idx + range.min; + MemoryCopy(&items.v[dst_idx], n->v, sizeof(n->v[0]) * dim_1u64(range)); + } + } + + //- rjf: sort items + ProfScope("sort items") + { + + } + + //- rjf: bundle as artifact + artifact.u64[0] = (U64)arena; + artifact.u64[1] = (U64)items.v; + artifact.u64[2] = items.count; + } + scratch_end(scratch); + access_close(access); + ProfEnd(); + return artifact; +} + +internal void +di2_search_artifact_destroy(AC_Artifact artifact) +{ + Arena *arena = (Arena *)artifact.u64[0]; + if(arena != 0) + { + arena_release(arena); + } +} + +internal DI2_SearchItemArray +di2_search_item_array_from_target_query(Access *access, RDI_SectionKind target, String8 query, U64 endt_us) +{ + DI2_SearchItemArray result = {0}; + { + Temp scratch = scratch_begin(0, 0); + + // rjf: form key + String8List key_parts = {0}; + str8_list_push(scratch.arena, &key_parts, str8_struct(&target)); + str8_list_push(scratch.arena, &key_parts, str8_struct(&query.size)); + str8_list_push(scratch.arena, &key_parts, query); + String8 key = str8_list_join(scratch.arena, &key_parts, 0); + + // rjf: get artifact + AC_Artifact artifact = ac_artifact_from_key(access, key, di2_search_artifact_create, di2_search_artifact_destroy, endt_us, .gen = di2_load_gen(), .flags = AC_Flag_Wide); + + // rjf: unpack artifact + result.v = (DI2_SearchItem *)artifact.u64[1]; + result.count = artifact.u64[2]; + + scratch_end(scratch); + } + return result; +} diff --git a/src/dbg_info/dbg_info2.h b/src/dbg_info/dbg_info2.h new file mode 100644 index 00000000..d1129d3c --- /dev/null +++ b/src/dbg_info/dbg_info2.h @@ -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 diff --git a/src/demon/linux/demon_core_linux.c b/src/demon/linux/demon_core_linux.c index 206bb56b..8f613189 100644 --- a/src/demon/linux/demon_core_linux.c +++ b/src/demon/linux/demon_core_linux.c @@ -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); } } diff --git a/src/demon/win32/demon_core_win32.c b/src/demon/win32/demon_core_win32.c index 5c23ba35..5c93e2d2 100644 --- a/src/demon/win32/demon_core_win32.c +++ b/src/demon/win32/demon_core_win32.c @@ -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); } } diff --git a/src/demon/win32/demon_core_win32.h b/src/demon/win32/demon_core_win32.h index b06f1d85..c33f71c6 100644 --- a/src/demon/win32/demon_core_win32.h +++ b/src/demon/win32/demon_core_win32.h @@ -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 diff --git a/src/disasm/disasm.c b/src/disasm/disasm.c new file mode 100644 index 00000000..6143ceab --- /dev/null +++ b/src/disasm/disasm.c @@ -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, ¶ms); + 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, ¶ms.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; +} diff --git a/src/dasm_cache/dasm_cache.h b/src/disasm/disasm.h similarity index 55% rename from src/dasm_cache/dasm_cache.h rename to src/disasm/disasm.h index 521e359a..7ccd09f8 100644 --- a/src/dasm_cache/dasm_cache.h +++ b/src/disasm/disasm.h @@ -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 diff --git a/src/draw/draw.c b/src/draw/draw.c index 82145c4a..250317cb 100644 --- a/src/draw/draw.c +++ b/src/draw/draw.c @@ -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 diff --git a/src/dwarf/dwarf_elf.c b/src/dwarf/dwarf_elf.c index 1e4c8bdd..6ea144f8 100644 --- a/src/dwarf/dwarf_elf.c +++ b/src/dwarf/dwarf_elf.c @@ -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; } diff --git a/src/dwarf/dwarf_elf.h b/src/dwarf/dwarf_elf.h index f4ec79ce..a9a54f38 100644 --- a/src/dwarf/dwarf_elf.h +++ b/src/dwarf/dwarf_elf.h @@ -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 diff --git a/src/dwarf/dwarf_parse.c b/src/dwarf/dwarf_parse.c index 70db8920..09e6a6a9 100644 --- a/src/dwarf/dwarf_parse.c +++ b/src/dwarf/dwarf_parse.c @@ -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) { diff --git a/src/dwarf/dwarf_parse.h b/src/dwarf/dwarf_parse.h index 28736331..fe98b90e 100644 --- a/src/dwarf/dwarf_parse.h +++ b/src/dwarf/dwarf_parse.h @@ -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 diff --git a/src/elf/elf.c b/src/elf/elf.c index ac3473cb..2eb6d0b3 100644 --- a/src/elf/elf.c +++ b/src/elf/elf.c @@ -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/) //////////////////////////////// diff --git a/src/elf/elf.h b/src/elf/elf.h index 5e1291a2..14955224 100644 --- a/src/elf/elf.h +++ b/src/elf/elf.h @@ -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; diff --git a/src/elf/elf_parse.c b/src/elf/elf_parse.c index 3b804c0f..5949ebad 100644 --- a/src/elf/elf_parse.c +++ b/src/elf/elf_parse.c @@ -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 diff --git a/src/elf/elf_parse.h b/src/elf/elf_parse.h index 1eec7c09..70a467e8 100644 --- a/src/elf/elf_parse.h +++ b/src/elf/elf_parse.h @@ -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 diff --git a/src/eval/eval_core.h b/src/eval/eval_core.h index 0de42d9d..dce2082c 100644 --- a/src/eval/eval_core.h +++ b/src/eval/eval_core.h @@ -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; diff --git a/src/eval/eval_ir.c b/src/eval/eval_ir.c index a551bba4..ab6da91f 100644 --- a/src/eval/eval_ir.c +++ b/src/eval/eval_ir.c @@ -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)) { diff --git a/src/eval/eval_parse.c b/src/eval/eval_parse.c index bf244b4b..7e87d294 100644 --- a/src/eval/eval_parse.c +++ b/src/eval/eval_parse.c @@ -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 // diff --git a/src/eval/eval_types.c b/src/eval/eval_types.c index 6d7a8e48..f4ddca7f 100644 --- a/src/eval/eval_types.c +++ b/src/eval/eval_types.c @@ -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; } diff --git a/src/eval_visualization/eval_visualization_core.c b/src/eval_visualization/eval_visualization_core.c index 669f4703..6d2c04ac 100644 --- a/src/eval_visualization/eval_visualization_core.c +++ b/src/eval_visualization/eval_visualization_core.c @@ -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); } diff --git a/src/file_stream/file_stream.c b/src/file_stream/file_stream.c index 959ce166..f68da6f5 100644 --- a/src/file_stream/file_stream.c +++ b/src/file_stream/file_stream.c @@ -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(); } diff --git a/src/file_stream/file_stream.h b/src/file_stream/file_stream.h index e4c06ba6..9622e48c 100644 --- a/src/file_stream/file_stream.h +++ b/src/file_stream/file_stream.h @@ -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 diff --git a/src/font_cache/font_cache.c b/src/font_cache/font_cache.c index 330e03bc..6779aa72 100644 --- a/src/font_cache/font_cache.c +++ b/src/font_cache/font_cache.c @@ -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; } diff --git a/src/geo_cache/geo_cache.c b/src/geo_cache/geo_cache.c deleted file mode 100644 index 298888b7..00000000 --- a/src/geo_cache/geo_cache.c +++ /dev/null @@ -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); - } -} diff --git a/src/geo_cache/geo_cache.h b/src/geo_cache/geo_cache.h deleted file mode 100644 index 7ba1cc99..00000000 --- a/src/geo_cache/geo_cache.h +++ /dev/null @@ -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 diff --git a/src/hash_store/hash_store.c b/src/hash_store/hash_store.c deleted file mode 100644 index 16b64186..00000000 --- a/src/hash_store/hash_store.c +++ /dev/null @@ -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); - } -} diff --git a/src/hash_store/hash_store.h b/src/hash_store/hash_store.h deleted file mode 100644 index f193b3b1..00000000 --- a/src/hash_store/hash_store.h +++ /dev/null @@ -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 diff --git a/src/lib_rdi/rdi.c b/src/lib_rdi/rdi.c index 29e98bdb..7f2e6515 100644 --- a/src/lib_rdi/rdi.c +++ b/src/lib_rdi/rdi.c @@ -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; } diff --git a/src/lib_rdi/rdi.h b/src/lib_rdi/rdi.h index 5692f306..20394e53 100644 --- a/src/lib_rdi/rdi.h +++ b/src/lib_rdi/rdi.h @@ -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 diff --git a/src/lib_rdi/rdi_parse.c b/src/lib_rdi/rdi_parse.c index b4ebc551..b0ea1ff4 100644 --- a/src/lib_rdi/rdi_parse.c +++ b/src/lib_rdi/rdi_parse.c @@ -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; } diff --git a/src/lib_rdi/rdi_parse.h b/src/lib_rdi/rdi_parse.h index 7ea6193c..b03f0bc4 100644 --- a/src/lib_rdi/rdi_parse.h +++ b/src/lib_rdi/rdi_parse.h @@ -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; diff --git a/src/lib_rdi_make/rdi_make.c b/src/lib_rdi_make/rdi_make.c index 54d402ad..2b4ceacd 100644 --- a/src/lib_rdi_make/rdi_make.c +++ b/src/lib_rdi_make/rdi_make.c @@ -4,8 +4,52 @@ //////////////////////////////// //~ rjf: API Implementation Helper Macros -#define rdim_require(root, b32, else_code, error_msg) do { if(!(b32)) {rdim_push_msg((root), (error_msg)); else_code;} }while(0) -#define rdim_requiref(root, b32, else_code, fmt, ...) do { if(!(b32)) {rdim_push_msgf((root), (fmt), __VA_ARGS__); else_code;} }while(0) +#define RDIM_IdxedChunkListPush(arena, list, chunk_type, element_type, cap_value, result, ...) \ +element_type *result = 0;\ +do\ +{\ +chunk_type *n = list->last;\ +if(n == 0 || n->count >= n->cap)\ +{\ +n = rdim_push_array(arena, chunk_type, 1);\ +n->cap = cap_value;\ +n->base_idx = list->total_count;\ +__VA_ARGS__;\ +n->v = rdim_push_array_no_zero(arena, element_type, n->cap);\ +RDIM_SLLQueuePush(list->first, list->last, n);\ +list->chunk_count += 1;\ +}\ +result = &n->v[n->count];\ +result->chunk = n;\ +n->count += 1;\ +list->total_count += 1;\ +}while(0) + +#define RDIM_IdxedChunkListElementGetIdx(ptr, result) \ +RDI_U64 idx = 0;\ +if(ptr != 0 && ptr->chunk != 0)\ +{\ +idx = ptr->chunk->base_idx + (ptr - ptr->chunk->v) + 1;\ +} + +#define RDIM_IdxedChunkListConcatInPlace(chunk_type, dst, to_push, ...) \ +for(chunk_type *n = to_push->first; n != 0; n = n->next)\ +{\ +n->base_idx += dst->total_count;\ +}\ +if(dst->last != 0 && to_push->first != 0)\ +{\ +dst->last->next = to_push->first;\ +dst->last = to_push->last;\ +dst->chunk_count += to_push->chunk_count;\ +dst->total_count += to_push->total_count;\ +__VA_ARGS__;\ +}\ +else if(dst->first == 0)\ +{\ +rdim_memcpy_struct(dst, to_push);\ +}\ +rdim_memzero_struct(to_push); //////////////////////////////// //~ rjf: Basic Helpers @@ -62,7 +106,7 @@ rdim_arena_pos_fallback(RDIM_Arena *arena) } RDI_PROC void * -rdim_arena_push_fallback(RDIM_Arena *arena, RDI_U64 size) +rdim_arena_push_fallback(RDIM_Arena *arena, RDI_U64 size, RDI_U64 align, RDI_U32 zero) { // TODO(rjf) return 0; @@ -277,6 +321,12 @@ rdim_str8_list_join(RDIM_Arena *arena, RDIM_String8List *list, RDIM_String8 sep) //- rjf: sortable range sorting +RDI_PROC RSFORCEINLINE int +rdim_sort_key_is_before(void *l, void *r) +{ + return ((RDIM_SortKey *)l)->key < ((RDIM_SortKey *)r)->key; +} + RDI_PROC RDIM_SortKey * rdim_sort_key_array(RDIM_Arena *arena, RDIM_SortKey *keys, RDI_U64 count) { @@ -476,150 +526,150 @@ rdim_rng1u64_chunk_list_push(RDIM_Arena *arena, RDIM_Rng1U64ChunkList *list, RDI } //////////////////////////////// -//~ Data Model +//~ rjf: [Building] Data Model RDI_PROC RDI_TypeKind rdim_short_type_kind_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_S16; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_S16; - case RDIM_DataModel_LP64 : return RDI_TypeKind_S16; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_S16; - case RDIM_DataModel_SILP64: return RDI_TypeKind_S64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_S16;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_S16;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_S16;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_S16;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_S64;}break; } - return RDI_TypeKind_NULL; + return result; } RDI_PROC RDI_TypeKind rdim_unsigned_short_type_kind_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_U16; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_U16; - case RDIM_DataModel_LP64 : return RDI_TypeKind_U16; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_U16; - case RDIM_DataModel_SILP64: return RDI_TypeKind_U64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_U16;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_U16;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_U16;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_U16;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_U64;}break; } - return RDI_TypeKind_NULL; + return result; } RDI_PROC RDI_TypeKind rdim_int_type_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_S32; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_S32; - case RDIM_DataModel_LP64 : return RDI_TypeKind_S32; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_S64; - case RDIM_DataModel_SILP64: return RDI_TypeKind_S64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_S32;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_S32;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_S32;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_S64;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_S64;}break; } - return RDI_TypeKind_NULL; + return result; } RDI_PROC RDI_TypeKind rdim_unsigned_int_type_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_U32; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_U32; - case RDIM_DataModel_LP64 : return RDI_TypeKind_U32; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_SILP64: return RDI_TypeKind_U64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_U32;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_U32;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_U32;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_U64;}break; } - return RDI_TypeKind_NULL; + return result; } RDI_PROC RDI_TypeKind rdim_long_type_kind_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_S32; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_S32; - case RDIM_DataModel_LP64 : return RDI_TypeKind_S64; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_S64; - case RDIM_DataModel_SILP64: return RDI_TypeKind_S64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_S32;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_S32;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_S64;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_S64;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_S64;}break; } - return RDI_TypeKind_NULL; + return result; } RDI_PROC RDI_TypeKind rdim_unsigned_long_type_kind_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_U32; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_U32; - case RDIM_DataModel_LP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_SILP64: return RDI_TypeKind_U64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_U32;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_U32;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_U64;}break; } - return RDI_TypeKind_NULL; + return result; } RDI_PROC RDI_TypeKind rdim_long_long_type_kind_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_S64; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_S64; - case RDIM_DataModel_LP64 : return RDI_TypeKind_S64; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_S64; - case RDIM_DataModel_SILP64: return RDI_TypeKind_S64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_S64;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_S64;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_S64;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_S64;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_S64;}break; } - return RDI_TypeKind_NULL; + return result; } RDI_PROC RDI_TypeKind rdim_unsigned_long_long_type_kind_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_U64; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_LP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_SILP64: return RDI_TypeKind_U64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_U64;}break; } - return RDI_TypeKind_NULL; + return result; } RDI_PROC RDI_TypeKind rdim_pointer_size_t_type_kind_from_data_model(RDIM_DataModel data_model) { - switch(data_model) + RDI_TypeKind result = RDI_TypeKind_NULL; + switch((RDIM_DataModelEnum)data_model) { - case RDIM_DataModel_Null : break; - case RDIM_DataModel_ILP32 : return RDI_TypeKind_U32; - case RDIM_DataModel_LLP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_LP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_ILP64 : return RDI_TypeKind_U64; - case RDIM_DataModel_SILP64: return RDI_TypeKind_U64; - default: InvalidPath; + case RDIM_DataModel_Null:{}break; + case RDIM_DataModel_ILP32 :{result = RDI_TypeKind_U32;}break; + case RDIM_DataModel_LLP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_LP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_ILP64 :{result = RDI_TypeKind_U64;}break; + case RDIM_DataModel_SILP64:{result = RDI_TypeKind_U64;}break; } - return RDI_TypeKind_NULL; + return result; } //////////////////////////////// @@ -641,55 +691,23 @@ rdim_binary_section_list_push(RDIM_Arena *arena, RDIM_BinarySectionList *list) RDI_PROC RDIM_SrcFile * rdim_src_file_chunk_list_push(RDIM_Arena *arena, RDIM_SrcFileChunkList *list, RDI_U64 cap) { - RDIM_SrcFileChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = rdim_push_array(arena, RDIM_SrcFileChunkNode, 1); - n->cap = cap; - n->base_idx = list->total_count; - n->v = rdim_push_array(arena, RDIM_SrcFile, n->cap); - RDIM_SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - RDIM_SrcFile *src_file = &n->v[n->count]; - src_file->chunk = n; - n->count += 1; - list->total_count += 1; - return src_file; + RDIM_IdxedChunkListPush(arena, list, RDIM_SrcFileChunkNode, RDIM_SrcFile, cap, result); + return result; } RDI_PROC RDI_U64 rdim_idx_from_src_file(RDIM_SrcFile *src_file) { - RDI_U64 idx = 0; - if(src_file != 0 && src_file->chunk != 0) - { - idx = (src_file->chunk->base_idx + (src_file - src_file->chunk->v) + 1); - } + RDIM_IdxedChunkListElementGetIdx(src_file, idx); return idx; } RDI_PROC void rdim_src_file_chunk_list_concat_in_place(RDIM_SrcFileChunkList *dst, RDIM_SrcFileChunkList *to_push) { - for(RDIM_SrcFileChunkNode *n = to_push->first; n != 0; n = n->next) - { - n->base_idx += dst->total_count; - } - if(dst->last != 0 && to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->chunk_count += to_push->chunk_count; - dst->total_count += to_push->total_count; - dst->source_line_map_count += to_push->source_line_map_count; - dst->total_line_count += to_push->total_line_count; - } - else if(dst->first == 0) - { - rdim_memcpy_struct(dst, to_push); - } - rdim_memzero_struct(to_push); + RDIM_IdxedChunkListConcatInPlace(RDIM_SrcFileChunkNode, dst, to_push, + dst->source_line_map_count += to_push->source_line_map_count, + dst->total_line_count += to_push->total_line_count); } RDI_PROC void @@ -702,6 +720,7 @@ rdim_src_file_push_line_sequence(RDIM_Arena *arena, RDIM_SrcFileChunkList *src_f RDIM_SrcFileLineMapFragment *fragment = rdim_push_array(arena, RDIM_SrcFileLineMapFragment, 1); fragment->seq = seq; RDIM_SLLQueuePush(src_file->first_line_map_fragment, src_file->last_line_map_fragment, fragment); + src_file->total_line_count += seq->line_count; src_files->total_line_count += seq->line_count; } @@ -711,56 +730,24 @@ rdim_src_file_push_line_sequence(RDIM_Arena *arena, RDIM_SrcFileChunkList *src_f RDI_PROC RDIM_LineTable * rdim_line_table_chunk_list_push(RDIM_Arena *arena, RDIM_LineTableChunkList *list, RDI_U64 cap) { - RDIM_LineTableChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = rdim_push_array(arena, RDIM_LineTableChunkNode, 1); - n->cap = cap; - n->base_idx = list->total_count; - n->v = rdim_push_array(arena, RDIM_LineTable, n->cap); - RDIM_SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - RDIM_LineTable *line_table = &n->v[n->count]; - line_table->chunk = n; - n->count += 1; - list->total_count += 1; - return line_table; + RDIM_IdxedChunkListPush(arena, list, RDIM_LineTableChunkNode, RDIM_LineTable, cap, result); + return result; } RDI_PROC RDI_U64 rdim_idx_from_line_table(RDIM_LineTable *line_table) { - RDI_U64 idx = 0; - if(line_table != 0 && line_table->chunk != 0) - { - idx = line_table->chunk->base_idx + (line_table - line_table->chunk->v) + 1; - } + RDIM_IdxedChunkListElementGetIdx(line_table, idx); return idx; } RDI_PROC void rdim_line_table_chunk_list_concat_in_place(RDIM_LineTableChunkList *dst, RDIM_LineTableChunkList *to_push) { - for(RDIM_LineTableChunkNode *n = to_push->first; n != 0; n = n->next) - { - n->base_idx += dst->total_count; - } - if(dst->last != 0 && to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->chunk_count += to_push->chunk_count; - dst->total_count += to_push->total_count; - dst->total_seq_count += to_push->total_seq_count; - dst->total_line_count += to_push->total_line_count; - dst->total_col_count += to_push->total_col_count; - } - else if(dst->first == 0) - { - rdim_memcpy_struct(dst, to_push); - } - rdim_memzero_struct(to_push); + RDIM_IdxedChunkListConcatInPlace(RDIM_LineTableChunkNode, dst, to_push, + dst->total_seq_count += to_push->total_seq_count, + dst->total_line_count += to_push->total_line_count, + dst->total_col_count += to_push->total_col_count); } RDI_PROC RDIM_LineSequence * @@ -788,58 +775,28 @@ rdim_line_table_push_sequence(RDIM_Arena *arena, RDIM_LineTableChunkList *line_t RDI_PROC RDIM_Unit * rdim_unit_chunk_list_push(RDIM_Arena *arena, RDIM_UnitChunkList *list, RDI_U64 cap) { - RDIM_UnitChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = rdim_push_array(arena, RDIM_UnitChunkNode, 1); - n->cap = cap; - n->base_idx = list->total_count; - n->v = rdim_push_array(arena, RDIM_Unit, n->cap); - RDIM_SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - RDIM_Unit *unit = &n->v[n->count]; - unit->chunk = n; - n->count += 1; - list->total_count += 1; - return unit; + RDIM_IdxedChunkListPush(arena, list, RDIM_UnitChunkNode, RDIM_Unit, cap, result); + return result; } RDI_PROC RDI_U64 rdim_idx_from_unit(RDIM_Unit *unit) { - RDI_U64 idx = 0; - if(unit != 0 && unit->chunk != 0) - { - idx = unit->chunk->base_idx + (unit - unit->chunk->v) + 1; - } + RDIM_IdxedChunkListElementGetIdx(unit, idx); return idx; } RDI_PROC void rdim_unit_chunk_list_concat_in_place(RDIM_UnitChunkList *dst, RDIM_UnitChunkList *to_push) { - for(RDIM_UnitChunkNode *n = to_push->first; n != 0; n = n->next) - { - n->base_idx += dst->total_count; - } - if(dst->last != 0 && to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->chunk_count += to_push->chunk_count; - dst->total_count += to_push->total_count; - } - else if(dst->first == 0) - { - rdim_memcpy_struct(dst, to_push); - } - rdim_memzero_struct(to_push); + RDIM_IdxedChunkListConcatInPlace(RDIM_UnitChunkNode, dst, to_push); } //////////////////////////////// //~ rjf: [Building] Type Info Building +//- rjf: type nodes + RDI_PROC RDIM_Type ** rdim_array_from_type_list(RDIM_Arena *arena, RDIM_TypeList list) { @@ -865,107 +822,45 @@ rdim_type_list_push(RDIM_Arena *arena, RDIM_TypeList *list, RDIM_Type *v) RDI_PROC RDIM_Type * rdim_type_chunk_list_push(RDIM_Arena *arena, RDIM_TypeChunkList *list, RDI_U64 cap) { - RDIM_TypeChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = rdim_push_array(arena, RDIM_TypeChunkNode, 1); - n->cap = cap; - n->base_idx = list->total_count; - n->v = rdim_push_array(arena, RDIM_Type, n->cap); - RDIM_SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - RDIM_Type *result = &n->v[n->count]; - result->chunk = n; - n->count += 1; - list->total_count += 1; + RDIM_IdxedChunkListPush(arena, list, RDIM_TypeChunkNode, RDIM_Type, cap, result); return result; } RDI_PROC RDI_U64 rdim_idx_from_type(RDIM_Type *type) { - RDI_U64 idx = 0; - if(type != 0 && type->chunk != 0) - { - idx = type->chunk->base_idx + (type - type->chunk->v) + 1; - } + RDIM_IdxedChunkListElementGetIdx(type, idx); return idx; } RDI_PROC void rdim_type_chunk_list_concat_in_place(RDIM_TypeChunkList *dst, RDIM_TypeChunkList *to_push) { - for(RDIM_TypeChunkNode *n = to_push->first; n != 0; n = n->next) - { - n->base_idx += dst->total_count; - } - if(dst->last != 0 && to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->chunk_count += to_push->chunk_count; - dst->total_count += to_push->total_count; - } - else if(dst->first == 0) - { - rdim_memcpy_struct(dst, to_push); - } - rdim_memzero_struct(to_push); + RDIM_IdxedChunkListConcatInPlace(RDIM_TypeChunkNode, dst, to_push); } +//- rjf: UDTs + RDI_PROC RDIM_UDT * rdim_udt_chunk_list_push(RDIM_Arena *arena, RDIM_UDTChunkList *list, RDI_U64 cap) { - RDIM_UDTChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = rdim_push_array(arena, RDIM_UDTChunkNode, 1); - n->cap = cap; - n->base_idx = list->total_count; - n->v = rdim_push_array(arena, RDIM_UDT, n->cap); - RDIM_SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - RDIM_UDT *result = &n->v[n->count]; - result->chunk = n; - n->count += 1; - list->total_count += 1; + RDIM_IdxedChunkListPush(arena, list, RDIM_UDTChunkNode, RDIM_UDT, cap, result); return result; } RDI_PROC RDI_U64 rdim_idx_from_udt(RDIM_UDT *udt) { - RDI_U64 idx = 0; - if(udt != 0 && udt->chunk != 0) - { - idx = udt->chunk->base_idx + (udt - udt->chunk->v) + 1; - } + RDIM_IdxedChunkListElementGetIdx(udt, idx); return idx; } RDI_PROC void rdim_udt_chunk_list_concat_in_place(RDIM_UDTChunkList *dst, RDIM_UDTChunkList *to_push) { - for(RDIM_UDTChunkNode *n = to_push->first; n != 0; n = n->next) - { - n->base_idx += dst->total_count; - } - if(dst->last != 0 && to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->chunk_count += to_push->chunk_count; - dst->total_count += to_push->total_count; - dst->total_member_count += to_push->total_member_count; - dst->total_enum_val_count += to_push->total_enum_val_count; - } - else if(dst->first == 0) - { - rdim_memcpy_struct(dst, to_push); - } - rdim_memzero_struct(to_push); + RDIM_IdxedChunkListConcatInPlace(RDIM_UDTChunkNode, dst, to_push, + dst->total_member_count += to_push->total_member_count, + dst->total_enum_val_count += to_push->total_enum_val_count); } RDI_PROC RDIM_UDTMember * @@ -994,54 +889,21 @@ rdim_udt_push_enum_val(RDIM_Arena *arena, RDIM_UDTChunkList *list, RDIM_UDT *udt RDI_PROC RDIM_Symbol * rdim_symbol_chunk_list_push(RDIM_Arena *arena, RDIM_SymbolChunkList *list, RDI_U64 cap) { - RDIM_SymbolChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = rdim_push_array(arena, RDIM_SymbolChunkNode, 1); - n->cap = cap; - n->base_idx = list->total_count; - n->v = rdim_push_array(arena, RDIM_Symbol, n->cap); - RDIM_SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - RDIM_Symbol *result = &n->v[n->count]; - result->chunk = n; - n->count += 1; - list->total_count += 1; + RDIM_IdxedChunkListPush(arena, list, RDIM_SymbolChunkNode, RDIM_Symbol, cap, result); return result; } RDI_PROC RDI_U64 rdim_idx_from_symbol(RDIM_Symbol *symbol) { - RDI_U64 idx = 0; - if(symbol != 0 && symbol->chunk != 0) - { - idx = symbol->chunk->base_idx + (symbol - symbol->chunk->v) + 1; - } + RDIM_IdxedChunkListElementGetIdx(symbol, idx); return idx; } RDI_PROC void rdim_symbol_chunk_list_concat_in_place(RDIM_SymbolChunkList *dst, RDIM_SymbolChunkList *to_push) { - for(RDIM_SymbolChunkNode *n = to_push->first; n != 0; n = n->next) - { - n->base_idx += dst->total_count; - } - if(dst->last != 0 && to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->chunk_count += to_push->chunk_count; - dst->total_count += to_push->total_count; - dst->total_value_data_size += to_push->total_value_data_size; - } - else if(dst->first == 0) - { - rdim_memcpy_struct(dst, to_push); - } - rdim_memzero_struct(to_push); + RDIM_IdxedChunkListConcatInPlace(RDIM_SymbolChunkNode, dst, to_push, dst->total_value_data_size += to_push->total_value_data_size); } internal void @@ -1057,131 +919,25 @@ rdim_symbol_push_value_data(RDIM_Arena *arena, RDIM_SymbolChunkList *list, RDIM_ RDI_PROC RDIM_InlineSite * rdim_inline_site_chunk_list_push(RDIM_Arena *arena, RDIM_InlineSiteChunkList *list, RDI_U64 cap) { - RDIM_InlineSiteChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = rdim_push_array(arena, RDIM_InlineSiteChunkNode, 1); - n->cap = cap; - n->base_idx = list->total_count; - n->v = rdim_push_array(arena, RDIM_InlineSite, n->cap); - RDIM_SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - RDIM_InlineSite *result = &n->v[n->count]; - result->chunk = n; - n->count += 1; - list->total_count += 1; + RDIM_IdxedChunkListPush(arena, list, RDIM_InlineSiteChunkNode, RDIM_InlineSite, cap, result); return result; } RDI_PROC RDI_U64 rdim_idx_from_inline_site(RDIM_InlineSite *inline_site) { - RDI_U64 idx = 0; - if(inline_site != 0 && inline_site->chunk != 0) - { - idx = inline_site->chunk->base_idx + (inline_site - inline_site->chunk->v) + 1; - } + RDIM_IdxedChunkListElementGetIdx(inline_site, idx); return idx; } RDI_PROC void rdim_inline_site_chunk_list_concat_in_place(RDIM_InlineSiteChunkList *dst, RDIM_InlineSiteChunkList *to_push) { - for(RDIM_InlineSiteChunkNode *n = to_push->first; n != 0; n = n->next) - { - n->base_idx += dst->total_count; - } - if(dst->last != 0 && to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->chunk_count += to_push->chunk_count; - dst->total_count += to_push->total_count; - } - else if(dst->first == 0) - { - rdim_memcpy_struct(dst, to_push); - } - rdim_memzero_struct(to_push); + RDIM_IdxedChunkListConcatInPlace(RDIM_InlineSiteChunkNode, dst, 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) -{ - RDIM_ScopeChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = rdim_push_array(arena, RDIM_ScopeChunkNode, 1); - n->cap = cap; - n->base_idx = list->total_count; - n->v = rdim_push_array(arena, RDIM_Scope, n->cap); - RDIM_SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - RDIM_Scope *result = &n->v[n->count]; - result->chunk = n; - n->count += 1; - list->total_count += 1; - return result; -} - -RDI_PROC RDI_U64 -rdim_idx_from_scope(RDIM_Scope *scope) -{ - RDI_U64 idx = 0; - if(scope != 0 && scope->chunk != 0) - { - idx = scope->chunk->base_idx + (scope - scope->chunk->v) + 1; - } - return idx; -} - -RDI_PROC void -rdim_scope_chunk_list_concat_in_place(RDIM_ScopeChunkList *dst, RDIM_ScopeChunkList *to_push) -{ - for(RDIM_ScopeChunkNode *n = to_push->first; n != 0; n = n->next) - { - n->base_idx += dst->total_count; - } - if(dst->last != 0 && to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->chunk_count += to_push->chunk_count; - dst->total_count += to_push->total_count; - dst->scope_voff_count += to_push->scope_voff_count; - dst->local_count += to_push->local_count; - dst->location_count += to_push->location_count; - } - else if(dst->first == 0) - { - rdim_memcpy_struct(dst, to_push); - } - rdim_memzero_struct(to_push); -} - -RDI_PROC void -rdim_scope_push_voff_range(RDIM_Arena *arena, RDIM_ScopeChunkList *list, RDIM_Scope *scope, RDIM_Rng1U64 range) -{ - rdim_rng1u64_list_push(arena, &scope->voff_ranges, range); - list->scope_voff_count += 2; -} - -RDI_PROC RDIM_Local * -rdim_scope_push_local(RDIM_Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *scope) -{ - RDIM_Local *local = rdim_push_array(arena, RDIM_Local, 1); - RDIM_SLLQueuePush(scope->first_local, scope->last_local, local); - scope->local_count += 1; - scopes->local_count += 1; - return local; -} +//~ rjf: [Building] Location Info Building //- rjf: bytecode @@ -1266,234 +1022,136 @@ rdim_bytecode_concat_in_place(RDIM_EvalBytecode *left_dst, RDIM_EvalBytecode *ri } } -//- rjf: individual locations +//- rjf: locations -RDI_PROC RDIM_Location * -rdim_push_location_addr_bytecode_stream(RDIM_Arena *arena, RDIM_EvalBytecode *bytecode) +RDI_PROC RDI_U64 +rdim_encoded_size_from_location_info(RDIM_LocationInfo *info) { - RDIM_Location *result = rdim_push_array(arena, RDIM_Location, 1); - result->kind = RDI_LocationKind_AddrBytecodeStream; - result->bytecode = *bytecode; + RDI_U64 result = 0; + switch((RDI_LocationKindEnum)info->kind) + { + case RDI_LocationKind_NULL:{}break; + + case RDI_LocationKind_AddrBytecodeStream: + case RDI_LocationKind_ValBytecodeStream: + { + result = sizeof(RDI_LocationBytecodeStream) + info->bytecode.encoded_size + 1; + }break; + + case RDI_LocationKind_AddrRegPlusU16: + case RDI_LocationKind_AddrAddrRegPlusU16: + { + result = sizeof(RDI_LocationRegPlusU16); + }break; + + case RDI_LocationKind_ValReg: + { + result = sizeof(RDI_LocationReg); + }break; + } return result; } RDI_PROC RDIM_Location * -rdim_push_location_val_bytecode_stream(RDIM_Arena *arena, RDIM_EvalBytecode *bytecode) +rdim_location_chunk_list_push_new(RDIM_Arena *arena, RDIM_LocationChunkList *list, RDI_U64 cap, RDIM_LocationInfo *info) { - RDIM_Location *result = rdim_push_array(arena, RDIM_Location, 1); - result->kind = RDI_LocationKind_ValBytecodeStream; - result->bytecode = *bytecode; + RDIM_IdxedChunkListPush(arena, list, RDIM_LocationChunkNode, RDIM_Location, cap, result, n->base_encoding_off = list->total_encoded_size); + { + RDI_U64 encoded_size = rdim_encoded_size_from_location_info(info); + rdim_memcpy_struct(&result->info, info); + result->relative_encoding_off = list->last->encoded_size; + list->last->encoded_size += encoded_size; + list->total_encoded_size += encoded_size; + } return result; } -RDI_PROC RDIM_Location * -rdim_push_location_addr_reg_plus_u16(RDIM_Arena *arena, RDI_U8 reg_code, RDI_U16 offset) +RDI_PROC RDI_U64 +rdim_off_from_location(RDIM_Location *location) { - RDIM_Location *result = rdim_push_array(arena, RDIM_Location, 1); - result->kind = RDI_LocationKind_AddrRegPlusU16; - result->reg_code = reg_code; - result->offset = offset; - return result; + RDI_U64 off = 0; + if(location != 0 && location->chunk != 0) + { + off = location->chunk->base_encoding_off + location->relative_encoding_off + 1; + } + return off; } -RDI_PROC RDIM_Location * -rdim_push_location_addr_addr_reg_plus_u16(RDIM_Arena *arena, RDI_U8 reg_code, RDI_U16 offset) -{ - RDIM_Location *result = rdim_push_array(arena, RDIM_Location, 1); - result->kind = RDI_LocationKind_AddrAddrRegPlusU16; - result->reg_code = reg_code; - result->offset = offset; - return result; -} - -RDI_PROC RDIM_Location * -rdim_push_location_val_reg(RDIM_Arena *arena, RDI_U8 reg_code) -{ - RDIM_Location *result = rdim_push_array(arena, RDIM_Location, 1); - result->kind = RDI_LocationKind_ValReg; - result->reg_code = reg_code; - return result; -} - -//- 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) +rdim_location_chunk_list_concat_in_place(RDIM_LocationChunkList *dst, RDIM_LocationChunkList *to_push) { - RDIM_LocationCase *location_case = rdim_push_array(arena, RDIM_LocationCase, 1); - SLLQueuePush(locset->first_location_case, locset->last_location_case, location_case); - locset->location_case_count += 1; - location_case->voff_range = voff_range; - location_case->location = location; - scopes->location_count +=1; -} - -//- 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_LocationBlock *result = rdim_push_array(arena, RDI_LocationBlock, count); - RDIM_String8 string = rdim_str8((RDI_U8*)result, sizeof(result[0]) * count); - rdim_str8_list_push(arena, list, string); - return result; -} - -RDI_PROC RDI_U32 -rdim_count_from_location_block_chunk_list(RDIM_String8List *list) -{ - RDI_U32 count = list->total_size / sizeof(RDI_LocationBlock); - return count; + for(RDIM_LocationChunkNode *n = to_push->first; n != 0; n = n->next) + { + n->base_encoding_off += dst->total_encoded_size; + } + RDIM_IdxedChunkListConcatInPlace(RDIM_LocationChunkNode, dst, to_push, dst->total_encoded_size += to_push->total_encoded_size); } //////////////////////////////// -//~ 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) { - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - - //- rjf: sort markers - RDIM_SortKey *sorted_keys = rdim_sort_key_array(scratch.arena, keys, marker_count); - - //- rjf: determine if an extra vmap entry for zero is needed - RDI_U32 extra_vmap_entry = 0; - if(marker_count > 0 && sorted_keys[0].key != 0) - { - extra_vmap_entry = 1; - } - - //- rjf: fill output vmap entries - RDI_U32 vmap_count_raw = marker_count - 1 + extra_vmap_entry; - RDI_VMapEntry *vmap = rdim_push_array(arena, RDI_VMapEntry, vmap_count_raw + 1); - RDI_U32 vmap_entry_count_pass_1 = 0; - { - typedef struct RDIM_VMapRangeTracker RDIM_VMapRangeTracker; - struct RDIM_VMapRangeTracker - { - RDIM_VMapRangeTracker *next; - RDI_U32 idx; - }; - RDI_VMapEntry *vmap_ptr = vmap; - if(extra_vmap_entry) - { - vmap_ptr->voff = 0; - vmap_ptr->idx = 0; - vmap_ptr += 1; - } - RDIM_VMapRangeTracker *tracker_stack = 0; - RDIM_VMapRangeTracker *tracker_free = 0; - RDIM_SortKey *key_ptr = sorted_keys; - RDIM_SortKey *key_opl = sorted_keys + marker_count; - for(;key_ptr < key_opl;) - { - // rjf: get initial map state from tracker stack - RDI_U32 initial_idx = (RDI_U32)0xffffffff; - if(tracker_stack != 0) - { - initial_idx = tracker_stack->idx; - } - - // rjf: update tracker stack - // - // * we must process _all_ of the changes that apply at this voff before moving on - // - RDI_U64 voff = key_ptr->key; - - for(;key_ptr < key_opl && key_ptr->key == voff; key_ptr += 1) - { - RDIM_VMapMarker *marker = (RDIM_VMapMarker*)key_ptr->val; - RDI_U32 idx = marker->idx; - - // rjf: range begin -> push to stack - if(marker->begin_range) - { - RDIM_VMapRangeTracker *new_tracker = tracker_free; - if(new_tracker != 0) - { - RDIM_SLLStackPop(tracker_free); - } - else - { - new_tracker = rdim_push_array(scratch.arena, RDIM_VMapRangeTracker, 1); - } - RDIM_SLLStackPush(tracker_stack, new_tracker); - new_tracker->idx = idx; - } - - // rjf: range ending -> pop matching node from stack (not always the top) - else - { - RDIM_VMapRangeTracker **ptr_in = &tracker_stack; - RDIM_VMapRangeTracker *match = 0; - for(RDIM_VMapRangeTracker *node = tracker_stack; node != 0;) - { - if(node->idx == idx) - { - match = node; - break; - } - ptr_in = &node->next; - node = node->next; - } - if(match != 0) - { - *ptr_in = match->next; - RDIM_SLLStackPush(tracker_free, match); - } - } - } - - // rjf: get final map state from tracker stack - RDI_U32 final_idx = 0; - if(tracker_stack != 0) - { - final_idx = tracker_stack->idx; - } - - // rjf: if final is different from initial - emit new vmap entry - if(final_idx != initial_idx) - { - vmap_ptr->voff = voff; - vmap_ptr->idx = final_idx; - vmap_ptr += 1; - } - } - - vmap_entry_count_pass_1 = (RDI_U32)(vmap_ptr - vmap); // TODO(rjf): @u64_to_u32 - } - - //- rjf: combine duplicate neighbors - RDI_U32 vmap_entry_count = 0; - { - RDI_VMapEntry *vmap_ptr = vmap; - RDI_VMapEntry *vmap_opl = vmap + vmap_entry_count_pass_1; - RDI_VMapEntry *vmap_out = vmap; - for(;vmap_ptr < vmap_opl;) - { - RDI_VMapEntry *vmap_range_first = vmap_ptr; - RDI_U64 idx = vmap_ptr->idx; - vmap_ptr += 1; - for(;vmap_ptr < vmap_opl && vmap_ptr->idx == idx;) vmap_ptr += 1; - rdim_memcpy_struct(vmap_out, vmap_range_first); - vmap_out += 1; - } - vmap_entry_count = (RDI_U32)(vmap_out - vmap); // TODO(rjf): @u64_to_u32 - } - - //- rjf: fill result - RDIM_BakeVMap result = {0}; - result.vmap = vmap; - result.count = vmap_entry_count-1; - rdim_scratch_end(scratch); + RDIM_IdxedChunkListPush(arena, list, RDIM_ScopeChunkNode, RDIM_Scope, cap, result); return result; } -//////////////////////////////// -//~ rjf: [Baking Helpers] Interned / Deduplicated Blob Data Structure Helpers +RDI_PROC RDI_U64 +rdim_idx_from_scope(RDIM_Scope *scope) +{ + RDIM_IdxedChunkListElementGetIdx(scope, idx); + return idx; +} -//- rjf: bake string chunk lists +RDI_PROC void +rdim_scope_chunk_list_concat_in_place(RDIM_ScopeChunkList *dst, RDIM_ScopeChunkList *to_push) +{ + RDIM_IdxedChunkListConcatInPlace(RDIM_ScopeChunkNode, dst, to_push, + dst->scope_voff_count += to_push->scope_voff_count, + dst->local_count += to_push->local_count, + dst->location_case_count += to_push->location_case_count); +} + +RDI_PROC void +rdim_scope_push_voff_range(RDIM_Arena *arena, RDIM_ScopeChunkList *list, RDIM_Scope *scope, RDIM_Rng1U64 range) +{ + rdim_rng1u64_list_push(arena, &scope->voff_ranges, range); + list->scope_voff_count += 2; +} + +RDI_PROC RDIM_Local * +rdim_scope_push_local(RDIM_Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *scope) +{ + RDIM_Local *local = rdim_push_array(arena, RDIM_Local, 1); + RDIM_SLLQueuePush(scope->first_local, scope->last_local, local); + scope->local_count += 1; + scopes->local_count += 1; + return local; +} + +RDI_PROC RDIM_LocationCase * +rdim_push_location_case(RDIM_Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_LocationCaseList *list, RDIM_Location *location, RDIM_Rng1U64 voff_range) +{ + RDIM_LocationCase *n = rdim_push_array(arena, RDIM_LocationCase, 1); + RDIM_SLLQueuePush(list->first, list->last, n); + list->count += 1; + n->location = location; + n->voff_range = voff_range; + scopes->location_case_count += 1; + return n; +} + +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) +{ + return rdim_push_location_case(arena, scopes, &local->location_cases, location, voff_range); +} + +//////////////////////////////// +//~ rjf: [Baking Helpers] Deduplicated String Baking Map + +//- rjf: chunk lists RDI_PROC RDIM_BakeString * rdim_bake_string_chunk_list_push(RDIM_Arena *arena, RDIM_BakeStringChunkList *list, RDI_U64 cap) @@ -1534,6 +1192,12 @@ rdim_bake_string_chunk_list_concat_in_place(RDIM_BakeStringChunkList *dst, RDIM_ rdim_memzero_struct(to_push); } +RDI_PROC RSFORCEINLINE int +rdim_bake_string_is_before(void *l, void *r) +{ + return str8_is_before(((RDIM_BakeString *)l)->string, ((RDIM_BakeString *)r)->string); +} + RDI_PROC RDIM_BakeStringChunkList rdim_bake_string_chunk_list_sorted_from_unsorted(RDIM_Arena *arena, RDIM_BakeStringChunkList *src) { @@ -1552,68 +1216,7 @@ rdim_bake_string_chunk_list_sorted_from_unsorted(RDIM_Arena *arena, RDIM_BakeStr //- rjf: sort chunk node if(dst.first != 0) { - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - typedef struct SortTask SortTask; - struct SortTask - { - SortTask *next; - RDI_U64 string_off; - RDIM_BakeString *v; - RDI_U64 count; - }; - SortTask start_task = {0, 0, dst.first->v, dst.first->count}; - SortTask *first_task = &start_task; - SortTask *last_task = &start_task; - - //- rjf: for each sort task range: - for(SortTask *t = first_task; t != 0; t = t->next) - { - //- rjf: loop through range, drop each element into bucket according to byte in string at task offset - RDIM_BakeStringChunkList *buckets = rdim_push_array(scratch.arena, RDIM_BakeStringChunkList, 256); - for(RDI_U64 idx = 0; idx < t->count; idx += 1) - { - U8 byte = t->string_off < t->v[idx].string.size ? t->v[idx].string.str[t->string_off] : 0; - RDIM_BakeStringChunkList *bucket = &buckets[byte]; - RDIM_BakeString *bstr = rdim_bake_string_chunk_list_push(scratch.arena, bucket, 8); - rdim_memcpy_struct(bstr, &t->v[idx]); - } - - //- rjf: in-place mutate the original source array to reflect the order per the buckets. - // build new sort tasks for buckets with many elements - { - RDI_U64 write_idx = 0; - for(RDI_U64 bucket_idx = 0; bucket_idx < 256; bucket_idx += 1) - { - // rjf: write each chunk node's array into original array, detect if there is size left to sort - RDI_U64 bucket_base_idx = write_idx; - RDI_U64 need_next_char_sort = 0; - for(RDIM_BakeStringChunkNode *n = buckets[bucket_idx].first; n != 0; n = n->next) - { - rdim_memcpy(t->v+write_idx, n->v, sizeof(n->v[0])*n->count); - write_idx += n->count; - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - if(n->v[idx].string.size > t->string_off+1) - { - need_next_char_sort = 1; - } - } - } - - // rjf: if any bucket has >1 element & has some amount of size left to sort, push new task for this - // bucket's region in the array, and for remainder of keys - if(buckets[bucket_idx].total_count > 1 && need_next_char_sort) - { - SortTask *new_task = rdim_push_array(scratch.arena, SortTask, 1); - RDIM_SLLQueuePush(first_task, last_task, new_task); - new_task->string_off = t->string_off+1; - new_task->v = t->v + bucket_base_idx; - new_task->count = write_idx-bucket_base_idx; - } - } - } - } - rdim_scratch_end(scratch); + radsort(dst.first->v, dst.first->count, rdim_bake_string_is_before); } //- rjf: iterate sorted chunk node, remove duplicates, count # of duplicates @@ -1666,11 +1269,10 @@ rdim_bake_string_chunk_list_sorted_from_unsorted(RDIM_Arena *arena, RDIM_BakeStr } } - return dst; } -//- 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) @@ -1714,23 +1316,6 @@ rdim_bake_string_map_loose_insert(RDIM_Arena *arena, RDIM_BakeStringMapTopology } } -RDI_PROC void -rdim_bake_string_map_loose_join_in_place(RDIM_BakeStringMapTopology *map_topology, RDIM_BakeStringMapLoose *dst, RDIM_BakeStringMapLoose *src) -{ - for(RDI_U64 idx = 0; idx < map_topology->slots_count; idx += 1) - { - if(dst->slots[idx] == 0) - { - dst->slots[idx] = src->slots[idx]; - } - else if(src->slots[idx] != 0) - { - rdim_bake_string_chunk_list_concat_in_place(dst->slots[idx], src->slots[idx]); - } - } - rdim_memzero_struct(src); -} - RDI_PROC RDIM_BakeStringMapBaseIndices rdim_bake_string_map_base_indices_from_map_loose(RDIM_Arena *arena, RDIM_BakeStringMapTopology *map_topology, RDIM_BakeStringMapLoose *map) { @@ -1749,25 +1334,7 @@ rdim_bake_string_map_base_indices_from_map_loose(RDIM_Arena *arena, RDIM_BakeStr return indices; } -//- 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) -{ - RDIM_BakeStringMapTight m = {0}; - m.slots_count = map_topology->slots_count; - m.slots = rdim_push_array(arena, RDIM_BakeStringChunkList, m.slots_count); - m.slots_base_idxs = map_base_indices->slots_base_idxs; - for(RDI_U64 idx = 0; idx < m.slots_count; idx += 1) - { - if(map->slots[idx] != 0) - { - rdim_memcpy_struct(&m.slots[idx], map->slots[idx]); - } - } - m.total_count = m.slots_base_idxs[m.slots_count]; - return m; -} +//- rjf: finalized / tight map RDI_PROC RDI_U32 rdim_bake_idx_from_string(RDIM_BakeStringMapTight *map, RDIM_String8 string) @@ -1792,6 +1359,9 @@ rdim_bake_idx_from_string(RDIM_BakeStringMapTight *map, RDIM_String8 string) return idx; } +//////////////////////////////// +//~ rjf: [Baking Helpers] Deduplicated Index Run Baking Map + //- rjf: bake idx run map reading/writing RDI_PROC RDI_U64 @@ -1807,97 +1377,412 @@ rdim_hash_from_idx_run(RDI_U32 *idx_run, RDI_U32 count) return hash; } -RDI_PROC RDI_U32 -rdim_bake_idx_from_idx_run(RDIM_BakeIdxRunMap *map, RDI_U32 *idx_run, RDI_U32 count) +//- rjf: chunk lists + +RDI_PROC RDIM_BakeIdxRun * +rdim_bake_idx_run_chunk_list_push(RDIM_Arena *arena, RDIM_BakeIdxRunChunkList *list, RDI_U64 cap) { - RDI_U64 hash = rdim_hash_from_idx_run(idx_run, count); - RDI_U64 slot_idx = hash%map->slots_count; - - // rjf: find existing node - RDIM_BakeIdxRunNode *node = 0; - for(RDIM_BakeIdxRunNode *n = map->slots[slot_idx]; n != 0; n = n->hash_next) + RDIM_BakeIdxRunChunkNode *n = list->last; + if(n == 0 || n->count >= n->cap) { - if(n->hash == hash) + n = rdim_push_array(arena, RDIM_BakeIdxRunChunkNode, 1); + n->cap = cap; + n->v = rdim_push_array(arena, RDIM_BakeIdxRun, n->cap); + RDIM_SLLQueuePush(list->first, list->last, n); + list->chunk_count += 1; + } + RDIM_BakeIdxRun *s = &n->v[n->count]; + n->count += 1; + list->total_count += 1; + return s; +} + +RDI_PROC void +rdim_bake_idx_run_chunk_list_concat_in_place(RDIM_BakeIdxRunChunkList *dst, RDIM_BakeIdxRunChunkList *to_push) +{ + if(dst->last != 0 && to_push->first != 0) + { + dst->last->next = to_push->first; + dst->last = to_push->last; + dst->chunk_count += to_push->chunk_count; + dst->total_count += to_push->total_count; + } + else if(dst->first == 0) + { + rdim_memcpy_struct(dst, to_push); + } + rdim_memzero_struct(to_push); +} + +RDI_PROC RSFORCEINLINE int +rdim_bake_idx_run_is_before(void *l, void *r) +{ + B32 is_less_than = 0; + { + RDIM_BakeIdxRun *lir = (RDIM_BakeIdxRun *)l; + RDIM_BakeIdxRun *rir = (RDIM_BakeIdxRun *)r; + U64 common_count = Min(lir->count, rir->count); + for(U64 off = 0; off < common_count; off += 1) { - RDI_S32 is_match = 1; - RDI_U32 *n_idx = n->idx_run; - for(RDI_U32 i = 0; i < count; i += 1) + if(lir->idxes[off] < rir->idxes[off]) { - if(n_idx[i] != idx_run[i]) - { - is_match = 0; - break; - } - } - if(is_match) - { - node = n; + is_less_than = 1; break; } + else if(lir->idxes[off] > rir->idxes[off]) + { + is_less_than = 0; + break; + } + else if(off+1 == common_count) + { + is_less_than = (lir->count < rir->count); + } + } + } + return is_less_than; +} + +RDI_PROC RDIM_BakeIdxRunChunkList +rdim_bake_idx_run_chunk_list_sorted_from_unsorted(RDIM_Arena *arena, RDIM_BakeIdxRunChunkList *src) +{ + //- rjf: produce unsorted destination list with single chunk node + RDIM_BakeIdxRunChunkList dst = {0}; + for(RDIM_BakeIdxRunChunkNode *n = src->first; n != 0; n = n->next) + { + for(RDI_U64 idx = 0; idx < n->count; idx += 1) + { + RDIM_BakeIdxRun *src_str = &n->v[idx]; + RDIM_BakeIdxRun *dst_str = rdim_bake_idx_run_chunk_list_push(arena, &dst, src->total_count); + rdim_memcpy_struct(dst_str, src_str); + } + } + + //- rjf: sort chunk node + if(dst.first != 0) + { + radsort(dst.first->v, dst.first->count, rdim_bake_idx_run_is_before); + } + + //- rjf: iterate sorted chunk node, remove duplicates, count # of duplicates + RDI_U64 num_duplicates = 0; + if(dst.first != 0) + { + RDI_U64 last_idx = 0; + for(RDI_U64 idx = 1; idx < dst.first->count; idx += 1) + { + if(dst.first->v[last_idx].count == dst.first->v[idx].count && + MemoryMatch(dst.first->v[last_idx].idxes, + dst.first->v[idx].idxes, + sizeof(dst.first->v[idx].idxes[0]) * dst.first->v[idx].count)) + { + rdim_memzero_struct(&dst.first->v[idx]); + num_duplicates += 1; + } + else + { + last_idx = idx; + } } } - // rjf: node -> index - RDI_U32 result = node ? node->first_idx : 0; + //- rjf: iterate sorted chunk node, make non-empty elements contiguous + if(num_duplicates != 0) + { + RDI_U64 last_idx = 0; + for(RDI_U64 idx = 1; idx < dst.first->count; idx += 1) + { + if(last_idx == 0 && dst.first->v[idx].hash == 0) + { + last_idx = idx; + } + if(last_idx != 0 && dst.first->v[idx].hash != 0) + { + rdim_memcpy_struct(&dst.first->v[last_idx], &dst.first->v[idx]); + rdim_memzero_struct(&dst.first->v[idx]); + last_idx += 1; + } + } + + //- rjf: pop extras + if(num_duplicates != 0) + { + RDI_U64 arena_pos_pre_pop = rdim_arena_pos(arena); + rdim_arena_pop_to(arena, arena_pos_pre_pop - num_duplicates*sizeof(dst.first->v[0])); + dst.first->count -= num_duplicates; + dst.first->cap -= num_duplicates; + dst.total_count -= num_duplicates; + } + } + + return dst; +} + +//- rjf: loose map + +RDI_PROC RDIM_BakeIdxRunMapLoose * +rdim_bake_idx_run_map_loose_make(RDIM_Arena *arena, RDIM_BakeIdxRunMapTopology *top) +{ + RDIM_BakeIdxRunMapLoose *map = rdim_push_array(arena, RDIM_BakeIdxRunMapLoose, 1); + map->slots = rdim_push_array(arena, RDIM_BakeIdxRunChunkList *, top->slots_count); + map->slots_idx_counts = rdim_push_array(arena, RDI_U64, top->slots_count); + return map; +} + +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) +{ + if(count != 0) + { + RDI_U64 hash = rdim_hash_from_idx_run(idxes, count); + RDI_U64 slot_idx = hash%map_topology->slots_count; + RDIM_BakeIdxRunChunkList *slot = map->slots[slot_idx]; + if(slot == 0) + { + slot = map->slots[slot_idx] = rdim_push_array(arena, RDIM_BakeIdxRunChunkList, 1); + } + RDI_S32 is_duplicate = 0; + for(RDIM_BakeIdxRunChunkNode *n = slot->first; n != 0; n = n->next) + { + for(RDI_U64 idx = 0; idx < n->count; idx += 1) + { + if(n->v[idx].hash == hash && + n->v[idx].count == count && + MemoryMatch(n->v[idx].idxes, idxes, sizeof(idxes[0])*count)) + { + is_duplicate = 1; + goto break_all; + } + } + } + break_all:; + if(!is_duplicate) + { + RDIM_BakeIdxRun *bir = rdim_bake_idx_run_chunk_list_push(arena, slot, chunk_cap); + bir->hash = hash; + bir->count = count; + bir->idxes = idxes; + map->slots_idx_counts[slot_idx] += count; + } + } +} + +//- rjf: finalized / tight map + +RDI_PROC RDI_U32 +rdim_bake_idx_from_idx_run(RDIM_BakeIdxRunMap *map, RDI_U32 *idxes, RDI_U32 count) +{ + RDI_U32 idx = 0; + if(count != 0) + { + RDI_U64 hash = rdim_hash_from_idx_run(idxes, count); + RDI_U64 slot_idx = hash%map->slots_count; + RDI_U64 off = 0; + for(RDIM_BakeIdxRunChunkNode *n = map->slots[slot_idx].first; n != 0; n = n->next) + { + for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) + { + if(n->v[chunk_idx].hash == hash && + n->v[chunk_idx].count == count && + MemoryMatch(n->v[chunk_idx].idxes, idxes, sizeof(idxes[0])*count)) + { + idx = (RDI_U32)(map->slots_base_idxs[slot_idx] + off); // TODO(rjf): @u64_to_u32 + goto end_lookup; + } + off += n->v[chunk_idx].count; + } + } + end_lookup:; + } + return idx; +} + +//////////////////////////////// +//~ 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) +{ + RDIM_BakeNameChunkNode *n = list->last; + if(n == 0 || n->count >= n->cap) + { + n = rdim_push_array(arena, RDIM_BakeNameChunkNode, 1); + n->cap = cap; + n->base_idx = list->total_count; + n->v = rdim_push_array(arena, RDIM_BakeName, n->cap); + RDIM_SLLQueuePush(list->first, list->last, n); + list->chunk_count += 1; + } + RDIM_BakeName *result = &n->v[n->count]; + n->count += 1; + list->total_count += 1; return result; } -RDI_PROC RDI_U32 -rdim_bake_idx_run_map_insert(RDIM_Arena *arena, RDIM_BakeIdxRunMap *map, RDI_U32 *idx_run, RDI_U32 count) +RDI_PROC void +rdim_bake_name_chunk_list_concat_in_place(RDIM_BakeNameChunkList *dst, RDIM_BakeNameChunkList *to_push) { - RDI_U64 hash = rdim_hash_from_idx_run(idx_run, count); - RDI_U64 slot_idx = hash%map->slots_count; - - // rjf: find existing node - RDIM_BakeIdxRunNode *node = 0; - for(RDIM_BakeIdxRunNode *n = map->slots[slot_idx]; n != 0; n = n->hash_next) + for(RDIM_BakeNameChunkNode *n = to_push->first; n != 0; n = n->next) { - if(n->hash == hash) - { - RDI_S32 is_match = 1; - RDI_U32 *n_idx = n->idx_run; - for(RDI_U32 i = 0; i < count; i += 1) - { - if(n_idx[i] != idx_run[i]) - { - is_match = 0; - break; - } - } - if(is_match) - { - node = n; - break; - } - } + n->base_idx += dst->total_count; } - - // rjf: no node -> make new node - if(node == 0) + if(dst->last != 0 && to_push->first != 0) { - node = rdim_push_array_no_zero(arena, RDIM_BakeIdxRunNode, 1); - RDI_U32 *idx_run_copy = rdim_push_array_no_zero(arena, RDI_U32, count); - for(RDI_U32 i = 0; i < count; i += 1) - { - idx_run_copy[i] = idx_run[i]; - } - node->idx_run = idx_run_copy; - node->hash = hash; - node->count = count; - node->first_idx = map->idx_count; - map->count += 1; - map->idx_count += count; - RDIM_SLLQueuePush_N(map->order_first, map->order_last, node, order_next); - RDIM_SLLStackPush_N(map->slots[slot_idx], node, hash_next); - map->slot_collision_count += (node->hash_next != 0); + dst->last->next = to_push->first; + dst->last = to_push->last; + dst->chunk_count += to_push->chunk_count; + dst->total_count += to_push->total_count; } - - // rjf: node -> index - RDI_U32 result = node->first_idx; - return result; + else if(dst->first == 0) + { + rdim_memcpy_struct(dst, to_push); + } + rdim_memzero_struct(to_push); } -//- rjf: bake path tree reading/writing +RDI_PROC RSFORCEINLINE int +rdim_bake_name_is_before(void *l, void *r) +{ + RDIM_BakeName *lhs = (RDIM_BakeName *)l; + RDIM_BakeName *rhs = (RDIM_BakeName *)r; + B32 lhs_name_lt = str8_is_before(lhs->string, rhs->string); + B32 is_before = lhs_name_lt; + if(!lhs_name_lt) + { + B32 rhs_name_lt = str8_is_before(rhs->string, lhs->string); + if(!rhs_name_lt) + { + is_before = (lhs->idx > rhs->idx); + } + } + return is_before; +} + +RDI_PROC RDIM_BakeNameChunkList +rdim_bake_name_chunk_list_sorted_from_unsorted(RDIM_Arena *arena, RDIM_BakeNameChunkList *src) +{ + //- rjf: produce unsorted destination list with single chunk node + RDIM_BakeNameChunkList dst = {0}; + for(RDIM_BakeNameChunkNode *n = src->first; n != 0; n = n->next) + { + for(RDI_U64 idx = 0; idx < n->count; idx += 1) + { + RDIM_BakeName *src_str = &n->v[idx]; + RDIM_BakeName *dst_str = rdim_bake_name_chunk_list_push(arena, &dst, src->total_count); + rdim_memcpy_struct(dst_str, src_str); + } + } + + //- rjf: sort chunk node + if(dst.first != 0) + { + radsort(dst.first->v, dst.first->count, rdim_bake_name_is_before); + } + + //- rjf: iterate sorted chunk node, remove duplicates, count # of duplicates + RDI_U64 num_duplicates = 0; + if(dst.first != 0) + { + RDI_U64 last_idx = 0; + for(RDI_U64 idx = 1; idx < dst.first->count; idx += 1) + { + if(rdim_str8_match(dst.first->v[last_idx].string, dst.first->v[idx].string, 0) && + dst.first->v[last_idx].idx == dst.first->v[idx].idx) + { + rdim_memzero_struct(&dst.first->v[idx]); + num_duplicates += 1; + } + else + { + last_idx = idx; + } + } + } + + //- rjf: iterate sorted chunk node, make non-empty elements contiguous + if(num_duplicates != 0) + { + RDI_U64 last_idx = 0; + for(RDI_U64 idx = 1; idx < dst.first->count; idx += 1) + { + if(last_idx == 0 && + dst.first->v[idx].string.RDIM_String8_SizeMember == 0 && + dst.first->v[idx].hash == 0) + { + last_idx = idx; + } + if(last_idx != 0 && dst.first->v[idx].string.RDIM_String8_SizeMember != 0) + { + rdim_memcpy_struct(&dst.first->v[last_idx], &dst.first->v[idx]); + rdim_memzero_struct(&dst.first->v[idx]); + last_idx += 1; + } + } + + //- rjf: pop extras + if(num_duplicates != 0) + { + RDI_U64 arena_pos_pre_pop = rdim_arena_pos(arena); + rdim_arena_pop_to(arena, arena_pos_pre_pop - num_duplicates*sizeof(dst.first->v[0])); + dst.first->count -= num_duplicates; + dst.first->cap -= num_duplicates; + dst.total_count -= num_duplicates; + } + } + + return dst; +} + +//- rjf: bake name chunk list maps + +RDI_PROC RDIM_BakeNameMap * +rdim_bake_name_map_make(RDIM_Arena *arena, RDIM_BakeNameMapTopology *top) +{ + RDIM_BakeNameMap *map = rdim_push_array(arena, RDIM_BakeNameMap, 1); + map->slots = rdim_push_array(arena, RDIM_BakeNameChunkList *, top->slots_count); + return map; +} + +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) +{ + if(string.RDIM_String8_SizeMember != 0) + { + RDI_U64 hash = rdi_hash(string.RDIM_String8_BaseMember, string.RDIM_String8_SizeMember); + RDI_U64 slot_idx = hash%map_topology->slots_count; + RDIM_BakeNameChunkList *slot = map->slots[slot_idx]; + if(slot == 0) + { + slot = map->slots[slot_idx] = rdim_push_array(arena, RDIM_BakeNameChunkList, 1); + } + RDI_S32 is_duplicate = 0; + for(RDIM_BakeNameChunkNode *n = slot->first; n != 0; n = n->next) + { + for(RDI_U64 idx = 0; idx < n->count; idx += 1) + { + if(rdim_str8_match(n->v[idx].string, string, 0) && + n->v[idx].idx == idx) + { + is_duplicate = 1; + goto break_all; + } + } + } + break_all:; + if(!is_duplicate) + { + RDIM_BakeName *bstr = rdim_bake_name_chunk_list_push(arena, slot, chunk_cap); + bstr->string = string; + bstr->idx = idx; + bstr->hash = hash; + } + } +} + +//////////////////////////////// +//~ rjf: [Baking Helpers] Deduplicated Path Baking Tree RDI_PROC RDIM_BakePathNode * rdim_bake_path_node_from_string(RDIM_BakePathTree *tree, RDIM_String8 string) @@ -2045,72 +1930,6 @@ rdim_bake_path_tree_insert(RDIM_Arena *arena, RDIM_BakePathTree *tree, RDIM_Stri return node; } -//- 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) -{ - if(string.size == 0) {return;} - - // rjf: hash - RDI_U64 hash = rdi_hash(string.RDIM_String8_BaseMember, string.RDIM_String8_SizeMember); - RDI_U64 slot_idx = hash%map->slots_count; - - // rjf: find existing node - RDIM_BakeNameMapNode *node = 0; - for(RDIM_BakeNameMapNode *n = map->slots[slot_idx]; n != 0; n = n->slot_next) - { - if(rdim_str8_match(string, n->string, 0)) - { - node = n; - break; - } - } - - // rjf: make node if necessary - if(node == 0) - { - node = rdim_push_array(arena, RDIM_BakeNameMapNode, 1); - node->string = string; - RDIM_SLLStackPush_N(map->slots[slot_idx], node, slot_next); - RDIM_SLLQueuePush_N(map->first, map->last, node, order_next); - map->name_count += 1; - map->slot_collision_count += (node->slot_next != 0); - } - - // rjf: find existing idx - RDI_S32 existing_idx = 0; - for(RDIM_BakeNameMapValNode *n = node->val_first; n != 0; n = n->next) - { - for(RDI_U32 i = 0; i < sizeof(n->val)/sizeof(n->val[0]); i += 1) - { - if(n->val[i] == 0) - { - break; - } - if(n->val[i] == idx) - { - existing_idx = 1; - break; - } - } - } - - // rjf: insert new idx if necessary - if(!existing_idx) - { - RDIM_BakeNameMapValNode *val_node = node->val_last; - RDI_U32 insert_i = node->val_count%(sizeof(val_node->val)/sizeof(val_node->val[0])); - if(insert_i == 0) - { - val_node = rdim_push_array(arena, RDIM_BakeNameMapValNode, 1); - SLLQueuePush(node->val_first, node->val_last, val_node); - } - val_node->val[insert_i] = idx; - node->val_count += 1; - } -} - //////////////////////////////// //~ rjf: [Baking Helpers] Data Section List Building Helpers @@ -2153,1849 +1972,6 @@ rdim_bake_section_list_concat_in_place(RDIM_BakeSectionList *dst, RDIM_BakeSecti rdim_memzero_struct(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) -{ - rdim_bake_string_map_loose_insert(arena, top, map, 1, tli->exe_name); - rdim_bake_string_map_loose_insert(arena, top, map, 1, tli->producer_name); -} - -RDI_PROC void -rdim_bake_string_map_loose_push_binary_sections(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_BinarySectionList *secs) -{ - for(RDIM_BinarySectionNode *n = secs->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_insert(arena, top, map, 1, n->v.name); - } -} - -RDI_PROC void -rdim_bake_string_map_loose_push_path_tree(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_BakePathTree *path_tree) -{ - for(RDIM_BakePathNode *n = path_tree->first; n != 0; n = n->next_order) - { - rdim_bake_string_map_loose_insert(arena, top, map, 1, n->name); - } -} - -//- rjf: chunk-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) -{ - for(RDI_U64 idx = 0; idx < count; idx += 1) - { - RDIM_String8 normalized_path = rdim_lower_from_str8(arena, v[idx].path); - rdim_bake_string_map_loose_insert(arena, top, map, 1, normalized_path); - } -} - -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) -{ - for(RDI_U64 idx = 0; idx < count; idx += 1) - { - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].unit_name); - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].compiler_name); - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].source_file); - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].object_file); - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].archive_file); - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].build_path); - } -} - -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) -{ - for(RDI_U64 idx = 0; idx < count; idx += 1) - { - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].name); - } -} - -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) -{ - for(RDI_U64 idx = 0; idx < count; idx += 1) - { - for(RDIM_UDTMember *mem = v[idx].first_member; mem != 0; mem = mem->next) - { - rdim_bake_string_map_loose_insert(arena, top, map, 4, mem->name); - } - for(RDIM_UDTEnumVal *mem = v[idx].first_enum_val; mem != 0; mem = mem->next) - { - rdim_bake_string_map_loose_insert(arena, top, map, 4, mem->name); - } - } -} - -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) -{ - for(RDI_U64 idx = 0; idx < count; idx += 1) - { - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].name); - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].link_name); - } -} - -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) -{ - for(RDI_U64 idx = 0; idx < count; idx += 1) - { - rdim_bake_string_map_loose_insert(arena, top, map, 4, v[idx].name); - } -} - -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) -{ - for(RDI_U64 idx = 0; idx < count; idx += 1) - { - for(RDIM_Local *local = v[idx].first_local; local != 0; local = local->next) - { - rdim_bake_string_map_loose_insert(arena, top, map, 4, local->name); - } - } -} - -//- 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) -{ - for(RDIM_SrcFileChunkNode *n = list->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_src_file_slice(arena, top, map, n->v, n->count); - } -} - -RDI_PROC void -rdim_bake_string_map_loose_push_units(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_UnitChunkList *list) -{ - for(RDIM_UnitChunkNode *n = list->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_unit_slice(arena, top, map, n->v, n->count); - } -} - -RDI_PROC void -rdim_bake_string_map_loose_push_types(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_TypeChunkList *list) -{ - for(RDIM_TypeChunkNode *n = list->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_type_slice(arena, top, map, n->v, n->count); - } -} - -RDI_PROC void -rdim_bake_string_map_loose_push_udts(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_UDTChunkList *list) -{ - for(RDIM_UDTChunkNode *n = list->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_udt_slice(arena, top, map, n->v, n->count); - } -} - -RDI_PROC void -rdim_bake_string_map_loose_push_symbols(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_SymbolChunkList *list) -{ - for(RDIM_SymbolChunkNode *n = list->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_symbol_slice(arena, top, map, n->v, n->count); - } -} - -RDI_PROC void -rdim_bake_string_map_loose_push_scopes(RDIM_Arena *arena, RDIM_BakeStringMapTopology *top, RDIM_BakeStringMapLoose *map, RDIM_ScopeChunkList *list) -{ - for(RDIM_ScopeChunkNode *n = list->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_scope_slice(arena, top, map, n->v, n->count); - } -} - -//- 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) -{ - RDIM_BakeNameMap *map = rdim_push_array(arena, RDIM_BakeNameMap, 1); - switch(kind) - { - default:{}break; - case RDI_NameMapKind_GlobalVariables: - { - map->slots_count = params->global_variables.total_count*2; - map->slots = rdim_push_array(arena, RDIM_BakeNameMapNode *, map->slots_count); - for(RDIM_SymbolChunkNode *n = params->global_variables.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - RDI_U32 symbol_idx = (RDI_U32)rdim_idx_from_symbol(&n->v[idx]); // TODO(rjf): @u64_to_u32 - rdim_bake_name_map_push(arena, map, n->v[idx].name, symbol_idx); - } - } - }break; - case RDI_NameMapKind_ThreadVariables: - { - map->slots_count = params->thread_variables.total_count*2; - map->slots = rdim_push_array(arena, RDIM_BakeNameMapNode *, map->slots_count); - for(RDIM_SymbolChunkNode *n = params->thread_variables.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - RDI_U32 symbol_idx = (RDI_U32)rdim_idx_from_symbol(&n->v[idx]); // TODO(rjf): @u64_to_u32 - rdim_bake_name_map_push(arena, map, n->v[idx].name, symbol_idx); - } - } - }break; - case RDI_NameMapKind_Constants: - { - map->slots_count = params->constants.total_count*2; - map->slots = rdim_push_array(arena, RDIM_BakeNameMapNode *, map->slots_count); - for(RDIM_SymbolChunkNode *n = params->constants.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - RDI_U32 symbol_idx = (RDI_U32)rdim_idx_from_symbol(&n->v[idx]); // TODO(rjf): @u64_to_u32 - rdim_bake_name_map_push(arena, map, n->v[idx].name, symbol_idx); - } - } - }break; - case RDI_NameMapKind_Procedures: - { - map->slots_count = params->procedures.total_count*2; - map->slots = rdim_push_array(arena, RDIM_BakeNameMapNode *, map->slots_count); - for(RDIM_SymbolChunkNode *n = params->procedures.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - RDI_U32 symbol_idx = (RDI_U32)rdim_idx_from_symbol(&n->v[idx]); // TODO(rjf): @u64_to_u32 - rdim_bake_name_map_push(arena, map, n->v[idx].name, symbol_idx); - } - } - }break; - case RDI_NameMapKind_Types: - { - map->slots_count = params->types.total_count; - map->slots = rdim_push_array(arena, RDIM_BakeNameMapNode *, map->slots_count); - for(RDIM_TypeChunkNode *n = params->types.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - RDI_U32 type_idx = (RDI_U32)rdim_idx_from_type(&n->v[idx]); // TODO(rjf): @u64_to_u32 - if(type_idx == 0) {continue;} - rdim_bake_name_map_push(arena, map, n->v[idx].name, type_idx); - } - } - }break; - case RDI_NameMapKind_LinkNameProcedures: - { - map->slots_count = params->procedures.total_count*2; - map->slots = rdim_push_array(arena, RDIM_BakeNameMapNode *, map->slots_count); - for(RDIM_SymbolChunkNode *n = params->procedures.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - if(n->v[idx].link_name.size == 0) {continue;} - RDI_U32 symbol_idx = (RDI_U32)rdim_idx_from_symbol(&n->v[idx]); // TODO(rjf): @u64_to_u32 - rdim_bake_name_map_push(arena, map, n->v[idx].link_name, symbol_idx); - } - } - }break; - case RDI_NameMapKind_NormalSourcePaths: - { - map->slots_count = params->src_files.total_count*2; - map->slots = rdim_push_array(arena, RDIM_BakeNameMapNode *, map->slots_count); - for(RDIM_SrcFileChunkNode *n = params->src_files.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - RDI_U64 src_file_idx = rdim_idx_from_src_file(&n->v[idx]); - RDIM_String8 normalized_path = rdim_lower_from_str8(arena, n->v[idx].path); - rdim_bake_name_map_push(arena, map, normalized_path, (RDI_U32)src_file_idx); // TODO(rjf): @u64_to_u32 - } - } - }break; - } - return map; -} - -//- rjf: 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: set up map - RDIM_BakeIdxRunMap *idx_runs = rdim_push_array(arena, RDIM_BakeIdxRunMap, 1); - idx_runs->slots_count = 64 + params->procedures.total_count*2 + params->global_variables.total_count*2 + params->thread_variables.total_count*2 + params->types.total_count*2; - idx_runs->slots = rdim_push_array(arena, RDIM_BakeIdxRunNode *, idx_runs->slots_count); - rdim_bake_idx_run_map_insert(arena, idx_runs, 0, 0); - - //- rjf: bake runs of function-type parameter lists - for(RDIM_TypeChunkNode *n = params->types.first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) - { - RDIM_Type *type = &n->v[chunk_idx]; - if(type->kind == RDI_TypeKind_Function || type->kind == RDI_TypeKind_Method) - { - RDI_U32 param_idx_run_count = type->count; - RDI_U32 *param_idx_run = rdim_push_array_no_zero(arena, RDI_U32, param_idx_run_count); - for(RDI_U32 idx = 0; idx < param_idx_run_count; idx += 1) - { - param_idx_run[idx] = (RDI_U32)rdim_idx_from_type(type->param_types[idx]); // TODO(rjf): @u64_to_u32 - } - rdim_bake_idx_run_map_insert(arena, idx_runs, param_idx_run, param_idx_run_count); - } - } - } - - //- rjf: bake runs of name map match lists - for(RDI_NameMapKind k = (RDI_NameMapKind)(RDI_NameMapKind_NULL+1); - k < RDI_NameMapKind_COUNT; - k = (RDI_NameMapKind)(k+1)) - { - RDIM_BakeNameMap *name_map = name_maps[k]; - if(name_map != 0 && name_map->name_count != 0) - { - for(RDIM_BakeNameMapNode *n = name_map->first; n != 0; n = n->order_next) - { - if(n->val_count > 1) - { - RDI_U32 *idx_run = rdim_push_array(arena, RDI_U32, n->val_count); - RDI_U64 val_idx = 0; - for(RDIM_BakeNameMapValNode *idxnode = n->val_first; - idxnode != 0; - idxnode = idxnode->next) - { - for(RDI_U32 i = 0; i < sizeof(idxnode->val)/sizeof(idxnode->val[0]); i += 1) - { - if(idxnode->val[i] == 0) - { - goto dblbreak; - } - idx_run[val_idx] = idxnode->val[i]; - val_idx += 1; - } - } - dblbreak:; - rdim_bake_idx_run_map_insert(arena, idx_runs, idx_run, (RDI_U32)n->val_count); // TODO(rjf): @u64_to_u32 - } - } - } - } - - return idx_runs; -} - -//- rjf: bake path tree building - -RDI_PROC RDIM_BakePathTree * -rdim_bake_path_tree_from_params(RDIM_Arena *arena, RDIM_BakeParams *params) -{ - //- rjf: set up tree - RDIM_BakePathTree *tree = rdim_push_array(arena, RDIM_BakePathTree, 1); - rdim_bake_path_tree_insert(arena, tree, rdim_str8_lit("")); - - //- rjf: bake unit file paths - RDIM_ProfScope("bake unit file paths") - { - for(RDIM_UnitChunkNode *n = params->units.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - rdim_bake_path_tree_insert(arena, tree, n->v[idx].source_file); - rdim_bake_path_tree_insert(arena, tree, n->v[idx].object_file); - rdim_bake_path_tree_insert(arena, tree, n->v[idx].archive_file); - rdim_bake_path_tree_insert(arena, tree, n->v[idx].build_path); - } - } - } - - //- rjf: bake source file paths - RDIM_ProfScope("bake source file paths") - { - for(RDIM_SrcFileChunkNode *n = params->src_files.first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - RDIM_BakePathNode *node = rdim_bake_path_tree_insert(arena, tree, n->v[idx].path); - node->src_file = &n->v[idx]; - } - } - } - - return tree; -} - -//////////////////////////////// -//~ 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) -{ - RDIM_NameMapBakeResult result = {0}; - if(src->name_count != 0) - { - RDI_U32 baked_buckets_count = src->name_count; - RDI_U32 baked_nodes_count = src->name_count; - RDI_NameMapBucket *baked_buckets = rdim_push_array(arena, RDI_NameMapBucket, baked_buckets_count); - RDI_NameMapNode *baked_nodes = rdim_push_array_no_zero(arena, RDI_NameMapNode, baked_nodes_count); - { - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - - // rjf: setup the final bucket layouts - typedef struct RDIM_NameMapSemiNode RDIM_NameMapSemiNode; - struct RDIM_NameMapSemiNode - { - RDIM_NameMapSemiNode *next; - RDIM_BakeNameMapNode *node; - }; - typedef struct RDIM_NameMapSemiBucket RDIM_NameMapSemiBucket; - struct RDIM_NameMapSemiBucket - { - RDIM_NameMapSemiNode *first; - RDIM_NameMapSemiNode *last; - RDI_U64 count; - }; - RDIM_NameMapSemiBucket *sbuckets = rdim_push_array(scratch.arena, RDIM_NameMapSemiBucket, baked_buckets_count); - for(RDIM_BakeNameMapNode *node = src->first; - node != 0; - node = node->order_next) - { - RDI_U64 hash = rdi_hash(node->string.str, node->string.size); - RDI_U64 bi = hash%baked_buckets_count; - RDIM_NameMapSemiNode *snode = rdim_push_array(scratch.arena, RDIM_NameMapSemiNode, 1); - SLLQueuePush(sbuckets[bi].first, sbuckets[bi].last, snode); - snode->node = node; - sbuckets[bi].count += 1; - } - - // rjf: convert to serialized buckets & nodes - { - RDI_NameMapBucket *bucket_ptr = baked_buckets; - RDI_NameMapNode *node_ptr = baked_nodes; - for(RDI_U32 i = 0; i < baked_buckets_count; i += 1, bucket_ptr += 1) - { - bucket_ptr->first_node = (RDI_U32)((RDI_U64)(node_ptr - baked_nodes)); - bucket_ptr->node_count = sbuckets[i].count; - for(RDIM_NameMapSemiNode *snode = sbuckets[i].first; - snode != 0; - snode = snode->next) - { - RDIM_BakeNameMapNode *node = snode->node; - - // rjf: cons name and index(es) - RDI_U32 string_idx = rdim_bake_idx_from_string(strings, node->string); - RDI_U32 match_count = node->val_count; - RDI_U32 idx = 0; - if(match_count == 1) - { - idx = node->val_first->val[0]; - } - else - { - RDI_U64 temp_pos = rdim_arena_pos(scratch.arena); - RDI_U32 *idx_run = rdim_push_array_no_zero(scratch.arena, RDI_U32, match_count); - RDI_U32 *idx_ptr = idx_run; - for(RDIM_BakeNameMapValNode *idxnode = node->val_first; - idxnode != 0; - idxnode = idxnode->next) - { - for(RDI_U32 i = 0; i < sizeof(idxnode->val)/sizeof(idxnode->val[0]); i += 1) - { - if(idxnode->val[i] == 0) - { - goto dblbreak; - } - *idx_ptr = idxnode->val[i]; - idx_ptr += 1; - } - } - dblbreak:; - idx = rdim_bake_idx_from_idx_run(idx_runs, idx_run, match_count); - rdim_arena_pop_to(scratch.arena, temp_pos); - } - - // rjf: write to node - node_ptr->string_idx = string_idx; - node_ptr->match_count = match_count; - node_ptr->match_idx_or_idx_run_first = idx; - node_ptr += 1; - } - } - } - rdim_scratch_end(scratch); - } - - // rjf: sections for buckets/nodes - result.buckets = baked_buckets; - result.buckets_count = baked_buckets_count; - result.nodes = baked_nodes; - result.nodes_count = baked_nodes_count; - } - return result; -} - -//- 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) -{ - RDIM_NameMapBakeResult result = {0}; - { - //- rjf: count needed # of buckets/nodes - RDI_U64 all_buckets_count = 0; - RDI_U64 all_nodes_count = 0; - for(RDI_U64 idx = 0; idx < results_count; idx += 1) - { - all_buckets_count += results[idx].buckets_count; - all_nodes_count += results[idx].nodes_count; - } - - //- rjf: allocate outputs - result.buckets_count = all_buckets_count; - result.buckets = rdim_push_array_no_zero(arena, RDI_NameMapBucket, result.buckets_count); - result.nodes_count = all_nodes_count; - result.nodes = rdim_push_array_no_zero(arena, RDI_NameMapNode, result.nodes_count); - - //- rjf: fill outputs - { - RDI_U64 buckets_off = 0; - RDI_U64 nodes_off = 0; - for(RDI_U64 idx = 0; idx < results_count; idx += 1) - { - rdim_memcpy(result.buckets + buckets_off, results[idx].buckets, sizeof(result.buckets[0])*results[idx].buckets_count); - rdim_memcpy(result.nodes + nodes_off, results[idx].nodes, sizeof(result.nodes[0])*results[idx].nodes_count); - buckets_off += results[idx].buckets_count; - nodes_off += results[idx].nodes_count; - } - } - } - return result; -} - -//- 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) -{ - RDIM_TopLevelInfoBakeResult result = {0}; - { - result.top_level_info = rdim_push_array(arena, RDI_TopLevelInfo, 1); - result.top_level_info->arch = src->arch; - result.top_level_info->exe_name_string_idx = rdim_bake_idx_from_string(strings, src->exe_name); - result.top_level_info->exe_hash = src->exe_hash; - result.top_level_info->voff_max = src->voff_max; - result.top_level_info->producer_name_string_idx = rdim_bake_idx_from_string(strings, src->producer_name); - } - return result; -} - -RDI_PROC RDIM_BinarySectionBakeResult -rdim_bake_binary_sections(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BinarySectionList *src) -{ - RDIM_BinarySectionBakeResult result = {0}; - { - RDI_BinarySection *dst_base = rdim_push_array(arena, RDI_BinarySection, src->count+1); - U64 dst_idx = 1; - for(RDIM_BinarySectionNode *src_n = src->first; src_n != 0; src_n = src_n->next, dst_idx += 1) - { - RDIM_BinarySection *src = &src_n->v; - RDI_BinarySection *dst = &dst_base[dst_idx]; - dst->name_string_idx = rdim_bake_idx_from_string(strings, src->name); - dst->flags = src->flags; - dst->voff_first = src->voff_first; - dst->voff_opl = src->voff_opl; - dst->foff_first = src->foff_first; - dst->foff_opl = src->foff_opl; - } - result.binary_sections = dst_base; - result.binary_sections_count = dst_idx; - } - return result; -} - -RDI_PROC RDIM_UnitBakeResult -rdim_bake_units(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakePathTree *path_tree, RDIM_UnitChunkList *src) -{ - RDIM_UnitBakeResult result = {0}; - { - RDI_Unit *dst_base = rdim_push_array(arena, RDI_Unit, src->total_count+1); - RDI_U64 dst_idx = 1; - for(RDIM_UnitChunkNode *src_n = src->first; src_n != 0; src_n = src_n->next) - { - for(RDI_U64 src_chunk_idx = 0; src_chunk_idx < src_n->count; src_chunk_idx += 1, dst_idx += 1) - { - RDIM_Unit *src = &src_n->v[src_chunk_idx]; - RDI_Unit *dst = &dst_base[dst_idx]; - dst->unit_name_string_idx = rdim_bake_idx_from_string(strings, src->unit_name); - dst->compiler_name_string_idx = rdim_bake_idx_from_string(strings, src->compiler_name); - dst->source_file_path_node = rdim_bake_path_node_idx_from_string(path_tree, src->source_file); - dst->object_file_path_node = rdim_bake_path_node_idx_from_string(path_tree, src->object_file); - dst->archive_file_path_node = rdim_bake_path_node_idx_from_string(path_tree, src->archive_file); - dst->build_path_node = rdim_bake_path_node_idx_from_string(path_tree, src->build_path); - dst->language = src->language; - dst->line_table_idx = (RDI_U32)rdim_idx_from_line_table(src->line_table); // TODO(rjf): @u64_to_u32 - } - } - result.units = dst_base; - result.units_count = dst_idx; - } - return result; -} - -RDI_PROC RDIM_UnitVMapBakeResult -rdim_bake_unit_vmap(RDIM_Arena *arena, RDIM_UnitChunkList *units) -{ - //- rjf: build vmap from unit voff ranges - RDIM_BakeVMap unit_vmap = {0}; - { - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - - // rjf: count voff ranges - RDI_U64 voff_range_count = 0; - for(RDIM_UnitChunkNode *n = units->first; n != 0; n = n->next) - { - for(RDI_U64 idx = 0; idx < n->count; idx += 1) - { - RDIM_Unit *unit = &n->v[idx]; - voff_range_count += unit->voff_ranges.total_count; - } - } - - // rjf: count necessary markers - RDI_U64 marker_count = voff_range_count*2; - - // rjf: build keys/markers arrays - RDIM_SortKey *keys = rdim_push_array_no_zero(scratch.arena, RDIM_SortKey, marker_count); - RDIM_VMapMarker *markers = rdim_push_array_no_zero(scratch.arena, RDIM_VMapMarker, marker_count); - { - RDIM_SortKey *key_ptr = keys; - RDIM_VMapMarker *marker_ptr = markers; - RDI_U32 unit_idx = 1; - for(RDIM_UnitChunkNode *unit_chunk_n = units->first; - unit_chunk_n != 0; - unit_chunk_n = unit_chunk_n->next) - { - for(RDI_U64 idx = 0; idx < unit_chunk_n->count; idx += 1) - { - RDIM_Unit *unit = &unit_chunk_n->v[idx]; - for(RDIM_Rng1U64ChunkNode *n = unit->voff_ranges.first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) - { - RDIM_Rng1U64 range = n->v[chunk_idx]; - if(range.min < range.max) - { - key_ptr->key = range.min; - key_ptr->val = marker_ptr; - marker_ptr->idx = unit_idx; - marker_ptr->begin_range = 1; - key_ptr += 1; - marker_ptr += 1; - - key_ptr->key = range.max; - key_ptr->val = marker_ptr; - marker_ptr->idx = unit_idx; - marker_ptr->begin_range = 0; - key_ptr += 1; - marker_ptr += 1; - } - } - } - unit_idx += 1; - } - } - } - - // rjf: keys/markers -> unit vmap - unit_vmap = rdim_bake_vmap_from_markers(arena, markers, keys, marker_count); - rdim_scratch_end(scratch); - } - - //- rjf: fill result - RDIM_UnitVMapBakeResult result = {unit_vmap}; - return result; -} - -RDI_PROC RDIM_SrcFileBakeResult -rdim_bake_src_files(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakePathTree *path_tree, RDIM_SrcFileChunkList *src) -{ - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - - //////////////////////////// - //- rjf: iterate all source files, fill serialized version, fill line maps, fill line map tables - // - typedef struct RDIM_DataNode RDIM_DataNode; - struct RDIM_DataNode - { - RDIM_DataNode *next; - void *data; - RDI_U64 size; - }; - RDI_U32 dst_files_count = src->total_count + 1; - RDI_U32 dst_maps_count = src->source_line_map_count + 1; - RDI_SourceFile *dst_files = rdim_push_array(arena, RDI_SourceFile, dst_files_count); - RDI_SourceLineMap *dst_maps = rdim_push_array(arena, RDI_SourceLineMap, dst_maps_count); - RDIM_DataNode *first_dst_nums_node = 0; - RDIM_DataNode *last_dst_nums_node = 0; - RDIM_DataNode *first_dst_rngs_node = 0; - RDIM_DataNode *last_dst_rngs_node = 0; - RDIM_DataNode *first_dst_voffs_node = 0; - RDIM_DataNode *last_dst_voffs_node = 0; - RDI_U64 dst_nums_idx = 0; - RDI_U64 dst_rngs_idx = 0; - RDI_U64 dst_voffs_idx = 0; - RDI_U32 dst_file_idx = 1; - RDI_U32 dst_map_idx = 1; - for(RDIM_SrcFileChunkNode *chunk_n = src->first; - chunk_n != 0; - chunk_n = chunk_n->next) - { - for(RDI_U64 idx = 0; idx < chunk_n->count; idx += 1, dst_file_idx += 1) - { - RDIM_SrcFile *src_file = &chunk_n->v[idx]; - RDI_SourceFile *dst_file = &dst_files[dst_file_idx]; - - //////////////////////// - //- rjf: produce combined source file line info - // - RDI_U32 *src_file_line_nums = 0; - RDI_U32 *src_file_line_ranges = 0; - RDI_U64 *src_file_voffs = 0; - RDI_U32 src_file_line_count = 0; - RDI_U32 src_file_voff_count = 0; - { - //- rjf: gather line number map - typedef struct RDIM_SrcLineMapVoffBlock RDIM_SrcLineMapVoffBlock; - struct RDIM_SrcLineMapVoffBlock - { - RDIM_SrcLineMapVoffBlock *next; - RDI_U64 voff; - }; - typedef struct RDIM_SrcLineMapBucket RDIM_SrcLineMapBucket; - struct RDIM_SrcLineMapBucket - { - RDIM_SrcLineMapBucket *order_next; - RDIM_SrcLineMapBucket *hash_next; - RDI_U32 line_num; - RDIM_SrcLineMapVoffBlock *first_voff_block; - RDIM_SrcLineMapVoffBlock *last_voff_block; - RDI_U64 voff_count; - }; - RDIM_SrcLineMapBucket *first_bucket = 0; - RDIM_SrcLineMapBucket *last_bucket = 0; - RDI_U64 line_hash_slots_count = 2048; - RDIM_SrcLineMapBucket **line_hash_slots = rdim_push_array(scratch.arena, RDIM_SrcLineMapBucket *, line_hash_slots_count); - RDI_U64 line_count = 0; - RDI_U64 voff_count = 0; - RDI_U64 max_line_num = 0; - { - for(RDIM_SrcFileLineMapFragment *map_fragment = src_file->first_line_map_fragment; - map_fragment != 0; - map_fragment = map_fragment->next) - { - RDIM_LineSequence *sequence = map_fragment->seq; - RDI_U64 *seq_voffs = sequence->voffs; - RDI_U32 *seq_line_nums = sequence->line_nums; - RDI_U64 seq_line_count = sequence->line_count; - for(RDI_U64 i = 0; i < seq_line_count; i += 1) - { - RDI_U32 line_num = seq_line_nums[i]; - RDI_U64 voff = seq_voffs[i]; - RDI_U64 line_hash_slot_idx = line_num%line_hash_slots_count; - - // rjf: update unique voff counter & max line number - voff_count += 1; - max_line_num = Max(max_line_num, line_num); - - // rjf: find match - RDIM_SrcLineMapBucket *match = 0; - { - for(RDIM_SrcLineMapBucket *node = line_hash_slots[line_hash_slot_idx]; - node != 0; - node = node->hash_next) - { - if(node->line_num == line_num) - { - match = node; - break; - } - } - } - - // rjf: introduce new map if no match - if(match == 0) - { - match = rdim_push_array(scratch.arena, RDIM_SrcLineMapBucket, 1); - RDIM_SLLQueuePush_N(first_bucket, last_bucket, match, order_next); - RDIM_SLLStackPush_N(line_hash_slots[line_hash_slot_idx], match, hash_next); - match->line_num = line_num; - line_count += 1; - } - - // rjf: insert new voff - { - RDIM_SrcLineMapVoffBlock *block = rdim_push_array(scratch.arena, RDIM_SrcLineMapVoffBlock, 1); - RDIM_SLLQueuePush(match->first_voff_block, match->last_voff_block, block); - match->voff_count += 1; - block->voff = voff; - } - } - } - } - - //- rjf: bake sortable keys array - RDIM_SortKey *keys = rdim_push_array_no_zero(scratch.arena, RDIM_SortKey, line_count); - { - RDIM_SortKey *key_ptr = keys; - for(RDIM_SrcLineMapBucket *node = first_bucket; - node != 0; - node = node->order_next, key_ptr += 1){ - key_ptr->key = node->line_num; - key_ptr->val = node; - } - } - - //- rjf: sort keys array - RDIM_SortKey *sorted_keys = rdim_sort_key_array(scratch.arena, keys, line_count); - - //- rjf: bake result - RDI_U32 *line_nums = rdim_push_array_no_zero(scratch.arena, RDI_U32, line_count); - RDI_U32 *line_ranges = rdim_push_array_no_zero(scratch.arena, RDI_U32, line_count + 1); - RDI_U64 *voffs = rdim_push_array_no_zero(scratch.arena, RDI_U64, voff_count); - { - RDI_U64 *voff_ptr = voffs; - for(RDI_U32 i = 0; i < line_count; i += 1) - { - line_nums[i] = sorted_keys[i].key; - line_ranges[i] = (RDI_U32)(voff_ptr - voffs); // TODO(rjf): @u64_to_u32 - RDIM_SrcLineMapBucket *bucket = (RDIM_SrcLineMapBucket*)sorted_keys[i].val; - for(RDIM_SrcLineMapVoffBlock *node = bucket->first_voff_block; node != 0; node = node->next) - { - *voff_ptr = node->voff; - voff_ptr += 1; - } - } - line_ranges[line_count] = voff_count; - } - - //- rjf: fill output - src_file_line_nums = line_nums; - src_file_line_ranges = line_ranges; - src_file_line_count = line_count; - src_file_voffs = voffs; - src_file_voff_count = voff_count; - } - - //////////////////////// - //- rjf: grab & fill the next line map, if this file has one - // - RDI_SourceLineMap *dst_map = 0; - if(src_file->first_line_map_fragment != 0) - { - dst_map = &dst_maps[dst_map_idx]; - dst_map_idx += 1; - dst_map->line_count = (RDI_U32)src_file_line_count; // TODO(rjf): @u64_to_u32 - dst_map->voff_count = (RDI_U32)src_file_voff_count; // TODO(rjf): @u64_to_u32 - dst_map->line_map_nums_base_idx = (RDI_U32)dst_nums_idx; // TODO(rjf): @u64_to_u32 - dst_map->line_map_range_base_idx = (RDI_U32)dst_rngs_idx; // TODO(rjf): @u64_to_u32 - dst_map->line_map_voff_base_idx = (RDI_U32)dst_voffs_idx; // TODO(rjf): @u64_to_u32 - } - - //////////////////////// - //- rjf: gather line map data chunks for later collation & storage into their own top-level sections - // - { - RDIM_DataNode *dst_num_node = rdim_push_array(scratch.arena, RDIM_DataNode, 1); - RDIM_SLLQueuePush(first_dst_nums_node, last_dst_nums_node, dst_num_node); - dst_num_node->data = src_file_line_nums; - dst_num_node->size = sizeof(RDI_U32)*src_file_line_count; - RDIM_DataNode *dst_rng_node = rdim_push_array(scratch.arena, RDIM_DataNode, 1); - RDIM_SLLQueuePush(first_dst_rngs_node, last_dst_rngs_node, dst_rng_node); - dst_rng_node->data = src_file_line_ranges; - dst_rng_node->size = sizeof(RDI_U32)*(src_file_line_count+1); - RDIM_DataNode *dst_voff_node = rdim_push_array(scratch.arena, RDIM_DataNode, 1); - RDIM_SLLQueuePush(first_dst_voffs_node, last_dst_voffs_node, dst_voff_node); - dst_voff_node->data = src_file_voffs; - dst_voff_node->size = sizeof(RDI_U64)*(src_file_voff_count); - dst_nums_idx += src_file_line_count; - dst_rngs_idx += src_file_line_count+1; - dst_voffs_idx+= src_file_voff_count; - } - - //////////////////////// - //- rjf: fill file info - // - RDI_U64 scratch_pos_restore = rdim_arena_pos(scratch.arena); - RDIM_String8 normalized_path = rdim_lower_from_str8(scratch.arena, src_file->path); - dst_file->file_path_node_idx = rdim_bake_path_node_idx_from_string(path_tree, src_file->path); - dst_file->normal_full_path_string_idx = rdim_bake_idx_from_string(strings, normalized_path); - dst_file->source_line_map_idx = (RDI_U32)(dst_map ? (dst_map - dst_maps) : 0); - rdim_arena_pop_to(scratch.arena, scratch_pos_restore); - } - } - - //////////////////////////// - //- rjf: coalesce source line map data blobs - // - RDI_U32 *source_line_map_nums = rdim_push_array_no_zero(arena, RDI_U32, dst_nums_idx); - RDI_U32 *source_line_map_rngs = rdim_push_array_no_zero(arena, RDI_U32, dst_rngs_idx); - RDI_U64 *source_line_map_voffs= rdim_push_array_no_zero(arena, RDI_U64, dst_voffs_idx); - { - RDI_U64 num_idx = 0; - RDI_U64 rng_idx = 0; - RDI_U64 voff_idx= 0; - for(RDIM_DataNode *num_n = first_dst_nums_node; num_n != 0; num_n = num_n->next) - { - rdim_memcpy(source_line_map_nums+num_idx, num_n->data, num_n->size); - num_idx += num_n->size/sizeof(RDI_U32); - } - for(RDIM_DataNode *rng_n = first_dst_rngs_node; rng_n != 0; rng_n = rng_n->next) - { - rdim_memcpy(source_line_map_rngs+rng_idx, rng_n->data, rng_n->size); - rng_idx += rng_n->size/sizeof(RDI_U32); - } - for(RDIM_DataNode *voff_n = first_dst_voffs_node; voff_n != 0; voff_n = voff_n->next) - { - rdim_memcpy(source_line_map_voffs+voff_idx, voff_n->data, voff_n->size); - voff_idx += voff_n->size/sizeof(RDI_U64); - } - } - - //////////////////////////// - //- rjf: fill result - // - RDIM_SrcFileBakeResult result = {0}; - result.source_files = dst_files; - result.source_files_count = dst_files_count; - result.source_line_maps = dst_maps; - result.source_line_maps_count = dst_maps_count; - result.source_line_map_nums = source_line_map_nums; - result.source_line_map_nums_count = dst_nums_idx; - result.source_line_map_rngs = source_line_map_rngs; - result.source_line_map_rngs_count = dst_rngs_idx; - result.source_line_map_voffs = source_line_map_voffs; - result.source_line_map_voffs_count= dst_voffs_idx; - - rdim_scratch_end(scratch); - return result; -} - -RDI_PROC RDIM_LineTableBakeResult -rdim_bake_line_tables(RDIM_Arena *arena, RDIM_LineTableChunkList *src) -{ - ////////////////////////////// - //- rjf: build all combined line info - // - RDI_LineTable *dst_line_tables = push_array(arena, RDI_LineTable, src->total_count+1); - RDI_U64 *dst_line_voffs = push_array(arena, RDI_U64, src->total_line_count + 2*src->total_seq_count); - RDI_Line *dst_lines = push_array(arena, RDI_Line, src->total_line_count + src->total_seq_count); - RDI_Column *dst_cols = push_array(arena, RDI_Column, 1); - { - RDI_U64 dst_table_idx = 1; - RDI_U64 dst_voff_idx = 0; - RDI_U64 dst_line_idx = 0; - RDI_U64 dst_col_idx = 0; - for(RDIM_LineTableChunkNode *src_n = src->first; src_n != 0; src_n = src_n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < src_n->count; chunk_idx += 1) - { - RDIM_LineTable *src_line_table = &src_n->v[chunk_idx]; - RDI_LineTable *dst_line_table = &dst_line_tables[dst_table_idx]; - - //- rjf: fill combined line table info - { - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - - //- rjf: gather up all line info into two arrays: - // - // [1] keys: sortable array; pairs voffs with line info records; null records are sequence enders - // [2] recs: contains all the source coordinates for a range of voffs - // - 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; - }; - RDI_U64 line_count = src_line_table->line_count; - RDI_U64 seq_count = src_line_table->seq_count; - RDI_U64 key_count = line_count + seq_count; - RDIM_SortKey *line_keys = rdim_push_array_no_zero(scratch.arena, RDIM_SortKey, key_count); - RDIM_LineRec *line_recs = rdim_push_array_no_zero(scratch.arena, RDIM_LineRec, line_count); - { - RDIM_SortKey *key_ptr = line_keys; - RDIM_LineRec *rec_ptr = line_recs; - for(RDIM_LineSequenceNode *seq_n = src_line_table->first_seq; seq_n != 0; seq_n = seq_n->next) - { - RDIM_LineSequence *seq = &seq_n->v; - for(RDI_U64 line_idx = 0; line_idx < seq->line_count; line_idx += 1) - { - key_ptr->key = seq->voffs[line_idx]; - key_ptr->val = rec_ptr; - key_ptr += 1; - rec_ptr->file_id = (RDI_U32)rdim_idx_from_src_file(seq->src_file); // TODO(rjf): @u64_to_u32 - rec_ptr->line_num = seq->line_nums[line_idx]; - if(seq->col_nums != 0) - { - rec_ptr->col_first = seq->col_nums[line_idx*2]; - rec_ptr->col_opl = seq->col_nums[line_idx*2 + 1]; - } - rec_ptr += 1; - } - key_ptr->key = seq->voffs[seq->line_count]; - key_ptr->val = 0; - key_ptr += 1; - } - } - - //- rjf: sort - RDIM_SortKey *sorted_line_keys = 0; - { - sorted_line_keys = rdim_sort_key_array(scratch.arena, line_keys, key_count); - } - - // TODO(rjf): do a pass over sorted keys to make sure duplicate keys - // are sorted with null record first, and no more than one null - // record and one non-null record - - //- rjf: arrange output - RDI_U64 *arranged_voffs = dst_line_voffs + dst_voff_idx; - RDI_Line *arranged_lines = dst_lines + dst_line_idx; - { - for(RDI_U64 i = 0; i < key_count; i += 1) - { - arranged_voffs[i] = sorted_line_keys[i].key; - } - arranged_voffs[key_count] = ~0ull; - for(RDI_U64 i = 0; i < key_count; i += 1) - { - RDIM_LineRec *rec = (RDIM_LineRec*)sorted_line_keys[i].val; - if(rec != 0) - { - arranged_lines[i].file_idx = rec->file_id; - arranged_lines[i].line_num = rec->line_num; - } - else - { - arranged_lines[i].file_idx = 0; - arranged_lines[i].line_num = 0; - } - } - } - - rdim_scratch_end(scratch); - } - - //- rjf: fill destination table - dst_line_table->voffs_base_idx = (RDI_U32)dst_voff_idx; // TODO(rjf): @u64_to_u32 - dst_line_table->lines_base_idx = (RDI_U32)dst_line_idx; // TODO(rjf): @u64_to_u32 - dst_line_table->cols_base_idx = (RDI_U32)dst_col_idx; // TODO(rjf): @u64_to_u32 - dst_line_table->lines_count = (RDI_U32)src_line_table->line_count + src_line_table->seq_count; // TODO(rjf): @u64_to_u32 - - //- rjf: increment - dst_table_idx += 1; - dst_voff_idx += src_line_table->line_count + 2*src_line_table->seq_count; - dst_line_idx += src_line_table->line_count + src_line_table->seq_count; - } - } - } - - ////////////////////////////// - //- rjf: fill result - // - RDIM_LineTableBakeResult result = {0}; - { - result.line_tables = dst_line_tables; - result.line_tables_count = src->total_count+1; - result.line_table_voffs = dst_line_voffs; - result.line_table_voffs_count = (src->total_line_count + 2*src->total_seq_count); - result.line_table_lines = dst_lines; - result.line_table_lines_count = (src->total_line_count + src->total_seq_count); - result.line_table_columns = dst_cols; - result.line_table_columns_count = src->total_col_count; - } - return result; -} - -RDI_PROC RDIM_TypeNodeBakeResult -rdim_bake_types(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakeIdxRunMap *idx_runs, RDIM_TypeChunkList *src) -{ - RDI_TypeNode *type_nodes = push_array(arena, RDI_TypeNode, src->total_count+1); - for(RDIM_TypeChunkNode *n = src->first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) - { - RDIM_Type *src = &n->v[chunk_idx]; - U64 dst_idx = rdim_idx_from_type(src); - RDI_TypeNode *dst = &type_nodes[dst_idx]; - - //- rjf: fill shared type node info - dst->kind = src->kind; - dst->flags = (RDI_U16)src->flags; // TODO(rjf): @u32_to_u16 - dst->byte_size = src->byte_size; - - //- rjf: fill built-in-only type node info - if(RDI_TypeKind_FirstBuiltIn <= dst->kind && dst->kind <= RDI_TypeKind_LastBuiltIn) - { - dst->built_in.name_string_idx = rdim_bake_idx_from_string(strings, src->name); - } - - else if(dst->kind == RDI_TypeKind_Array) - { - U64 direct_byte_size = 1; - if(src->direct_type && src->direct_type->byte_size > 0) - { - direct_byte_size = src->direct_type->byte_size; - } - dst->constructed.direct_type_idx = (RDI_U32)rdim_idx_from_type(src->direct_type); - dst->constructed.count = src->byte_size / direct_byte_size; - } - - //- rjf: fill constructed type node info - else if(RDI_TypeKind_FirstConstructed <= dst->kind && dst->kind <= RDI_TypeKind_LastConstructed) - { - dst->constructed.direct_type_idx = (RDI_U32)rdim_idx_from_type(src->direct_type); // TODO(rjf): @u64_to_u32 - dst->constructed.count = src->count; - if(dst->kind == RDI_TypeKind_Function || dst->kind == RDI_TypeKind_Method) - { - RDI_U32 param_idx_run_count = src->count; - RDI_U32 *param_idx_run = rdim_push_array_no_zero(arena, RDI_U32, param_idx_run_count); - for(RDI_U32 idx = 0; idx < param_idx_run_count; idx += 1) - { - param_idx_run[idx] = (RDI_U32)rdim_idx_from_type(src->param_types[idx]); // TODO(rjf): @u64_to_u32 - } - dst->constructed.param_idx_run_first = rdim_bake_idx_from_idx_run(idx_runs, param_idx_run, param_idx_run_count); - } - else if(dst->kind == RDI_TypeKind_MemberPtr) - { - // TODO(rjf): member pointers not currently supported. - } - } - - //- rjf: fill user-defined-type info - else if(RDI_TypeKind_FirstUserDefined <= dst->kind && dst->kind <= RDI_TypeKind_LastUserDefined) - { - dst->user_defined.name_string_idx = rdim_bake_idx_from_string(strings, src->name); - dst->user_defined.udt_idx = (RDI_U32)rdim_idx_from_udt(src->udt); // TODO(rjf): @u64_to_u32 - dst->user_defined.direct_type_idx = (RDI_U32)rdim_idx_from_type(src->direct_type); // TODO(rjf): @u64_to_u32 - } - - //- rjf: fill bitfield info - else if(dst->kind == RDI_TypeKind_Bitfield) - { - dst->bitfield.direct_type_idx = (RDI_U32)rdim_idx_from_type(src->direct_type); // TODO(rjf): @u64_to_u32 - dst->bitfield.off = src->off; - dst->bitfield.size = src->count; - } - } - } - RDIM_TypeNodeBakeResult result = {0}; - result.type_nodes = type_nodes; - result.type_nodes_count = (src->total_count+1); - return result; -} - -RDI_PROC RDIM_UDTBakeResult -rdim_bake_udts(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_UDTChunkList *src) -{ - //- rjf: build tables - RDI_UDT * udts = push_array(arena, RDI_UDT, src->total_count+1); - RDI_Member * members = push_array(arena, RDI_Member, src->total_member_count+1); - RDI_EnumMember *enum_members = push_array(arena, RDI_EnumMember, src->total_enum_val_count+1); - { - RDI_U32 dst_udt_idx = 1; - RDI_U32 dst_member_idx = 1; - RDI_U32 dst_enum_member_idx = 1; - for(RDIM_UDTChunkNode *n = src->first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1, dst_udt_idx += 1) - { - RDIM_UDT *src_udt = &n->v[chunk_idx]; - RDI_UDT *dst_udt = &udts[dst_udt_idx]; - - //- rjf: fill basics - dst_udt->self_type_idx = (RDI_U32)rdim_idx_from_type(src_udt->self_type); // TODO(rjf): @u64_to_u32 - dst_udt->file_idx = (RDI_U32)rdim_idx_from_src_file(src_udt->src_file); // TODO(rjf): @u64_to_u32 - dst_udt->line = src_udt->line; - dst_udt->col = src_udt->col; - - //- rjf: fill members - if(src_udt->member_count != 0) - { - dst_udt->member_first = dst_member_idx; - dst_udt->member_count = src_udt->member_count; - for(RDIM_UDTMember *src_member = src_udt->first_member; - src_member != 0; - src_member = src_member->next, dst_member_idx += 1) - { - RDI_Member *dst_member = &members[dst_member_idx]; - dst_member->kind = src_member->kind; - dst_member->name_string_idx = rdim_bake_idx_from_string(strings, src_member->name); - dst_member->type_idx = (RDI_U32)rdim_idx_from_type(src_member->type); // TODO(rjf): @u64_to_u32 - dst_member->off = src_member->off; - } - } - - //- rjf: fill enum members - else if(src_udt->enum_val_count != 0) - { - dst_udt->flags |= RDI_UDTFlag_EnumMembers; - dst_udt->member_first = dst_enum_member_idx; - dst_udt->member_count = src_udt->enum_val_count; - for(RDIM_UDTEnumVal *src_member = src_udt->first_enum_val; - src_member != 0; - src_member = src_member->next, dst_enum_member_idx += 1) - { - RDI_EnumMember *dst_member = &enum_members[dst_enum_member_idx]; - dst_member->name_string_idx = rdim_bake_idx_from_string(strings, src_member->name); - dst_member->val = src_member->val; - } - } - } - } - } - - //- rjf: fill result - RDIM_UDTBakeResult result = {0}; - { - result.udts = udts; - result.udts_count = src->total_count+1; - result.members = members; - result.members_count = src->total_member_count+1; - result.enum_members = enum_members; - result.enum_members_count = src->total_enum_val_count+1; - } - return result; -} - -RDI_PROC RDIM_GlobalVariableBakeResult -rdim_bake_global_variables(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_SymbolChunkList *src) -{ - RDI_GlobalVariable *global_variables = push_array(arena, RDI_GlobalVariable, src->total_count+1); - RDI_U32 dst_idx = 1; - for(RDIM_SymbolChunkNode *n = src->first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1, dst_idx += 1) - { - RDIM_Symbol *src = &n->v[chunk_idx]; - RDI_GlobalVariable *dst = &global_variables[dst_idx]; - dst->name_string_idx = rdim_bake_idx_from_string(strings, src->name); - dst->voff = src->offset; - dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); // TODO(rjf): @u64_to_u32 - if(src->is_extern) - { - dst->link_flags |= RDI_LinkFlag_External; - } - if(src->container_type != 0) - { - dst->link_flags |= RDI_LinkFlag_TypeScoped; - dst->container_idx = src->container_type ? (RDI_U32)rdim_idx_from_udt(src->container_type->udt) : 0; // TODO(rjf): @u64_to_u32 - } - else if(src->container_symbol != 0) - { - dst->link_flags |= RDI_LinkFlag_ProcScoped; - dst->container_idx = (RDI_U32)rdim_idx_from_symbol(src->container_symbol); // TODO(rjf): @u64_to_u32 - } - } - } - RDIM_GlobalVariableBakeResult result = {0}; - result.global_variables = global_variables; - result.global_variables_count = (src->total_count+1); - return result; -} - -RDI_PROC RDIM_GlobalVMapBakeResult -rdim_bake_global_vmap(RDIM_Arena *arena, RDIM_SymbolChunkList *src) -{ - //- rjf: build global vmap - RDIM_BakeVMap global_vmap = {0}; - { - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - - //- rjf: allocate keys/markers - RDI_U64 marker_count = src->total_count*2 + 2; - RDIM_SortKey *keys = rdim_push_array_no_zero(scratch.arena, RDIM_SortKey, marker_count); - RDIM_VMapMarker *markers = rdim_push_array_no_zero(scratch.arena, RDIM_VMapMarker, marker_count); - - //- rjf: fill - { - RDIM_SortKey *key_ptr = keys; - RDIM_VMapMarker *marker_ptr = markers; - - // rjf: fill actual globals - for(RDIM_SymbolChunkNode *n = src->first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) - { - RDIM_Symbol *global_var = &n->v[chunk_idx]; - RDI_U32 global_var_idx = (RDI_U32)rdim_idx_from_symbol(global_var); // TODO(rjf): @u64_to_u32 - RDI_U64 global_var_size = global_var->type ? global_var->type->byte_size : 1; - - RDI_U64 first = global_var->offset; - RDI_U64 opl = first + global_var_size; - - key_ptr->key = first; - key_ptr->val = marker_ptr; - marker_ptr->idx = global_var_idx; - marker_ptr->begin_range = 1; - key_ptr += 1; - marker_ptr += 1; - - key_ptr->key = opl; - key_ptr->val = marker_ptr; - marker_ptr->idx = global_var_idx; - marker_ptr->begin_range = 0; - key_ptr += 1; - marker_ptr += 1; - } - } - - // rjf: fill nil global - { - RDI_U32 global_idx = 0; - RDI_U64 first = 0; - RDI_U64 opl = 0xffffffffffffffffull; - key_ptr->key = first; - key_ptr->val = marker_ptr; - marker_ptr->idx = global_idx; - marker_ptr->begin_range = 1; - key_ptr += 1; - marker_ptr += 1; - key_ptr->key = opl; - key_ptr->val = marker_ptr; - marker_ptr->idx = global_idx; - marker_ptr->begin_range = 0; - key_ptr += 1; - marker_ptr += 1; - } - } - - // rjf: construct vmap - global_vmap = rdim_bake_vmap_from_markers(arena, markers, keys, marker_count); - - rdim_scratch_end(scratch); - } - - //- rjf: fill result - RDIM_GlobalVMapBakeResult result = {global_vmap}; - return result; -} - -RDI_PROC RDIM_ThreadVariableBakeResult -rdim_bake_thread_variables(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_SymbolChunkList *src) -{ - RDI_ThreadVariable *thread_variables = push_array(arena, RDI_ThreadVariable, src->total_count+1); - RDI_U32 dst_idx = 1; - for(RDIM_SymbolChunkNode *n = src->first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1, dst_idx += 1) - { - RDIM_Symbol *src = &n->v[chunk_idx]; - RDI_ThreadVariable *dst = &thread_variables[dst_idx]; - dst->name_string_idx = rdim_bake_idx_from_string(strings, src->name); - dst->tls_off = (RDI_U32)src->offset; // TODO(rjf): @u64_to_u32 - dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); - if(src->is_extern) - { - dst->link_flags |= RDI_LinkFlag_External; - } - if(src->container_type != 0) - { - dst->link_flags |= RDI_LinkFlag_TypeScoped; - dst->container_idx = src->container_type ? (RDI_U32)rdim_idx_from_udt(src->container_type->udt) : 0; // TODO(rjf): @u64_to_u32 - } - else if(src->container_symbol != 0) - { - dst->link_flags |= RDI_LinkFlag_ProcScoped; - dst->container_idx = (RDI_U32)rdim_idx_from_symbol(src->container_symbol); // TODO(rjf): @u64_to_u32 - } - } - } - RDIM_ThreadVariableBakeResult result = {0}; - result.thread_variables = thread_variables; - result.thread_variables_count = src->total_count+1; - return result; -} - -RDI_PROC RDIM_ConstantsBakeResult -rdim_bake_constants(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_SymbolChunkList *src) -{ - RDI_Constant *constants = push_array(arena, RDI_Constant, src->total_count+1); - RDI_U32 *constant_values = push_array(arena, RDI_U32, src->total_count+2); - RDI_U8 *constant_value_data = push_array(arena, RDI_U8, src->total_value_data_size+1); - RDI_U32 dst_idx = 1; - RDI_U64 dst_constant_value_data_off = 1; - for(RDIM_SymbolChunkNode *n = src->first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1, dst_idx += 1) - { - RDIM_Symbol *src = &n->v[chunk_idx]; - RDI_Constant *dst = &constants[dst_idx]; - RDI_U32 *dst_value_idx = &constant_values[dst_idx]; - dst->name_string_idx = rdim_bake_idx_from_string(strings, src->name); - dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); // TODO(rjf): @u64_to_u32 - dst->constant_value_idx = dst_idx; - dst_value_idx[0] = dst_constant_value_data_off; - rdim_memcpy(constant_value_data + dst_constant_value_data_off, src->value_data.str, src->value_data.size); - dst_constant_value_data_off += src->value_data.size; - } - } - constant_values[dst_idx] = dst_constant_value_data_off; - RDIM_ConstantsBakeResult result = {0}; - result.constants = constants; - result.constants_count = src->total_count+1; - result.constant_values = constant_values; - result.constant_values_count = src->total_count+1; - result.constant_value_data = constant_value_data; - result.constant_value_data_size = dst_constant_value_data_off; - return result; -} - -RDI_PROC U64 -rdim_bake_location(RDIM_Arena *arena, RDIM_String8List *location_data_blobs, RDIM_Location *src_location) -{ - U64 location_data_off = location_data_blobs->total_size; - - // rjf: nil location - if(src_location == 0) - { - rdim_str8_list_push_align(arena, location_data_blobs, 8); - rdim_str8_list_push(arena, location_data_blobs, rdim_str8_lit("\0")); - } - - // rjf: valid location - else switch(src_location->kind) - { - // rjf: catchall unsupported case - default: - { - rdim_str8_list_push_align(arena, location_data_blobs, 8); - rdim_str8_list_push(arena, location_data_blobs, rdim_str8_lit("\0")); - }break; - - // rjf: bytecode streams - case RDI_LocationKind_AddrBytecodeStream: - case RDI_LocationKind_ValBytecodeStream: - { - rdim_str8_list_push(arena, location_data_blobs, rdim_str8_copy(arena, rdim_str8_struct(&src_location->kind))); - for(RDIM_EvalBytecodeOp *op_node = src_location->bytecode.first_op; - op_node != 0; - op_node = op_node->next) - { - RDI_U8 op_data[9]; - op_data[0] = op_node->op; - rdim_memcpy(op_data + 1, &op_node->p, op_node->p_size); - RDIM_String8 op_data_str = rdim_str8(op_data, 1 + op_node->p_size); - rdim_str8_list_push(arena, location_data_blobs, rdim_str8_copy(arena, op_data_str)); - } - { - RDI_U64 data = 0; - RDIM_String8 data_str = rdim_str8((RDI_U8 *)&data, 1); - rdim_str8_list_push(arena, location_data_blobs, rdim_str8_copy(arena, data_str)); - } - }break; - - // rjf: simple addr+off cases - case RDI_LocationKind_AddrRegPlusU16: - case RDI_LocationKind_AddrAddrRegPlusU16: - { - RDI_LocationRegPlusU16 loc = {0}; - loc.kind = src_location->kind; - loc.reg_code = src_location->reg_code; - loc.offset = src_location->offset; - rdim_str8_list_push(arena, location_data_blobs, rdim_str8_copy(arena, rdim_str8_struct(&loc))); - }break; - - // rjf: register cases - case RDI_LocationKind_ValReg: - { - RDI_LocationReg loc = {0}; - loc.kind = src_location->kind; - loc.reg_code = src_location->reg_code; - rdim_str8_list_push(arena, location_data_blobs, rdim_str8_copy(arena, rdim_str8_struct(&loc))); - }break; - } - - return location_data_off; -} - -RDI_PROC RDI_U32 -rdim_bake_locset(RDIM_Arena *arena, - RDIM_String8List *location_blocks, - RDIM_String8List *location_data_blobs, - RDIM_LocationSet locset) -{ - RDI_U32 locset_idx = 0; - if(locset.location_case_count > 0) - { - locset_idx = rdim_count_from_location_block_chunk_list(location_blocks); - - RDI_LocationBlock *dst_arr = rdim_location_block_chunk_list_push_array(arena, location_blocks, locset.location_case_count); - RDI_LocationBlock *dst = dst_arr; - for(RDIM_LocationCase *src = locset.first_location_case; src != 0; src = src->next, ++dst) - { - dst->scope_off_first = src->voff_range.min; - dst->scope_off_opl = src->voff_range.max; - dst->location_data_off = rdim_bake_location(arena, location_data_blobs, src->location); - } - } - return locset_idx; -} - -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_Procedure *procedures = push_array(arena, RDI_Procedure, src->total_count+1); - RDI_U32 dst_idx = 1; - for(RDIM_SymbolChunkNode *n = src->first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1, dst_idx += 1) - { - RDIM_Symbol *src = &n->v[chunk_idx]; - RDI_Procedure *dst = &procedures[dst_idx]; - - RDI_U32 frame_base_location_first = rdim_bake_locset(arena, location_blocks, location_data_blobs, src->frame_base); - RDI_U32 frame_base_location_opl = frame_base_location_first + src->frame_base.location_case_count; - - dst->name_string_idx = rdim_bake_idx_from_string(strings, src->name); - dst->link_name_string_idx = rdim_bake_idx_from_string(strings, src->link_name); - if(src->is_extern) - { - dst->link_flags |= RDI_LinkFlag_External; - } - if(src->container_type != 0) - { - dst->link_flags |= RDI_LinkFlag_TypeScoped; - dst->container_idx = src->container_type ? (RDI_U32)rdim_idx_from_udt(src->container_type->udt) : 0; // TODO(rjf): @u64_to_u32 - } - else if(src->container_symbol != 0) - { - dst->link_flags |= RDI_LinkFlag_ProcScoped; - dst->container_idx = (RDI_U32)rdim_idx_from_symbol(src->container_symbol); // TODO(rjf): @u64_to_u32 - } - dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); // TODO(rjf): @u64_to_u32 - dst->root_scope_idx = (RDI_U32)rdim_idx_from_scope(src->root_scope); // TODO(rjf): @u64_to_u32 - dst->frame_base_location_first = frame_base_location_first; - dst->frame_base_location_opl = frame_base_location_opl; - } - } - RDIM_ProcedureBakeResult result = {0}; - result.procedures = procedures; - result.procedures_count = src->total_count+1; - return result; -} - -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) -{ - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - - //////////////////////////// - //- rjf: build all scopes, scope voffs, locals, and location blocks - // - RDI_Scope *scopes = rdim_push_array(arena, RDI_Scope, src->total_count+1); - RDI_U64 *scope_voffs = rdim_push_array(arena, RDI_U64, src->scope_voff_count+1); - RDI_Local *locals = rdim_push_array(arena, RDI_Local, src->local_count+1); - - RDIM_ProfScope("build all scopes, scope voffs, locals, and location blocks") - { - RDI_U64 dst_scope_idx = 1; - RDI_U64 dst_scope_voff_idx = 1; - RDI_U64 dst_local_idx = 1; - for(RDIM_ScopeChunkNode *chunk_n = src->first; chunk_n != 0; chunk_n = chunk_n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < chunk_n->count; chunk_idx += 1, dst_scope_idx += 1) - { - RDIM_Scope *src_scope = &chunk_n->v[chunk_idx]; - RDI_Scope *dst_scope = &scopes[dst_scope_idx]; - - //- rjf: push scope's voffs - RDI_U64 voff_idx_first = dst_scope_voff_idx; - { - for(RDIM_Rng1U64Node *n = src_scope->voff_ranges.first; n != 0; n = n->next) - { - scope_voffs[dst_scope_voff_idx] = n->v.min; - dst_scope_voff_idx += 1; - scope_voffs[dst_scope_voff_idx] = n->v.max; - dst_scope_voff_idx += 1; - } - } - RDI_U64 voff_idx_opl = dst_scope_voff_idx; - - //- rjf: push locals - RDI_U64 local_idx_first = dst_local_idx; - for(RDIM_Local *src_local = src_scope->first_local; - src_local != 0; - src_local = src_local->next, dst_local_idx += 1) - { - // bake location sets - RDI_U32 location_block_idx_first = rdim_bake_locset(arena, location_blocks, location_data_blobs, src_local->locset); - RDI_U32 location_block_idx_opl = location_block_idx_first + src_local->locset.location_case_count; - - //- rjf: fill local - RDI_Local *dst_local = &locals[dst_local_idx]; - dst_local->kind = src_local->kind; - dst_local->name_string_idx = rdim_bake_idx_from_string(strings, src_local->name); - dst_local->type_idx = (RDI_U32)rdim_idx_from_type(src_local->type); // TODO(rjf): @u64_to_u32 - dst_local->location_first = location_block_idx_first; - dst_local->location_opl = location_block_idx_opl; - } - RDI_U64 local_idx_opl = dst_local_idx; - - //- rjf: fill scope - dst_scope->proc_idx = (RDI_U32)rdim_idx_from_symbol(src_scope->symbol); // TODO(rjf): @u64_to_u32 - dst_scope->parent_scope_idx = (RDI_U32)rdim_idx_from_scope(src_scope->parent_scope); // TODO(rjf): @u64_to_u32 - dst_scope->first_child_scope_idx = (RDI_U32)rdim_idx_from_scope(src_scope->first_child); // TODO(rjf): @u64_to_u32 - dst_scope->next_sibling_scope_idx = (RDI_U32)rdim_idx_from_scope(src_scope->next_sibling); // TODO(rjf): @u64_to_u32 - dst_scope->voff_range_first = (RDI_U32)voff_idx_first; // TODO(rjf): @u64_to_u32 - dst_scope->voff_range_opl = (RDI_U32)voff_idx_opl; // TODO(rjf): @u64_to_u32 - dst_scope->local_first = (RDI_U32)local_idx_first; // TODO(rjf): @u64_to_u32 - dst_scope->local_count = (RDI_U32)(local_idx_opl - local_idx_first); // TODO(rjf): @u64_to_u32 - dst_scope->inline_site_idx = (RDI_U32)rdim_idx_from_inline_site(src_scope->inline_site); // TODO(rjf): @u64_to_u32 - } - } - } - - //////////////////////////// - //- rjf: fill result - // - RDIM_ScopeBakeResult result = {0}; - result.scopes = scopes; - result.scopes_count = src->total_count+1; - result.scope_voffs = scope_voffs; - result.scope_voffs_count = src->scope_voff_count+1; - result.locals = locals; - result.locals_count = src->local_count+1; - rdim_scratch_end(scratch); - return result; -} - -RDI_PROC RDIM_ScopeVMapBakeResult -rdim_bake_scope_vmap(RDIM_Arena *arena, RDIM_ScopeChunkList *src) -{ - RDIM_BakeVMap scope_vmap = {0}; - { - RDIM_Temp scratch = rdim_scratch_begin(&arena, 1); - - // rjf: allocate keys/markers - RDI_U64 marker_count = src->scope_voff_count; - RDIM_SortKey *keys = rdim_push_array_no_zero(scratch.arena, RDIM_SortKey, marker_count); - RDIM_VMapMarker *markers = rdim_push_array_no_zero(scratch.arena, RDIM_VMapMarker, marker_count); - - // rjf: fill - { - RDIM_SortKey *key_ptr = keys; - RDIM_VMapMarker *marker_ptr = markers; - for(RDIM_ScopeChunkNode *chunk_n = src->first; chunk_n != 0; chunk_n = chunk_n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < chunk_n->count; chunk_idx += 1) - { - RDIM_Scope *src_scope = &chunk_n->v[chunk_idx]; - RDI_U32 scope_idx = (RDI_U32)rdim_idx_from_scope(src_scope); // TODO(rjf): @u64_to_u32 - for(RDIM_Rng1U64Node *n = src_scope->voff_ranges.first; n != 0; n = n->next) - { - key_ptr->key = n->v.min; - key_ptr->val = marker_ptr; - marker_ptr->idx = scope_idx; - marker_ptr->begin_range = 1; - key_ptr += 1; - marker_ptr += 1; - - key_ptr->key = n->v.max; - key_ptr->val = marker_ptr; - marker_ptr->idx = scope_idx; - marker_ptr->begin_range = 0; - key_ptr += 1; - marker_ptr += 1; - } - } - } - } - - // rjf: produce vmap - scope_vmap = rdim_bake_vmap_from_markers(arena, markers, keys, marker_count); - rdim_scratch_end(scratch); - } - RDIM_ScopeVMapBakeResult result = {scope_vmap}; - return result; -} - -RDI_PROC RDIM_InlineSiteBakeResult -rdim_bake_inline_sites(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_InlineSiteChunkList *src) -{ - RDIM_InlineSiteBakeResult result = {0}; - { - result.inline_sites_count = src->total_count+1; - result.inline_sites = rdim_push_array(arena, RDI_InlineSite, result.inline_sites_count+1); - RDI_U64 dst_idx = 1; - for(RDIM_InlineSiteChunkNode *n = src->first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1, dst_idx += 1) - { - RDI_InlineSite *dst = &result.inline_sites[dst_idx]; - RDIM_InlineSite *src = &n->v[chunk_idx]; - dst->name_string_idx = rdim_bake_idx_from_string(strings, src->name); - dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); // TODO(rjf): @u64_to_u32 - dst->owner_type_idx = (RDI_U32)rdim_idx_from_type(src->owner); // TODO(rjf): @u64_to_u32 - dst->line_table_idx = (RDI_U32)rdim_idx_from_line_table(src->line_table); // TODO(rjf): @u64_to_u32 - } - } - } - return result; -} - -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_NameMap *dst_maps = rdim_push_array(arena, RDI_NameMap, RDI_NameMapKind_COUNT); - { - RDI_U64 dst_map_bucket_idx = 0; - RDI_U64 dst_map_node_idx = 0; - for(RDI_NameMapKind k = (RDI_NameMapKind)(RDI_NameMapKind_NULL+1); - k < RDI_NameMapKind_COUNT; - k = (RDI_NameMapKind)(k+1)) - { - RDI_NameMap *dst_map = &dst_maps[k]; - RDIM_BakeNameMap *src_map = name_maps[k]; - dst_map->bucket_base_idx = (RDI_U32)dst_map_bucket_idx; // TODO(rjf): @u64_to_u32 - dst_map->node_base_idx = (RDI_U32)dst_map_node_idx; // TODO(rjf): @u64_to_u32 - dst_map->bucket_count = (RDI_U32)src_map->name_count; // TODO(rjf): @u64_to_u32 - dst_map->node_count = (RDI_U32)src_map->name_count; // TODO(rjf): @u64_to_u32 - dst_map_bucket_idx += dst_map->bucket_count; - dst_map_node_idx += dst_map->node_count; - } - } - RDIM_TopLevelNameMapBakeResult result = {0}; - result.name_maps = dst_maps; - result.name_maps_count = RDI_NameMapKind_COUNT; - return result; -} - -RDI_PROC RDIM_FilePathBakeResult -rdim_bake_file_paths(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings, RDIM_BakePathTree *path_tree) -{ - RDI_U32 dst_nodes_count = path_tree->count; - RDI_FilePathNode *dst_nodes = rdim_push_array(arena, RDI_FilePathNode, dst_nodes_count); - { - RDI_U32 dst_node_idx = 0; - for(RDIM_BakePathNode *src_node = path_tree->first; - src_node != 0; - src_node = src_node->next_order, dst_node_idx += 1) - { - RDI_FilePathNode *dst_node = &dst_nodes[dst_node_idx]; - dst_node->name_string_idx = rdim_bake_idx_from_string(strings, src_node->name); - dst_node->source_file_idx = rdim_idx_from_src_file(src_node->src_file); - if(src_node->parent != 0) - { - dst_node->parent_path_node = src_node->parent->idx; - } - if(src_node->first_child != 0) - { - dst_node->first_child = src_node->first_child->idx; - } - if(src_node->next_sibling != 0) - { - dst_node->next_sibling = src_node->next_sibling->idx; - } - } - } - RDIM_FilePathBakeResult result = {0}; - result.nodes = dst_nodes; - result.nodes_count = dst_nodes_count; - return result; -} - -RDI_PROC RDIM_StringBakeResult -rdim_bake_strings(RDIM_Arena *arena, RDIM_BakeStringMapTight *strings) -{ - RDI_U32 *str_offs = rdim_push_array_no_zero(arena, RDI_U32, strings->total_count + 1); - RDI_U32 off_cursor = 0; - { - RDI_U32 *off_ptr = str_offs; - *off_ptr = 0; - off_ptr += 1; - for(RDI_U64 slot_idx = 0; slot_idx < strings->slots_count; slot_idx += 1) - { - for(RDIM_BakeStringChunkNode *n = strings->slots[slot_idx].first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) - { - RDIM_BakeString *bake_string = &n->v[chunk_idx]; - *off_ptr = off_cursor; - off_cursor += bake_string->string.size; - off_ptr += 1; - } - } - } - } - RDI_U8 *buf = rdim_push_array(arena, RDI_U8, off_cursor); - { - RDI_U8 *ptr = buf; - for(RDI_U64 slot_idx = 0; slot_idx < strings->slots_count; slot_idx += 1) - { - for(RDIM_BakeStringChunkNode *n = strings->slots[slot_idx].first; n != 0; n = n->next) - { - for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) - { - RDIM_BakeString *bake_string = &n->v[chunk_idx]; - rdim_memcpy(ptr, bake_string->string.str, bake_string->string.size); - ptr += bake_string->string.size; - } - } - } - } - RDIM_StringBakeResult result = {0}; - result.string_offs = str_offs; - result.string_offs_count = strings->total_count+1; - result.string_data = buf; - result.string_data_size = off_cursor; - return result; -} - -RDI_PROC RDIM_IndexRunBakeResult -rdim_bake_index_runs(RDIM_Arena *arena, RDIM_BakeIdxRunMap *idx_runs) -{ - RDI_U32 *idx_data = rdim_push_array_no_zero(arena, RDI_U32, idx_runs->idx_count); - { - RDI_U32 *out_ptr = idx_data; - RDI_U32 *opl = out_ptr + idx_runs->idx_count; - for(RDIM_BakeIdxRunNode *node = idx_runs->order_first; - node != 0 && out_ptr < opl; - node = node->order_next) - { - rdim_memcpy(out_ptr, node->idx_run, sizeof(*node->idx_run)*node->count); - out_ptr += node->count; - } - } - RDIM_IndexRunBakeResult result = {0}; - result.idx_runs = idx_data; - result.idx_count = idx_runs->idx_count; - return result; -} - //////////////////////////////// //~ rjf: [Serializing] Bake Results -> String Blobs @@ -4015,7 +1991,7 @@ rdim_serialized_section_bundle_from_bake_results(RDIM_BakeResults *results) { RDIM_SerializedSectionBundle bundle; rdim_memzero_struct(&bundle); - bundle.sections[RDI_SectionKind_TopLevelInfo] = rdim_serialized_section_make_unpacked_struct(results->top_level_info.top_level_info); + bundle.sections[RDI_SectionKind_TopLevelInfo] = rdim_serialized_section_make_unpacked_struct(&results->top_level_info.top_level_info); bundle.sections[RDI_SectionKind_StringData] = rdim_serialized_section_make_unpacked_array(results->strings.string_data, results->strings.string_data_size); bundle.sections[RDI_SectionKind_StringTable] = rdim_serialized_section_make_unpacked_array(results->strings.string_offs, results->strings.string_offs_count); bundle.sections[RDI_SectionKind_IndexRuns] = rdim_serialized_section_make_unpacked_array(results->idx_runs.idx_runs, results->idx_runs.idx_count); @@ -4031,23 +2007,28 @@ rdim_serialized_section_bundle_from_bake_results(RDIM_BakeResults *results) bundle.sections[RDI_SectionKind_SourceLineMapRanges] = rdim_serialized_section_make_unpacked_array(results->src_files.source_line_map_rngs, results->src_files.source_line_map_rngs_count); bundle.sections[RDI_SectionKind_SourceLineMapVOffs] = rdim_serialized_section_make_unpacked_array(results->src_files.source_line_map_voffs, results->src_files.source_line_map_voffs_count); bundle.sections[RDI_SectionKind_Units] = rdim_serialized_section_make_unpacked_array(results->units.units, results->units.units_count); - bundle.sections[RDI_SectionKind_UnitVMap] = rdim_serialized_section_make_unpacked_array(results->unit_vmap.vmap.vmap, results->unit_vmap.vmap.count+1); + bundle.sections[RDI_SectionKind_UnitVMap] = rdim_serialized_section_make_unpacked_array(results->unit_vmap.vmap.vmap, results->unit_vmap.vmap.count); bundle.sections[RDI_SectionKind_TypeNodes] = rdim_serialized_section_make_unpacked_array(results->type_nodes.type_nodes, results->type_nodes.type_nodes_count); bundle.sections[RDI_SectionKind_UDTs] = rdim_serialized_section_make_unpacked_array(results->udts.udts, results->udts.udts_count); bundle.sections[RDI_SectionKind_Members] = rdim_serialized_section_make_unpacked_array(results->udts.members, results->udts.members_count); bundle.sections[RDI_SectionKind_EnumMembers] = rdim_serialized_section_make_unpacked_array(results->udts.enum_members, results->udts.enum_members_count); bundle.sections[RDI_SectionKind_GlobalVariables] = rdim_serialized_section_make_unpacked_array(results->global_variables.global_variables, results->global_variables.global_variables_count); - bundle.sections[RDI_SectionKind_GlobalVMap] = rdim_serialized_section_make_unpacked_array(results->global_vmap.vmap.vmap, results->global_vmap.vmap.count+1); + bundle.sections[RDI_SectionKind_GlobalVMap] = rdim_serialized_section_make_unpacked_array(results->global_vmap.vmap.vmap, results->global_vmap.vmap.count); bundle.sections[RDI_SectionKind_ThreadVariables] = rdim_serialized_section_make_unpacked_array(results->thread_variables.thread_variables, results->thread_variables.thread_variables_count); bundle.sections[RDI_SectionKind_Constants] = rdim_serialized_section_make_unpacked_array(results->constants.constants, results->constants.constants_count); bundle.sections[RDI_SectionKind_Procedures] = rdim_serialized_section_make_unpacked_array(results->procedures.procedures, results->procedures.procedures_count); bundle.sections[RDI_SectionKind_Scopes] = rdim_serialized_section_make_unpacked_array(results->scopes.scopes, results->scopes.scopes_count); bundle.sections[RDI_SectionKind_ScopeVOffData] = rdim_serialized_section_make_unpacked_array(results->scopes.scope_voffs, results->scopes.scope_voffs_count); - bundle.sections[RDI_SectionKind_ScopeVMap] = rdim_serialized_section_make_unpacked_array(results->scope_vmap.vmap.vmap, results->scope_vmap.vmap.count+1); + bundle.sections[RDI_SectionKind_ScopeVMap] = rdim_serialized_section_make_unpacked_array(results->scope_vmap.vmap.vmap, results->scope_vmap.vmap.count); bundle.sections[RDI_SectionKind_InlineSites] = rdim_serialized_section_make_unpacked_array(results->inline_sites.inline_sites, results->inline_sites.inline_sites_count); bundle.sections[RDI_SectionKind_Locals] = rdim_serialized_section_make_unpacked_array(results->scopes.locals, results->scopes.locals_count); bundle.sections[RDI_SectionKind_LocationBlocks] = rdim_serialized_section_make_unpacked_array(results->location_blocks.str, results->location_blocks.size); bundle.sections[RDI_SectionKind_LocationData] = rdim_serialized_section_make_unpacked_array(results->location_data.str, results->location_data.size); + if(results->location_blocks.size == 0) + { + bundle.sections[RDI_SectionKind_LocationBlocks] = rdim_serialized_section_make_unpacked_array(results->location_blocks2.location_blocks, results->location_blocks2.location_blocks_count); + bundle.sections[RDI_SectionKind_LocationData] = rdim_serialized_section_make_unpacked_array(results->locations.location_data, results->locations.location_data_size); + } bundle.sections[RDI_SectionKind_ConstantValueData] = rdim_serialized_section_make_unpacked_array(results->constants.constant_value_data, results->constants.constant_value_data_size); bundle.sections[RDI_SectionKind_ConstantValueTable] = rdim_serialized_section_make_unpacked_array(results->constants.constant_values, results->constants.constant_values_count); bundle.sections[RDI_SectionKind_NameMaps] = rdim_serialized_section_make_unpacked_array(results->top_level_name_maps.name_maps, results->top_level_name_maps.name_maps_count); diff --git a/src/lib_rdi_make/rdi_make.h b/src/lib_rdi_make/rdi_make.h index 7dd6a216..5e872a64 100644 --- a/src/lib_rdi_make/rdi_make.h +++ b/src/lib_rdi_make/rdi_make.h @@ -127,7 +127,7 @@ enum // #define rdim_arena_alloc Arena*> // #define rdim_arena_release void> // #define rdim_arena_pos U64> -// #define rdim_arena_push void*> +// #define rdim_arena_push void*> // #define rdim_arena_pop_to 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 diff --git a/src/linker/base_ext/base_arena.c b/src/linker/base_ext/base_arena.c index 669cf47f..166d3ea6 100644 --- a/src/linker/base_ext/base_arena.c +++ b/src/linker/base_ext/base_arena.c @@ -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) { diff --git a/src/linker/base_ext/base_arena.h b/src/linker/base_ext/base_arena.h index cb7f00ee..4cb25d21 100644 --- a/src/linker/base_ext/base_arena.h +++ b/src/linker/base_ext/base_arena.h @@ -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); diff --git a/src/linker/base_ext/base_arrays.c b/src/linker/base_ext/base_arrays.c index 040f220d..fa50c36f 100644 --- a/src/linker/base_ext/base_arrays.c +++ b/src/linker/base_ext/base_arrays.c @@ -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 diff --git a/src/linker/base_ext/base_arrays.h b/src/linker/base_ext/base_arrays.h index 1c6f6f4d..45ad68e8 100644 --- a/src/linker/base_ext/base_arrays.h +++ b/src/linker/base_ext/base_arrays.h @@ -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 diff --git a/src/linker/base_ext/base_bit_array.c b/src/linker/base_ext/base_bit_array.c index 2ab94b77..a4f39b4a 100644 --- a/src/linker/base_ext/base_bit_array.c +++ b/src/linker/base_ext/base_bit_array.c @@ -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 diff --git a/src/linker/base_ext/base_bit_array.h b/src/linker/base_ext/base_bit_array.h index 76e19f87..995facd7 100644 --- a/src/linker/base_ext/base_bit_array.h +++ b/src/linker/base_ext/base_bit_array.h @@ -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); diff --git a/src/linker/base_ext/base_blake3.c b/src/linker/base_ext/base_blake3.c index ca29e21a..59f063c4 100644 --- a/src/linker/base_ext/base_blake3.c +++ b/src/linker/base_ext/base_blake3.c @@ -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 diff --git a/src/linker/base_ext/base_blake3.h b/src/linker/base_ext/base_blake3.h index 72e8986f..4c426ea2 100644 --- a/src/linker/base_ext/base_blake3.h +++ b/src/linker/base_ext/base_blake3.h @@ -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" diff --git a/src/linker/base_ext/base_blake3_asm.c b/src/linker/base_ext/base_blake3_asm.c deleted file mode 100644 index 1b91df31..00000000 --- a/src/linker/base_ext/base_blake3_asm.c +++ /dev/null @@ -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") diff --git a/src/linker/base_ext/base_blake3_asm.h b/src/linker/base_ext/base_blake3_asm.h deleted file mode 100644 index e39c3b41..00000000 --- a/src/linker/base_ext/base_blake3_asm.h +++ /dev/null @@ -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 diff --git a/src/linker/base_ext/base_core.c b/src/linker/base_ext/base_core.c index b1873b10..890b0831 100644 --- a/src/linker/base_ext/base_core.c +++ b/src/linker/base_ext/base_core.c @@ -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 diff --git a/src/linker/base_ext/base_core.h b/src/linker/base_ext/base_core.h index deb18e8c..c7b07e3d 100644 --- a/src/linker/base_ext/base_core.h +++ b/src/linker/base_ext/base_core.h @@ -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; diff --git a/src/linker/base_ext/base_crc32.c b/src/linker/base_ext/base_crc32.c index 28b90da6..30ec7c2a 100644 --- a/src/linker/base_ext/base_crc32.c +++ b/src/linker/base_ext/base_crc32.c @@ -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 diff --git a/src/linker/base_ext/base_crc32.h b/src/linker/base_ext/base_crc32.h index e6d55bae..7a062237 100644 --- a/src/linker/base_ext/base_crc32.h +++ b/src/linker/base_ext/base_crc32.h @@ -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 diff --git a/src/linker/base_ext/base_inc.c b/src/linker/base_ext/base_inc.c index c3886302..8a99d53d 100644 --- a/src/linker/base_ext/base_inc.c +++ b/src/linker/base_ext/base_inc.c @@ -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" diff --git a/src/linker/base_ext/base_inc.h b/src/linker/base_ext/base_inc.h index 67081bf6..2074a32d 100644 --- a/src/linker/base_ext/base_inc.h +++ b/src/linker/base_ext/base_inc.h @@ -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 diff --git a/src/linker/base_ext/base_md5.c b/src/linker/base_ext/base_md5.c index 38ffac51..5e77bd7b 100644 --- a/src/linker/base_ext/base_md5.c +++ b/src/linker/base_ext/base_md5.c @@ -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 diff --git a/src/linker/base_ext/base_md5.h b/src/linker/base_ext/base_md5.h index b435fe58..288cddc4 100644 --- a/src/linker/base_ext/base_md5.h +++ b/src/linker/base_ext/base_md5.h @@ -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 diff --git a/src/linker/base_ext/base_strings.c b/src/linker/base_ext/base_strings.c index fe465f5e..232a4f99 100644 --- a/src/linker/base_ext/base_strings.c +++ b/src/linker/base_ext/base_strings.c @@ -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; +} diff --git a/src/linker/base_ext/base_strings.h b/src/linker/base_ext/base_strings.h index b469cccf..c982e700 100644 --- a/src/linker/base_ext/base_strings.h +++ b/src/linker/base_ext/base_strings.h @@ -1,17 +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 str8_list_push_struct(a,l,d) str8_list_push_raw(a, l, d, sizeof(*d)) -internal String8Node * str8_list_push_raw(Arena *arena, String8List *list, void *data_ptr, U64 data_size); -internal U64 str8_list_push_pad(Arena *arena, String8List *list, U64 offset, U64 align); -internal U64 str8_list_push_pad_front(Arena *arena, String8List *list, U64 offset, U64 align); -internal String8List str8_list_arr_concat(String8List *v, U64 count); -internal String8Node * str8_list_push_many(Arena *arena, String8List *list, U64 count); - // TODO: remove internal String8Node * str8_list_pop_front(String8List *list); -internal U64 hash_from_str8(String8 string); +internal B32 str8_starts_with(String8 string, String8 expected_prefix); +internal U64 str8_array_bsearch(String8Array arr, String8 value); diff --git a/src/linker/codeview_ext/codeview.c b/src/linker/codeview_ext/codeview.c index af4aeba7..4dceeaa2 100644 --- a/src/linker/codeview_ext/codeview.c +++ b/src/linker/codeview_ext/codeview.c @@ -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/) //////////////////////////////// @@ -587,7 +587,7 @@ cv_file_chksms_from_debug_s(CV_DebugS debug_s) internal U64 cv_string_hash_table_hash(String8 string) { - return hash_from_str8(string); + return u64_hash_from_str8(string); } internal int @@ -1175,6 +1175,7 @@ cv_debug_t_array_count_leaves(U64 count, CV_DebugT *arr) return total_leaf_count; } +internal THREAD_POOL_TASK_FUNC(cv_str8_list_from_debug_t_task) { CV_Str8ListFromDebugT *task = raw_task; @@ -1605,6 +1606,12 @@ cv_c13_collect_source_file_names(Arena *arena, CV_ChecksumList checksum_list, St // $$Lines +internal void +cv_c13_lines_header_list_concat_in_place(CV_C13LinesHeaderList *list, CV_C13LinesHeaderList *to_concat) +{ + SLLConcatInPlace(list, to_concat); +} + internal CV_C13LinesHeaderList cv_c13_lines_from_sub_sections(Arena *arena, String8 c13_data, Rng1U64 ss_range) { @@ -1785,7 +1792,7 @@ cv_c13_patch_checksum_offsets_in_frame_data_list(String8List frame_data, U32 che //////////////////////////////// // $$Lines Accel -int +internal int cv_c13_voff_map_compar(const void *raw_a, const void *raw_b) { CV_Line *a = (CV_Line*)raw_a; @@ -1839,7 +1846,64 @@ cv_c13_make_lines_accel(Arena *arena, U64 lines_count, CV_LineArray *lines) return accel; } -#if 0 +internal CV_LinesAccel * +cv_lines_accel_from_debug_s(Arena *arena, CV_DebugS debug_s) +{ + // parse $$LINES + U64 c13_lines_count = 0; + CV_LineArray *c13_lines = 0; + { + String8List raw_lines_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_Lines); + + for (String8Node *raw_lines_node = raw_lines_list.first; raw_lines_node != 0; raw_lines_node = raw_lines_node->next) { + Temp temp = temp_begin(arena); + CV_C13LinesHeaderList parsed_list = cv_c13_lines_from_sub_sections(temp.arena, raw_lines_node->string, rng_1u64(0, raw_lines_node->string.size)); + c13_lines_count += parsed_list.count; + temp_end(temp); + } + + c13_lines = push_array_no_zero(arena, CV_LineArray, c13_lines_count); + + U64 c13_lines_idx = 0; + for (String8Node *raw_lines_node = raw_lines_list.first; raw_lines_node != 0; raw_lines_node = raw_lines_node->next) { + String8 raw_lines = raw_lines_node->string; + CV_C13LinesHeaderList parsed_list = cv_c13_lines_from_sub_sections(arena, raw_lines, rng_1u64(0, raw_lines.size)); + + for(CV_C13LinesHeaderNode *header_node = parsed_list.first; header_node != 0; header_node = header_node->next) { + c13_lines[c13_lines_idx++] = cv_c13_line_array_from_data(arena, raw_lines, 0, header_node->v); + } + } + } + + return cv_c13_make_lines_accel(arena, c13_lines_count, c13_lines); +} + +internal U64 +cv_nearest_line(CV_Line *arr, U64 count, U64 value) +{ + if(count > 1 && arr[0].voff <= value && value < arr[count-1].voff) + { + U64 l = 0; + U64 r = count - 1; + for (; l <= r; ) { + U64 m = l + (r - l) / 2; + if (arr[m].voff == value) { + return m; + } else if (arr[m].voff < value) { + l = m + 1; + } else { + r = m - 1; + } + } + return l; + } + else if (count == 1 && arr[0].voff == value) + { + return 0; + } + return max_U64; +} + internal CV_Line * cv_line_from_voff(CV_LinesAccel *accel, U64 voff, U64 *out_line_count) { @@ -1848,7 +1912,7 @@ cv_line_from_voff(CV_LinesAccel *accel, U64 voff, U64 *out_line_count) U64 voff_line_count = 0; CV_Line *lines = 0; - U64 map_idx = bsearch_nearest_u64(accel->map, accel->map_count, voff, sizeof(accel->map[0]), OffsetOf(CV_Line, voff)); + U64 map_idx = cv_nearest_line(accel->map, accel->map_count, voff); if(map_idx < accel->map_count) { U64 near_voff = accel->map[map_idx].voff; @@ -1873,7 +1937,6 @@ cv_line_from_voff(CV_LinesAccel *accel, U64 voff, U64 *out_line_count) ProfEnd(); return lines; } -#endif //////////////////////////////// // $$InlineeLines Accel diff --git a/src/linker/codeview_ext/codeview.h b/src/linker/codeview_ext/codeview.h index 3b0315ad..421af3c2 100644 --- a/src/linker/codeview_ext/codeview.h +++ b/src/linker/codeview_ext/codeview.h @@ -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 @@ -455,7 +455,7 @@ internal String8List cv_c13_collect_source_file_names(Arena *arena, CV_ChecksumL // $$Lines internal CV_C13LinesHeaderList cv_c13_lines_from_sub_sections(Arena *arena, String8 c13_data, Rng1U64 ss_range); -internal CV_LineArray cv_c13_line_array_from_data(Arena *arena, String8 c13_data, U64 sec_base, CV_C13LinesHeader parsed_lines); +internal CV_LineArray cv_c13_line_array_from_data(Arena *arena, String8 c13_data, U64 sec_base, CV_C13LinesHeader parsed_lines); // $$InlineeLines internal CV_C13InlineeLinesParsedList cv_c13_inlinee_lines_from_sub_sections(Arena *arena, String8List raw_inlinee_lines); diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index e6adec79..22276591 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -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 void @@ -24,17 +24,10 @@ bucket_list_pop(BucketList *list) return result; } -//////////////////////////////// - -#define XXH_STATIC_LINKING_ONLY -#include "third_party/xxHash/xxhash.c" -#include "third_party/xxHash/xxhash.h" - internal U64 hash_table_hasher(String8 string) { - XXH64_hash_t hash64 = XXH3_64bits(string.str, string.size); - return hash64; + return u64_hash_from_str8(string); } internal HashTable * @@ -211,13 +204,6 @@ hash_table_search_u64(HashTable *ht, U64 key_u64) return 0; } -internal void * -hash_table_search_u64_raw(HashTable *ht, U64 key_u64) -{ - KeyValuePair *kv = hash_table_search_u64(ht, key_u64); - return kv ? kv->value_raw : 0; -} - internal KeyValuePair * hash_table_search_path(HashTable *ht, String8 path) { @@ -230,11 +216,18 @@ hash_table_search_path(HashTable *ht, String8 path) return result; } -internal void * -hash_table_search_path_raw(HashTable *ht, String8 path) +internal KeyValuePair * +hash_table_search_raw(HashTable *ht, void *key) { - KeyValuePair *kv = hash_table_search_path(ht, path); - return kv ? kv->value_raw : 0; + U64 hash = hash_table_hasher(str8_struct(&key)); + U64 ibucket = hash % ht->cap; + BucketList *bucket = ht->buckets + ibucket; + for (BucketNode *n = bucket->first; n != 0; n = n->next) { + if (n->v.key_raw == key) { + return &n->v; + } + } + return 0; } internal B32 @@ -250,6 +243,20 @@ hash_table_search_path_u64(HashTable *ht, String8 key, U64 *value_out) return 0; } +internal BucketNode * +hash_table_push_u32_u32(Arena *arena, HashTable *ht, U32 key, U32 value) +{ + U64 hash = hash_table_hasher(str8_struct(&key)); + return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_u32 = key, .value_u32 = value }); +} + +internal BucketNode * +hash_table_push_raw_raw(Arena *arena, HashTable *ht, void *key, void *value) +{ + U64 hash = hash_table_hasher(str8_struct(&key)); + return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_raw = key, .value_raw = value }); +} + internal B32 hash_table_search_string_u64(HashTable *ht, String8 key, U64 *value_out) { @@ -263,19 +270,6 @@ hash_table_search_string_u64(HashTable *ht, String8 key, U64 *value_out) return 0; } -internal B32 -hash_table_search_string_raw(HashTable *ht, String8 key, void *value_out) -{ - KeyValuePair *result = hash_table_search_string(ht, key); - if (result) { - if (value_out) { - (*(void **)value_out) = result->value_raw; - } - return 1; - } - return 0; -} - internal B32 hash_table_search_string_string(HashTable *ht, String8 key, String8 *value_out) { @@ -289,7 +283,49 @@ hash_table_search_string_string(HashTable *ht, String8 key, String8 *value_out) return 0; } -//////////////////////////////// +internal B32 +hash_table_search_u32_u32(HashTable *ht, U32 key, U32 *value_out) +{ + KeyValuePair *result = hash_table_search_u32(ht, key); + if (result) { + if (value_out) { + *value_out = result->value_u32; + } + return 1; + } + return 0; +} + +internal void * +hash_table_search_string_raw(HashTable *ht, String8 key) +{ + KeyValuePair *result = hash_table_search_string(ht, key); + if (result) { + return result->value_raw; + } + return 0; +} + +internal void * +hash_table_search_u64_raw(HashTable *ht, U64 key_u64) +{ + KeyValuePair *kv = hash_table_search_u64(ht, key_u64); + return kv ? kv->value_raw : 0; +} + +internal void * +hash_table_search_path_raw(HashTable *ht, String8 path) +{ + KeyValuePair *kv = hash_table_search_path(ht, path); + return kv ? kv->value_raw : 0; +} + +internal void * +hash_table_search_raw_raw(HashTable *ht, void *key) +{ + KeyValuePair *kv = hash_table_search_raw(ht, key); + return kv ? kv->value_raw : 0; +} internal int key_value_pair_is_before_u32(void *a, void *b) @@ -361,6 +397,19 @@ key_value_pairs_from_hash_table(Arena *arena, HashTable *ht) return pairs; } +internal void * +keys_from_hash_table_raw(Arena *arena, HashTable *ht) +{ + void **result = push_array(arena, void *, ht->count); + for (U64 bucket_idx = 0, cursor = 0; bucket_idx < ht->cap; ++bucket_idx) { + for (BucketNode *n = ht->buckets[bucket_idx].first; n != 0; n = n->next) { + Assert(cursor < ht->count); + result[cursor++] = n->v.key_raw; + } + } + return result; +} + internal void * values_from_hash_table_raw(Arena *arena, HashTable *ht) { @@ -373,6 +422,7 @@ values_from_hash_table_raw(Arena *arena, HashTable *ht) } return result; } + #include "third_party/radsort/radsort.h" internal void diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index 2bda690a..67e9e3dd 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -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 @@ -39,22 +39,20 @@ typedef struct HashTable BucketList free_buckets; } HashTable; -//////////////////////////////// - -//- bucket list helpers +// --- Bucket List ------------------------------------------------------------- internal void bucket_list_concat_in_place(BucketList *list, BucketList *to_concat); -internal BucketNode * bucket_list_pop(BucketList *list); +internal BucketNode * bucket_list_pop (BucketList *list); -//- main +// --- Hash Table -------------------------------------------------------------- -internal U64 hash_table_hasher(String8 string); -internal HashTable * hash_table_init(Arena *arena, U64 cap); +internal U64 hash_table_hasher(String8 string); + +internal HashTable * hash_table_init (Arena *arena, U64 cap); internal void hash_table_purge(HashTable *ht); -//- push +internal BucketNode * hash_table_push(Arena *arena, HashTable *ht, U64 hash, KeyValuePair kv); -internal BucketNode * hash_table_push (Arena *arena, HashTable *ht, U64 hash, KeyValuePair v); internal BucketNode * hash_table_push_u32_string (Arena *arena, HashTable *ht, U32 key, String8 value); internal BucketNode * hash_table_push_u64_string (Arena *arena, HashTable *ht, U64 key, String8 value); internal BucketNode * hash_table_push_string_string(Arena *arena, HashTable *ht, String8 key, String8 value); @@ -64,34 +62,39 @@ internal BucketNode * hash_table_push_u64_raw (Arena *arena, HashTable *ht, internal BucketNode * hash_table_push_path_raw (Arena *arena, HashTable *ht, String8 path, void *value); internal BucketNode * hash_table_push_path_u64 (Arena *arena, HashTable *ht, String8 path, U64 value); internal BucketNode * hash_table_push_u64_u64 (Arena *arena, HashTable *ht, U64 key, U64 value); +internal BucketNode * hash_table_push_u32_u32 (Arena *arena, HashTable *ht, U32 key, U32 value); -//- search +internal KeyValuePair * hash_table_search_string (HashTable *ht, String8 key); +internal KeyValuePair * hash_table_search_u32 (HashTable *ht, U32 key); +internal KeyValuePair * hash_table_search_u64 (HashTable *ht, U64 key); +internal KeyValuePair * hash_table_search_path (HashTable *ht, String8 key); +internal KeyValuePair * hash_table_search_raw (HashTable *ht, void *key); -internal KeyValuePair * hash_table_search_string (HashTable *ht, String8 string); -internal KeyValuePair * hash_table_search_u32 (HashTable *ht, U32 key ); -internal KeyValuePair * hash_table_search_u64 (HashTable *ht, U64 key ); -internal KeyValuePair * hash_table_search_path (HashTable *ht, String8 path ); -internal void * hash_table_search_path_raw(HashTable *ht, String8 path ); - -internal B32 hash_table_search_path_u64(HashTable *ht, String8 key, U64 *value_out); -internal B32 hash_table_search_string_u64(HashTable *ht, String8 key, U64 *value_out); -internal B32 hash_table_search_string_raw(HashTable *ht, String8 key, void *value_out); +internal B32 hash_table_search_path_u64 (HashTable *ht, String8 key, U64 *value_out); +internal B32 hash_table_search_string_u64 (HashTable *ht, String8 key, U64 *value_out); internal B32 hash_table_search_string_string(HashTable *ht, String8 key, String8 *value_out); +internal B32 hash_table_search_u32_u32 (HashTable *ht, U32 key, U32 *value_out); -//- key-value helpers +internal void * hash_table_search_string_raw(HashTable *ht, String8 key); +internal void * hash_table_search_u64_raw (HashTable *ht, U64 key); +internal void * hash_table_search_path_raw (HashTable *ht, String8 key); +internal void * hash_table_search_raw_raw (HashTable *ht, void *key); + +// --- Key Value Helpers ------------------------------------------------------- internal U32 * keys_from_hash_table_u32 (Arena *arena, HashTable *ht); internal U64 * keys_from_hash_table_u64 (Arena *arena, HashTable *ht); internal String8 keys_from_hash_table_str8 (Arena *arena, HashTable *ht); internal KeyValuePair * key_value_pairs_from_hash_table(Arena *arena, HashTable *ht); +internal void * keys_from_hash_table_raw (Arena *arena, HashTable *ht); internal void * values_from_hash_table_raw(Arena *arena, HashTable *ht); -internal void sort_key_value_pairs_as_u32(KeyValuePair *pairs, U64 count); -internal void sort_key_value_pairs_as_u64(KeyValuePair *pairs, U64 count); +internal void sort_key_value_pairs_as_u32 (KeyValuePair *pairs, U64 count); +internal void sort_key_value_pairs_as_u64 (KeyValuePair *pairs, U64 count); internal void sort_key_value_pairs_as_string_sensitive(KeyValuePair *pairs, U64 count); -//////////////////////////////// +// --- Misc -------------------------------------------------------------------- internal U64Array remove_duplicates_u64_array(Arena *arena, U64Array arr); internal String8List remove_duplicates_str8_list(Arena *arena, String8List list); diff --git a/src/linker/linker.natvis b/src/linker/linker.natvis index 3c82cf96..64de6bfb 100644 --- a/src/linker/linker.natvis +++ b/src/linker/linker.natvis @@ -4,6 +4,14 @@ {{ name={name} flags={flags} id={id} sort_index={sort_index} }} + + + lib->was_member_linked[member_idx] + lib + member_idx + + + @@ -16,6 +24,9 @@ + + + {{count={count} first={first} last={last} }} @@ -32,6 +43,8 @@ + + {{count={count} first={first} }} @@ -89,6 +102,38 @@ + + + count + + + + + + idx = 0 + + + node->v[idx] + idx += 1 + + node = node->next + + + + + + + {count,cap,v} + + count + cap + + count + v + + + + @@ -139,6 +184,9 @@ + + + empty @@ -148,16 +196,28 @@ + cap count free_buckets - - cap - buckets - + + + + + node = buckets[bucket_idx].first + + + + node + node = node->next + + bucket_idx += 1 + + + diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 87081b2a..aa13ab29 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -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/) // --- Build Options ----------------------------------------------------------- @@ -14,8 +14,12 @@ #include "base_ext/base_blake3.h" #include "base_ext/base_blake3.c" +#define MD5_API static #include "third_party/md5/md5.c" #include "third_party/md5/md5.h" +#define XXH_INLINE_ALL +#define XXH_IMPLEMENTATION +#define XXH_STATIC_LINKING_ONLY #include "third_party/xxHash/xxhash.c" #include "third_party/xxHash/xxhash.h" #include "third_party/radsort/radsort.h" @@ -99,7 +103,6 @@ #include "lnk_timer.h" #include "lnk_io.h" #include "lnk_cmd_line.h" -#include "lnk_input.h" #include "lnk_config.h" #include "lnk_symbol_table.h" #include "lnk_section_table.h" @@ -114,7 +117,6 @@ #include "lnk_timer.c" #include "lnk_io.c" #include "lnk_cmd_line.c" -#include "lnk_input.c" #include "lnk_config.c" #include "lnk_symbol_table.c" #include "lnk_section_table.c" @@ -130,7 +132,10 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) { Temp scratch = scratch_begin(&arena, 1); - String8List raw_cmd_line = os_string_list_from_argcv(arena, argc, argv); + String8List raw_cmd_line = {0}; + for EachIndex(i, argc) { + str8_list_push(arena, &raw_cmd_line, str8_cstring(argv[i])); + } // remove exe name first argument str8_list_pop_front(&raw_cmd_line); @@ -146,33 +151,30 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) if (lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Dll)) { lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_SubSystem, "%S", pe_string_from_subsystem(PE_WindowsSubsystem_WINDOWS_GUI)); } - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_FunctionPadMin, ""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_HighEntropyVa, ""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_ManifestUac, "\"level='asInvoker' uiAccess='false'\""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_NxCompat, ""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_LargeAddressAware, ""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_PdbAltPath, "%%_RAD_PDB_PATH%%"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_PdbPageSize, "%u", KB(4)); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_TimeStamp, "%u", os_get_process_start_time_unix()); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Age, "%u", 1); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, ""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_DoMerge, ""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_EnvLib, ""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Exe, ""); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Guid, "imageblake3"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_LargePages, "no"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_LinkVer, "14.0"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_OsVer, "6.0"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_PageSize, "%u", KB(4)); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_PathStyle, "system"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Workers, "%u", os_get_system_info()->logical_processor_count); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_TargetOs, "windows"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SymbolTableCapDefined, "0x3ffff"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SymbolTableCapInternal, "0x1000"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SymbolTableCapWeak, "0x3ffff"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SymbolTableCapLib, "0x3ffff"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_DebugAltPath, "%%_RAD_RDI_PATH%%"); - lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_MemoryMapFiles, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_HighEntropyVa, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_ManifestUac, "\"level='asInvoker' uiAccess='false'\""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_NxCompat, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_LargeAddressAware, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_PdbAltPath, "%%_RAD_PDB_PATH%%"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_PdbPageSize, "%u", KB(4)); + if (!lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Brepro)) { + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_TimeStamp, "%u", os_get_process_start_time_unix()); + } + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Age, "%u", 1); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_DoMerge, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_EnvLib, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Exe, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Guid, "imageblake3"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_LargePages, "no"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_LinkVer, "14.0"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_OsVer, "6.0"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_PageSize, "%u", KB(4)); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_PathStyle, "system"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Workers, "%u", os_get_system_info()->logical_processor_count); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_TargetOs, "windows"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_DebugAltPath, "%%_RAD_RDI_PATH%%"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_MemoryMapFiles, ""); #if BUILD_DEBUG lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Log, "debug"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_Log, "io_write"); @@ -185,9 +187,9 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".00cfg=.rdata"); // TODO: .tls must be always first contribution in .data section because compiler generates TLS relative movs //lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".tls=.data"); - lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".edata=.rdata"); - //lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".idata=.rdata"); + lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".idata=.data"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".didat=.data"); + lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".edata=.rdata"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".RAD_LINK_PE_DEBUG_DIR=.rdata"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".RAD_LINK_PE_DEBUG_DATA=.rdata"); @@ -197,6 +199,11 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".gfids"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".gxfg"); + // set limits on unresolved symbol errors + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, "1000"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, "10"); + // set default max worker count if (lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Rad_SharedThreadPool)) { lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, ""); @@ -212,7 +219,7 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) } // init config - LNK_Config *config = lnk_config_from_cmd_line(arena, raw_cmd_line, cmd_line); + LNK_Config *config = lnk_config_from_cmd_line(raw_cmd_line, cmd_line); #if PROFILE_TELEMETRY { @@ -375,7 +382,7 @@ lnk_merge_manifest_files(String8 mt_path, String8 out_name, String8List manifest for (String8Node *man_node = manifest_path_list.first; man_node != 0; man_node = man_node->next) { - // resolve relativ path inputs + // resolve relative inputs String8 full_path = path_absolute_dst_from_relative_dst_src(scratch.arena, man_node->string, work_dir); // normalize slashes @@ -390,12 +397,12 @@ lnk_merge_manifest_files(String8 mt_path, String8 out_name, String8List manifest OS_ProcessLaunchParams launch_opts = {0}; launch_opts.cmd_line = cmd_line; launch_opts.inherit_env = 1; - launch_opts.consoleless = 1; + launch_opts.consoleless = 0; OS_Handle mt_handle = os_process_launch(&launch_opts); if (os_handle_match(mt_handle, os_handle_zero())) { lnk_error(LNK_Error_Mt, "unable to start process: %S", mt_path); } else { - os_process_join(mt_handle, max_U64); + os_process_join(mt_handle, max_U64, 0); os_process_detach(mt_handle); } @@ -462,11 +469,11 @@ lnk_make_null_obj(Arena *arena) { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_Unknown); - // make import stub - { - COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("RAD_IMPORT_STUB_NULL"), 0, COFF_SymStorageClass_Static); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit(LNK_IMPORT_STUB), COFF_WeakExt_AntiDependency, tag); - } + // push null symbol + COFF_ObjSymbol *null_abs = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit(LNK_NULL_SYMBOL), 0, COFF_SymStorageClass_External); + + // push import stub + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit(LNK_IMPORT_STUB), COFF_WeakExt_Null, null_abs); // push .debug$T sections with null leaf String8 null_debug_data; @@ -872,53 +879,6 @@ lnk_make_linker_coff_obj(Arena *arena, return obj; } -internal String8 -lnk_get_lib_name(String8 path) -{ - static String8 LIB_EXT = str8_lit_comp(".LIB"); - - // strip path - String8 name = str8_skip_last_slash(path); - - // strip extension - String8 name_ext = str8_postfix(name, LIB_EXT.size); - if (str8_match(name_ext, LIB_EXT, StringMatchFlag_CaseInsensitive)) { - name = str8_chop(name, LIB_EXT.size); - } - - return name; -} - -internal B32 -lnk_is_lib_disallowed(HashTable *disallow_lib_ht, String8 path) -{ - String8 lib_name = lnk_get_lib_name(path); - return hash_table_search_path(disallow_lib_ht, lib_name) != 0; -} - -internal B32 -lnk_is_lib_loaded(HashTable *loaded_lib_ht, String8 path) -{ - KeyValuePair *is_loaded = hash_table_search_path(loaded_lib_ht, path); - return is_loaded != 0; -} - -internal void -lnk_push_disallow_lib(Arena *arena, HashTable *disallow_lib_ht, String8 path) -{ - String8 lib_name = lnk_get_lib_name(path); - hash_table_push_path_u64(arena, disallow_lib_ht, lib_name, 0); -} - -internal void -lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path) -{ - if (!hash_table_search_path(loaded_lib_ht, path)) { - String8 path_copy = push_str8_copy(arena, path); - hash_table_push_path_u64(arena, loaded_lib_ht, path_copy, 0); - } -} - internal String8 lnk_make_linker_obj(Arena *arena, LNK_Config *config) { @@ -962,1150 +922,1562 @@ lnk_make_linker_obj(Arena *arena, LNK_Config *config) return obj; } +internal String8 +lnk_make_obj_with_undefined_symbols(Arena *arena, String8List symbol_names) +{ + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); + for (String8Node *name_n = symbol_names.first; name_n != 0; name_n = name_n->next) { + coff_obj_writer_push_symbol_undef(obj_writer, name_n->string); + } + String8 obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); + return obj; +} + internal void -lnk_queue_lib_member_input(Arena *arena, - PathStyle path_style, - LNK_SymbolLib *symbol, - LNK_InputImportList *input_import_list, - LNK_InputObjList *input_obj_list) +lnk_input_list_push_node(LNK_InputList *list, LNK_Input *node) { - LNK_Lib *lib = symbol->lib; - U64 input_idx = Compose64Bit(lib->input_idx, symbol->member_offset); - - // parse member - COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, symbol->member_offset); - COFF_DataType member_type = coff_data_type_from_data(member_info.data); - - switch (member_type) { - case COFF_DataType_Null: break; - case COFF_DataType_Import: { - LNK_InputImportNode *input = lnk_input_import_list_push(arena, input_import_list); - input->data.coff_import = member_info.data; - input->data.input_idx = input_idx; - } break; - case COFF_DataType_BigObj: - case COFF_DataType_Obj: { - String8 obj_path = coff_parse_long_name(lib->long_names, member_info.header.name); - - // obj path in thin archive has slash appended which screws up - // file lookup on disk; it couble be there to enable paths to symbols - // but we don't use this feature - String8 slash = str8_lit("/"); - if (str8_ends_with(obj_path, slash, 0)) { - obj_path = str8_chop(obj_path, slash.size); - } - - // obj path in thin archive is relative to directory with archive - B32 is_thin = lib->type == COFF_Archive_Thin; - if (is_thin) { - Temp scratch = scratch_begin(&arena, 1); - String8List obj_path_list = {0}; - str8_list_push(scratch.arena, &obj_path_list, str8_chop_last_slash(lib->path)); - str8_list_push(scratch.arena, &obj_path_list, obj_path); - obj_path = str8_path_list_join_by_style(arena, &obj_path_list, path_style); - scratch_end(scratch); - } - - LNK_InputObj *input = lnk_input_obj_list_push(arena, input_obj_list); - input->is_thin = is_thin; - input->dedup_id = push_str8f(arena, "%S/%S", lib->path, obj_path); - input->path = obj_path; - input->data = member_info.data; - input->lib = lib; - input->input_idx = input_idx; - } break; - } + SLLQueuePush(list->first, list->last, node); + list->count += 1; } -internal -THREAD_POOL_TASK_FUNC(lnk_undef_symbol_finder) +internal void +lnk_input_list_concat_in_place(LNK_InputList *list, LNK_InputList *to_concat) { - LNK_SymbolFinder *task = raw_task; - LNK_SymbolFinderResult *result = &task->result_arr[task_id]; - Rng1U64 range = task->range_arr[task_id]; - - for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) { - LNK_SymbolNode *symbol_n = task->lookup_node_arr.v[symbol_idx]; - LNK_Symbol *symbol = symbol_n->data; - - LNK_Symbol *has_defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol->name); - if (has_defn) { - continue; - } - - LNK_Symbol *member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); - if (member_symbol) { - lnk_queue_lib_member_input(arena, task->path_style, &member_symbol->u.lib, &result->input_import_list, &result->input_obj_list); - } else { - lnk_symbol_list_push_node(&result->unresolved_symbol_list, symbol_n); - } - } + SLLConcatInPlace(list, to_concat); } -internal -THREAD_POOL_TASK_FUNC(lnk_weak_symbol_finder) +internal LNK_InputPtrArray +lnk_array_from_input_list(Arena *arena, LNK_InputList list) { - LNK_SymbolFinder *task = raw_task; - LNK_SymbolFinderResult *result = &task->result_arr[task_id]; - Rng1U64 range = task->range_arr[task_id]; - - for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) { - LNK_SymbolNode *symbol_n = task->lookup_node_arr.v[symbol_idx]; - LNK_Symbol *symbol = symbol_n->data; - - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol->name); - if (defn) { - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn); - COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed); - if (defn_interp != COFF_SymbolValueInterp_Weak) { - continue; - } - } - - LNK_Symbol *member_symbol = 0; - { - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(parsed_symbol, symbol->u.defined.obj->header.is_big_obj); - switch (weak_ext->characteristics) { - case COFF_WeakExt_NoLibrary: { - // NOLIBRARY means weak symbol should be resolved in case where strong definition pulls in lib member. - } break; - case COFF_WeakExt_AntiDependency: - case COFF_WeakExt_SearchLibrary: { - member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); - } break; - case COFF_WeakExt_SearchAlias: { - member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); - if (member_symbol == 0) { - if (str8_match_lit(".weak.", symbol->name, StringMatchFlag_RightSideSloppy)) { - // TODO: Clang and MingGW encode extra info in alias - // - // __attribute__((weak,alias("foo"))) void bar(void); - // static void foo() {} - // - // Clang write these COFF symbols in obj for code above: - // - // 30 00000000 0000000001 0 FUNC NULL EXTERNAL foo - // ... - // 33 00000000 UNDEF 1 NULL NULL WEAK_EXTERNAL bar - // Tag Index 35, Characteristics SEARCH_ALIAS - // 35 00000000 0000000001 0 NULL NULL EXTERNAL .weak.bar.default.foo - // - // In this case linker needs to parse .weak.bar.default.foo and search for bar and foo as well. - Assert("TODO: MinGW weak symbol"); - } else { - COFF_ParsedSymbol tag = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, weak_ext->tag_index); - member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, tag.name); - } - } - } break; - } - } - - if (member_symbol) { - lnk_queue_lib_member_input(arena, task->path_style, &member_symbol->u.lib, &result->input_import_list, &result->input_obj_list); - } else { - lnk_symbol_list_push_node(&result->unresolved_symbol_list, symbol_n); - } + LNK_InputPtrArray result = {0}; + result.v = push_array(arena, LNK_Input *, list.count); + for (LNK_Input *node = list.first; node != 0; node = node->next, result.count += 1) { + result.v[result.count] = node; } + return result; } -internal LNK_SymbolFinderResult -lnk_run_symbol_finder(TP_Context *tp, - TP_Arena *arena, - LNK_Config *config, - LNK_SymbolTable *symtab, - LNK_SymbolList lookup_list, - TP_TaskFunc *task_func) +internal LNK_Inputer * +lnk_inputer_init(void) +{ + Arena *arena = arena_alloc(); + LNK_Inputer *inputer = push_array(arena, LNK_Inputer, 1); + inputer->arena = arena; + inputer->objs_ht = hash_table_init(arena, 0x20000); + inputer->libs_ht = hash_table_init(arena, 0x1000); + inputer->missing_lib_ht = hash_table_init(arena, 0x100); + return inputer; +} + +internal LNK_Input * +lnk_input_push(Arena *arena, LNK_InputList *list, String8 path, String8 data) +{ + LNK_Input *node = push_array(arena, LNK_Input, 1); + node->path = path; + node->data = data; + lnk_input_list_push_node(list, node); + return node; +} + +internal LNK_Input * +lnk_inputer_push_linkgen(Arena *arena, LNK_InputList *list, String8 data, String8 path) +{ + LNK_Input *input = lnk_input_push(arena, list, data, path); + input->exclude_from_debug_info = 1; + return input; +} + +internal LNK_Input * +lnk_inputer_push_thin(Arena *arena, LNK_InputList *list, HashTable *ht, String8 full_path) +{ + Temp scratch = scratch_begin(&arena, 1); + LNK_Input *input = hash_table_search_path_raw(ht, full_path); + if (input == 0) { + input = lnk_input_push(arena, list, full_path, str8_zero()); + input->path = push_str8_copy(arena, full_path); + input->is_thin = 1; + + hash_table_push_path_raw(arena, ht, full_path, input); + } + scratch_end(scratch); + return input; +} + +internal LNK_Input * +lnk_inputer_push_obj(LNK_Inputer *inputer, LNK_LibMemberRef *link_member, String8 path, String8 data) +{ + lnk_log(LNK_Log_InputObj, "Input Obj: %S", path); + LNK_Input *input = lnk_input_push(inputer->arena, &inputer->new_objs, path, data); + input->link_member = link_member; + return input; +} + +internal LNK_Input * +lnk_inputer_push_obj_linkgen(LNK_Inputer *inputer, LNK_LibMemberRef *link_member, String8 path, String8 data) +{ + lnk_log(LNK_Log_InputObj, "Input Obj: %S", path); + LNK_Input *input = lnk_inputer_push_linkgen(inputer->arena, &inputer->new_objs, path, data); + input->link_member = link_member; + return input; +} + +internal LNK_Input * +lnk_inputer_push_obj_thin(LNK_Inputer *inputer, LNK_LibMemberRef *link_member, String8 path) +{ + lnk_log(LNK_Log_InputObj, "Input Obj: %S", path); + Temp scratch = scratch_begin(0,0); + String8 full_path = os_full_path_from_path(scratch.arena, path); + LNK_Input *input = lnk_inputer_push_thin(inputer->arena, &inputer->new_objs, inputer->objs_ht, full_path); + input->link_member = link_member; + scratch_end(scratch); + return input; +} + +internal LNK_Input * +lnk_inputer_push_lib(LNK_Inputer *inputer, LNK_InputSourceType input_source, String8 path, String8 data) +{ + lnk_log(LNK_Log_InputLib, "Input Lib: %S", path); + return lnk_input_push(inputer->arena, &inputer->new_libs[input_source], path, data); +} + +internal LNK_Input * +lnk_inputer_push_lib_linkgen(LNK_Inputer *inputer, LNK_InputSourceType input_source, String8 path, String8 data) +{ + lnk_log(LNK_Log_InputLib, "Input Lib: %S", path); + return lnk_input_push(inputer->arena, &inputer->new_libs[input_source], path, data); +} + +internal LNK_Input * +lnk_input_from_path(HashTable *load_ht, String8 path) +{ + LNK_Input *input = hash_table_search_path_raw(load_ht, path); + if (input == 0) { + Temp scratch = scratch_begin(0, 0); + String8 full_path = os_full_path_from_path(scratch.arena, path); + input = hash_table_search_path_raw(load_ht, full_path); + scratch_end(scratch); + } + return input; +} + +internal LNK_Input * +lnk_inputer_push_lib_thin(LNK_Inputer *inputer, LNK_Config *config, LNK_InputSourceType input_source, String8 path) +{ + Temp scratch = scratch_begin(0,0); + + LNK_Input *input = 0; + + // default libraries may omit extension + if (input_source == LNK_InputSource_Default || input_source == LNK_InputSource_Obj) { + if (!str8_ends_with(path, str8_lit(".lib"), StringMatchFlag_CaseInsensitive)) { + path = push_str8f(scratch.arena, "%S.lib", path); + } + if (lnk_is_lib_disallowed(config, path)) { + goto exit; + } + } + + // was library already loaded? + input = hash_table_search_path_raw(inputer->libs_ht, path); + if (input) { + goto exit; + } + + // search disk for library + String8 first_match = lnk_find_first_file(scratch.arena, config->lib_dir_list, path); + + // warn about missing library + if (first_match.size == 0) { + KeyValuePair *was_reported = hash_table_search_path(inputer->missing_lib_ht, path); + if (was_reported == 0) { + hash_table_push_path_u64(inputer->arena, inputer->missing_lib_ht, path, 0); + lnk_error(LNK_Warning_FileNotFound, "unable to find library `%S`", path); + } + goto exit; + } + + // was input with full path already loaded? + input = hash_table_search_path_raw(inputer->libs_ht, first_match); + if (input) { + goto exit; + } + + lnk_log(LNK_Log_InputLib, "Input Lib: %S", first_match); + input = lnk_inputer_push_thin(inputer->arena, &inputer->new_libs[input_source], inputer->libs_ht, first_match); + + // store input path to early-out of file searches for default libs + if (!str8_match(first_match, path, StringMatchFlag_CaseInsensitive)) { + hash_table_push_path_raw(inputer->arena, inputer->libs_ht, path, input); + } + + exit:; + scratch_end(scratch); + return input; +} + +internal B32 +lnk_inputer_has_items(LNK_Inputer *inputer) +{ + if (inputer->new_objs.count > 0) { + return 1; + } + + for EachIndex(i, ArrayCount(inputer->new_libs)) { + if (inputer->new_libs[i].count > 0) { + return 1; + } + } + + return 0; +} + +internal LNK_InputPtrArray +lnk_inputer_flush(Arena *arena, TP_Context *tp, LNK_Inputer *inputer, LNK_IO_Flags io_flags, LNK_InputList *all_inputs, LNK_InputList *new_inputs) { ProfBeginFunction(); - Temp scratch = scratch_begin(arena->v, arena->count); - - ProfBegin("Setup Task"); - LNK_SymbolFinder task = {0}; - task.path_style = config->path_style; - task.symtab = symtab; - task.lookup_node_arr = lnk_symbol_node_array_from_list(scratch.arena, lookup_list); - task.result_arr = push_array(scratch.arena, LNK_SymbolFinderResult, tp->worker_count); - task.range_arr = tp_divide_work(scratch.arena, task.lookup_node_arr.count, tp->worker_count); - ProfEnd(); - - ProfBegin("Run Task"); - tp_for_parallel(tp, arena, tp->worker_count, task_func, &task); - ProfEnd(); - - ProfBegin("Concat Results"); - LNK_SymbolFinderResult result = {0}; - for (U64 i = 0; i < tp->worker_count; ++i) { - LNK_SymbolFinderResult *src = &task.result_arr[i]; - lnk_symbol_list_concat_in_place(&result.unresolved_symbol_list, &src->unresolved_symbol_list); - lnk_input_obj_list_concat_in_place(&result.input_obj_list, &src->input_obj_list); - lnk_input_import_list_concat_in_place(&result.input_import_list, &src->input_import_list); + + Temp scratch = scratch_begin(&arena, 1); + + ProfBegin("Gather Thin Inputs"); + U64 thin_inputs_count = 0; + for (LNK_Input *node = new_inputs->first; node != 0; node = node->next) { + if (node->is_thin) { + thin_inputs_count += 1; + } + } + LNK_Input **thin_inputs = push_array(scratch.arena, LNK_Input *, thin_inputs_count); + U64 thin_idx = 0; + for (LNK_Input *node = new_inputs->first; node != 0; node = node->next) { + if (node->is_thin) { + thin_inputs[thin_idx++] = node; + } + } + String8Array thin_input_paths = {0}; + thin_input_paths.count = thin_inputs_count; + thin_input_paths.v = push_array(scratch.arena, String8, thin_inputs_count); + for EachIndex(i, thin_inputs_count) { + thin_input_paths.v[i] = thin_inputs[i]->path; } ProfEnd(); - - // to get deterministic output accross multiple linker runs we have to sort inputs - ProfBegin("Sort Objs [Count %llu]", result.input_obj_list.count); - LNK_InputObj **input_obj_ptr_arr = lnk_array_from_input_obj_list(scratch.arena, result.input_obj_list); - qsort(input_obj_ptr_arr, result.input_obj_list.count, sizeof(input_obj_ptr_arr[0]), lnk_input_obj_compar); - //radsort(input_obj_ptr_arr, result.input_obj_list.count, lnk_input_obj_compar_is_before); - result.input_obj_list = lnk_list_from_input_obj_arr(input_obj_ptr_arr, result.input_obj_list.count); + + ProfBegin("Load Inputs From Disk"); + String8Array thin_input_datas = lnk_read_data_from_file_path_parallel(tp, inputer->arena, io_flags, thin_input_paths); + for EachIndex(thin_input_idx, thin_inputs_count) { + thin_inputs[thin_input_idx]->has_disk_read_failed = thin_input_datas.v[thin_input_idx].size == 0; + thin_inputs[thin_input_idx]->data = thin_input_datas.v[thin_input_idx]; + } ProfEnd(); - - ProfBegin("Sort Imports [Count %llu]", result.input_import_list.count); - LNK_InputImportNode **input_imp_ptr_arr = lnk_input_import_arr_from_list(scratch.arena, result.input_import_list); - //radsort(input_imp_ptr_arr, result.input_import_list.count, lnk_input_import_is_before); - qsort(input_imp_ptr_arr, result.input_import_list.count, sizeof(input_imp_ptr_arr[0]), lnk_input_import_node_compar); - result.input_import_list = lnk_list_from_input_import_arr(input_imp_ptr_arr, result.input_import_list.count); + + ProfBegin("Disk Read Check"); + for EachIndex(i, thin_inputs_count) { + if (thin_inputs[i]->has_disk_read_failed) { + lnk_error(LNK_Error_InvalidPath, "unable to find file \"%S\"", thin_inputs[i]->path); + } + } ProfEnd(); - + + LNK_InputPtrArray result = lnk_array_from_input_list(arena, *new_inputs); + + lnk_input_list_concat_in_place(all_inputs, new_inputs); + scratch_end(scratch); ProfEnd(); return result; } -internal LNK_LinkContext -lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) +internal void +lnk_lib_member_ref_list_push_node(LNK_LibMemberRefList *list, LNK_LibMemberRef *node) { - enum State { - State_Null, - State_InputDisallowLibs, - State_InputImports, - State_InputSymbols, - State_InputObjs, - State_InputLibs, - State_InputAlternateNames, - State_PushDllHelperUndefSymbol, - State_InputLinkerObjs, - State_PushLoadConfigUndefSymbol, - State_LookupUndef, - State_LookupWeak, - State_LookupEntryPoint, - State_ReportUnresolvedSymbols, - }; - struct StateNode { struct StateNode *next; enum State state; }; - struct StateList { U64 count; struct StateNode *first; struct StateNode *last; }; -#define state_list_push(a, l, s) do { \ - struct StateNode *node = push_array(a, struct StateNode, 1); \ - node->state = s; \ - SLLQueuePush(l.first, l.last, node); \ - l.count += 1; \ -} while (0) -#define state_list_pop(l) (l).first->state; SLLQueuePop((l).first, (l).last); (l).count -= 1 - - ProfBeginFunction(); - Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); - - // inputs - String8Node **last_include_symbol = &config->include_symbol_list.first; - String8Node **last_disallow_lib = &config->disallow_lib_list.first; - LNK_AltNameNode **last_alt_name = &config->alt_name_list.first; - LNK_InputObjList input_obj_list = {0}; - LNK_InputImportList input_import_list = {0}; - LNK_InputLib **input_libs[LNK_InputSource_Count] = { - &config->input_list[LNK_Input_Lib].first, - &config->input_default_lib_list.first, - &config->input_obj_lib_list.first - }; + SLLQueuePush(list->first, list->last, node); + list->count += 1; +} - // input :null_obj - { - LNK_InputObj *null_obj_input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - null_obj_input->path = str8_lit("* Null Obj *"); - null_obj_input->dedup_id = null_obj_input->path; - null_obj_input->data = lnk_make_null_obj(tp_arena->v[0]); +internal void +lnk_lib_member_ref_list_concat_in_place_array(LNK_LibMemberRefList *list, LNK_LibMemberRefList *to_concat_arr, U64 count) +{ + SLLConcatInPlaceArray(list, to_concat_arr, count); +} + +internal int +lnk_lib_member_ref_is_before(void *raw_a, void *raw_b) +{ + LNK_LibMemberRef **a = raw_a, **b = raw_b; + LNK_Symbol *a_pull_in_ref = (*a)->lib->member_links[(*a)->member_idx]; + LNK_Symbol *b_pull_in_ref = (*b)->lib->member_links[(*b)->member_idx]; + return lnk_symbol_is_before(a_pull_in_ref, b_pull_in_ref); +} + +internal LNK_LibMemberRef ** +lnk_array_from_lib_member_list(Arena *arena, LNK_LibMemberRefList list) +{ + LNK_LibMemberRef **result = push_array(arena, LNK_LibMemberRef *, list.count); + U64 idx = 0; + for (LNK_LibMemberRef *node = list.first; node != 0; node = node->next, idx += 1) { + result[idx] = node; } - - // input command line objs - LNK_InputObjList cmd_line_obj_inputs = lnk_input_obj_list_from_string_list(scratch.arena, config->input_list[LNK_Input_Obj]); - lnk_input_obj_list_concat_in_place(&input_obj_list, &cmd_line_obj_inputs); + return result; +} - // state - LNK_SymbolTable *symtab = lnk_symbol_table_init(tp_arena); - LNK_SectionTable *sectab = 0; - HashTable *static_imports = hash_table_init(scratch.arena, 512); - HashTable *delayed_imports = hash_table_init(scratch.arena, 512); - LNK_ObjList obj_list = {0}; - LNK_LibList lib_index[LNK_InputSource_Count] = {0}; - Arena *ht_arena = arena_alloc(); - String8 delay_load_helper_name = {0}; - HashTable *disallow_lib_ht = hash_table_init(scratch.arena, 0x100); - HashTable *delay_load_dll_ht = hash_table_init(scratch.arena, 0x100); - HashTable *loaded_lib_ht = hash_table_init(scratch.arena, 0x100); - HashTable *missing_lib_ht = hash_table_init(scratch.arena, 0x100); - HashTable *loaded_obj_ht = hash_table_init(scratch.arena, 0x4000); - LNK_SymbolList lookup_undef_list = {0}; - LNK_SymbolList lookup_weak_list = {0}; - LNK_SymbolList unresolved_undef_list = {0}; - LNK_SymbolList unresolved_weak_list = {0}; - U64 entry_point_lookup_attempts = 0; - B32 report_unresolved_symbols = 1; - B32 input_linker_objs = 1; +internal LNK_ObjNode * +lnk_load_objs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, U64 *objs_count_out) +{ + ProfBeginV("Load Objs [Count %llu]", inputer->new_objs.count); + Temp scratch = scratch_begin(arena->v, arena->count); - // - // Init state machine - // - struct StateList state_list = {0}; - state_list_push(scratch.arena, state_list, State_InputDisallowLibs); - state_list_push(scratch.arena, state_list, State_InputObjs); - state_list_push(scratch.arena, state_list, State_InputLibs); - if (config->delay_load_dll_list.node_count) { - for (String8Node *delay_load_dll_node = config->delay_load_dll_list.first; - delay_load_dll_node != 0; - delay_load_dll_node = delay_load_dll_node->next) { - hash_table_push_path_u64(scratch.arena, delay_load_dll_ht, delay_load_dll_node->string, 0); + // load obj inputer from disk + LNK_InputPtrArray new_input_objs = lnk_inputer_flush(arena->v[0], tp, inputer, config->io_flags, &inputer->objs, &inputer->new_objs); + + if (lnk_get_log_status(LNK_Log_InputObj) && new_input_objs.count) { + U64 input_size = 0; + for EachIndex(i, new_input_objs.count) { input_size += new_input_objs.v[i]->data.size; } + lnk_log(LNK_Log_InputObj, "[ Obj Input Size %M ]", input_size); + } + + LNK_ObjNode *new_objs = lnk_obj_from_input_many(tp, arena, config->machine, new_input_objs.count, new_input_objs.v); + + // if machine type was unspecified on the command line, derive it from obj file + if (config->machine == COFF_MachineType_Unknown) { + for EachIndex(obj_idx, new_input_objs.count) { + if (new_objs[obj_idx].data.header.machine != COFF_MachineType_Unknown) { + config->machine = new_objs[obj_idx].data.header.machine; + break; + } } - state_list_push(scratch.arena, state_list, State_PushDllHelperUndefSymbol); } - if (config->guard_flags != LNK_Guard_None) { - state_list_push(scratch.arena, state_list, State_PushLoadConfigUndefSymbol); + + ProfBegin("Apply Directives"); + for EachIndex(obj_idx, new_input_objs.count) { + LNK_Obj *obj = &new_objs[obj_idx].data; + String8List raw_directives = lnk_raw_directives_from_obj(scratch.arena, obj); + LNK_DirectiveInfo directive_info = lnk_directive_info_from_raw_directives(scratch.arena, obj, raw_directives); + for EachIndex(i, ArrayCount(directive_info.v)) { + for (LNK_Directive *dir = directive_info.v[i].first; dir != 0; dir = dir->next) { + lnk_apply_cmd_option_to_config(config, dir->id, dir->value_list, obj); + } + } } + ProfEnd(); + + if (objs_count_out) { + *objs_count_out = new_input_objs.count; + } + + scratch_end(scratch); + ProfEnd(); + return new_objs; +} + +internal void +lnk_load_libs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_Link *link) +{ + for EachIndex(input_source, LNK_InputSource_Count) { + ProfBegin("Input Libs [Count %llu]", inputer->new_libs[input_source].count); + + LNK_InputPtrArray new_input_libs = lnk_inputer_flush(arena->v[0], tp, inputer, config->io_flags, &inputer->libs, &inputer->new_libs[input_source]); + + if (lnk_get_log_status(LNK_Log_InputLib) && new_input_libs.count) { + U64 input_size = 0; + for EachIndex(i, new_input_libs.count) { input_size += new_input_libs.v[i]->data.size; } + lnk_log(LNK_Log_InputObj, "[ Lib Input Size %M ]", input_size); + } + + lnk_lib_list_push_parallel(tp, arena, &link->libs, new_input_libs.count, new_input_libs.v); + + ProfEnd(); + } +} + +internal void +lnk_load_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(arena->v, arena->count); + + U64 obj_id_base = link->objs.count; + + U64 objs_count = 0; + LNK_ObjNode *objs = lnk_load_objs(tp, arena, config, inputer, symtab, link, &objs_count); + lnk_obj_list_push_node_many(&link->objs, objs_count, objs); - // - // Run states - // - for (;;) { - for (; state_list.count > 0; ) { - enum State state = state_list_pop(state_list); - switch (state) { - case State_Null: break; + // if delay load DLLs are present, include delay load helper symbol + if (config->machine != COFF_MachineType_Unknown && config->delay_load_helper_name.size == 0 && config->delay_load_dll_list.node_count) { + config->delay_load_helper_name = mscrt_delay_load_helper_name_from_machine(config->machine); + if (config->delay_load_helper_name.size) { + lnk_include_symbol(config, config->delay_load_helper_name, 0); + } + } - case State_InputDisallowLibs: { - ProfBegin("Input /disallowlib"); - for (; *last_disallow_lib; last_disallow_lib = &(*last_disallow_lib)->next) { - if ( ! lnk_is_lib_disallowed(disallow_lib_ht, (*last_disallow_lib)->string)) { - lnk_push_disallow_lib(scratch.arena, disallow_lib_ht, (*last_disallow_lib)->string); + { + ProfBegin("Process /INCLUDE"); + + // group include symbols by obj + HashTable *ht = hash_table_init(scratch.arena, 64); + for (; *link->last_include; link->last_include = &(*link->last_include)->next) { + LNK_IncludeSymbol *include_symbol = &(*link->last_include)->v; + + // skip, include symbol is already in the global symbol table + if (lnk_symbol_table_search(symtab, include_symbol->name)) { + continue; + } + + // was obj already seen? + String8List *include_name_list = hash_table_search_raw_raw(ht, include_symbol->obj); + + if (include_name_list == 0) { + // push entry for new obj + include_name_list = push_array(scratch.arena, String8List, 1); + hash_table_push_raw_raw(scratch.arena, ht, include_symbol->obj, include_name_list); + } + + // append include symbol to obj's name list + str8_list_push(scratch.arena, include_name_list, include_symbol->name); + } + + LNK_Obj **objs_with_includes = keys_from_hash_table_raw(scratch.arena, ht); + String8List **include_names = values_from_hash_table_raw(scratch.arena, ht); + for EachIndex(i, ht->count) { + LNK_Obj *obj_with_includes = objs_with_includes[i]; + String8 include_obj_path = obj_with_includes ? obj_with_includes->path : str8_lit("RADLINK"); + String8 include_obj_data = lnk_make_obj_with_undefined_symbols(arena->v[0], *include_names[i]); + lnk_inputer_push_obj_linkgen(inputer, obj_with_includes ? obj_with_includes->link_member : 0, include_obj_path, include_obj_data); + + U64 include_obj_count = 0; + LNK_ObjNode *include_obj = lnk_load_objs(tp, arena, config, inputer, symtab, link, &include_obj_count); + AssertAlways(include_obj_count == 1); + + if (obj_with_includes) { + DLLInsert(link->objs.first, link->objs.last, obj_with_includes->node, include_obj); + link->objs.count += 1; + } else { + lnk_obj_list_push_node(&link->objs, include_obj); + } + } + + ProfEnd(); + } + + // finalize input indices on new objs and push external symbols to the symbol table + { + U64 node_idx = 0; + for (LNK_ObjNode **n = link->last_symbol_input; *n; n = &(*n)->next, node_idx += 1) { + (*n)->data.input_idx = obj_id_base + node_idx; + } + + U64 new_objs_count = node_idx; + LNK_Obj **new_objs = push_array(scratch.arena, LNK_Obj *, node_idx); + node_idx = 0; + for (; *link->last_symbol_input; link->last_symbol_input = &(*link->last_symbol_input)->next, node_idx += 1) { + new_objs[node_idx] = &(*link->last_symbol_input)->data; + } + + lnk_push_obj_symbols(tp, arena, symtab, new_objs_count, new_objs); + } + + // input default libraries + for (; *link->last_default_lib; link->last_default_lib = &(*link->last_default_lib)->next) { + lnk_inputer_push_lib_thin(inputer, config, LNK_InputSource_Default, (*link->last_default_lib)->string); + } + + // input libraries referenced in objs + for (; *link->last_obj_lib; link->last_obj_lib = &(*link->last_obj_lib)->next) { + lnk_inputer_push_lib_thin(inputer, config, LNK_InputSource_Obj, (*link->last_obj_lib)->string); + } + + // load new libs + lnk_load_libs(tp, arena, config, inputer, link); + + // resolve entry point + if (link->try_to_resolve_entry_point) { + B32 is_entry_point_name_inferred = config->entry_point_name.size == 0; + + // loop over all possible subsystems and entry point names and pick + // subsystem that has a defined entry point symbol + if (config->entry_point_name.size == 0) { + PE_WindowsSubsystem subsys_first = config->subsystem; + PE_WindowsSubsystem subsys_last = config->subsystem == PE_WindowsSubsystem_UNKNOWN ? PE_WindowsSubsystem_COUNT : config->subsystem+1; + LNK_Symbol *entry_point_symbol = 0; + for (U64 subsys_idx = subsys_first; subsys_idx < subsys_last; subsys_idx += 1) { + String8Array entry_points = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); + for EachIndex(i, entry_points.count) { + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, entry_points.v[i]); + if (symbol) { + config->subsystem = subsys_idx; + config->entry_point_name = entry_points.v[i]; + goto found_entry_and_subsystem; } } - ProfEnd(); - } break; - case State_InputImports: { - ProfBegin("Input Imports"); - for (LNK_InputImportNode *input = input_import_list.first; input != 0; input = input->next) { - COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(input->data.coff_import); - - // import machine compat check - if (import_header.machine != config->machine) { - lnk_error(LNK_Error_IncompatibleMachine, "symbol %S pulled in import with incompatible machine %S (expected %S)", - import_header.func_name, - coff_string_from_machine_type(import_header.machine), - coff_string_from_machine_type(config->machine)); - continue; - } + } + found_entry_and_subsystem:; + } - // was import already created? - if (lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, import_header.func_name)) { - continue; - } - - // create import stubs (later replaced with acutal imports generated by linker) - LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit(LNK_IMPORT_STUB)); - LNK_Symbol *thunk_symbol = lnk_make_defined_symbol(scratch.arena, import_header.func_name, import_stub->u.defined.obj, import_stub->u.defined.symbol_idx); - LNK_Symbol *imp_symbol = lnk_make_defined_symbol(scratch.arena, push_str8f(scratch.arena, "__imp_%S", import_header.func_name), import_stub->u.defined.obj, import_stub->u.defined.symbol_idx); - lnk_symbol_table_push(symtab, LNK_SymbolScope_Defined, thunk_symbol); - lnk_symbol_table_push(symtab, LNK_SymbolScope_Defined, imp_symbol); - - // pick imports hash table - HashTable *imports_ht; - { - B32 is_delay_load_dll = hash_table_search_path_u64(delay_load_dll_ht, import_header.dll_name, 0); - if (is_delay_load_dll) { - imports_ht = delayed_imports; - } else { - imports_ht = static_imports; - } - } - - // search DLL symbol list - String8List *import_symbols = hash_table_search_path_raw(imports_ht, import_header.dll_name); - if (import_symbols == 0) { - import_symbols = push_array(scratch.arena, String8List, 1); - hash_table_push_path_raw(scratch.arena, imports_ht, import_header.dll_name, import_symbols); - } - - // push symbol - str8_list_push(scratch.arena, import_symbols, input->data.coff_import); - } - - // reset input - MemoryZeroStruct(&input_import_list); - - ProfEnd(); - } break; - case State_InputSymbols: { - ProfBegin("Input Symbols"); - - // push a relocation which references an undefined include symbol - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".radinc$"), 0, str8_zero()); - for (; *last_include_symbol; last_include_symbol = &(*last_include_symbol)->next) { - COFF_ObjSymbol *include_symbol = coff_obj_writer_push_symbol_undef(obj_writer, (*last_include_symbol)->string); - coff_obj_writer_section_push_reloc(obj_writer, sect, 0, include_symbol, 0); - } - - // input obj with includes - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* INCLUDE SYMBOLS *"); - input->dedup_id = push_str8f(scratch.arena, "%S %llu", input->path, input_obj_list.count); - input->data = coff_obj_writer_serialize(tp_arena->v[0], obj_writer); - - coff_obj_writer_release(&obj_writer); - - ProfEnd(); - } break; - case State_InputObjs: { - ProfBegin("Input Objs [Count %llu]", input_obj_list.count); - - ProfBegin("Collect Obj Paths"); - LNK_InputObjList unique_obj_input_list = {0}; - for (LNK_InputObj *input = input_obj_list.first, *next; input != 0; input = next) { - next = input->next; - - B32 was_obj_loaded = hash_table_search_path_u64(loaded_obj_ht, input->dedup_id, 0); - if (was_obj_loaded) { continue; } - - if (input->is_thin) { - String8 full_path = input->dedup_id.size ? os_full_path_from_path(scratch.arena, input->dedup_id) : str8_zero(); - B32 was_full_path_used = hash_table_search_path_u64(loaded_obj_ht, full_path, 0); - if (was_full_path_used) { continue; } - if (!str8_match(input->dedup_id, full_path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive)) { - hash_table_push_path_u64(scratch.arena, loaded_obj_ht, full_path, 0); - } - } - - hash_table_push_path_u64(scratch.arena, loaded_obj_ht, input->dedup_id, 0); - - lnk_input_obj_list_push_node(&unique_obj_input_list, input); - - lnk_log(LNK_Log_InputObj, "Input Obj: %S", input->path); - } - ProfEnd(); - - ProfBegin("Load Objs From Disk"); - U64 thin_inputs_count = 0; - LNK_InputObj **thin_inputs = lnk_thin_array_from_input_obj_list(scratch.arena, unique_obj_input_list, &thin_inputs_count); - String8Array thin_input_paths = lnk_path_array_from_input_obj_array(scratch.arena, thin_inputs, thin_inputs_count); - String8Array thin_input_datas = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], config->io_flags, thin_input_paths); - for EachIndex(thin_input_idx, thin_inputs_count) { - thin_inputs[thin_input_idx]->has_disk_read_failed = thin_input_datas.v[thin_input_idx].size == 0; - thin_inputs[thin_input_idx]->data = thin_input_datas.v[thin_input_idx]; - } - ProfEnd(); - - ProfBegin("Disk Read Check"); - LNK_InputObj **input_obj_arr = lnk_array_from_input_obj_list(scratch.arena, unique_obj_input_list); - for EachIndex(input_idx, unique_obj_input_list.count) { - if (input_obj_arr[input_idx]->has_disk_read_failed) { - lnk_error(LNK_Error_InvalidPath, "unable to find obj \"%S\"", input_obj_arr[input_idx]->path); + // search for entry point in libs + if (config->entry_point_name.size == 0 && config->subsystem != PE_WindowsSubsystem_UNKNOWN) { + String8Array entry_points = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics); + for EachIndex(entry_idx, entry_points.count) { + for (LNK_LibNode *lib_n = link->libs.first; lib_n != 0; lib_n = lib_n->next) { + if (lnk_search_lib(&lib_n->data, entry_points.v[entry_idx], 0)) { + config->entry_point_name = entry_points.v[entry_idx]; + goto found_entry_in_libs; } } - ProfEnd(); - - if (lnk_get_log_status(LNK_Log_InputObj)) { - U64 input_size = 0; - for EachIndex(i, unique_obj_input_list.count) { input_size += input_obj_arr[i]->data.size; } - lnk_log(LNK_Log_InputObj, "[ Obj Input Size %M ]", input_size); - } - - LNK_ObjNodeArray obj_node_arr = lnk_obj_list_push_parallel(tp, tp_arena, &obj_list, config->machine, unique_obj_input_list.count, input_obj_arr); + } + found_entry_in_libs:; + } - // if the machine was omitted on the command line, derive machine from obj - if (config->machine == COFF_MachineType_Unknown) { - for (U64 obj_idx = 0; obj_idx < obj_node_arr.count; obj_idx += 1) { - if (obj_node_arr.v[obj_idx].data.header.machine != COFF_MachineType_Unknown) { - config->machine = obj_node_arr.v[obj_idx].data.header.machine; - break; - } + // infer subsystem from entry point name + if (config->entry_point_name.size != 0 && config->subsystem == PE_WindowsSubsystem_UNKNOWN) { + for EachIndex(subsys_idx, PE_WindowsSubsystem_COUNT) { + String8Array entry_points = pe_get_entry_point_names(config->machine, subsys_idx, config->file_characteristics); + for EachIndex(i, entry_points.count) { + if (str8_match(entry_points.v[i], config->entry_point_name, 0)) { + config->subsystem = subsys_idx; + goto subsystem_inferred_from_entry; } } + } + subsystem_inferred_from_entry:; + } - // infer minimal padding size for functions from the target machine - if (config->machine != COFF_MachineType_Unknown && config->infer_function_pad_min) { - config->function_pad_min = lnk_get_default_function_pad_min(config->machine); - config->infer_function_pad_min = 0; + // do we have an entry point name? + if (config->entry_point_name.size) { + if (is_entry_point_name_inferred) { + // redirect user entry to appropriate CRT entry + String8 crt_entry_point_name = msvcrt_ctr_entry_from_user_entry(config->entry_point_name); + config->entry_point_name = crt_entry_point_name.size ? crt_entry_point_name : config->entry_point_name; + } + + // generate undefined symbol for entry point + lnk_include_symbol(config, config->entry_point_name, 0); + + // do we have a subsystem? + if (config->subsystem != PE_WindowsSubsystem_UNKNOWN) { + // if subsystem version not specified set default values + if (config->subsystem_ver.major == 0 && config->subsystem_ver.minor == 0) { + config->subsystem_ver = lnk_get_default_subsystem_version(config->subsystem, config->machine); } - ProfBegin("Apply Directives"); - for EachIndex(obj_idx, obj_node_arr.count) { - LNK_Obj *obj = &obj_node_arr.v[obj_idx].data; - String8List raw_directives = lnk_raw_directives_from_obj(scratch.arena, obj); - LNK_DirectiveInfo directive_info = lnk_directive_info_from_raw_directives(scratch.arena, obj, raw_directives); - for EachIndex(i, ArrayCount(directive_info.v)) { - for (LNK_Directive *dir = directive_info.v[i].first; dir != 0; dir = dir->next) { - lnk_apply_cmd_option_to_config(tp_arena->v[0], config, dir->id, dir->value_list, obj); - } - } + // check subsystem version against allowed min version + Version min_subsystem_ver = lnk_get_min_subsystem_version(config->subsystem, config->machine); + if (version_compar(config->subsystem_ver, min_subsystem_ver) < 0) { + lnk_error(LNK_Error_Cmdl, "subsystem version %I64u.%I64u can't be lower than %I64u.%I64u", + config->subsystem_ver.major, config->subsystem_ver.minor, min_subsystem_ver.major, min_subsystem_ver.minor); } - ProfEnd(); - // input extern symbols from each obj to the symbol table - LNK_SymbolInputResult input_result = lnk_input_obj_symbols(tp, tp_arena, symtab, obj_node_arr); - - // schedule symbol input - lnk_symbol_list_concat_in_place(&lookup_undef_list, &unresolved_undef_list); - lnk_symbol_list_concat_in_place(&lookup_undef_list, &input_result.undef_symbols); - lnk_symbol_list_concat_in_place(&lookup_weak_list, &input_result.weak_symbols); - - // reset input objs - MemoryZeroStruct(&input_obj_list); - - ProfEnd(); - } break; - case State_InputLibs: { - ProfBegin("Input Libs"); - - // input libs from command line only - U64 input_source_opl = config->no_default_libs ? LNK_InputSource_Default: LNK_InputSource_Count; - for EachIndex(input_source, input_source_opl) { - ProfBeginV("Input Source %S", lnk_string_from_input_source(input_source)); - - Temp temp = temp_begin(scratch.arena); - LNK_InputLibList unique_input_lib_list = {0}; - - ProfBegin("Collect unique input libs"); - for (; *input_libs[input_source] != 0; input_libs[input_source] = &(*input_libs[input_source])->next) { - String8 path = (*input_libs[input_source])->string; - - if (input_source == LNK_InputSource_Default || input_source == LNK_InputSource_Obj) { - if (!str8_ends_with(path, str8_lit(".lib"), StringMatchFlag_CaseInsensitive)) { - path = push_str8f(temp.arena, "%S.lib", path); - } - if (lnk_is_lib_disallowed(disallow_lib_ht, path)) { - continue; - } - } - - if (lnk_is_lib_loaded(loaded_lib_ht, path)) { - continue; - } - - // search disk for library - String8List match_list = lnk_file_search(temp.arena, config->lib_dir_list, path); - - // warn about missing lib - if (match_list.node_count == 0) { - KeyValuePair *was_reported = hash_table_search_path(missing_lib_ht, path); - if (was_reported == 0) { - hash_table_push_path_u64(ht_arena, missing_lib_ht, path, 0); - lnk_error(LNK_Warning_FileNotFound, "unable to find library `%S`", path); - } - continue; - } - - // pick first match - String8 full_path = str8_list_first(&match_list); - - if (lnk_is_lib_loaded(loaded_lib_ht, full_path)) { - continue; - } - - // warn about multiple matches - if (match_list.node_count > 1) { - lnk_error(LNK_Warning_MultipleLibMatch, "multiple libs match `%S` (picking first match)", path); - lnk_supplement_error_list(match_list); - } - - // push library for loading - str8_list_push(temp.arena, &unique_input_lib_list, full_path); - - // save paths for future checks - lnk_push_loaded_lib(ht_arena, loaded_lib_ht, path); - lnk_push_loaded_lib(ht_arena, loaded_lib_ht, full_path); - - lnk_log(LNK_Log_InputLib, "Input Lib: %S", full_path); - } - ProfEnd(); - - ProfBegin("Disk Read Libs"); - String8Array paths = str8_array_from_list(temp.arena, &unique_input_lib_list); - String8Array datas = lnk_read_data_from_file_path_parallel(tp, tp_arena->v[0], config->io_flags, paths); - ProfEnd(); - - ProfBegin("Lib Init"); - LNK_LibNodeArray libs = lnk_lib_list_push_parallel(tp, tp_arena, &lib_index[input_source], datas, paths); - ProfEnd(); - - lnk_input_lib_symbols(tp, symtab, libs); - - if (lnk_get_log_status(LNK_Log_InputLib)) { - if (libs.count > 0) { - U64 input_size = 0; - for (U64 i = 0; i < libs.count; ++i) { input_size += libs.v[i].data.data.size; } - lnk_log(LNK_Log_InputObj, "[ Lib Input Size %M ]", input_size); - } - } - - temp_end(temp); - ProfEnd(); - } - - ProfEnd(); - } break; - case State_InputAlternateNames: { - ProfBegin("Input Alternate Names"); - COFF_ObjWriter *obj_writer = 0; - for (; *last_alt_name; last_alt_name = &(*last_alt_name)->next) { - // make object writer if it was reset - if (obj_writer == 0) { - obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); - } - - // append weak symbol - COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, (*last_alt_name)->data.to); - coff_obj_writer_push_symbol_weak(obj_writer, (*last_alt_name)->data.from, COFF_WeakExt_AntiDependency, tag); - - // flush on last directive or next directive is issued from a different obj - if ((*last_alt_name)->next == 0 || (*last_alt_name)->data.obj != (*last_alt_name)->next->data.obj) { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = (*last_alt_name)->data.obj ? (*last_alt_name)->data.obj->path : str8_lit("RADLINK"); - input->dedup_id = push_str8f(scratch.arena, "* ALTERNATE NAMES FOR %S *", input->path); - input->data = coff_obj_writer_serialize(tp_arena->v[0], obj_writer); - input->lib = (*last_alt_name)->data.obj ? (*last_alt_name)->data.obj->lib : 0; - - // reset obj writer - coff_obj_writer_release(&obj_writer); - obj_writer = 0; - } - } - ProfEnd(); - } break; - case State_PushDllHelperUndefSymbol: { - ProfBegin("Push Dll Helper Undef Symbol"); - delay_load_helper_name = mscrt_delay_load_helper_name_from_machine(config->machine); - - // TODO: config_refactor - String8List value_strings = {0}; - str8_list_push(scratch.arena, &value_strings, delay_load_helper_name); - lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0); - - ProfEnd(); - } break; - case State_PushLoadConfigUndefSymbol: { - ProfBegin("Push Load Config Undef Symbol"); - String8 load_config_name = str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME); - - // TODO: config_refactor - String8List value_strings = {0}; - str8_list_push(scratch.arena, &value_strings, load_config_name); - lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0); - - ProfEnd(); - } break; - case State_LookupUndef: { - ProfBegin("Lookup Undefined Symbols"); - - // search archives - LNK_SymbolFinderResult result = lnk_run_symbol_finder(tp, tp_arena, config, symtab, lookup_undef_list, lnk_undef_symbol_finder); // TODO: put these on temp arena - - // new inputs found - input_obj_list = result.input_obj_list; - input_import_list = result.input_import_list; - - // undefined symbols that weren't resolved - lnk_symbol_list_concat_in_place(&unresolved_undef_list, &result.unresolved_symbol_list); - - // reset input - MemoryZeroStruct(&lookup_undef_list); - - ProfEnd(); - } break; - case State_LookupWeak: { - ProfBegin("Lookup Weak Symbols"); - - // search archives - LNK_SymbolFinderResult result = lnk_run_symbol_finder(tp, tp_arena, config, symtab, lookup_weak_list, lnk_weak_symbol_finder); // TODO: put these on temp arena - - // schedule new inputs - input_obj_list = result.input_obj_list; - input_import_list = result.input_import_list; - - // weak symbols that weren't resolved - lnk_symbol_list_concat_in_place(&unresolved_weak_list, &result.unresolved_symbol_list); - - // reset input - MemoryZeroStruct(&lookup_weak_list); - - ProfEnd(); - } break; - case State_LookupEntryPoint: { - ProfBegin("Lookup Entry Point"); - LNK_Symbol *entry_point_symbol = 0; - - B32 is_entry_point_unspecified = config->entry_point_name.size == 0; - if (is_entry_point_unspecified) { - if (config->subsystem == PE_WindowsSubsystem_UNKNOWN) { - // we don't have a subsystem and entry point name, - // so we loop over every subsystem and search potential entry - // points in the symbol table - for (U64 subsys_idx = 0; subsys_idx < PE_WindowsSubsystem_COUNT; subsys_idx += 1) { - String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); - for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { - entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, name_arr.v[entry_idx]); - if (entry_point_symbol) { - config->subsystem = (PE_WindowsSubsystem)subsys_idx; - goto dbl_break; - } - } - } - - // search for potential entry points in libs - if (!entry_point_symbol) { - for (U64 subsys_idx = 0; subsys_idx < PE_WindowsSubsystem_COUNT; subsys_idx += 1) { - String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); - for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { - entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, name_arr.v[entry_idx]); - if (entry_point_symbol) { - config->subsystem = (PE_WindowsSubsystem)subsys_idx; - goto dbl_break; - } - } - } - } - - dbl_break:; - } else { - // we have subsystem but no entry point name, get potential entry point names - // and see which is in the symbol table - String8Array name_arr = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics); - for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, name_arr.v[entry_idx]); - if (symbol) { - if (entry_point_symbol) { - lnk_error(LNK_Error_EntryPoint, - "multiple entry point symbols found: %S(%S) and %S(%S)", - entry_point_symbol->name, entry_point_symbol->u.defined.obj->path, - symbol->name, symbol->u.defined.obj->path); - } else { - entry_point_symbol = symbol; - } - } - } - - // search for entry point in libs - if (!entry_point_symbol) { - for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { - entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, name_arr.v[entry_idx]); - if (entry_point_symbol) { - break; - } - } - } - } - - // redirect user entry to appropriate CRT entry - if (entry_point_symbol) { - config->entry_point_name = entry_point_symbol->name; - if (str8_match_lit("wmain", config->entry_point_name, 0)) { - config->entry_point_name = str8_lit("wmainCRTStartup"); - } else if (str8_match_lit("main", config->entry_point_name, 0)) { - config->entry_point_name = str8_lit("mainCRTStartup"); - } else if (str8_match_lit("WinMain", config->entry_point_name, 0)) { - config->entry_point_name = str8_lit("WinMainCRTStartup"); - } else if (str8_match_lit("wWinMain", config->entry_point_name, 0)) { - config->entry_point_name = str8_lit("wWinMainCRTStartup"); - } - } - } - - // generate undefined symbol so in case obj is in lib it will be linked - if (config->entry_point_name.size) { - // TODO: config_refactor - String8List value_strings = {0}; - str8_list_push(scratch.arena, &value_strings, config->entry_point_name); - lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0); - } - // no entry point, error and exit - else { - lnk_error(LNK_Error_EntryPoint, "unable to find entry point symbol"); - } - // by default terminal server is enabled for windows and console applications - if (~config->flags & LNK_ConfigFlag_NoTsAware && - ~config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) { + if (~config->flags & LNK_ConfigFlag_NoTsAware && ~config->file_characteristics & PE_ImageFileCharacteristic_FILE_DLL) { if (config->subsystem == PE_WindowsSubsystem_WINDOWS_GUI || config->subsystem == PE_WindowsSubsystem_WINDOWS_CUI) { config->dll_characteristics |= PE_DllCharacteristic_TERMINAL_SERVER_AWARE; } } - - // do we have a subsystem? - if (config->subsystem == PE_WindowsSubsystem_UNKNOWN) { - lnk_error(LNK_Error_NoSubsystem, "unknown subsystem, please use /SUBSYSTEM to set subsytem type you need"); - } - - if (config->subsystem_ver.major == 0 && config->subsystem_ver.minor == 0) { - // subsystem version not specified, set default values - config->subsystem_ver = lnk_get_default_subsystem_version(config->subsystem, config->machine); - } - - // check subsystem version against allowed min version - Version min_subsystem_ver = lnk_get_min_subsystem_version(config->subsystem, config->machine); - int ver_cmp = version_compar(config->subsystem_ver, min_subsystem_ver); - if (ver_cmp < 0) { - lnk_error(LNK_Error_Cmdl, "subsystem version %I64u.%I64u can't be lower than %I64u.%I64u", - config->subsystem_ver.major, config->subsystem_ver.minor, min_subsystem_ver.major, min_subsystem_ver.minor); - } - - ProfEnd(); - } break; - case State_ReportUnresolvedSymbols: { - // report unresolved symbols - for (LNK_SymbolNode *node = unresolved_undef_list.first; node != 0; node = node->next) { - lnk_error_obj(LNK_Error_UnresolvedSymbol, node->data->u.undef.obj, "unresolved symbol %S", node->data->name); - } - if (unresolved_undef_list.count) { - goto exit; - } - } break; - case State_InputLinkerObjs: { - { - ProfBegin("Push Linker Symbols"); - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* Linker Symbols *"); - input->dedup_id = input->path; - input->data = lnk_make_linker_obj(tp_arena->v[0], config); - ProfEnd(); - } - // warn about unused delayloads - if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { - for (String8Node *dll_name_n = config->delay_load_dll_list.first; dll_name_n != 0; dll_name_n = dll_name_n->next) { - if (!hash_table_search_path_raw(delayed_imports, dll_name_n->string)) { - lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", dll_name_n->string); - } - } - } - - // make and input delayed imports - if (delayed_imports->count) { - ProfBegin("Build Delay Import Table"); - - COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; - B32 emit_biat = config->import_table_emit_biat == LNK_SwitchState_Yes; - B32 emit_uiat = config->import_table_emit_uiat == LNK_SwitchState_Yes; - String8 *dll_names = keys_from_hash_table_string(scratch.arena, delayed_imports); - String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, delayed_imports); - - for (U64 dll_idx = 0; dll_idx < delayed_imports->count; dll_idx += 1) { - String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = input_obj_list.count; - input->data = pe_make_import_dll_obj_delayed(tp_arena->v[0], time_stamp, config->machine, dll_names[dll_idx], delay_load_helper_name, import_debug_symbols, *dll_import_headers[dll_idx], emit_biat, emit_uiat); - input->path = dll_names[dll_idx]; - input->dedup_id = input->path; - } - String8 linker_debug_symbols = lnk_make_linker_debug_symbols(tp_arena->v[0], config->machine); - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = input_obj_list.count; - input->data = pe_make_null_import_descriptor_delayed(tp_arena->v[0], time_stamp, config->machine, linker_debug_symbols); - input->path = str8_lit("* Delayed Null Import Descriptor *"); - input->dedup_id = input->path; - } - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = input_obj_list.count; - input->data = pe_make_null_thunk_data_obj_delayed(tp_arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); - input->path = str8_lit("* Delayed Null Thunk Data *"); - input->dedup_id = input->path; - } - - ProfEnd(); - } - - // make and input static imports - if (static_imports->count) { - ProfBegin("Build Static Import Table"); - - COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; - String8 *dll_names = keys_from_hash_table_string(scratch.arena, static_imports); - String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, static_imports); - for (U64 dll_idx = 0; dll_idx < static_imports->count; dll_idx += 1) { - String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = input_obj_list.count; - input->data = pe_make_import_dll_obj_static(tp_arena->v[0], time_stamp, config->machine, dll_names[dll_idx], import_debug_symbols, *dll_import_headers[dll_idx]); - input->path = dll_names[dll_idx]; - input->dedup_id = dll_names[dll_idx]; - } - String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = input_obj_list.count; - input->data = pe_make_null_import_descriptor_obj(tp_arena->v[0], time_stamp, config->machine, linker_debug_symbols); - input->path = str8_lit("* Null Import Descriptor *"); - input->dedup_id = input->path; - } - { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->input_idx = input_obj_list.count; - input->data = pe_make_null_thunk_data_obj(tp_arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); - input->path = str8_lit("* Null Thunk Data *"); - input->dedup_id = input->path; - } - - ProfEnd(); - } - - if (config->export_symbol_list.count) { - ProfBegin("Build Export Table"); - - PE_ExportParseList resolved_exports = {0}; - for (PE_ExportParseNode *exp_n = config->export_symbol_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { - exp_n_next = exp_n->next; - PE_ExportParse *exp = &exp_n->data; - - if (str8_match(exp->name, config->entry_point_name, 0)) { - lnk_error_with_loc(LNK_Warning_TryingToExportEntryPoint, exp->obj_path, exp->lib_path, "exported entry point \"%S\"", exp->name); - } - if (str8_match(exp->alias, config->entry_point_name, 0)) { - lnk_error_with_loc(LNK_Warning_TryingToExportEntryPoint, exp->obj_path, exp->lib_path, "alias exports entry point \"%S=%S\"", exp->name, exp->alias); - continue; - } - - if (!exp->is_forwarder) { - // filter out unresolved exports - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, exp_n->data.name); - if (symbol == 0) { - lnk_error_with_loc(LNK_Warning_IllExport, exp->obj_path, exp->lib_path, "unresolved export symbol %S\n", exp->name); - continue; - } - } - - // push resolved export - pe_export_parse_list_push_node(&resolved_exports, exp_n); - } - - PE_FinalizedExports finalized_exports = pe_finalize_export_list(scratch.arena, resolved_exports); - String8 edata_obj = pe_make_edata_obj(tp_arena->v[0], str8_skip_last_slash(config->image_name), COFF_TimeStamp_Max, config->machine, finalized_exports); - - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* Exports *"); - input->dedup_id = input->path; - input->data = edata_obj; - - ProfEnd(); - } - - { - String8List res_data_list = {0}; - String8List res_path_list = {0}; - - // do we have manifest deps passed through pragma alone? - LNK_ManifestOpt manifest_opt = config->manifest_opt; - if (config->manifest_dependency_list.node_count > 0 && manifest_opt == LNK_ManifestOpt_Null) { - manifest_opt = LNK_ManifestOpt_Embed; - } - - switch (manifest_opt) { - case LNK_ManifestOpt_Embed: { - ProfBegin("Embed Manifest"); - // TODO: currently we convert manifest to res and parse res again, this unnecessary instead push manifest - // resource to the tree directly - String8 manifest_data = lnk_manifest_from_inputs(scratch.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, config->input_list[LNK_Input_Manifest], config->manifest_dependency_list); - String8 manifest_res = pe_make_manifest_resource(scratch.arena, *config->manifest_resource_id, manifest_data); - str8_list_push(scratch.arena, &res_data_list, manifest_res); - str8_list_push(scratch.arena, &res_path_list, str8_lit("* Manifest *")); - ProfEnd(); - } break; - case LNK_ManifestOpt_WriteToFile: { - ProfBeginDynamic("Write Manifest To: %.*s", str8_varg(config->manifest_name)); - Temp temp = temp_begin(scratch.arena); - String8 manifest_data = lnk_manifest_from_inputs(temp.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, config->input_list[LNK_Input_Manifest], config->manifest_dependency_list); - lnk_write_data_to_file_path(config->manifest_name, str8_zero(), manifest_data); - temp_end(temp); - ProfEnd(); - } break; - case LNK_ManifestOpt_Null: { - Assert(config->input_list[LNK_Input_Manifest].node_count == 0); - Assert(config->manifest_dependency_list.node_count == 0); - } break; - case LNK_ManifestOpt_No: { - // omit manifest generation - } break; - } - - ProfBegin("Load .res files from disk"); - for (String8Node *node = config->input_list[LNK_Input_Res].first; node != 0; node = node->next) { - String8 res_data = lnk_read_data_from_file_path(scratch.arena, config->io_flags, node->string); - if (res_data.size > 0) { - if (pe_is_res(res_data)) { - str8_list_push(scratch.arena, &res_data_list, res_data); - String8 stable_res_path = lnk_make_full_path(scratch.arena, config->path_style, config->work_dir, node->string); - str8_list_push(scratch.arena, &res_path_list, stable_res_path); - } else { - lnk_error(LNK_Error_LoadRes, "file is not of RES format: %S", node->string); - } - } else { - lnk_error(LNK_Error_LoadRes, "unable to open res file: %S", node->string); - } - } - ProfEnd(); - - if (res_data_list.node_count > 0) { - ProfBegin("Build * Resources *"); - - String8 obj_name = str8_lit("* Resources *"); - String8 obj_data = lnk_make_res_obj(tp_arena->v[0], - res_data_list, - res_path_list, - config->machine, - config->time_stamp, - config->work_dir, - config->path_style, - obj_name); - - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->dedup_id = obj_name; - input->path = obj_name; - input->data = obj_data; - - ProfEnd(); - } - } - - if (lnk_do_debug_info(config)) { - { - ProfBegin("Build * Linker * Obj"); - - String8 obj_name = str8_lit("* Linker *"); - String8 raw_cmd_line = str8_list_join(scratch.arena, &config->raw_cmd_line, &(StringJoin){ str8_lit_comp(""), str8_lit_comp(" "), str8_lit_comp("") }); - String8 obj_data = lnk_make_linker_coff_obj(tp_arena->v[0], config->time_stamp, config->machine, config->work_dir, config->image_name, config->pdb_name, raw_cmd_line, obj_name); - - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->dedup_id = obj_name; - input->path = obj_name; - input->data = obj_data; - - ProfEnd(); - } - - { - ProfBegin("Build * Debug Directories *"); - if (config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null) { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* Debug Directory PDB *"); - input->dedup_id = input->path; - input->data = pe_make_debug_directory_pdb_obj(tp_arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->pdb_alt_path); - } - if (config->rad_debug == LNK_SwitchState_Yes) { - LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); - input->path = str8_lit("* Debug Directory RDI *"); - input->dedup_id = input->path; - input->data = pe_make_debug_directory_rdi_obj(tp_arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->rad_debug_alt_path); - } - ProfEnd(); - } - } - } break; + // entry point found! + link->try_to_resolve_entry_point = 0; + } else { + lnk_error(LNK_Error_NoSubsystem, "unknown subsystem, please use /SUBSYSTEM to set subsytem type you need"); } } - - if (*last_disallow_lib != 0) { - state_list_push(scratch.arena, state_list, State_InputDisallowLibs); - continue; - } - if (input_import_list.count) { - state_list_push(scratch.arena, state_list, State_InputImports); - continue; - } - if (*last_include_symbol != 0) { - state_list_push(scratch.arena, state_list, State_InputSymbols); - continue; - } - if (*last_alt_name != 0) { - state_list_push(scratch.arena, state_list, State_InputAlternateNames); - continue; - } - if (input_obj_list.count) { - state_list_push(scratch.arena, state_list, State_InputObjs); - continue; - } - { - B32 have_pending_lib_inputs = 0; - for (U64 i = 0; i < ArrayCount(input_libs); ++i) { - if (*input_libs[i] != 0) { - have_pending_lib_inputs = 1; - break; - } - } - if (have_pending_lib_inputs) { - state_list_push(scratch.arena, state_list, State_InputLibs); - continue; - } - } - if (lookup_undef_list.count) { - state_list_push(scratch.arena, state_list, State_LookupUndef); - continue; - } - if (lookup_weak_list.count) { - state_list_push(scratch.arena, state_list, State_LookupWeak); - continue; - } - if (unresolved_weak_list.count) { - // we can't find strong definitions for unresolved weak symbols - // so now we have to use fallback symbols - MemoryZeroStruct(&unresolved_weak_list); - continue; - } - if (entry_point_lookup_attempts == 0) { - state_list_push(scratch.arena, state_list, State_LookupEntryPoint); - entry_point_lookup_attempts += 1; - continue; - } - if (input_linker_objs) { - input_linker_objs = 0; - state_list_push(scratch.arena, state_list, State_InputLinkerObjs); - continue; - } - if (unresolved_undef_list.count) { - if (report_unresolved_symbols) { - report_unresolved_symbols = 0; - state_list_push(scratch.arena, state_list, State_ReportUnresolvedSymbols); - continue; - } - } - - break; } - // pass over symbol table and replace weak symbols without a strong definition with fallback definitions - lnk_finalize_weak_symbols(tp_arena, tp, symtab); - - // log - { - if (lnk_get_log_status(LNK_Log_InputObj)) { - U64 total_input_size = 0; - for (LNK_ObjNode *obj_n = obj_list.first; obj_n != 0; obj_n = obj_n->next) { total_input_size += obj_n->data.data.size; } - lnk_log(LNK_Log_InputObj, "[Total Obj Input Size %M]", total_input_size); - } - if (lnk_get_log_status(LNK_Log_InputLib)) { - U64 total_input_size = 0; - for (U64 i = 0; i < ArrayCount(lib_index); ++i) { - LNK_LibList list = lib_index[i]; - for (LNK_LibNode *lib_n = list.first; lib_n != 0; lib_n = lib_n->next) { total_input_size += lib_n->data.data.size; } - } - lnk_log(LNK_Log_InputLib, "[Total Lib Input Size %M]", total_input_size); - } - } - - exit:; - // TODO: include symbol list - LNK_LinkContext link_ctx = {0}; - link_ctx.symtab = symtab; - link_ctx.objs_count = obj_list.count; - link_ctx.objs = lnk_array_from_obj_list(tp_arena->v[0], obj_list); - MemoryCopyTyped(&link_ctx.lib_index[0], &lib_index[0], ArrayCount(lib_index)); - - ProfEnd(); scratch_end(scratch); - return link_ctx; - -#undef state_list_push -#undef state_list_pop + ProfEnd(); +} + +internal void +lnk_queue_lib_member(Arena *arena, LNK_LibMemberRefList *queued_members, LNK_Symbol *link_symbol, LNK_Lib *lib, U32 member_idx) +{ + B32 was_linked = lnk_lib_set_link_symbol(lib, member_idx, link_symbol); + if (was_linked) { + LNK_LibMemberRef *member_ref = push_array(arena, LNK_LibMemberRef, 1); + member_ref->lib = lib; + member_ref->member_idx = member_idx; + lnk_lib_member_ref_list_push_node(queued_members, member_ref); + } +} + +internal +THREAD_POOL_TASK_FUNC(lnk_search_lib_task) +{ + LNK_SearchLibTask *task = raw_task; + LNK_Lib *lib = task->lib; + LNK_SymbolTable *symtab = task->symtab; + B32 search_anti_deps = task->search_anti_deps; + LNK_LibMemberRefList *member_ref_list = &task->member_ref_lists[task_id]; + + for EachNode(c, LNK_SymbolHashTrieChunk, symtab->search_chunks[task_id].first) { + for EachIndex(i, c->count) { + LNK_Symbol *symbol = c->v[i].symbol; + + LNK_ObjSymbolRef symbol_ref = lnk_ref_from_symbol(symbol); + COFF_ParsedSymbol symbol_parsed = lnk_parsed_from_symbol(symbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); + if (symbol_interp == COFF_SymbolValueInterp_Undefined) { + U32 member_idx; + if (lnk_search_lib(lib, symbol->name, &member_idx)) { + lnk_queue_lib_member(arena, member_ref_list, symbol, lib, member_idx); + } + } else if (symbol_interp == COFF_SymbolValueInterp_Weak) { + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol_ref.obj->header.is_big_obj); + if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { + U32 member_idx; + if (lnk_search_lib(lib, symbol->name, &member_idx)) { + lnk_queue_lib_member(arena, member_ref_list, symbol, lib, member_idx); + } + } else if (weak_ext->characteristics == COFF_WeakExt_AntiDependency) { + if (search_anti_deps) { + LNK_ObjSymbolRef dep_symbol = {0}; + if (lnk_resolve_weak_symbol(symtab, symbol_ref, &dep_symbol)) { + COFF_ParsedSymbol dep_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dep_symbol.obj, dep_symbol.symbol_idx); + COFF_SymbolValueInterpType dep_interp = coff_interp_from_parsed_symbol(dep_parsed); + if (dep_interp == COFF_SymbolValueInterp_Weak) { + U32 member_idx; + if (lnk_search_lib(lib, symbol_parsed.name, &member_idx)) { + lnk_queue_lib_member(arena, member_ref_list, symbol, lib, member_idx); + } + } + } + } + } + } + } + } +} + +internal void +lnk_link_inputs(TP_Context *tp, + TP_Arena *arena, + LNK_Config *config, + LNK_Inputer *inputer, + LNK_SymbolTable *symtab, + LNK_Link *link) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(arena->v, arena->count); + + LNK_LibMemberRefList *member_ref_lists = push_array(scratch.arena, LNK_LibMemberRefList, tp->worker_count); + B32 search_anti_deps = 0; + for (U64 resolved_members_count = 0; ; resolved_members_count = 0) { + lnk_load_inputs(tp, arena, config, inputer, symtab, link); + + for EachNode(lib_n, LNK_LibNode, link->libs.first) { + LNK_Lib *lib = &lib_n->data; + + ProfBeginV("Search %S", str8_skip_last_slash(lib->path)); + do { + lnk_load_inputs(tp, arena, config, inputer, symtab, link); + + // search symbols in lib + MemoryZeroTyped(member_ref_lists, tp->worker_count); + tp_for_parallel(tp, arena, tp->worker_count, lnk_search_lib_task, &(LNK_SearchLibTask){ .search_anti_deps = search_anti_deps, .lib = lib, .symtab = symtab, .member_ref_lists = member_ref_lists }); + + LNK_LibMemberRefList queued_members = {0}; + lnk_lib_member_ref_list_concat_in_place_array(&queued_members, member_ref_lists, tp->worker_count); + + // sort library member refs to match the order of their appearance in obj symbol tables + LNK_LibMemberRef **member_refs = lnk_array_from_lib_member_list(scratch.arena, queued_members); + //qsort(member_refs, queued_members.count, sizeof(member_refs[0]), lnk_lib_member_ref_compar); + radsort(member_refs, queued_members.count, lnk_lib_member_ref_is_before); + + if (queued_members.count) { + lnk_log(LNK_Log_Links, "Searching %S:", lib_n->data.path); + + for EachIndex(i, queued_members.count) { + Temp temp = temp_begin(scratch.arena); + + LNK_LibMemberRef *member_ref = member_refs[i]; + LNK_Lib *lib = member_ref->lib; + LNK_Symbol *link_symbol = lib->member_links[member_ref->member_idx]; + + COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, lib->member_offsets[member_ref->member_idx]); + COFF_DataType member_type = coff_data_type_from_data(member_info.data); + String8 member_name = coff_decode_member_name(lib->long_names, member_info.header.name); + + U64 refs_count = 0; + LNK_ObjSymbolRef **refs = lnk_ref_from_symbol_many(temp.arena, link_symbol, &refs_count); + lnk_log(LNK_Log_Links, "\tFound %S in %S", link_symbol->name, str8_skip_last_slash(member_name)); + for EachIndex(i, refs_count) { + lnk_log(LNK_Log_Links, "\t\tReferenced in %S", lnk_loc_from_obj(temp.arena, refs[i]->obj)); + } + + temp_end(temp); + } + } + + // push inputs for lib member refs + for EachIndex(i, queued_members.count) { + LNK_LibMemberRef *member_ref = member_refs[i]; + U64 member_idx = member_ref->member_idx; + + // parse member info + COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, lib->member_offsets[member_idx]); + COFF_DataType member_type = coff_data_type_from_data(member_info.data); + String8 member_name = coff_decode_member_name(lib->long_names, member_info.header.name); + + switch (member_type) { + case COFF_DataType_Import: { + LNK_Symbol *link_symbol = lib->member_links[member_idx]; + + LNK_Symbol *import_symbols[2] = {0}; + if (str8_starts_with(link_symbol->name, str8_lit("__imp_"))) { + import_symbols[0] = link_symbol; + import_symbols[1] = lnk_symbol_table_search(symtab, str8_skip(link_symbol->name, str8_lit("__imp_").size)); + } else { + Temp temp = temp_begin(scratch.arena); + String8 imp_name = push_str8f(temp.arena, "__imp_%S", link_symbol->name); + import_symbols[0] = lnk_symbol_table_search(symtab, imp_name); + import_symbols[1] = link_symbol; + temp_end(temp); + } + + for EachIndex(i, ArrayCount(import_symbols)) { + LNK_Symbol *import_symbol = import_symbols[i]; + if (import_symbol == 0) { continue; } + + LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, str8_lit(LNK_IMPORT_STUB)); + + // same import symbol must never be queued more than once, if it is, there is a bug in the link set logic + AssertAlways(import_symbol->refs != import_stub->refs); + + // replace the import symbol with a stub, which is later replaced with the real import symbol once import obj is ready + import_symbol->refs = import_stub->refs; + } + + // push import member for import obj generation + lnk_lib_member_ref_list_push_node(&link->imports, member_ref); + } break; + case COFF_DataType_BigObj: + case COFF_DataType_Obj: { + if (lib->type == COFF_Archive_Thin) { + // obj path in thin archive is relative to the directory with lib + String8List obj_path_list = {0}; + str8_list_push(scratch.arena, &obj_path_list, str8_chop_last_slash(lib->path)); + str8_list_push(scratch.arena, &obj_path_list, member_name); + String8 obj_path = str8_path_list_join_by_style(inputer->arena, &obj_path_list, config->path_style); + + lnk_inputer_push_obj_thin(inputer, member_ref, obj_path); + } else { + lnk_inputer_push_obj(inputer, member_ref, member_name, member_info.data); + } + } break; + case COFF_DataType_Null: break; + default: { InvalidPath; } break; + } + } + + resolved_members_count += queued_members.count; + } while (lnk_inputer_has_items(inputer)); + ProfEnd(); + } + + if (resolved_members_count == 0) { + search_anti_deps = 0; + + // replace undefined symbols that have an alternate name with a weak symbol + for (LNK_AltNameNode *alt_name_n = config->alt_name_list.first; alt_name_n != 0; alt_name_n = alt_name_n->next) { + LNK_SymbolHashTrie *symbol_ht = lnk_symbol_table_search_(symtab, alt_name_n->v.from); + if (symbol_ht) { + COFF_SymbolValueInterpType interp = lnk_interp_from_symbol(symbol_ht->symbol); + if (interp == COFF_SymbolValueInterp_Undefined) { + // clear out slot so weak symbol can replace undefined symbol (general rule is + // weak symbol is not allowed to replace undefined) + LNK_Symbol *undef_symbol = symbol_ht->symbol; + symbol_ht->symbol = 0; + + // make obj with alternamte name symbol + String8 alt_name_obj_data; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); + COFF_ObjSymbol *from_symbol = coff_obj_writer_push_symbol_weak(obj_writer, alt_name_n->v.from, COFF_WeakExt_SearchLibrary, 0); + COFF_ObjSymbol *to_symbol = coff_obj_writer_push_symbol_weak(obj_writer, alt_name_n->v.to, COFF_WeakExt_AntiDependency, from_symbol); + coff_obj_writer_set_default_symbol(from_symbol, to_symbol); + alt_name_obj_data = coff_obj_writer_serialize(arena->v[0], obj_writer); + coff_obj_writer_release(&obj_writer); + } + + LNK_Obj *obj_with_alt_name = alt_name_n->v.obj; + String8 obj_with_alt_name_path = obj_with_alt_name ? obj_with_alt_name->path : str8_lit("RADLINK"); + lnk_inputer_push_obj_linkgen(inputer, obj_with_alt_name ? obj_with_alt_name->link_member : 0, obj_with_alt_name_path, alt_name_obj_data); + + search_anti_deps = 1; + } + } + } + + resolved_members_count = lnk_inputer_has_items(inputer); + } + + if (resolved_members_count == 0) { break; } + } + + scratch_end(scratch); + ProfEnd(); +} + +internal LNK_Link * +lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(arena->v, arena->count); + + // + // init link context + // + LNK_Link *link = push_array(arena->v[0], LNK_Link, 1); + link->last_symbol_input = &link->objs.first; + link->last_include = &config->include_symbol_list.first; + link->last_default_lib = &config->input_default_lib_list.first; + link->last_obj_lib = &config->input_obj_lib_list.first; + link->last_cmd_lib = &config->input_list[LNK_Input_Lib].first; + link->try_to_resolve_entry_point = 1; + + // input :null_obj + String8 null_obj = lnk_make_null_obj(inputer->arena); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Null *"), null_obj); + + // input objs on command line + for (String8Node *obj_path = config->input_list[LNK_Input_Obj].first; obj_path != 0; obj_path = obj_path->next) { + lnk_inputer_push_obj_thin(inputer, 0, obj_path->string); + } + + // input libs from command line + for (; *link->last_cmd_lib; link->last_cmd_lib = &(*link->last_cmd_lib)->next) { + lnk_inputer_push_lib_thin(inputer, config, LNK_InputSource_CmdLine, (*link->last_cmd_lib)->string); + } + + // link inputer + lnk_link_inputs(tp, arena, config, inputer, symtab, link); + + // TODO: need to figure out under what condition to include load config + //lnk_include_symbol(config, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME), 0); + + { + ProfBegin("Push Linker Symbols"); + String8 linker_symbols_obj = lnk_make_linker_obj(arena->v[0], config); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Linker Symbols *"), linker_symbols_obj); + ProfEnd(); + } + + // + // make imports + // + { + HashTable *static_imports_ht = hash_table_init(scratch.arena, 0x1000); + HashTable *delayed_imports_ht = hash_table_init(scratch.arena, 0x1000); + String8List delayed_dll_names = {0}; + String8List static_dll_names = {0}; + + for EachNode(member_ref, LNK_LibMemberRef, link->imports.first) { + LNK_Lib *lib = member_ref->lib; + U64 member_idx = member_ref->member_idx; + LNK_Symbol *link_symbol = lib->member_links[member_idx]; + + COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, lib->member_offsets[member_idx]); + COFF_DataType member_type = coff_data_type_from_data(member_info.data); + String8 member_name = coff_decode_member_name(lib->long_names, member_info.header.name); + COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(member_info.data); + + // import machine compat check + if (import_header.machine != config->machine) { + LNK_ObjSymbolRef ref = lnk_ref_from_symbol(link_symbol); + lnk_error_obj(LNK_Error_IncompatibleMachine, + ref.obj, + "symbol %S pulls-in import from %S with an incompatible machine %S (expected machine %S)", + link_symbol->name, + str8_chop_last_slash(lib->path), + coff_string_from_machine_type(import_header.machine), + coff_string_from_machine_type(config->machine)); + break; + } + + // find DLL with import symbols + B32 is_delay_load = lnk_is_dll_delay_load(config, import_header.dll_name); + String8List *dll_names = is_delay_load ? &delayed_dll_names : &static_dll_names; + HashTable *imports_ht = is_delay_load ? delayed_imports_ht : static_imports_ht; + PE_MakeImportList *import_symbols = hash_table_search_path_raw(imports_ht, import_header.dll_name); + + // create record for a first time-DLL + if (import_symbols == 0) { + import_symbols = push_array(scratch.arena, PE_MakeImportList, 1); + str8_list_push(scratch.arena, dll_names, import_header.dll_name); + hash_table_push_path_raw(scratch.arena, imports_ht, import_header.dll_name, import_symbols); + } + + B32 make_jump_thunk = 1; + if (str8_starts_with(link_symbol->name, str8_lit("__imp_"))) { + LNK_Symbol *thunk_symbol = lnk_symbol_table_search(symtab, str8_skip(link_symbol->name, str8_lit("__imp_").size)); + make_jump_thunk = thunk_symbol != 0; + } + + // push make import info + pe_make_import_header_list_push(scratch.arena, import_symbols, (PE_MakeImport){ .header = member_info.data, .make_jump_thunk = make_jump_thunk }); + } + AssertAlways(delayed_dll_names.node_count == delayed_imports_ht->count); + AssertAlways(static_dll_names.node_count == static_imports_ht->count); + + // make and input delayed imports + if (delayed_imports_ht->count) { + ProfBegin("Build Delay Import Table"); + + COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; + B32 emit_biat = config->import_table_emit_biat == LNK_SwitchState_Yes; + B32 emit_uiat = config->import_table_emit_uiat == LNK_SwitchState_Yes; + + for (String8Node *dll_name_n = delayed_dll_names.first; dll_name_n != 0; dll_name_n = dll_name_n->next) { + PE_MakeImportList *imports = hash_table_search_path_raw(delayed_imports_ht, dll_name_n->string); + String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_name_n->string); + String8 import_obj = pe_make_import_dll_obj_delayed(arena->v[0], time_stamp, config->machine, dll_name_n->string, config->delay_load_helper_name, import_debug_symbols, *imports, emit_biat, emit_uiat); + lnk_inputer_push_obj(inputer, 0, dll_name_n->string, import_obj); + } + + String8 linker_debug_symbols = lnk_make_linker_debug_symbols(arena->v[0], config->machine); + String8 null_desc_obj = pe_make_null_import_descriptor_delayed(arena->v[0], time_stamp, config->machine, linker_debug_symbols); + String8 null_thunk_obj = pe_make_null_thunk_data_obj_delayed(arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); + lnk_inputer_push_obj(inputer, 0, str8_lit("* Delayed Null Import Descriptor *"), null_desc_obj); + lnk_inputer_push_obj(inputer, 0, str8_lit("* Delayed Null Thunk Data *"), null_thunk_obj); + + ProfEnd(); + } + + // make and input static imports + if (static_imports_ht->count) { + ProfBegin("Build Static Import Table"); + + COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; + + for (String8Node *dll_name_n = static_dll_names.first; dll_name_n != 0; dll_name_n = dll_name_n->next) { + PE_MakeImportList *imports = hash_table_search_path_raw(static_imports_ht, dll_name_n->string); + String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_name_n->string); + String8 import_obj = pe_make_import_dll_obj_static(arena->v[0], time_stamp, config->machine, dll_name_n->string, import_debug_symbols, *imports); + lnk_inputer_push_obj(inputer, 0, dll_name_n->string, import_obj); + } + + String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); + String8 null_desc_obj = pe_make_null_import_descriptor_obj(arena->v[0], time_stamp, config->machine, linker_debug_symbols); + String8 null_thunk_obj = pe_make_null_thunk_data_obj(arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Null Import Descriptor *"), null_desc_obj); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Null Thunk Data *"), null_thunk_obj); + + ProfEnd(); + } + + // warn about unused delayloads + if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { + for (String8Node *dll_name_n = config->delay_load_dll_list.first; dll_name_n != 0; dll_name_n = dll_name_n->next) { + if (!hash_table_search_path_raw(delayed_imports_ht, dll_name_n->string)) { + lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", dll_name_n->string); + } + } + } + } + + if (config->export_symbol_list.count) { + ProfBegin("Build Export Table"); + + PE_ExportParseList resolved_exports = {0}; + for (PE_ExportParseNode *exp_n = config->export_symbol_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) { + exp_n_next = exp_n->next; + PE_ExportParse *exp = &exp_n->data; + + if (str8_match(exp->name, config->entry_point_name, 0)) { + lnk_error_with_loc(LNK_Warning_TryingToExportEntryPoint, exp->obj_path, exp->lib_path, "exported entry point \"%S\"", exp->name); + } + if (str8_match(exp->alias, config->entry_point_name, 0)) { + lnk_error_with_loc(LNK_Warning_TryingToExportEntryPoint, exp->obj_path, exp->lib_path, "alias exports entry point \"%S=%S\"", exp->name, exp->alias); + continue; + } + + if (!exp->is_forwarder) { + // filter out unresolved exports + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, exp_n->data.name); + if (symbol == 0) { + lnk_error_with_loc(LNK_Warning_IllExport, exp->obj_path, exp->lib_path, "unresolved export symbol %S\n", exp->name); + continue; + } + } + + // push resolved export + pe_export_parse_list_push_node(&resolved_exports, exp_n); + } + + PE_FinalizedExports finalized_exports = pe_finalize_export_list(scratch.arena, resolved_exports); + String8 edata_obj = pe_make_edata_obj(arena->v[0], str8_skip_last_slash(config->image_name), COFF_TimeStamp_Max, config->machine, finalized_exports); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Exports *"), edata_obj); + + ProfEnd(); + } + + { + String8List res_data_list = {0}; + String8List res_path_list = {0}; + + // do we have manifest deps passed through pragma alone? + LNK_ManifestOpt manifest_opt = config->manifest_opt; + if (config->manifest_dependency_list.node_count > 0 && manifest_opt == LNK_ManifestOpt_Null) { + manifest_opt = LNK_ManifestOpt_Embed; + } + + switch (manifest_opt) { + case LNK_ManifestOpt_Embed: { + ProfBegin("Embed Manifest"); + // TODO: currently we convert manifest to res and parse res again, this unnecessary instead push manifest + // resource to the tree directly + String8 manifest_data = lnk_manifest_from_inputs(scratch.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, config->input_list[LNK_Input_Manifest], config->manifest_dependency_list); + String8 manifest_res = pe_make_manifest_resource(scratch.arena, *config->manifest_resource_id, manifest_data); + str8_list_push(scratch.arena, &res_data_list, manifest_res); + str8_list_push(scratch.arena, &res_path_list, str8_lit("* Manifest *")); + ProfEnd(); + } break; + case LNK_ManifestOpt_WriteToFile: { + ProfBeginDynamic("Write Manifest To: %.*s", str8_varg(config->manifest_name)); + Temp temp = temp_begin(scratch.arena); + String8 manifest_data = lnk_manifest_from_inputs(temp.arena, config->io_flags, config->mt_path, config->manifest_name, config->manifest_uac, config->manifest_level, config->manifest_ui_access, config->input_list[LNK_Input_Manifest], config->manifest_dependency_list); + lnk_write_data_to_file_path(config->manifest_name, str8_zero(), manifest_data); + temp_end(temp); + ProfEnd(); + } break; + case LNK_ManifestOpt_Null: { + Assert(config->input_list[LNK_Input_Manifest].node_count == 0); + Assert(config->manifest_dependency_list.node_count == 0); + } break; + case LNK_ManifestOpt_No: { + // omit manifest generation + } break; + } + + ProfBegin("Load .res files from disk"); + for (String8Node *node = config->input_list[LNK_Input_Res].first; node != 0; node = node->next) { + String8 res_data = lnk_read_data_from_file_path(scratch.arena, config->io_flags, node->string); + if (res_data.size > 0) { + if (pe_is_res(res_data)) { + str8_list_push(scratch.arena, &res_data_list, res_data); + String8 stable_res_path = lnk_make_full_path(scratch.arena, config->path_style, config->work_dir, node->string); + str8_list_push(scratch.arena, &res_path_list, stable_res_path); + } else { + lnk_error(LNK_Error_LoadRes, "file is not of RES format: %S", node->string); + } + } else { + lnk_error(LNK_Error_LoadRes, "unable to open res file: %S", node->string); + } + } + ProfEnd(); + + if (res_data_list.node_count > 0) { + ProfBegin("Build * Resources *"); + String8 obj_name = str8_lit("* Resources *"); + String8 obj_data = lnk_make_res_obj(arena->v[0], res_data_list, res_path_list, config->machine, config->time_stamp, config->work_dir, config->path_style, obj_name); + lnk_inputer_push_obj_linkgen(inputer, 0, obj_name, obj_data); + ProfEnd(); + } + } + + if (lnk_do_debug_info(config)) { + { + ProfBegin("Build * Linker * Obj"); + String8 obj_name = str8_lit("* Linker *"); + String8 raw_cmd_line = str8_list_join(scratch.arena, &config->raw_cmd_line, &(StringJoin){ str8_lit_comp(""), str8_lit_comp(" "), str8_lit_comp("") }); + String8 obj_data = lnk_make_linker_coff_obj(arena->v[0], config->time_stamp, config->machine, config->work_dir, config->image_name, config->pdb_name, raw_cmd_line, obj_name); + lnk_inputer_push_obj_linkgen(inputer, 0, obj_name, obj_data); + ProfEnd(); + } + + ProfBegin("Build * Debug Directories *"); + if (config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null) { + String8 pdb_dir_obj = pe_make_debug_directory_pdb_obj(arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->pdb_alt_path); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Debug Directory PDB *"), pdb_dir_obj); + } + if (config->rad_debug == LNK_SwitchState_Yes) { + String8 rdi_dir_obj = pe_make_debug_directory_rdi_obj(arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->rad_debug_alt_path); + lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Debug Directory RDI *"), rdi_dir_obj); + } + ProfEnd(); + } + + // + // link linker made objs + // + lnk_link_inputs(tp, arena, config, inputer, symtab, link); + + // + // finalize symbol table + // + lnk_replace_weak_with_default_symbols(tp, symtab); + + // + // was entry point resolved? + // + if (config->entry_point_name.size == 0 || link->try_to_resolve_entry_point) { + lnk_error(LNK_Error_EntryPoint, "unable to find entry point symbol"); + } + + // + // report unresolved symbols + // + { + ProfBegin("Report Unresolved Symbols"); + + U64 unresolved_symbols_count = 0; + LNK_Symbol **unresolved_symbols = 0; + { + U64 chunks_count = 0; + LNK_SymbolHashTrieChunk **chunks = lnk_array_from_symbol_hash_trie_chunk_list(scratch.arena, symtab->chunks, symtab->arena->count, &chunks_count); + + for EachIndex(chunk_idx, chunks_count) { + LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; + for EachIndex(i, chunk->count) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol); + if (symbol_interp == COFF_SymbolValueInterp_Undefined) { + unresolved_symbols_count += 1; + } + } + } + + unresolved_symbols = push_array(scratch.arena, LNK_Symbol *, unresolved_symbols_count); + if (unresolved_symbols_count) { + U64 cursor = 0; + for EachIndex(chunk_idx, chunks_count) { + LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; + for EachIndex(i, chunk->count) { + LNK_Symbol *symbol = chunk->v[i].symbol; + if (lnk_interp_from_symbol(symbol) == COFF_SymbolValueInterp_Undefined) { + unresolved_symbols[cursor++] = chunk->v[i].symbol; + } + } + } + } + + radsort(unresolved_symbols, unresolved_symbols_count, lnk_symbol_ptr_is_before); + } + + for EachIndex(i, unresolved_symbols_count) { + LNK_Symbol *symbol = unresolved_symbols[i]; + + if (i > config->unresolved_symbol_limit) { + lnk_error(LNK_Error_UnresolvedSymbol, "too many unresolved symbol errors, stopping now"); + break; + } + + String8List supp_info = {0}; + { + U64 refs_count = 0; + LNK_ObjSymbolRef **refs = lnk_ref_from_symbol_many(scratch.arena, symbol, &refs_count); + for EachIndex(ref_idx, refs_count) { + LNK_ObjSymbolRef *ref = refs[ref_idx]; + LNK_Obj *obj = ref->obj; + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + String8 string_table = lnk_coff_string_table_from_obj(obj); + + CV_DebugS debug_s = {0}; + CV_LinesAccel *debug_lines = 0; + String8 debug_checksums = {0}; + String8 debug_strings = {0}; + + for EachIndex(sect_idx, obj->header.section_count_no_null) { + COFF_SectionHeader *section_header = §ion_table[sect_idx]; + if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } + + String8 section_name = coff_name_from_section_header(string_table, section_header); + U64 section_number = sect_idx+1; + COFF_RelocArray relocs = lnk_coff_relocs_from_section_header(obj, section_header); + for EachIndex(reloc_idx, relocs.count) { + if (supp_info.node_count > config->unresolved_symbol_ref_limit) { + str8_list_pushf(scratch.arena, &supp_info, "too many unresolved symbol references reported, stopping now"); + goto next_undefined_symbol; + } + COFF_Reloc *reloc = &relocs.v[reloc_idx]; + if (reloc->isymbol == ref->symbol_idx) { + U64 line_matches_count = 0; + CV_Line *line_matches = 0; + if (config->map_lines_for_unresolved_symbols == LNK_SwitchState_Yes) { + if (debug_lines == 0) { + debug_s = lnk_debug_s_from_obj(scratch.arena, obj); + String8List raw_checksums = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms); + String8List raw_strings = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_StringTable); + debug_lines = cv_lines_accel_from_debug_s(scratch.arena, debug_s); + debug_checksums = str8_list_first(&raw_checksums); + debug_strings = str8_list_first(&raw_strings); + } + line_matches_count = 0; + line_matches = cv_line_from_voff(debug_lines, reloc->apply_off, &line_matches_count); + } + + if (line_matches) { + for EachIndex(i, line_matches_count) { + CV_Line line = line_matches[i]; + CV_C13Checksum checksum = {0}; + String8 file_name = {0}; + str8_deserial_read_struct(debug_checksums, line.file_off, &checksum); + str8_deserial_read_cstr(debug_strings, checksum.name_off, &file_name); + str8_list_pushf(scratch.arena, &supp_info, "%S: %S:%u", lnk_loc_from_obj(scratch.arena, obj), file_name, line.line_num); + } + } else { + str8_list_pushf(scratch.arena, &supp_info, "%S: %S(%llx)+%x", lnk_loc_from_obj(scratch.arena, obj), section_name, section_number, reloc->apply_off); + } + } + } + } + } + next_undefined_symbol:; + } + + lnk_error(LNK_Error_UnresolvedSymbol, "unresolved symbol %S", symbol->name); + lnk_supplement_error_list(supp_info); + } + + // TODO: /FORCE + if (unresolved_symbols_count) { + lnk_exit(LNK_Error_UnresolvedSymbol); + } + + ProfEnd(); + } + + // + // discard COMDAT sections that are not referenced + // + if (config->opt_ref == LNK_SwitchState_Yes) { + lnk_opt_ref(tp, symtab, config, link->objs); + } + + // + // infer minimal padding size for functions from the target machine + // + if (config->machine != COFF_MachineType_Unknown && config->infer_function_pad_min) { + config->function_pad_min = lnk_get_default_function_pad_min(config->machine); + config->infer_function_pad_min = 0; + } + + // + // log + // + if (lnk_get_log_status(LNK_Log_InputObj)) { + U64 total_input_size = 0; + for (LNK_ObjNode *obj_n = link->objs.first; obj_n != 0; obj_n = obj_n->next) { total_input_size += obj_n->data.data.size; } + lnk_log(LNK_Log_InputObj, "[Total Obj Input Size %M]", total_input_size); + } + if (lnk_get_log_status(LNK_Log_InputLib)) { + U64 total_input_size = 0; + for (LNK_LibNode *lib_n = link->libs.first; lib_n != 0; lib_n = lib_n->next) { total_input_size += lib_n->data.data.size; } + lnk_log(LNK_Log_InputLib, "[Total Lib Input Size %M]", total_input_size); + } + + scratch_end(scratch); + ProfEnd(); + return link; +} + +internal void +lnk_reloc_refs_list_push_node(LNK_RelocRefsList *list, LNK_RelocRefsNode *node) +{ + LNK_RelocRefsPointer old_head = list->head; + node->next = old_head.node; + list->head = (LNK_RelocRefsPointer){ .node = node, .tag = old_head.tag + 1 }; +} + +internal LNK_RelocRefsNode * +lnk_reloc_refs_list_pop_node(LNK_RelocRefsList *list) +{ + LNK_RelocRefsPointer old_head = list->head; + if (old_head.node) { + list->head = (LNK_RelocRefsPointer){ .node = old_head.node->next, .tag = old_head.tag + 1}; + } + return old_head.node; +} + +internal LNK_RelocRefsNode * +lnk_reloc_refs_list_push(Arena *arena, LNK_RelocRefsList *list, LNK_RelocRefs *v) +{ + LNK_RelocRefsNode *node = push_array(arena, LNK_RelocRefsNode, 1); + node->v = v; + lnk_reloc_refs_list_push_node(list, node); + return node; +} + +internal LNK_RelocRefsNode * +lnk_reloc_refs_list_pop_node_atomic(LNK_RelocRefsList *list) +{ + LNK_RelocRefsPointer old_head = { .node = ins_atomic_ptr_eval(&list->head.node), .tag = ins_atomic_u64_eval(&list->head.tag) }; + for (;;) { + if (old_head.node == 0) { break; } + LNK_RelocRefsPointer new_head = { .node = old_head.node->next, .tag = old_head.tag + 1 }; + if (ins_atomic_u128_eval_cond_assign(&list->head, &new_head, &old_head)) { break; } + } + return old_head.node; +} + +internal void +lnk_reloc_refs_list_push_node_atomic(LNK_RelocRefsList *list, LNK_RelocRefsNode *node) +{ + LNK_RelocRefsPointer old_head = { .node = ins_atomic_ptr_eval(&list->head.node), .tag = ins_atomic_u64_eval(&list->head.tag) }; + for (;;) { + node->next = old_head.node; + LNK_RelocRefsPointer new_head = { .node = node, .tag = old_head.tag + 1 }; + if (ins_atomic_u128_eval_cond_assign(&list->head, &new_head, &old_head)) { break; } + } +} + +internal void +lnk_reloc_refs_list_concat_in_place(LNK_RelocRefsList *list, LNK_RelocRefsNode *first, LNK_RelocRefsNode *last) +{ + LNK_RelocRefsPointer old_head = { .node = ins_atomic_ptr_eval(&list->head.node), .tag = ins_atomic_u64_eval(&list->head.tag) }; + for (;;) { + last->next = old_head.node; + LNK_RelocRefsPointer new_head = { .node = first, .tag = old_head.tag + 1 }; + if (ins_atomic_u128_eval_cond_assign(&list->head, &new_head, &old_head)) { break; } + } +} + +internal +THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0,0); + + LNK_OptRefTask *task = raw_task; + LNK_SymbolTable *symtab = task->symtab; + + LNK_RelocRefsList free_list = {0}; + for (;;) { + // update active thread count + ins_atomic_u32_inc_eval(&task->active_thread_count); + + for (;;) { + // pop head node + LNK_RelocRefsNode *node = lnk_reloc_refs_list_pop_node_atomic(task->reloc_refs); + if (!node) { break; } + + LNK_RelocRefs *reloc_refs = node->v; + + LNK_RelocRefsNode *first_node = 0, *last_node = 0; + for EachIndex(reloc_idx, reloc_refs->relocs.count) { + COFF_Reloc *reloc = &reloc_refs->relocs.v[reloc_idx]; + LNK_ObjSymbolRef reloc_defn = (LNK_ObjSymbolRef){ .obj = reloc_refs->obj, .symbol_idx = reloc->isymbol }; + COFF_ParsedSymbol reloc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(reloc_defn.obj, reloc_defn.symbol_idx); + COFF_SymbolValueInterpType reloc_interp = coff_interp_from_parsed_symbol(reloc_parsed); + + LNK_ObjSymbolRef ref_symbol = reloc_defn; + for (;;) { + COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx); + COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed); + + LNK_ObjSymbolRef next_ref = {0}; + if (ref_interp == COFF_SymbolValueInterp_Regular) { + LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(ref_symbol.obj, ref_parsed.section_number); + if (symlink) { + ref_symbol = lnk_ref_from_symbol(symlink); + } + break; + } else if (ref_interp == COFF_SymbolValueInterp_Undefined) { + if (reloc_parsed.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, ref_parsed.name); + next_ref = lnk_ref_from_symbol(defn); + } else { + MemoryZeroStruct(&ref_symbol); + break; + } + } else if (ref_interp == COFF_SymbolValueInterp_Weak) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, ref_parsed.name); + next_ref = lnk_ref_from_symbol(defn); + } else { + break; + } + + if (MemoryMatchStruct(&next_ref, &ref_symbol)) { + MemoryZeroStruct(&ref_symbol); + break; + } + ref_symbol = next_ref; + } + + // skip unresolved symbol + if (ref_symbol.obj == 0) { continue; } + + COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx); + COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed); + LNK_Obj *ref_obj = ref_symbol.obj; + + if (ref_interp == COFF_SymbolValueInterp_Regular) { + // make section number list (reloc section + associates) + U32Node *section_number_list = push_array(scratch.arena, U32Node, 1); + section_number_list->data = ref_parsed.section_number; + section_number_list->next = ref_obj->associated_sections[ref_parsed.section_number]; + + // push section headers relocations to the task stack + for EachNode(section_number_n, U32Node, section_number_list) { + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(ref_obj, section_number_n->data); + + // is section eligible for walking? + if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } + if (section_header->flags & COFF_SectionFlag_LnkInfo) { continue; } + if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } + if (section_header->flags & LNK_SECTION_FLAG_LIVE) { continue; } + + // mark section live + section_header->flags |= LNK_SECTION_FLAG_LIVE; + + LNK_RelocRefsNode *node; + if (free_list.head.node) { + node = lnk_reloc_refs_list_pop_node(&free_list); + } else { + node = push_array(scratch.arena, LNK_RelocRefsNode, 1); + node->v = push_array(scratch.arena, LNK_RelocRefs, 1); + } + + node->v->obj = ref_obj; + node->v->relocs = lnk_coff_reloc_info_from_section_number(ref_obj, section_number_n->data); + + if (first_node == 0) { + first_node = node; + last_node = node; + } else { + node->next = first_node; + first_node = node; + } + } + } + } + + lnk_reloc_refs_list_push_node(&free_list, node); + + if (first_node && last_node) { + lnk_reloc_refs_list_concat_in_place(task->reloc_refs, first_node, last_node); + } + } + + // are all threads done walking? + U32 active_thread_count = ins_atomic_u32_dec_eval(&task->active_thread_count); + if (active_thread_count == 0 && ins_atomic_ptr_eval(&task->reloc_refs->head.node) == 0) { + break; + } + + // comprehensive solution to the waiting problem + for (; ins_atomic_ptr_eval(&task->reloc_refs->head.node) == 0; ) { + // was signaled to exit? + if (ins_atomic_u64_eval(&task->active_thread_count) == 0) { goto exit; } + } + } + exit:; + + scratch_end(scratch); + ProfEnd(); +} + +internal void +lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_ObjList objs) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0,0); + + LNK_RelocRefsList reloc_refs = {0}; + + // + // reset live flag on sections + // + for EachNode(obj_n, LNK_ObjNode, objs.first) { + for EachIndex(sect_idx, obj_n->data.header.section_count_no_null) { + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(&obj_n->data, sect_idx+1); + section_header->flags &= ~LNK_SECTION_FLAG_LIVE; + } + } + + // + // define roots + // + { + // tls + LNK_Symbol *tls_symbol = lnk_symbol_table_searchf(symtab, MSCRT_TLS_SYMBOL_NAME); + if (tls_symbol) { + lnk_include_symbol(config, str8_lit(MSCRT_TLS_SYMBOL_NAME), 0); + } + + // push tasks for each root symbol + for EachNode(root_n, LNK_IncludeSymbolNode, config->include_symbol_list.first) { + LNK_Symbol *root = lnk_symbol_table_search(symtab, root_n->v.name); + LNK_ObjSymbolRef root_ref = lnk_ref_from_symbol(root); + + LNK_RelocRefs *r = push_array(scratch.arena, LNK_RelocRefs, 1); + r->obj = root_ref.obj; + r->relocs.count = 1; + r->relocs.v = push_array(scratch.arena, COFF_Reloc, 1); + r->relocs.v[0].isymbol = root_ref.symbol_idx; + + lnk_reloc_refs_list_push(scratch.arena, &reloc_refs, r); + } + + // push task for every non-COMDAT section + for EachNode(obj_n, LNK_ObjNode, objs.first) { + LNK_Obj *obj = &obj_n->data; + for EachIndex(sect_idx, obj->header.section_count_no_null) { + U32 section_number = sect_idx+1; + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number); + + // is section eligible for walking? + if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } + if (section_header->flags & COFF_SectionFlag_LnkCOMDAT) { continue; } + if (section_header->flags & COFF_SectionFlag_LnkInfo) { continue; } + if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } + + // divide relocs and push task for each reloc block + COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(obj, section_number); + U64 new_task_count = CeilIntegerDiv(relocs.count, LNK_RELOCS_PER_TASK); + LNK_RelocRefs *new_tasks = push_array(scratch.arena, LNK_RelocRefs, new_task_count); + for EachIndex(new_task_idx, new_task_count) { + LNK_RelocRefs *r = new_tasks + new_task_idx; + r->obj = obj; + r->relocs.count = Min(LNK_RELOCS_PER_TASK, relocs.count - (new_task_idx * LNK_RELOCS_PER_TASK)); + r->relocs.v = relocs.v + (new_task_idx * LNK_RELOCS_PER_TASK); + + lnk_reloc_refs_list_push(scratch.arena, &reloc_refs, r); + } + } + } + } + + // + // walk relocations and mark referenced sections with live flag + // + LNK_OptRefTask task = {0}; + task.symtab = symtab; + task.reloc_refs = &reloc_refs; + tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_walk_relocs_and_mark_ref_sections_task, &task, "Mark Live Sections"); + + ProfBegin("Remove Unreachable Sections"); + for EachNode(obj_n, LNK_ObjNode, objs.first) { + LNK_Obj *obj = &obj_n->data; + + for EachIndex(sect_idx, obj->header.section_count_no_null) { + U32 section_number = sect_idx+1; + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number); + + if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } + + // remove unreferenced sections + if (~section_header->flags & LNK_SECTION_FLAG_LIVE && section_header->flags & COFF_SectionFlag_LnkCOMDAT) { + section_header->flags |= COFF_SectionFlag_LnkRemove; + } + + // remove associated sections + if (section_header->flags & COFF_SectionFlag_LnkRemove) { + for EachNode(section_number_n, U32Node, obj->associated_sections[section_number]) { + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number_n->data); + section_header->flags |= COFF_SectionFlag_LnkRemove; + } + } + + // TODO: Reset reserved flag so it does not get propagated to the image sections. + // We need to mask out reserved flags when gathering section definitions to actually + // prevent propagation. + section_header->flags &= ~LNK_SECTION_FLAG_LIVE; + } + } + ProfEnd(); + + scratch_end(scratch); + ProfEnd(); } internal B32 -lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_SymbolDefined symbol, LNK_SymbolDefined *symbol_out) +lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymbolRef *symbol_out) { B32 is_resolved = 1; COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol.obj, symbol.symbol_idx); @@ -2113,34 +2485,34 @@ lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_SymbolDefined symbol, LNK_Symbol switch (symbol_interp) { case COFF_SymbolValueInterp_Regular: { LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(symbol.obj, symbol_parsed.section_number); - *symbol_out = symlink ? symlink->u.defined : symbol; + *symbol_out = symlink ? lnk_ref_from_symbol(symlink) : symbol; } break; case COFF_SymbolValueInterp_Weak: { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol_parsed.name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class); + LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); + COFF_ParsedSymbol defn_parsed = lnk_parsed_from_symbol(defn); + COFF_SymbolValueInterpType defn_interp = lnk_interp_from_symbol(defn); if (defn_interp != COFF_SymbolValueInterp_Undefined) { - *symbol_out = defn->u.defined; + *symbol_out = lnk_ref_from_symbol(defn); } else { is_resolved = 0; } } break; case COFF_SymbolValueInterp_Undefined: { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol_parsed.name); + LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); if (defn) { - *symbol_out = defn->u.defined; + *symbol_out = lnk_ref_from_symbol(defn); } else { is_resolved = 0; } } break; case COFF_SymbolValueInterp_Common: { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol_parsed.name); - *symbol_out = defn->u.defined; + LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); + *symbol_out = lnk_ref_from_symbol(defn); } break; case COFF_SymbolValueInterp_Abs: { if (symbol_parsed.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol_parsed.name); - *symbol_out = defn->u.defined; + LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); + *symbol_out = lnk_ref_from_symbol(defn); } else { *symbol_out = symbol; } @@ -2150,166 +2522,6 @@ lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_SymbolDefined symbol, LNK_Symbol return is_resolved; } -internal void -lnk_gc_comdats(TP_Context *tp, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs, LNK_Config *config) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); - - struct Task { struct Task *next; LNK_Obj *obj; COFF_RelocArray relocs; }; - struct Task *task_stack = 0; - const U64 RELOCS_PER_TASK = 1024; - - // - // define roots - // - { - String8List roots = str8_list_copy(scratch.arena, &config->include_symbol_list); - - // tls - LNK_Symbol *tls_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, MSCRT_TLS_SYMBOL_NAME); - if (tls_symbol) { - str8_list_pushf(scratch.arena, &roots, MSCRT_TLS_SYMBOL_NAME); - } - - // push tasks for each root symbol - for (String8Node *root_n = roots.first; root_n != 0; root_n = root_n->next) { - LNK_Symbol *root = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, root_n->string); - - struct Task *t = push_array(scratch.arena, struct Task, 1); - t->obj = root->u.defined.obj; - t->relocs.count = 1; - t->relocs.v = push_array(scratch.arena, COFF_Reloc, 1); - t->relocs.v[0].isymbol = root->u.defined.symbol_idx; - - SLLStackPush(task_stack, t); - } - - // push task for every non-COMDAT section - for EachIndex(obj_idx, objs_count) { - LNK_Obj *obj = objs[obj_idx]; - for EachIndex(sect_idx, obj->header.section_count_no_null) { - if (lnk_is_coff_section_debug(obj, sect_idx)) { continue; } - - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, sect_idx+1); - if ((~section_header->flags & COFF_SectionFlag_LnkCOMDAT) && (~section_header->flags & COFF_SectionFlag_LnkRemove)) { - // extract reloc info - COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(obj, sect_idx+1); - - // alloc new tasks - U64 new_task_count = CeilIntegerDiv(relocs.count, RELOCS_PER_TASK); - struct Task *new_tasks = push_array(scratch.arena, struct Task, new_task_count); - - // divide relocs and push tasks - for EachIndex(new_task_idx, new_task_count) { - struct Task *t = new_tasks + new_task_idx; - t->obj = obj; - t->relocs.count = Min(RELOCS_PER_TASK, relocs.count - (new_task_idx * RELOCS_PER_TASK)); - t->relocs.v = relocs.v + (new_task_idx * RELOCS_PER_TASK); - SLLStackPush(task_stack, t); - } - } - } - } - } - - // - // begin with COMDAT sections flagged as removed - // - for EachIndex(obj_idx, objs_count) { - LNK_Obj *obj = objs[obj_idx]; - COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); - for EachIndex(sect_idx, obj->header.section_count_no_null) { - COFF_SectionHeader *section_header = §ion_table[sect_idx]; - if (section_header->flags & COFF_SectionFlag_LnkCOMDAT) { - section_header->flags |= COFF_SectionFlag_LnkRemove; - } - } - } - - // - // init per section flag array - // - B8 **was_section_visited = push_array(scratch.arena, B8 *, objs_count); - for EachIndex(obj_idx, objs_count) { was_section_visited[obj_idx] = push_array(scratch.arena, B8, objs[obj_idx]->header.section_count_no_null + 1); } - - // - // walk relocations and unset the remove flag on visited sections - // - for (; task_stack; ) { - struct Task *t = task_stack; SLLStackPop(task_stack); - for EachIndex(reloc_idx, t->relocs.count) { - COFF_Reloc *reloc = &t->relocs.v[reloc_idx]; - LNK_SymbolDefined reloc_symbol = {0}; - B32 is_reloc_symbol_resolved = lnk_resolve_symbol(symtab, (LNK_SymbolDefined){ .obj = t->obj, .symbol_idx = reloc->isymbol }, &reloc_symbol); - if (is_reloc_symbol_resolved) { - // parse and interp reloc symbol - LNK_Obj *reloc_obj = reloc_symbol.obj; - COFF_ParsedSymbol reloc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(reloc_obj, reloc_symbol.symbol_idx); - COFF_SymbolValueInterpType reloc_interp = coff_interp_from_parsed_symbol(reloc_parsed); - if (reloc_interp == COFF_SymbolValueInterp_Regular) { - // make section number list (reloc section + associates) - U32Node *section_number_list = push_array(scratch.arena, U32Node, 1); - section_number_list->data = reloc_parsed.section_number; - section_number_list->next = reloc_obj->associated_sections[reloc_parsed.section_number]; - - // push section headers relocations to the task stack - for (U32Node *section_number_n = section_number_list; section_number_n != 0; section_number_n = section_number_n->next) { - if (was_section_visited[reloc_symbol.obj->input_idx][section_number_n->data]) { continue; } - was_section_visited[reloc_symbol.obj->input_idx][section_number_n->data] = 1; - - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(reloc_symbol.obj, section_number_n->data); - if (lnk_is_coff_section_debug(reloc_obj, section_number_n->data-1)) { continue; } - - // skip regular sections that were removed - if (~section_header->flags & COFF_SectionFlag_LnkCOMDAT && section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } - - // on reachable COMDAT sections, unset remove flag - if (section_header->flags & COFF_SectionFlag_LnkCOMDAT) { section_header->flags &= ~COFF_SectionFlag_LnkRemove; } - - // extract reloc info - COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(reloc_symbol.obj, section_number_n->data); - - // alloc new tasks - U64 new_task_count = CeilIntegerDiv(relocs.count, RELOCS_PER_TASK); - struct Task *new_tasks = push_array(scratch.arena, struct Task, new_task_count); - - // divide relocs and push tasks - for EachIndex(new_task_idx, new_task_count) { - struct Task *t = new_tasks + new_task_idx; - t->obj = reloc_obj; - t->relocs.count = Min(RELOCS_PER_TASK, relocs.count - (new_task_idx * RELOCS_PER_TASK)); - t->relocs.v = relocs.v + (new_task_idx * RELOCS_PER_TASK); - SLLStackPush(task_stack, t); - } - } - } - } - } - } - - // - // unset flag on debug sections that associate with live sections - // - for EachIndex(obj_idx, objs_count) { - LNK_Obj *obj = objs[obj_idx]; - for EachIndex(sect_idx, obj->header.section_count_no_null) { - U32 section_number = sect_idx+1; - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number); - if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } - for (U32Node *section_number_n = obj->associated_sections[section_number]; section_number_n != 0; section_number_n = section_number_n->next) { - if (lnk_is_coff_section_debug(obj, section_number_n->data-1)) { - COFF_SectionHeader *associated_section_header = lnk_coff_section_header_from_section_number(obj, section_number_n->data); - associated_section_header->flags &= ~COFF_SectionFlag_LnkRemove; - } - } - } - } - - scratch_end(scratch); - ProfEnd(); -} - internal THREAD_POOL_TASK_FUNC(lnk_gather_section_definitions_task) { @@ -2326,14 +2538,13 @@ THREAD_POOL_TASK_FUNC(lnk_gather_section_definitions_task) for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - if (~sect_header->flags & COFF_SectionFlag_LnkRemove && sect_header->fsize > 0) { + if (~sect_header->flags & COFF_SectionFlag_LnkRemove && ~sect_header->flags & COFF_SectionFlag_LnkInfo && sect_header->fsize > 0) { Temp temp = temp_begin(scratch.arena); // was section defined? String8 sect_name = coff_name_from_section_header(string_table, sect_header); String8 sect_name_with_flags = lnk_make_name_with_flags(temp.arena, sect_name, sect_header->flags & ~COFF_SectionFlags_LnkFlags); - LNK_SectionDefinition *sect_defn = 0; - hash_table_search_string_raw(sect_defn_ht, sect_name_with_flags, §_defn); + LNK_SectionDefinition *sect_defn = hash_table_search_string_raw(sect_defn_ht, sect_name_with_flags); // push new section definition if (sect_defn == 0) { @@ -2373,13 +2584,13 @@ THREAD_POOL_TASK_FUNC(lnk_gather_section_contribs_task) for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { LNK_SectionContrib *sc = task->null_sc; COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - if (~sect_header->flags & COFF_SectionFlag_LnkRemove && sect_header->fsize > 0) { + if (~sect_header->flags & COFF_SectionFlag_LnkRemove && ~sect_header->flags & COFF_SectionFlag_LnkInfo && sect_header->fsize > 0) { LNK_SectionContribChunk *sc_chunk = 0; { Temp temp = temp_begin(scratch.arena); String8 sect_name = coff_name_from_section_header(string_table, sect_header); String8 sect_name_with_flags = lnk_make_name_with_flags(temp.arena, sect_name, sect_header->flags & ~COFF_SectionFlags_LnkFlags); - hash_table_search_string_raw(task->contribs_ht, sect_name_with_flags, &sc_chunk); + sc_chunk = hash_table_search_string_raw(task->contribs_ht, sect_name_with_flags); temp_end(temp); } @@ -2408,25 +2619,6 @@ THREAD_POOL_TASK_FUNC(lnk_gather_section_contribs_task) scratch_end(scratch); } -internal -THREAD_POOL_TASK_FUNC(lnk_flag_debug_symbols_task) -{ - LNK_BuildImageTask *task = raw_task; - U64 obj_idx = task_id; - LNK_Obj *obj = task->objs[obj_idx]; - - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular) { - if (lnk_is_coff_section_debug(obj, symbol.section_number-1)) { - task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1; - } - } - } -} - internal THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_contribs_task) { @@ -2444,12 +2636,33 @@ THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_contribs_task) LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(obj, section_number); if (symlink == 0) { continue; } - COFF_ParsedSymbol symlink_parsed = lnk_parsed_symbol_from_defined(symlink); - task->sect_map[obj_idx][sect_idx] = task->sect_map[symlink->u.defined.obj->input_idx][symlink_parsed.section_number - 1]; + COFF_ParsedSymbol symlink_parsed = lnk_parsed_from_symbol(symlink); + LNK_ObjSymbolRef symlink_ref = lnk_ref_from_symbol(symlink); + task->sect_map[obj_idx][sect_idx] = task->sect_map[symlink_ref.obj->input_idx][symlink_parsed.section_number - 1]; } ProfEnd(); } +internal +THREAD_POOL_TASK_FUNC(lnk_flag_debug_symbols_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { + task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1; + } + } + } +} + internal THREAD_POOL_TASK_FUNC(lnk_patch_comdat_leaders_task) { @@ -2469,29 +2682,32 @@ THREAD_POOL_TASK_FUNC(lnk_patch_comdat_leaders_task) COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); if (interp == COFF_SymbolValueInterp_Regular) { LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(obj, symbol.section_number); - if (symlink && symlink->u.defined.obj != obj) { - U32 section_number; - U32 value; - if (symbol.storage_class == COFF_SymStorageClass_External) { - // COMDAT leader may be at a different offset, so update this symbol with leader's offset - COFF_ParsedSymbol parsed_symlink = lnk_parsed_symbol_from_coff_symbol_idx(symlink->u.defined.obj, symlink->u.defined.symbol_idx); - section_number = symbol.section_number; - value = parsed_symlink.value; - } else { - // COMDAT section may have static symbols which are now invalid to relocate against - section_number = LNK_REMOVED_SECTION_NUMBER_32; - value = max_U32; - task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1; - } + if (symlink) { + LNK_ObjSymbolRef symlink_ref = lnk_ref_from_symbol(symlink); + if (symlink_ref.obj != obj) { + U32 section_number; + U32 value; + if (symbol.storage_class == COFF_SymStorageClass_External) { + // COMDAT leader may be at a different offset, so update this symbol with leader's offset + COFF_ParsedSymbol parsed_symlink = lnk_parsed_from_symbol(symlink); + section_number = symbol.section_number; + value = parsed_symlink.value; + } else { + // COMDAT section may have static symbols which are now invalid to relocate against + section_number = lnk_obj_get_removed_section_number(obj); + value = max_U32; + task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1; + } - if (obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = symbol.raw_symbol; - symbol32->section_number = section_number; - symbol32->value = value; - } else { - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - symbol16->section_number = (U16)section_number; - symbol16->value = value; + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = section_number; + symbol32->value = value; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = (U16)section_number; + symbol16->value = value; + } } } } @@ -2532,11 +2748,7 @@ lnk_common_block_contrib_is_before(void *raw_a, void *raw_b) if (a->u.size == b->u.size) { LNK_Symbol *a_symbol = a->symbol; LNK_Symbol *b_symbol = b->symbol; - if (a_symbol->u.defined.obj->input_idx == b_symbol->u.defined.obj->input_idx) { - is_before = a_symbol->u.defined.symbol_idx < b_symbol->u.defined.symbol_idx; - } else { - is_before = a_symbol->u.defined.obj->input_idx < b_symbol->u.defined.obj->input_idx; - } + is_before = lnk_symbol_is_before(a_symbol, b_symbol); } else { is_before = a->u.size > b->u.size; } @@ -2555,11 +2767,11 @@ THREAD_POOL_TASK_FUNC(lnk_patch_common_block_leaders_task) for (U64 contrib_idx = contrib_range.min; contrib_idx < contrib_range.max; contrib_idx += 1) { LNK_CommonBlockContrib *contrib = &task->u.patch_symtabs.common_block_contribs[contrib_idx]; LNK_Symbol *symbol = contrib->symbol; - LNK_Obj *obj = symbol->u.defined.obj; - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol->u.defined.symbol_idx); + LNK_ObjSymbolRef symbol_ref = lnk_ref_from_symbol(symbol); + COFF_ParsedSymbol parsed_symbol = lnk_parsed_from_symbol(symbol); U64 section_number = task->u.patch_symtabs.common_block_sect->sect_idx + 1; - if (obj->header.is_big_obj) { + if (symbol_ref.obj->header.is_big_obj) { COFF_Symbol32 *symbol32 = parsed_symbol.raw_symbol; symbol32->value = contrib->u.offset; symbol32->section_number = safe_cast_u32(section_number); @@ -2569,7 +2781,7 @@ THREAD_POOL_TASK_FUNC(lnk_patch_common_block_leaders_task) symbol16->section_number = safe_cast_u16(section_number); } - task->u.patch_symtabs.was_symbol_patched[obj->input_idx][symbol->u.defined.symbol_idx] = 1; + task->u.patch_symtabs.was_symbol_patched[symbol_ref.obj->input_idx][symbol_ref.symbol_idx] = 1; } ProfEnd(); @@ -2587,11 +2799,10 @@ THREAD_POOL_TASK_FUNC(lnk_patch_common_block_symbols_task) for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Common) { - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - Assert(coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class) == COFF_SymbolValueInterp_Regular); + LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, symbol.name); + COFF_ParsedSymbol defn_parsed = lnk_parsed_from_symbol(defn); + Assert(lnk_interp_from_symbol(defn) == COFF_SymbolValueInterp_Regular); if (defn) { if (obj->header.is_big_obj) { COFF_Symbol32 *symbol32 = symbol.raw_symbol; @@ -2617,7 +2828,7 @@ THREAD_POOL_TASK_FUNC(lnk_patch_regular_symbols_task) U64 obj_idx = task_id; LNK_Obj *obj = task->objs[obj_idx]; - ProfBegin("Patch Regular Symbols [%S]", obj->path); + ProfBeginV("Patch Regular Symbols [%S]", obj->path); COFF_ParsedSymbol symbol; for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); @@ -2631,10 +2842,10 @@ THREAD_POOL_TASK_FUNC(lnk_patch_regular_symbols_task) COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; - U16 section_number; + U32 section_number; U32 value; if (sc == task->null_sc) { - section_number = LNK_REMOVED_SECTION_NUMBER_16; + section_number = lnk_obj_get_removed_section_number(obj); value = max_U32; } else { section_number = safe_cast_u32(sc->u.sect_idx + 1); @@ -2668,27 +2879,33 @@ lnk_patch_obj_symtab(LNK_SymbolTable *symtab, LNK_Obj *obj, B8 *was_symbol_patch COFF_SymbolValueInterpType fixup_dst_type = coff_interp_symbol(fixup_dst.section_number, fixup_dst.value, fixup_dst.storage_class); if (fixup_type != fixup_dst_type) { continue; } - LNK_SymbolDefined symbol_to_resolve = { .obj = obj, .symbol_idx = symbol_idx }; - LNK_SymbolDefined fixup_symbol = {0}; + LNK_ObjSymbolRef symbol_to_resolve = { .obj = obj, .symbol_idx = symbol_idx }; + LNK_ObjSymbolRef fixup_symbol = {0}; B32 is_resolved = lnk_resolve_symbol(symtab, symbol_to_resolve, &fixup_symbol); if (is_resolved) { - COFF_ParsedSymbol fixup_src = lnk_parsed_symbol_from_coff_symbol_idx(fixup_symbol.obj, fixup_symbol.symbol_idx); - COFF_SymbolValueInterpType fixup_type = coff_interp_symbol(fixup_src.section_number, fixup_src.value, fixup_src.storage_class); - AssertAlways(fixup_type == COFF_SymbolValueInterp_Regular || - fixup_type == COFF_SymbolValueInterp_Abs || - fixup_type == COFF_SymbolValueInterp_Common); + COFF_ParsedSymbol fixup_src = lnk_parsed_symbol_from_coff_symbol_idx(fixup_symbol.obj, fixup_symbol.symbol_idx); + COFF_SymbolValueInterpType fixup_type = coff_interp_symbol(fixup_src.section_number, fixup_src.value, fixup_src.storage_class); + B32 was_fixup_removed = fixup_src.section_number == lnk_obj_get_removed_section_number(fixup_symbol.obj); + + U32 section_number; + U32 value; + if (was_fixup_removed || fixup_type == COFF_SymbolValueInterp_Undefined || fixup_type == COFF_SymbolValueInterp_Weak) { + section_number = lnk_obj_get_removed_section_number(obj); + value = 0; + } else { + section_number = fixup_src.section_number; + value = fixup_src.value; + } if (obj->header.is_big_obj) { COFF_Symbol32 *symbol32 = fixup_dst.raw_symbol; - symbol32->section_number = fixup_src.section_number; - symbol32->value = fixup_src.value; - symbol32->type = fixup_src.type; + symbol32->section_number = section_number; + symbol32->value = value; symbol32->storage_class = COFF_SymStorageClass_Static; } else { COFF_Symbol16 *symbol16 = fixup_dst.raw_symbol; - symbol16->section_number = (U16)fixup_src.section_number; - symbol16->value = fixup_src.value; - symbol16->type = fixup_src.type; + symbol16->section_number = (U16)section_number; + symbol16->value = value; symbol16->storage_class = COFF_SymStorageClass_Static; } @@ -2727,6 +2944,27 @@ THREAD_POOL_TASK_FUNC(lnk_patch_weak_symbols_task) lnk_patch_obj_symtab(task->symtab, task->objs[task_id], task->u.patch_symtabs.was_symbol_patched[task_id], COFF_SymbolValueInterp_Weak); } +internal +THREAD_POOL_TASK_FUNC(lnk_image_fill_task) +{ + ProfBeginFunction(); + LNK_BuildImageTask *task = raw_task; + String8 image_data = task->u.image_fill.image_data; + for EachNode(n, LNK_ImageFillNode, task->u.image_fill.fill_nodes[task_id]) { + for EachIndex(i, n->sc_count) { + LNK_SectionContrib *sc = n->sc[i]; + U64 cursor = 0; + for EachNode(data_n, String8Node, &sc->first_data_node) { + U64 image_off = sc->u.off + n->base_foff + cursor; + Assert(image_off + data_n->string.size <= image_data.size); + MemoryCopyStr8(image_data.str + image_off, data_n->string); + cursor += data_n->string.size; + } + } + } + ProfEnd(); +} + internal U64 lnk_compute_win32_image_header_size(LNK_Config *config, U64 sect_count) { @@ -2743,32 +2981,36 @@ lnk_compute_win32_image_header_size(LNK_Config *config, U64 sect_count) internal THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) { + ProfBeginFunction(); + LNK_ObjRelocPatcher *task = raw_task; LNK_Obj *obj = task->objs[task_id]; COFF_FileHeaderInfo obj_header = obj->header; - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj_header.section_table_range).str; - String8 symbol_table = str8_substr(obj->data, obj_header.symbol_table_range); - String8 string_table = str8_substr(obj->data, obj_header.string_table_range); + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + String8 symbol_table = lnk_coff_symbol_table_from_obj(obj); + String8 string_table = lnk_coff_string_table_from_obj(obj); - for (U64 sect_idx = 0; sect_idx < obj_header.section_count_no_null; sect_idx += 1) { + U32 closest_sect = 0; + U32 closest_reloc = 0; + U32 closest_foff = max_U32; + + for EachIndex(sect_idx, obj_header.section_count_no_null) { COFF_SectionHeader *section_header = §ion_table[sect_idx]; + if (section_header->flags & COFF_SectionFlag_LnkInfo) { continue; } if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } if (section_header->flags & COFF_SectionFlag_CntUninitializedData) { continue; } // get section bytes (special case debug info because it is not copied to the image) - String8 data = lnk_is_coff_section_debug(obj, sect_idx) ? obj->data : task->image_data; + String8 data = section_header->flags & LNK_SECTION_FLAG_DEBUG ? obj->data : task->image_data; Rng1U64 section_frange = rng_1u64(section_header->foff, section_header->foff + section_header->fsize); String8 section_data = str8_substr(data, section_frange); - // find section relocs - COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, section_header); - COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); - // apply relocs - for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { - COFF_Reloc *reloc = &relocs[reloc_idx]; + COFF_RelocArray relocs = lnk_coff_relocs_from_section_header(obj, section_header); + for EachIndex(reloc_idx, relocs.count) { + COFF_Reloc *reloc = &relocs.v[reloc_idx]; // error check relocation if (obj->header.machine == COFF_MachineType_X64) { @@ -2776,7 +3018,8 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) lnk_error_obj(LNK_Error_IllegalRelocation, obj, "unknown relocation type 0x%x", reloc->type); } } else if (obj->header.machine != COFF_MachineType_Unknown) { - NotImplemented; + lnk_not_implemented("relocation patching is not implemented for %S", coff_string_from_machine_type(obj->header.machine)); + continue; } // compute virtual offsets @@ -2788,16 +3031,15 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) S64 symbol_voff = 0; { COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, reloc->isymbol); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol); if (interp == COFF_SymbolValueInterp_Regular) { if (symbol.section_number == lnk_obj_get_removed_section_number(obj)) { - if (!lnk_is_coff_section_debug(obj, sect_idx)) { + if (~section_header->flags & LNK_SECTION_FLAG_DEBUG) { String8 sect_name = coff_name_from_section_header(string_table, §ion_table[sect_idx]); lnk_error_obj(LNK_Error_RelocationAgainstRemovedSection, obj, "relocating against symbol that is in a removed section (symbol: %S, reloc-section: %S 0x%llx, reloc-index: 0x%llx)", symbol.name, sect_name, sect_idx+1, reloc_idx); } continue; } - symbol_secnum = symbol.section_number; symbol_secoff = symbol.value; symbol_voff = safe_cast_u32((U64)task->image_section_table[symbol.section_number]->voff + (U64)symbol_secoff); @@ -2808,7 +3050,6 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) if (str8_match(symbol.name, str8_lit("__ImageBase"), 0)) { symbol.value = task->image_base; } - symbol_secnum = 0; symbol_secoff = 0; symbol_voff = (S64)symbol.value - (S64)task->image_base; @@ -2842,6 +3083,8 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) MemoryCopy(section_data.str + reloc->apply_off, &reloc_result, reloc_value.size); } } + + ProfEnd(); } internal int @@ -2859,10 +3102,10 @@ THREAD_POOL_TASK_FUNC(lnk_count_common_block_contribs_task) LNK_BuildImageTask *task = raw_task; LNK_SymbolTable *symtab = task->symtab; - for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][task_id].first; chunk != 0; chunk = chunk->next) { - for (U64 i = 0; i < chunk->count; i += 1) { + for (LNK_SymbolHashTrieChunk *chunk = symtab->chunks[task_id].first; chunk != 0; chunk = chunk->next) { + for EachIndex(i, chunk->count) { LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_ParsedSymbol parsed_symbol = lnk_parsed_from_symbol(symbol); COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); if (parsed_interp == COFF_SymbolValueInterp_Common) { task->u.common_block.counts[task_id] += 1; @@ -2878,10 +3121,10 @@ THREAD_POOL_TASK_FUNC(lnk_fill_out_common_block_contribs_task) LNK_SymbolTable *symtab = task->symtab; U64 cursor = task->u.common_block.offsets[task_id]; - for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][task_id].first; chunk != 0; chunk = chunk->next) { - for (U64 i = 0; i < chunk->count; i += 1) { + for (LNK_SymbolHashTrieChunk *chunk = symtab->chunks[task_id].first; chunk != 0; chunk = chunk->next) { + for EachIndex(i, chunk->count) { LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_ParsedSymbol parsed_symbol = lnk_parsed_from_symbol(symbol); COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); if (parsed_interp == COFF_SymbolValueInterp_Common) { LNK_CommonBlockContrib *contrib = &task->u.common_block.contribs[cursor++]; @@ -2899,15 +3142,17 @@ THREAD_POOL_TASK_FUNC(lnk_flag_hotpatch_contribs_task) U64 obj_idx = task_id; LNK_Obj *obj = task->objs[obj_idx]; - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) { - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); - LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; - if (sc != task->null_sc) { - sc->hotpatch = !!(section_header->flags & COFF_SectionFlag_CntCode); + if (obj->hotpatch) { + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) { + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; + if (sc != task->null_sc) { + sc->hotpatch = !!(section_header->flags & COFF_SectionFlag_CntCode); + } } } } @@ -3259,90 +3504,6 @@ lnk_build_guard_tables(TP_Context *tp, #endif } -internal -THREAD_POOL_TASK_FUNC(lnk_emit_base_relocs_from_objs_task) -{ - ProfBeginFunction(); - - LNK_ObjBaseRelocTask *task = raw_task; - Rng1U64 range = task->ranges[task_id]; - - HashTable *page_ht = task->page_ht_arr[task_id]; - LNK_BaseRelocPageList *page_list = &task->list_arr[task_id]; - - for (U64 obj_idx = range.min; obj_idx < range.max; ++obj_idx) { - LNK_Obj *obj = task->obj_arr[obj_idx]; - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; - for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { - COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - - if (sect_header->flags & COFF_SectionFlag_LnkRemove) { - continue; - } - - COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, sect_header); - COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); - - for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { - COFF_Reloc *r = &relocs[reloc_idx]; - - COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - B32 is_symbol_address = symbol_interp != COFF_SymbolValueInterp_Abs; - - if (is_symbol_address) { - B32 is_addr32 = 0, is_addr64 = 0; - switch (obj->header.machine) { - case COFF_MachineType_Unknown: {} break; - case COFF_MachineType_X64: { - is_addr32 = r->type == COFF_Reloc_X64_Addr32; - is_addr64 = r->type == COFF_Reloc_X64_Addr64; - } break; - default: { NotImplemented; } break; - } - - if (is_addr32 || is_addr64) { - U64 reloc_voff = sect_header->voff + r->apply_off; - U64 page_voff = AlignDownPow2(reloc_voff, task->page_size); - - LNK_BaseRelocPageNode *page; - { - KeyValuePair *is_page_present = hash_table_search_u64(page_ht, page_voff); - if (is_page_present) { - page = is_page_present->value_raw; - } else { - // fill out page - page = push_array(arena, LNK_BaseRelocPageNode, 1); - page->v.voff = page_voff; - - // push page - SLLQueuePush(page_list->first, page_list->last, page); - page_list->count += 1; - - // register page voff - hash_table_push_u64_raw(arena, page_ht, page_voff, page); - } - } - - if (is_addr32) { - if (task->is_large_addr_aware) { - COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); - lnk_error_obj(LNK_Error_LargeAddrAwareRequired, obj, "found out of range ADDR32 relocation for '%S', link with /LARGEADDRESSAWARE:NO", symbol.name); - } else { - u64_list_push(arena, &page->v.entries_addr32, reloc_voff); - } - } else { - u64_list_push(arena, &page->v.entries_addr64, reloc_voff); - } - } - } - } - } - } - - ProfEnd(); -} - internal THREAD_POOL_TASK_FUNC(lnk_patch_virtual_offsets_and_sizes_in_obj_section_headers_task) { @@ -3376,7 +3537,7 @@ THREAD_POOL_TASK_FUNC(lnk_patch_file_offsets_and_sizes_in_obj_section_headers_ta for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { COFF_SectionHeader *sect_header = §ion_table[sect_idx]; B32 patch_section_header = (~sect_header->flags & COFF_SectionFlag_LnkRemove) && - !lnk_is_coff_section_debug(obj, sect_idx); + (~sect_header->flags & LNK_SECTION_FLAG_DEBUG); if (patch_section_header) { LNK_SectionContrib *sc = task->sect_map[obj_idx][sect_idx]; LNK_Section *sect = task->image_sects.v[sc->u.sect_idx]; @@ -3430,177 +3591,246 @@ THREAD_POOL_TASK_FUNC(lnk_patch_section_symbols_task) ProfEnd(); } -int -lnk_base_reloc_page_compar(const void *raw_a, const void *raw_b) +internal +THREAD_POOL_TASK_FUNC(lnk_gather_base_reloc_pages_task) { - const LNK_BaseRelocPage *a = raw_a, *b = raw_b; - return u64_compar(&a->voff, &b->voff); + LNK_BaseRelocsTask *task = raw_task; + HashTable *page_ht = task->gather.page_ht[worker_id]; + LNK_BaseRelocPageList *pages = &task->gather.pages[worker_id]; + LNK_Obj *obj = task->gather.objs[task_id]; + COFF_SectionHeader *sect_table = lnk_coff_section_table_from_obj(obj); + + ProfBeginV("%S", obj->path); + for EachIndex(sect_idx, obj->header.section_count_no_null) { + COFF_SectionHeader *sect_header = §_table[sect_idx]; + if (sect_header->flags & COFF_SectionFlag_LnkRemove) { continue; } + + COFF_RelocArray relocs = lnk_coff_relocs_from_section_header(obj, sect_header); + for EachIndex(reloc_idx, relocs.count) { + COFF_Reloc *r = &relocs.v[reloc_idx]; + + COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, r->isymbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol); + if (symbol_interp == COFF_SymbolValueInterp_Abs) { continue; } + + U64 is_addr = coff_is_addr_reloc(obj->header.machine, r->type); + if (is_addr == 0) { continue; } + + U64 reloc_voff = sect_header->voff + r->apply_off; + U64 page_voff = AlignDownPow2(reloc_voff, task->page_size); + LNK_BaseRelocPageNode *page = hash_table_search_u64_raw(page_ht, page_voff); + if (page == 0) { + // fill out page + page = push_array(arena, LNK_BaseRelocPageNode, 1); + page->v.voff = page_voff; + page->v.entries_addr32 = push_array(arena, U64List, 1); + page->v.entries_addr64 = push_array(arena, U64List, 1); + + // push page + SLLQueuePush(pages->first, pages->last, page); + pages->count += 1; + + // register page voff + hash_table_push_u64_raw(arena, page_ht, page_voff, page); + } + + switch (is_addr) { + case 4: { + if (task->is_large_addr_aware) { + lnk_error_obj(LNK_Error_LargeAddrAwareRequired, obj, "found out of range ADDR32 relocation for '%S', link with /LARGEADDRESSAWARE:NO", symbol.name); + } else { + u64_list_push(arena, page->v.entries_addr32, reloc_voff); + } + } break; + case 8: { + u64_list_push(arena, page->v.entries_addr64, reloc_voff); + } break; + default: { InvalidPath; } break; + } + } + + } + ProfEnd(); } -int +internal +THREAD_POOL_TASK_FUNC(lnk_serialize_base_reloc_pages_task) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + + LNK_BaseRelocsTask *task = raw_task; + HashTable *voff_ht = hash_table_init(scratch.arena, task->page_size); + + U64 voffs_max = task->page_size; + U32 *voffs32 = push_array(scratch.arena, U32, voffs_max); + U64 *voffs64 = push_array(scratch.arena, U64, voffs_max); + + for EachInRange(page_idx, task->serialize.ranges[task_id]) { + LNK_BaseRelocPage *page = &task->serialize.pages.v[page_idx]; + + // filter out duplicate 32-bit virtual offsets + U64 voff_count32 = 0; + for EachNode(voff_n, U64Node, page->entries_addr32->first) { + if (hash_table_search_u64(voff_ht, voff_n->data)) { continue; } + hash_table_push_u64_u64(scratch.arena, voff_ht, voff_n->data, 0); + voffs32[voff_count32] = voff_n->data; + voff_count32 += 1; + } + + // filter out duplicate 64-bit virtual offsets + U64 voff_count64 = 0; + for EachNode(voff_n, U64Node, page->entries_addr64->first) { + if (hash_table_search_u64(voff_ht, voff_n->data)) { continue; } + hash_table_push_u64_u64(scratch.arena, voff_ht, voff_n->data, 0); + voffs64[voff_count64] = voff_n->data; + voff_count64 += 1; + } + + // gather step is not deterministic + radsort(voffs32, voff_count32, u32_is_before); + radsort(voffs64, voff_count64, u64_is_before); + + // find block bytes in the buffer + void *block = task->serialize.buffer + page->buffer_offset; + + // setup pointers into the block + U32 *page_voff_ptr = block; + U32 *block_size_ptr = page_voff_ptr + 1; + U16 *reloc_arr_base = (U16 *)(block_size_ptr + 1); + U16 *reloc_arr_ptr = reloc_arr_base; + + // write 32-bit relocation entries + for EachIndex(i, voff_count32) { + U64 rel_off = voffs32[i] - page->voff; + *reloc_arr_ptr = PE_BaseRelocMake(PE_BaseRelocKind_HIGHLOW, rel_off); + reloc_arr_ptr += 1; + } + + // write 64-bit relocation entries + for EachIndex(i, voff_count64) { + U64 rel_off = voffs64[i] - page->voff; + *reloc_arr_ptr = PE_BaseRelocMake(PE_BaseRelocKind_DIR64, rel_off); + reloc_arr_ptr += 1; + } + + // compute block size + U64 reloc_arr_size = IntFromPtr(reloc_arr_ptr - reloc_arr_base) * sizeof(reloc_arr_ptr[0]); + U64 block_size = sizeof(*page_voff_ptr) + sizeof(*block_size_ptr) + reloc_arr_size; + U64 block_size_aligned = AlignPow2(block_size, sizeof(U32)); + + // zero-out alignment + U64 align_size = block_size_aligned - block_size; + MemoryZero(reloc_arr_ptr, align_size); + + // write page header + *page_voff_ptr = safe_cast_u32(page->voff); + *block_size_ptr = safe_cast_u32(block_size_aligned); + + // purge hash table for the next run + hash_table_purge(voff_ht); + } + + scratch_end(scratch); + ProfEnd(); +} + +internal int lnk_base_reloc_page_is_before(void *raw_a, void *raw_b) { - LNK_BaseRelocPage* a = raw_a; - LNK_BaseRelocPage* b = raw_b; - return a->voff < b->voff; + return ((LNK_BaseRelocPage *)raw_a)->voff < ((LNK_BaseRelocPage *)raw_b)->voff; } -internal String8List +internal String8 lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, U64 objs_count, LNK_Obj **objs) { ProfBeginFunction(); - Arena *arena = tp_arena->v[0]; Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); tp_arena->v[0] = scratch.arena; TP_Temp tp_temp = tp_temp_begin(tp_arena); + + LNK_BaseRelocsTask task = {0}; + task.page_size = config->machine_page_size; + task.is_large_addr_aware = !!(config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE); - LNK_BaseRelocPageArray page_arr; + LNK_BaseRelocPageArray pages = {0}; { - LNK_BaseRelocPageList *page_list_arr = push_array(scratch.arena, LNK_BaseRelocPageList, tp->worker_count); - HashTable **page_ht_arr = push_array_no_zero(scratch.arena, HashTable *, tp->worker_count); - for (U64 i = 0; i < tp->worker_count; ++i) { - page_ht_arr[i] = hash_table_init(scratch.arena, 1024); - } + LNK_BaseRelocPageList *page_lists = push_array(scratch.arena, LNK_BaseRelocPageList, tp->worker_count); + HashTable **page_ht = push_array(scratch.arena, HashTable *, tp->worker_count); + for EachIndex(i, tp->worker_count) { page_ht[i] = hash_table_init(scratch.arena, task.page_size/2); } - { - ProfBegin("Emit Relocs From Objs"); - LNK_ObjBaseRelocTask task = {0}; - task.ranges = tp_divide_work(scratch.arena, objs_count, tp->worker_count); - task.page_size = config->machine_page_size; - task.page_ht_arr = page_ht_arr; - task.list_arr = page_list_arr; - task.obj_arr = objs; - task.is_large_addr_aware = !!(config->file_characteristics & PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE); - tp_for_parallel(tp, tp_arena, tp->worker_count, lnk_emit_base_relocs_from_objs_task, &task); - ProfEnd(); - } + task.gather.objs = objs; + task.gather.pages = page_lists; + task.gather.page_ht = page_ht; + tp_for_parallel_prof(tp, tp_arena, objs_count, lnk_gather_base_reloc_pages_task, &task, "Gather"); - LNK_BaseRelocPageList *main_page_list = &page_list_arr[0]; - { - ProfBegin("Merge Worker Page Lists"); - HashTable *main_ht = page_ht_arr[0]; - for (U64 list_idx = 1; list_idx < tp->worker_count; ++list_idx) { - LNK_BaseRelocPageList src = page_list_arr[list_idx]; + ProfBegin("Merge Page Lists"); + LNK_BaseRelocPageList *main_page_list = &page_lists[0]; + HashTable *main_ht = page_ht[0]; + for (U64 list_idx = 1; list_idx < tp->worker_count; list_idx += 1) { + for (LNK_BaseRelocPageNode *src_page = page_lists[list_idx].first, *src_next; src_page != 0; src_page = src_next) { + src_next = src_page->next; - for (LNK_BaseRelocPageNode *src_page = src.first, *src_next; src_page != 0; src_page = src_next) { - src_next = src_page->next; + LNK_BaseRelocPageNode *page = hash_table_search_u64_raw(main_ht, src_page->v.voff); + if (page) { + // page exists, concat voffs + Assert(page != src_page); + u64_list_concat_in_place(page->v.entries_addr32, src_page->v.entries_addr32); + u64_list_concat_in_place(page->v.entries_addr64, src_page->v.entries_addr64); + } else { + // push page to the main list + SLLQueuePush(main_page_list->first, main_page_list->last, src_page); + main_page_list->count += 1; - KeyValuePair *is_page_present = hash_table_search_u64(main_ht, src_page->v.voff); - if (is_page_present) { - // page exists concat voffs - LNK_BaseRelocPageNode *page = is_page_present->value_raw; - Assert(page != src_page); - u64_list_concat_in_place(&page->v.entries_addr32, &src_page->v.entries_addr32); - u64_list_concat_in_place(&page->v.entries_addr64, &src_page->v.entries_addr64); - } else { - // push page to main list - SLLQueuePush(main_page_list->first, main_page_list->last, src_page); - main_page_list->count += 1; - - // store lookup voff - hash_table_push_u64_raw(scratch.arena, main_ht, src_page->v.voff, src_page); - } + // store lookup voff + hash_table_push_u64_raw(scratch.arena, main_ht, src_page->v.voff, src_page); } } - ProfEnd(); } + ProfEnd(); ProfBegin("Page List -> Array"); - page_arr.count = 0; - page_arr.v = push_array_no_zero(scratch.arena, LNK_BaseRelocPage, main_page_list->count); - for (LNK_BaseRelocPageNode* n = main_page_list->first; n != 0; n = n->next) { - page_arr.v[page_arr.count++] = n->v; + pages.v = push_array_no_zero(scratch.arena, LNK_BaseRelocPage, main_page_list->count); + for EachNode(n, LNK_BaseRelocPageNode, main_page_list->first) { pages.v[pages.count++] = n->v; } + ProfEnd(); + } + + ProfBeginV("Sort Pages [Count %llu]", pages.count); + radsort(pages.v, pages.count, lnk_base_reloc_page_is_before); + ProfEnd(); + + String8 base_relocs = {0}; + { + ProfBegin("Compute Buffer Size"); + U64 buffer_size = 0; + for EachIndex(page_idx, pages.count) { + LNK_BaseRelocPage *page = &pages.v[page_idx]; + page->buffer_offset = buffer_size; + buffer_size += /* page base voff */ sizeof(U32) + /* size of block */ sizeof(U32); // header + buffer_size += sizeof(U16)*page->entries_addr32->count; // 32-bit voff entries + buffer_size += sizeof(U16)*page->entries_addr64->count; // 64-bit voff entries + buffer_size = AlignPow2(buffer_size, sizeof(U32)); } ProfEnd(); - ProfBegin("Sort Pages on VOFF"); - //radsort(page_arr.v, page_arr.count, lnk_base_reloc_page_is_before); - qsort(page_arr.v, page_arr.count, sizeof(page_arr.v[0]), lnk_base_reloc_page_compar); + ProfBeginV("Alloc Buffer [%M]", buffer_size); + U8 *buffer = push_array_no_zero(arena, U8, buffer_size); ProfEnd(); + + task.serialize.buffer_size = buffer_size; + task.serialize.buffer = buffer; + task.serialize.pages = pages; + task.serialize.ranges = tp_divide_work(scratch.arena, pages.count, tp->worker_count); + tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_serialize_base_reloc_pages_task, &task, "Serialize"); + + base_relocs = str8(task.serialize.buffer, task.serialize.buffer_size); } - - String8List result = {0}; - if (page_arr.count) { - ProfBegin("Serialize Pages"); - HashTable *voff_ht = hash_table_init(scratch.arena, config->machine_page_size); - for (U64 page_idx = 0; page_idx < page_arr.count; ++page_idx) { - LNK_BaseRelocPage *page = &page_arr.v[page_idx]; - U64 total_entry_count = 0; - total_entry_count += page->entries_addr32.count; - total_entry_count += page->entries_addr64.count; - - U32 *page_voff_ptr; - U32 *block_size_ptr; - U16 *reloc_arr_base; - - // push buffer - U64 buf_size = AlignPow2(sizeof(*page_voff_ptr) + sizeof(*block_size_ptr) + sizeof(*reloc_arr_base)*total_entry_count, sizeof(U32)); - void *buf = push_array_no_zero(arena, U8, buf_size); - - // setup pointers into buffer - page_voff_ptr = buf; - block_size_ptr = page_voff_ptr + 1; - reloc_arr_base = (U16*)(block_size_ptr + 1); - - // write 32-bit relocations - U16 *reloc_arr_ptr = reloc_arr_base; - for (U64Node *i = page->entries_addr32.first; i != 0; i = i->next) { - // was base reloc_entry made? - if (hash_table_search_u64(voff_ht, i->data)) { - continue; - } - hash_table_push_u64_u64(scratch.arena, voff_ht, i->data, 0); - - // write entry - U64 rel_off = i->data - page->voff; - Assert(rel_off <= config->machine_page_size); - *reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_HIGHLOW, rel_off); - } - - // write 64-bit relocations - for (U64Node *i = page->entries_addr64.first; i != 0; i = i->next) { - // was base reloc entry made? - if (hash_table_search_u64(voff_ht, i->data)) { - continue; - } - hash_table_push_u64_u64(scratch.arena, voff_ht, i->data, 0); - - // write entry - U64 rel_off = i->data - page->voff; - Assert(rel_off <= config->machine_page_size); - *reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_DIR64, rel_off); - } - - // write pad - U64 pad_reloc_count = AlignPadPow2(total_entry_count, sizeof(reloc_arr_ptr[0])); - MemoryZeroTyped(reloc_arr_ptr, pad_reloc_count); // fill pad with PE_BaseRelocKind_ABSOLUTE - reloc_arr_ptr += pad_reloc_count; - - // compute block size - U64 reloc_arr_size = (U64)((U8*)reloc_arr_ptr - (U8*)reloc_arr_base); - U64 block_size = sizeof(*page_voff_ptr) + sizeof(*block_size_ptr) + reloc_arr_size; - - // write header - *page_voff_ptr = safe_cast_u32(page->voff); - *block_size_ptr = safe_cast_u32(block_size); - Assert(*block_size_ptr <= buf_size); - - // push page - str8_list_push(arena, &result, str8(buf, buf_size)); - - // purge voffs for next page - hash_table_purge(voff_ht); - } - ProfEnd(); - } - tp_temp_end(tp_temp); // scratch is cleared here tp_arena->v[0] = arena; - ProfEnd(); - return result; + return base_relocs; } internal String8List @@ -3812,9 +4042,9 @@ lnk_build_win32_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config COFF_SectionHeader **section_table = push_array(arena, COFF_SectionHeader *, coff_section_table_count + 1); for (U64 i = 1; i <= coff_section_table_count; i += 1) { section_table[i] = &coff_section_table[i-1]; } - LNK_Symbol *entry_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, config->entry_point_name); + LNK_Symbol *entry_symbol = lnk_symbol_table_search(symtab, config->entry_point_name); if (entry_symbol) { - *entry_point_va = safe_cast_u32(lnk_virt_off_from_symbol(section_table, entry_symbol)); + *entry_point_va = safe_cast_u32(lnk_voff_from_symbol(section_table, entry_symbol)); } scratch_end(scratch); @@ -3826,25 +4056,13 @@ lnk_build_win32_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config } internal LNK_ImageContext -lnk_build_image(TP_Arena *arena, - TP_Context *tp, - LNK_Config *config, - LNK_SymbolTable *symtab, - U64 objs_count, - LNK_Obj **objs) +lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs) { ProfBegin("Image"); lnk_timer_begin(LNK_Timer_Image); Temp scratch = scratch_begin(arena->v, arena->count); - // - // remove unreachable COMDAT sections - // - if (config->opt_ref == LNK_SwitchState_Yes) { - lnk_gc_comdats(tp, symtab, objs_count, objs, config); - } - // // init section table // @@ -3853,6 +4071,7 @@ lnk_build_image(TP_Arena *arena, lnk_section_table_push(sectab, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS); lnk_section_table_push(sectab, str8_lit(".data" ), PE_DATA_SECTION_FLAGS ); lnk_section_table_push(sectab, str8_lit(".bss" ), PE_BSS_SECTION_FLAGS ); + lnk_section_table_push(sectab, str8_lit(".pdata"), PE_PDATA_SECTION_FLAGS); LNK_Section *common_block_sect = lnk_section_table_search(sectab, str8_lit(".bss"), PE_BSS_SECTION_FLAGS); LNK_BuildImageTask task = { @@ -3885,8 +4104,7 @@ lnk_build_image(TP_Arena *arena, for EachIndex(defn_idx, sect_defns_count) { LNK_SectionDefinition *defn = sect_defns[defn_idx]; String8 name_with_flags = lnk_make_name_with_flags(arena->v[0], defn->name, defn->flags); - LNK_SectionDefinition *main_defn = 0; - hash_table_search_string_raw(task.u.gather_sects.defns[0], name_with_flags, &main_defn); + LNK_SectionDefinition *main_defn = hash_table_search_string_raw(task.u.gather_sects.defns[0], name_with_flags); if (main_defn == 0) { main_defn = sect_defns[defn_idx]; hash_table_push_string_raw(arena->v[0], task.u.gather_sects.defns[0], name_with_flags, main_defn); @@ -3941,8 +4159,7 @@ lnk_build_image(TP_Arena *arena, } String8 defn_name_with_flags = lnk_make_name_with_flags(sectab->arena, sect_defn->name, sect_defn->flags); - LNK_SectionContribChunk *contrib_chunk = 0; - hash_table_search_string_raw(task.contribs_ht, defn_name_with_flags, &contrib_chunk); + LNK_SectionContribChunk *contrib_chunk = hash_table_search_string_raw(task.contribs_ht, defn_name_with_flags); if (!contrib_chunk) { contrib_chunk = lnk_section_contrib_chunk_list_push_chunk(arena->v[0], §->contribs, sect_defn->contribs_count, sort_idx); hash_table_push_string_raw(sectab->arena, task.contribs_ht, defn_name_with_flags, contrib_chunk); @@ -4083,13 +4300,12 @@ lnk_build_image(TP_Arena *arena, { String8List empty_sect_list = {0}; for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { - LNK_Section *sect = §_n->data; - if (sect->vsize == 0) { - str8_list_push(scratch.arena, &empty_sect_list, sect->name); + if (sect_n->data.vsize == 0) { + str8_list_push(scratch.arena, &empty_sect_list, sect_n->data.name); } } for (String8Node *name_n = empty_sect_list.first; name_n != 0; name_n = name_n->next) { - lnk_section_table_remove(sectab, name_n->string); + lnk_section_table_purge(sectab, name_n->string); } } @@ -4153,15 +4369,15 @@ lnk_build_image(TP_Arena *arena, // build base relocs if (~config->flags & LNK_ConfigFlag_Fixed) { - String8List base_relocs_data = lnk_build_base_relocs(tp, arena, config, objs_count, objs); - if (base_relocs_data.total_size) { + String8 base_relocs_data = lnk_build_base_relocs(tp, arena, config, objs_count, objs); + if (base_relocs_data.size) { LNK_Section *reloc = lnk_section_table_push(sectab, str8_lit(".reloc"), PE_RELOC_SECTION_FLAGS); LNK_SectionContribChunk *first_sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &reloc->contribs, 1, str8_zero()); LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(first_sc_chunk, 1); - sc->first_data_node = *base_relocs_data.first; - sc->last_data_node = base_relocs_data.last; - sc->align = 1; - sc->u.obj_idx = max_U32; + sc->first_data_node.string = base_relocs_data; + sc->last_data_node = &sc->first_data_node; + sc->align = 1; + sc->u.obj_idx = max_U32; lnk_finalize_section_layout(reloc, config->file_align, config->function_pad_min); lnk_assign_section_virtual_space(reloc, config->sect_align, &voff_cursor); @@ -4196,55 +4412,59 @@ lnk_build_image(TP_Arena *arena, { ProfBegin("Image Fill"); - LNK_SectionArray sects = lnk_section_array_from_list(scratch.arena, sectab->list); + ProfBeginV("Alloc Image Buffer [%M]", lnk_section_table_total_fsize(sectab)); + image_data.size = lnk_section_table_total_fsize(sectab); + image_data.str = push_array_no_zero(arena->v[0], U8, image_data.size); + ProfEnd(); - U64 image_size = 0; - for EachIndex(sect_idx, sects.count) { image_size += sects.v[sect_idx]->fsize; } + ProfBegin("Fill Align Bytes"); + for EachNode(sect_n, LNK_SectionNode, sectab->list.first) { + LNK_Section *sect = §_n->data; + ProfBeginV("Section: %S Size: %M", sect->name, sect->fsize); + U8 fill_byte = sect->flags & COFF_SectionFlag_CntCode ? coff_code_align_byte_from_machine(config->machine) : 0; + MemorySet(image_data.str + sect->foff, fill_byte, sect->fsize); + ProfEnd(); + } + ProfEnd(); - image_data.size = image_size; - image_data.str = push_array_no_zero(arena->v[0], U8, image_size); + Temp temp = temp_begin(scratch.arena); - for EachIndex(sect_idx, sects.count) { - LNK_Section *sect = sects.v[sect_idx]; + ProfBegin("Prepare Worker Nodes"); + LNK_ImageFillNode **fill_nodes = push_array(scratch.arena, LNK_ImageFillNode *, tp->worker_count); + U64 worker_cap = 4096, worker_load = 0, worker_idx = 0; + for EachNode(sect_n, LNK_SectionNode, sectab->list.first) { + LNK_Section *sect = §_n->data; - if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { - // pick fill pick - U8 fill_byte = 0; - if (sect->flags & COFF_SectionFlag_CntCode) { - fill_byte = coff_code_align_byte_from_machine(config->machine); - } + // skip bss sections + if (sect->flags & COFF_SectionFlag_CntUninitializedData) { continue; } - // copy section contribution - U64 prev_sc_opl = 0; - for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { - for EachIndex(sc_idx, sc_chunk->count) { - LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; + for EachNode(sc_chunk, LNK_SectionContribChunk, sect->contribs.first) { + for (U64 sc_left = sc_chunk->count; sc_left > 0; ) { + U64 count = Min(worker_cap - worker_load, sc_left); + U64 sc_pos = sc_chunk->count - sc_left; + sc_left -= count; - // fill align bytes - Assert(sc->u.off >= prev_sc_opl); - U64 fill_size = sc->u.off - prev_sc_opl; - MemorySet(image_data.str + sect->foff + prev_sc_opl, fill_byte, fill_size); - prev_sc_opl = sc->u.off + lnk_size_from_section_contrib(sc); + LNK_ImageFillNode *n = push_array(scratch.arena, LNK_ImageFillNode, 1); + n->base_foff = sect->foff; + n->sc_count = count; + n->sc = sc_chunk->v + sc_pos; + SLLStackPush(fill_nodes[worker_idx], n); - // copy contrib contents - { - U64 cursor = 0; - for (String8Node *data_n = &sc->first_data_node; data_n != 0; data_n = data_n->next) { - Assert(sc->u.off + data_n->string.size <= sect->vsize); - MemoryCopy(image_data.str + sect->foff + sc->u.off + cursor, data_n->string.str, data_n->string.size); - cursor += data_n->string.size; - } - } + worker_load += count; + if (worker_load >= worker_cap) { + worker_load = 0; + worker_idx = (worker_idx + 1) % tp->worker_count; } } - - // fill section align bytes - { - U64 fill_size = sect->fsize - prev_sc_opl; - MemorySet(image_data.str + sect->foff + prev_sc_opl, fill_byte, fill_size); - } } } + ProfEnd(); + + task.u.image_fill.image_data = image_data; + task.u.image_fill.fill_nodes = fill_nodes; + tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_image_fill_task, &task, "Fill"); + + temp_end(temp); ProfEnd(); } @@ -4263,15 +4483,15 @@ lnk_build_image(TP_Arena *arena, // patch load config { - LNK_Symbol *load_config_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME)); + LNK_Symbol *load_config_symbol = lnk_symbol_table_search(symtab, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME)); if (load_config_symbol) { - U64 load_config_foff = lnk_file_off_from_symbol(image_section_table, load_config_symbol); + U64 load_config_foff = lnk_foff_from_symbol(image_section_table, load_config_symbol); String8 load_config_data = str8_skip(image_data, load_config_foff); U32 load_config_size = 0; if (sizeof(load_config_size) <= load_config_data.size) { PE_DataDirectory *load_config_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_LOAD_CONFIG); - load_config_dir->virt_off = lnk_virt_off_from_symbol(image_section_table, load_config_symbol); + load_config_dir->virt_off = lnk_voff_from_symbol(image_section_table, load_config_symbol); load_config_dir->virt_size = load_config_size; } else { // TODO: report corrupted load config @@ -4317,16 +4537,16 @@ lnk_build_image(TP_Arena *arena, // patch import and import addr { LNK_Section *idata_sect = lnk_section_table_search(sectab, str8_lit(".idata"), PE_IDATA_SECTION_FLAGS); - LNK_Symbol *null_import_desc = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "__NULL_IMPORT_DESCRIPTOR"); - LNK_Symbol *null_thunk_data = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "\x7f%S_NULL_THUNK_DATA", lnk_get_image_name(config)); + LNK_Symbol *null_import_desc = lnk_symbol_table_searchf(symtab, "__NULL_IMPORT_DESCRIPTOR"); + LNK_Symbol *null_thunk_data = lnk_symbol_table_searchf(symtab, "\x7f%S_NULL_THUNK_DATA", lnk_get_image_name(config)); if (idata_sect && null_import_desc && null_thunk_data) { - COFF_ParsedSymbol null_import_desc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_import_desc->u.defined.obj, null_import_desc->u.defined.symbol_idx); + COFF_ParsedSymbol null_import_desc_parsed = lnk_parsed_from_symbol(null_import_desc); LNK_SectionContrib *idata_first_contrib = lnk_get_first_section_contrib(idata_sect); PE_DataDirectory *import_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_IMPORT); import_dir->virt_off = image_section_table[idata_first_contrib->u.sect_idx + 1]->voff + idata_first_contrib->u.off; import_dir->virt_size = null_import_desc_parsed.value - idata_first_contrib->u.off; - COFF_ParsedSymbol null_thunk_data_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_thunk_data->u.defined.obj, null_thunk_data->u.defined.symbol_idx); + COFF_ParsedSymbol null_thunk_data_parsed = lnk_parsed_from_symbol(null_thunk_data); U64 null_thunk_data_voff = image_section_table[null_thunk_data_parsed.section_number]->voff + null_thunk_data_parsed.value; U64 first_import_foff = image_section_table[idata_first_contrib->u.sect_idx+1]->foff + idata_first_contrib->u.off; PE_ImportEntry *first_import = str8_deserial_get_raw_ptr(image_data, first_import_foff, sizeof(*first_import)); @@ -4339,10 +4559,10 @@ lnk_build_image(TP_Arena *arena, // patch delay imports { LNK_Section *didat_sect = lnk_section_table_search(sectab, str8_lit(".didat"), PE_IDATA_SECTION_FLAGS); - LNK_Symbol *null_import_desc = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("__NULL_DELAY_IMPORT_DESCRIPTOR")); - LNK_Symbol *last_null_thunk = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, "\x7f%S_NULL_THUNK_DATA_DLA", lnk_get_image_name(config)); + LNK_Symbol *null_import_desc = lnk_symbol_table_search(symtab, str8_lit("__NULL_DELAY_IMPORT_DESCRIPTOR")); + LNK_Symbol *last_null_thunk = lnk_symbol_table_searchf(symtab,"\x7f%S_NULL_THUNK_DATA_DLA", lnk_get_image_name(config)); if (didat_sect && null_import_desc && last_null_thunk) { - COFF_ParsedSymbol null_import_desc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(null_import_desc->u.defined.obj, null_import_desc->u.defined.symbol_idx); + COFF_ParsedSymbol null_import_desc_parsed = lnk_parsed_from_symbol(null_import_desc); LNK_SectionContrib *didat_first_contrib = lnk_get_first_section_contrib(didat_sect); PE_DataDirectory *import_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_DELAY_IMPORT); import_dir->virt_off = lnk_get_first_section_contrib_voff(image_section_table, didat_sect); @@ -4352,7 +4572,7 @@ lnk_build_image(TP_Arena *arena, // patch TLS { - LNK_Symbol *tls_used_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, MSCRT_TLS_SYMBOL_NAME); + LNK_Symbol *tls_used_symbol = lnk_symbol_table_searchf(symtab, MSCRT_TLS_SYMBOL_NAME); if (tls_used_symbol) { ProfBegin("Patch TLS"); @@ -4367,7 +4587,7 @@ lnk_build_image(TP_Arena *arena, } // patch-in align - U64 tls_header_foff = lnk_file_off_from_symbol(image_section_table, tls_used_symbol); + U64 tls_header_foff = lnk_foff_from_symbol(image_section_table, tls_used_symbol); B32 is_tls_header64 = coff_word_size_from_machine(config->machine) == 8; if (is_tls_header64) { PE_TLSHeader64 *tls_header = str8_deserial_get_raw_ptr(image_data, tls_header_foff, sizeof(*tls_header)); @@ -4379,7 +4599,7 @@ lnk_build_image(TP_Arena *arena, // patch directory PE_DataDirectory *tls_dir = pe_data_directory_from_idx(image_data, pe, PE_DataDirectoryIndex_TLS); - tls_dir->virt_off = lnk_virt_off_from_symbol(image_section_table, tls_used_symbol); + tls_dir->virt_off = lnk_voff_from_symbol(image_section_table, tls_used_symbol); tls_dir->virt_size = is_tls_header64 ? sizeof(PE_TLSHeader64) : sizeof(PE_TLSHeader32); ProfEnd(); @@ -4432,8 +4652,8 @@ lnk_build_image(TP_Arena *arena, // compute image guid, and patch PDB and RDI guids { - LNK_Symbol *guid_pdb_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINK_PE_DEBUG_GUID_PDB")); - LNK_Symbol *guid_rdi_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit("RAD_LINK_PE_DEBUG_GUID_RDI")); + LNK_Symbol *guid_pdb_symbol = lnk_symbol_table_search(symtab, str8_lit("RAD_LINK_PE_DEBUG_GUID_PDB")); + LNK_Symbol *guid_rdi_symbol = lnk_symbol_table_search(symtab, str8_lit("RAD_LINK_PE_DEBUG_GUID_RDI")); if (guid_pdb_symbol || guid_rdi_symbol) { switch (config->guid_type) { @@ -4448,13 +4668,13 @@ lnk_build_image(TP_Arena *arena, } if (guid_pdb_symbol) { - U64 cv_guid_foff = lnk_file_off_from_symbol(image_section_table, guid_pdb_symbol); + U64 cv_guid_foff = lnk_foff_from_symbol(image_section_table, guid_pdb_symbol); Guid *cv_guid = str8_deserial_get_raw_ptr(image_data, cv_guid_foff, sizeof(*cv_guid)); *cv_guid = config->guid; } if (guid_rdi_symbol) { - U64 cv_guid_foff = lnk_file_off_from_symbol(image_section_table, guid_rdi_symbol); + U64 cv_guid_foff = lnk_foff_from_symbol(image_section_table, guid_rdi_symbol); Guid *cv_guid = str8_deserial_get_raw_ptr(image_data, cv_guid_foff, sizeof(*cv_guid)); *cv_guid = config->guid; } @@ -4571,7 +4791,7 @@ lnk_pair_u32_nearest_section(PairU32 *arr, U64 count, LNK_Obj **objs, U32 voff) } internal String8List -lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs_count, LNK_Obj **objs, LNK_LibList lib_index[LNK_InputSource_Count], LNK_SectionTable *sectab) +lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs_count, LNK_Obj **objs, U64 libs_count, LNK_Lib **libs, LNK_SectionTable *sectab) { ProfBeginFunction(); Temp scratch = scratch_begin(&arena, 1); @@ -4633,11 +4853,11 @@ lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs String8List source_list = {0}; if (obj) { COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, sect_idx+1); - String8 string_table = str8_substr(obj->data, obj->header.string_table_range); - String8 section_name = coff_name_from_section_header(string_table, section_header); - if (obj->lib) { - String8 lib_path = lnk_obj_get_lib_path(obj); - String8 lib_name = str8_chop_last_dot(str8_skip_last_slash(lib_path)); + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + String8 section_name = coff_name_from_section_header(string_table, section_header); + LNK_Lib *lib = lnk_obj_get_lib(obj); + if (lib) { + String8 lib_name = str8_chop_last_dot(str8_skip_last_slash(lib->path)); String8 obj_name = str8_skip_last_slash(obj->path); str8_list_pushf(temp.arena, &source_list, "%S(%S) SECT%X (%S)", lib_name, obj_name, sect_idx+1, section_name); } else { @@ -4663,17 +4883,15 @@ lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs LNK_Obj *obj = objs[obj_idx]; COFF_SectionHeader *section_table = str8_deserial_get_raw_ptr(obj->data, obj->header.section_table_range.min, 0); for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { - if (lnk_is_coff_section_debug(obj, sect_idx)) { - COFF_SectionHeader *section_header = §ion_table[sect_idx]; - if (~section_header->flags & COFF_SectionFlag_LnkRemove) { - if (obj->lib) { - String8 lib_path = lnk_obj_get_lib_path(obj); - String8 lib_name = str8_chop_last_dot(str8_skip_last_slash(lib_path)); - String8 obj_name = str8_skip_last_slash(obj->path); - str8_list_pushf(arena, &map, "%S(%S) SECT%X\n", lib_name, obj_name, sect_idx+1); - } else { - str8_list_pushf(arena, &map, "%S SECT%X\n", obj->path, sect_idx+1); - } + COFF_SectionHeader *section_header = §ion_table[sect_idx]; + if (~section_header->flags & COFF_SectionFlag_LnkRemove && section_header->flags & LNK_SECTION_FLAG_DEBUG) { + LNK_Lib *lib = lnk_obj_get_lib(obj); + if (lib) { + String8 lib_name = str8_chop_last_dot(str8_skip_last_slash(lib->path)); + String8 obj_name = str8_skip_last_slash(obj->path); + str8_list_pushf(arena, &map, "%S(%S) SECT%X\n", lib_name, obj_name, sect_idx+1); + } else { + str8_list_pushf(arena, &map, "%S SECT%X\n", obj->path, sect_idx+1); } } } @@ -4681,12 +4899,10 @@ lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs str8_list_pushf(arena, &map, "\n"); ProfBegin("LIBS"); - for (U64 input_source = 0; input_source < LNK_InputSource_Count; ++input_source) { - if (lib_index[input_source].count) { - str8_list_pushf(arena, &map, "# LIBS (%S)\n", lnk_string_from_input_source(input_source)); - for (LNK_LibNode *lib_n = lib_index[input_source].first; lib_n != 0; lib_n = lib_n->next) { - str8_list_pushf(arena, &map, "%S\n", lib_n->data.path); - } + if (libs_count) { + str8_list_pushf(arena, &map, "# LIBS\n"); + for EachIndex(i, libs_count) { + str8_list_pushf(arena, &map, "%S\n", libs[i]->path); } } ProfEnd(); @@ -4746,27 +4962,42 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) Temp scratch = scratch_begin(arena->v, arena->count); // - // Link Inputs + // Input Context // - LNK_LinkContext link_ctx = lnk_build_link_context(tp, arena, config); + LNK_Inputer *inputer = lnk_inputer_init(); // - // Image + // Symbol Table // - LNK_ImageContext image_ctx = lnk_build_image(arena, tp, config, link_ctx.symtab, link_ctx.objs_count, link_ctx.objs); + LNK_SymbolTable *symtab = lnk_symbol_table_init(arena); + + // + // Link Image + // + LNK_Link *link = lnk_link_image(tp, arena, config, inputer, symtab); + + U64 objs_count = link->objs.count; + U64 libs_count = link->libs.count; + LNK_Obj **objs = lnk_array_from_obj_list(scratch.arena, link->objs); + LNK_Lib **libs = lnk_array_from_lib_list(scratch.arena, link->libs); + + // + // Layout Image + // + LNK_ImageContext image_ctx = lnk_build_image(arena, tp, config, symtab, objs_count, objs); // Write image in the background LNK_WriteThreadContext *image_write_ctx = push_array(scratch.arena, LNK_WriteThreadContext, 1); image_write_ctx->path = config->image_name; image_write_ctx->temp_path = config->temp_image_name; image_write_ctx->data = image_ctx.image_data; - OS_Handle image_write_thread = os_thread_launch(lnk_write_thread, image_write_ctx, 0); + Thread image_write_thread = thread_launch(lnk_write_thread, image_write_ctx); // // RAD Map // if (config->rad_chunk_map == LNK_SwitchState_Yes) { - String8List rad_map = lnk_build_rad_map(scratch.arena, image_ctx.image_data, config, link_ctx.objs_count, link_ctx.objs, link_ctx.lib_index, image_ctx.sectab); + String8List rad_map = lnk_build_rad_map(scratch.arena, image_ctx.image_data, config, objs_count, objs, libs_count, libs, image_ctx.sectab); lnk_write_data_list_to_file_path(config->rad_chunk_map_name, config->temp_rad_chunk_map_name, rad_map); } @@ -4777,8 +5008,8 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) ProfBegin("Build Import Library"); lnk_timer_begin(LNK_Timer_Lib); String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); - String8List lib_list = pe_make_import_lib(arena->v[0], config->machine, config->time_stamp, str8_skip_last_slash(config->image_name), linker_debug_symbols, config->export_symbol_list); - lnk_write_data_list_to_file_path(config->imp_lib_name, str8_zero(), lib_list); + String8 lib = pe_make_import_lib(arena->v[0], config->machine, config->time_stamp, str8_skip_last_slash(config->image_name), linker_debug_symbols, config->export_symbol_list); + lnk_write_data_to_file_path(config->imp_lib_name, str8_zero(), lib); lnk_timer_end(LNK_Timer_Lib); ProfEnd(); } @@ -4790,10 +5021,18 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) ProfBegin("Debug Info"); lnk_timer_begin(LNK_Timer_Debug); + U64 debug_info_objs_count = 0; + LNK_Obj **debug_info_objs = push_array(scratch.arena, LNK_Obj *, objs_count); + for EachIndex(obj_idx, objs_count) { + LNK_Obj *obj = objs[obj_idx]; + if (obj->exclude_from_debug_info) { continue; } + debug_info_objs[debug_info_objs_count++] = obj; + } + // // CodeView // - LNK_CodeViewInput input = lnk_make_code_view_input(tp, arena, config->io_flags, config->lib_dir_list, link_ctx.objs_count, link_ctx.objs); + LNK_CodeViewInput input = lnk_make_code_view_input(tp, arena, config->io_flags, config->lib_dir_list, config->alt_pch_dirs, debug_info_objs_count, debug_info_objs); CV_DebugT *types = lnk_import_types(tp, arena, &input); // @@ -4837,7 +5076,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) arena, image_ctx.image_data, config, - link_ctx.symtab, + symtab, input.count, input.obj_arr, input.debug_s_arr, @@ -4855,7 +5094,7 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) } // wait for the thread to finish writing image to disk - os_thread_join(image_write_thread, -1); + thread_join(image_write_thread, -1); // // Timers diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 3d7ce197..fd96e826 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -1,21 +1,96 @@ -// Copyright (c) 2025 Epic Games Tools +// Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once -// --- Link -------------------------------------------------------------------- +// --- Input ------------------------------------------------------------------- -typedef struct LNK_LinkContext +typedef struct LNK_LibMemberRef { - LNK_SymbolTable *symtab; - U64 objs_count; - LNK_Obj **objs; - LNK_LibList lib_index[LNK_InputSource_Count]; -} LNK_LinkContext; + LNK_Lib *lib; + U32 member_idx; + struct LNK_LibMemberRef *next; +} LNK_LibMemberRef; -// -- Image -------------------------------------------------------------------- +typedef struct LNK_LibMemberRefList +{ + U64 count; + LNK_LibMemberRef *first; + LNK_LibMemberRef *last; +} LNK_LibMemberRefList; + +typedef enum +{ + LNK_InputSource_CmdLine, // specified on command line + LNK_InputSource_Default, // specified through defaultlib switch + LNK_InputSource_Obj, // refrenced from objects + LNK_InputSource_Count +} LNK_InputSourceType; + +typedef struct LNK_Input +{ + String8 path; + String8 data; + B32 disallow; + B32 is_thin; + B32 has_disk_read_failed; + B32 exclude_from_debug_info; + LNK_LibMemberRef *link_member; + void *loaded_input; + + struct LNK_Input *next; +} LNK_Input; + +typedef struct LNK_InputList +{ + U64 count; + LNK_Input *first; + LNK_Input *last; +} LNK_InputList; + +typedef struct LNK_InputPtrArray +{ + U64 count; + LNK_Input **v; +} LNK_InputPtrArray; + +typedef struct LNK_Inputer +{ + Arena *arena; + + LNK_InputList objs; + HashTable *objs_ht; + LNK_InputList new_objs; + + HashTable *libs_ht; + HashTable *missing_lib_ht; + LNK_InputList libs; + LNK_InputList new_libs[LNK_InputSource_Count]; +} LNK_Inputer; + +// --- Image Link ------------------------------------------------------------- #define LNK_IMPORT_STUB "*** RAD_IMPORT_STUB ***" +#define LNK_NULL_SYMBOL "*** RAD_NULL_SYMBOL ***" + +#define LNK_SECTION_FLAG_LIVE (1 << 0) +#define LNK_SECTION_FLAG_DEBUG (1 << 1) + +typedef struct LNK_Link +{ + LNK_ObjList objs; + LNK_LibList libs; + LNK_ObjNode **last_symbol_input; + LNK_IncludeSymbolNode **last_include; + String8Node **last_cmd_lib; + String8Node **last_default_lib; + String8Node **last_obj_lib; + LNK_LibMemberRefList imports; + B32 try_to_resolve_entry_point; +} LNK_Link; + +// -- Image Layout ------------------------------------------------------------ + #define LNK_REMOVED_SECTION_NUMBER_32 (U32)-3 #define LNK_REMOVED_SECTION_NUMBER_16 (U16)-3 @@ -43,13 +118,44 @@ typedef struct LNK_CommonBlockContrib } u; } LNK_CommonBlockContrib; +// --- Ref --------------------------------------------------------------------- + +#define LNK_RELOCS_PER_TASK 0x1000 + +typedef struct LNK_RelocRefs +{ + LNK_Obj *obj; + COFF_RelocArray relocs; +} LNK_RelocRefs; + +typedef struct LNK_RelocRefsNode +{ + LNK_RelocRefs *v; + struct LNK_RelocRefsNode *next; +} LNK_RelocRefsNode; + +typedef union LNK_RelocRefsPointer +{ + struct { + LNK_RelocRefsNode *node; + U64 tag; + }; + U64 v[2]; +} LNK_RelocRefsPointer; + +typedef struct AlignType(16) LNK_RelocRefsList +{ + LNK_RelocRefsPointer head; +} LNK_RelocRefsList; + // --- Base Reloc -------------------------------------------------------------- typedef struct LNK_BaseRelocPage { - U64 voff; - U64List entries_addr32; - U64List entries_addr64; + U32 buffer_offset; + U32 voff; + U64List *entries_addr32; + U64List *entries_addr64; } LNK_BaseRelocPage; typedef struct LNK_BaseRelocPageNode @@ -73,6 +179,57 @@ typedef struct LNK_BaseRelocPageArray // --- Workers Contexts -------------------------------------------------------- +typedef struct +{ + B32 search_anti_deps; + LNK_SymbolTable *symtab; + LNK_Lib *lib; + LNK_LibMemberRefList *member_ref_lists; +} LNK_SearchLibTask; + +typedef struct +{ + LNK_SymbolTable *symtab; + U32 active_thread_count; + LNK_RelocRefsList *reloc_refs; +} LNK_OptRefTask; + +typedef struct +{ + String8 image_data; + LNK_Obj **objs; + U64 image_base; + COFF_SectionHeader **image_section_table; +} LNK_ObjRelocPatcher; + +typedef struct +{ + U64 page_size; + B32 is_large_addr_aware; + union { + struct { + LNK_Obj **objs; + LNK_BaseRelocPageList *pages; + HashTable **page_ht; + } gather; + struct { + U64 buffer_size; + U8 *buffer; + LNK_BaseRelocPageArray pages; + Rng1U64 *ranges; + } serialize; + }; +} LNK_BaseRelocsTask; + +typedef struct LNK_ImageFillNode +{ + U64 base_foff; + U64 sc_count; + LNK_SectionContrib **sc; + + struct LNK_ImageFillNode *next; +} LNK_ImageFillNode; + typedef struct { LNK_SymbolTable *symtab; @@ -104,54 +261,13 @@ typedef struct LNK_CommonBlockContrib *common_block_contribs; COFF_SymbolValueInterpType fixup_type; } patch_symtabs; + struct { + String8 image_data; + LNK_ImageFillNode **fill_nodes; + } image_fill; } u; } LNK_BuildImageTask; -typedef struct -{ - U64 page_size; - Rng1U64 *range_arr; - LNK_BaseRelocPageList *list_arr; - HashTable **page_ht_arr; - B32 is_large_addr_aware; -} LNK_BaseRelocTask; - -typedef struct -{ - Rng1U64 *ranges; - U64 page_size; - LNK_BaseRelocPageList *list_arr; - LNK_Obj **obj_arr; - HashTable **page_ht_arr; - B32 is_large_addr_aware; -} LNK_ObjBaseRelocTask; - -typedef struct -{ - LNK_InputObjList input_obj_list; - U64 input_imports_count; - LNK_InputImport *input_imports; - LNK_InputImportList input_import_list; - LNK_SymbolList unresolved_symbol_list; -} LNK_SymbolFinderResult; - -typedef struct -{ - PathStyle path_style; - LNK_SymbolTable *symtab; - LNK_SymbolNodeArray lookup_node_arr; - LNK_SymbolFinderResult *result_arr; - Rng1U64 *range_arr; -} LNK_SymbolFinder; - -typedef struct -{ - String8 image_data; - LNK_Obj **objs; - U64 image_base; - COFF_SectionHeader **image_section_table; -} LNK_ObjRelocPatcher; - typedef struct { String8 path; @@ -166,15 +282,6 @@ typedef struct U128 *hashes; } LNK_Blake3Hasher; -typedef struct -{ - LNK_SymbolTable *symtab; - union { - LNK_ObjNodeArray objs; - LNK_LibNodeArray libs; - } u; -} LNK_SymbolPusher; - // --- Config ----------------------------------------------------------------- internal LNK_Config * lnk_config_from_argcv(Arena *arena, int argc, char **argv); @@ -201,25 +308,51 @@ internal String8 lnk_manifest_from_inputs(Arena *arena, LNK_IO_Flags io_flags, S internal String8 lnk_make_null_obj(Arena *arena); internal String8 lnk_make_res_obj(Arena *arena, String8List res_file_list, String8List res_path_list, COFF_MachineType machine, U32 time_stamp, String8 work_dir, PathStyle system_path_style, String8 obj_name); -internal String8 lnk_make_linker_coff_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 cwd_path, String8 exe_path, String8 pdb_path, String8 cmd_line, String8 obj_name); +internal String8 lnk_make_linker_obj(Arena *arena, LNK_Config *config); + +// --- Inputer ----------------------------------------------------------------- + +internal void lnk_input_list_push_node(LNK_InputList *list, LNK_Input *node); +internal void lnk_input_list_concat_in_place(LNK_InputList *list, LNK_InputList *to_concat); +internal LNK_InputPtrArray lnk_array_from_input_list(Arena *arena, LNK_InputList list); + +internal LNK_Inputer * lnk_inputer_init(void); + +internal LNK_Input * lnk_input_push(Arena *arena, LNK_InputList *list, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_linkgen(Arena *arena, LNK_InputList *list, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_thin(Arena *arena, LNK_InputList *list, HashTable *ht, String8 full_path); + +internal LNK_Input * lnk_inputer_push_obj(LNK_Inputer *inputer, LNK_LibMemberRef *link_member, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_obj_linkgen(LNK_Inputer *inputer, LNK_LibMemberRef *link_member, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_obj_thin(LNK_Inputer *inputer, LNK_LibMemberRef *link_member, String8 path); + +internal LNK_Input * lnk_inputer_push_lib(LNK_Inputer *inputer, LNK_InputSourceType input_source, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_lib_linkgen(LNK_Inputer *inputer, LNK_InputSourceType input_source, String8 path, String8 data); +internal LNK_Input * lnk_inputer_push_lib_thin(LNK_Inputer *inputer, LNK_Config *config, LNK_InputSourceType input_source, String8 lib_path); + +internal B32 lnk_inputer_has_items(LNK_Inputer *inputer); +internal LNK_InputPtrArray lnk_inputer_flush(Arena *arena, TP_Context *tp, LNK_Inputer *inputer, LNK_IO_Flags io_flags, LNK_InputList *all_inputs, LNK_InputList *new_inputs); // --- Link Context ------------------------------------------------------------ -internal String8 lnk_get_lib_name(String8 path); -internal B32 lnk_is_lib_disallowed(HashTable *disallow_lib_ht, String8 path); -internal B32 lnk_is_lib_loaded(HashTable *loaded_lib_ht, String8 lib_path); -internal void lnk_push_disallow_lib(Arena *arena, HashTable *disallow_lib_ht, String8 path); -internal void lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path); +internal void lnk_lib_member_ref_list_push_node(LNK_LibMemberRefList *list, LNK_LibMemberRef *node); +internal void lnk_lib_member_ref_list_concat_in_place_array(LNK_LibMemberRefList *list, LNK_LibMemberRefList *to_concat_arr, U64 count); +internal int lnk_lib_member_ref_is_before(void *raw_a, void *raw_b); +internal LNK_LibMemberRef ** lnk_array_from_lib_member_list(Arena *arena, LNK_LibMemberRefList list); -internal LNK_InputObjList lnk_push_linker_symbols(Arena *arena, LNK_Config *config); -internal void lnk_queue_lib_member_input(Arena *arena, PathStyle path_style, LNK_SymbolLib *symbol, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list); +internal LNK_ObjNode * lnk_load_objs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link, U64 *objs_count_out); +internal void lnk_load_libs (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_Link *link); +internal void lnk_link_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab, LNK_Link *link); +internal LNK_Link * lnk_link_image (TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer *inputer, LNK_SymbolTable *symtab); -internal LNK_LinkContext lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config); +// --- Optimizations ----------------------------------------------------------- + +internal void lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_ObjList objs); // --- Win32 Image ------------------------------------------------------------- internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag); -internal String8List lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_temp, LNK_Config *config, U64 objs_count, LNK_Obj **objs); +internal String8 lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_temp, LNK_Config *config, U64 objs_count, LNK_Obj **objs); internal String8List lnk_build_win32_image_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sect_arr, U64 expected_image_header_size); internal LNK_ImageContext lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 obj_count, LNK_Obj **objs); diff --git a/src/linker/lnk_cmd_line.c b/src/linker/lnk_cmd_line.c index f2828156..cf1909dd 100644 --- a/src/linker/lnk_cmd_line.c +++ b/src/linker/lnk_cmd_line.c @@ -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 String8List diff --git a/src/linker/lnk_cmd_line.h b/src/linker/lnk_cmd_line.h index b130715f..4330bc09 100644 --- a/src/linker/lnk_cmd_line.h +++ b/src/linker/lnk_cmd_line.h @@ -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 diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index ae28b190..a06de23b 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -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/) global read_only LNK_CmdSwitch g_cmd_switch_map[] = @@ -15,6 +15,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_NotImplemented, 0, "ASSEMBLYMODULE", "", "" }, // .NET { LNK_CmdSwitch_NotImplemented, 0, "ASSEMBLYRESOURCE", "", "" }, // .NET { LNK_CmdSwitch_Base, 0, "BASE", "{ADDRESS[,SIZE]|@FILENAME,KEY}", "" }, + { LNK_CmdSwitch_Brepro, 0, "BREPRO", "", "Not supported" }, { LNK_CmdSwitch_NotImplemented, 0, "CLRIMAGETYPE", "", "" }, // .NET { LNK_CmdSwitch_NotImplemented, 0, "CLRLOADEROPTIMIZATION","", "" }, // .NET { LNK_CmdSwitch_NotImplemented, 0, "CLRSUPPORTLASTERROR", "", "" }, // .NET @@ -31,11 +32,12 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_Dll, 0, "DLL", "", "" }, { LNK_CmdSwitch_NotImplemented, 0, "DRIVER", "", "" }, { LNK_CmdSwitch_DisallowLib, 1, "DISALLOWLIB", ":LIBRARY", "", }, + { LNK_CmdSwitch_D2, 0, "D2", "" }, { LNK_CmdSwitch_EditAndContinue, 1, "EDITANDCONTINUE", "[:NO]", "" }, { LNK_CmdSwitch_DynamicBase, 0, "DYNAMICBASE", "[:NO]", "" }, { LNK_CmdSwitch_NotImplemented, 0, "EMITVOLATILEMETADATA", "", "" }, { LNK_CmdSwitch_Entry, 1, "ENTRY", ":FUNCTION", "" }, - { LNK_CmdSwitch_Null, 0, "ERRORREPORT", "", "Deprecated starting Windows Vista." }, + { LNK_CmdSwitch_ErrorReport, 0, "ERRORREPORT", "", "Deprecated starting Windows Vista." }, { LNK_CmdSwitch_Export, 1, "EXPORT", ":SYMBOL", "" }, { LNK_CmdSwitch_NotImplemented, 0, "EXPORTADMIN", "", "" }, { LNK_CmdSwitch_FastFail, 0, "FASTFAIL", "", "Not used." }, @@ -121,42 +123,42 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_NotImplemented, 0, "WX", "", "" }, //- internal switches - { LNK_CmdSwitch_Rad_Age, 0, "RAD_AGE", ":#", "Age embeded in EXE and PDB, used to validate incremental build. Default is 1." }, - { LNK_CmdSwitch_Rad_BuildInfo, 0, "RAD_BUILD_INFO", "", "Print build info and exit." }, - { LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, 0, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Map, 0, "RAD_MAP", ":FILENAME", "Emit file with the output image's layout description." }, - { LNK_CmdSwitch_Rad_MemoryMapFiles, 0, "RAD_MEMORY_MAP_FILES", "[:NO]", "When enabled, files are memory-mapped instead of being read entirely on request." }, - { LNK_CmdSwitch_Rad_Debug, 0, "RAD_DEBUG", "[:NO]", "Emit RAD debug info file." }, - { LNK_CmdSwitch_Rad_DebugAltPath, 0, "RAD_DEBUGALTPATH", "", "" }, - { LNK_CmdSwitch_Rad_DebugName, 0, "RAD_DEBUG_NAME", ":FILENAME", "Sets file name for RAD debug info file." }, - { LNK_CmdSwitch_Rad_DelayBind, 0, "RAD_DELAY_BIND", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_DoMerge, 0, "RAD_DO_MERGE", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_EnvLib, 0, "RAD_ENV_LIB", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Exe, 0, "RAD_EXE", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Guid, 0, "RAD_GUID", ":{IMAGEBLAKE3|XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX}", "" }, - { LNK_CmdSwitch_Rad_LargePages, 0, "RAD_LARGE_PAGES", "[:NO]", "Disabled by default on Windows." }, - { LNK_CmdSwitch_Rad_LinkVer, 0, "RAD_LINK_VER", ":##,##", "" }, - { LNK_CmdSwitch_Rad_Log, 0, "RAD_LOG", ":{ALL,INPUT_OBJ,INPUT_LIB,IO,LINK_STATS,TIMERS}", "" }, - { LNK_CmdSwitch_Rad_MtPath, 0, "RAD_MT_PATH", ":EXEPATH", "Exe path to manifest tool, default: " LNK_MANIFEST_MERGE_TOOL_NAME }, - { LNK_CmdSwitch_Rad_OsVer, 0, "RAD_OS_VER", ":##,##", "" }, - { LNK_CmdSwitch_Rad_PageSize, 0, "RAD_PAGE_SIZE", ":#", "Must be power of two." }, - { LNK_CmdSwitch_Rad_PathStyle, 0, "RAD_PATH_STYLE", ":{WindowsAbsolute|UnixAbsolute}", "" }, - { LNK_CmdSwitch_Rad_PdbHashTypeNameLength, 0, "RAD_PDB_HASH_TYPE_NAME_LENGTH", ":#", "Number of hash bytes to use to replace type name. Default 8 bytes (Max 16)." }, - { LNK_CmdSwitch_Rad_PdbHashTypeNameMap, 0, "RAD_PDB_HASH_TYPE_NAME_MAP", ":FILENAME", "Produce map file with hash -> type name mappings." }, - { LNK_CmdSwitch_Rad_PdbHashTypeNames, 0, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." }, - { LNK_CmdSwitch_Rad_RemoveSection, 0, "RAD_REMOVE_SECTION", ":NAME", "Removes a section from output image." }, - { LNK_CmdSwitch_Rad_SharedThreadPool, 0, "RAD_SHARED_THREAD_POOL", "[:STRING]", "Default value \"" LNK_DEFAULT_THREAD_POOL_NAME "\"" }, - { LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, 0, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." }, - { LNK_CmdSwitch_Rad_SuppressError, 0, "RAD_SUPPRESS_ERROR", ":#", "" }, - { LNK_CmdSwitch_Rad_SymbolTableCapDefined, 0, "RAD_SYMBOL_TABLE_CAP_DEFINED", ":#", "Number of buckets allocated in the symbol table for defined symbols." }, - { LNK_CmdSwitch_Rad_SymbolTableCapInternal, 0, "RAD_SYMBOL_TABLE_CAP_INTERNAL", ":#", "Number of buckets allocated in the symbol table for internal symbols." }, - { LNK_CmdSwitch_Rad_SymbolTableCapLib, 0, "RAD_SYMBOL_TABLE_CAP_LIB", ":#", "Number of buckets allocated in the symbol table for library symbols." }, - { LNK_CmdSwitch_Rad_SymbolTableCapWeak, 0, "RAD_SYMBOL_TABLE_CAP_WEAK", ":#", "Number of buckets allocated in the symbol table for weak symbols." }, - { LNK_CmdSwitch_Rad_TargetOs, 0, "RAD_TARGET_OS", ":{WINDOWS,LINUX,MAC}" }, - { LNK_CmdSwitch_Rad_WriteTempFiles, 0, "RAD_WRITE_TEMP_FILES", "[:NO]", "When speicifed linker writes image and debug info to temporary files and renames after link is done." }, - { LNK_CmdSwitch_Rad_TimeStamp, 0, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." }, - { LNK_CmdSwitch_Rad_Version, 0, "RAD_VERSION", "", "Print version and exit." }, - { LNK_CmdSwitch_Rad_Workers, 0, "RAD_WORKERS", ":#", "Sets number of workers created in the pool. Number is capped at 1024. When /RAD_SHARED_THREAD_POOL is specified this number cant exceed /RAD_SHARED_THREAD_POOL_MAX_WORKERS." }, + { LNK_CmdSwitch_Rad_Age, 0, "RAD_AGE", ":#", "Age embeded in EXE and PDB, used to validate incremental build. Default is 1." }, + { LNK_CmdSwitch_Rad_AltPchDir, 0, "RAD_ALT_PCH_DIR", ":PATH", "Alternative directory to search for PCH object files." }, + { LNK_CmdSwitch_Rad_BuildInfo, 0, "RAD_BUILD_INFO", "", "Print build info and exit." }, + { LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, 0, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Map, 0, "RAD_MAP", ":FILENAME", "Emit file with the output image's layout description." }, + { LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, 0, "RAD_MAP_LINES_FOR_UNRESOLVED_SYMBOLS", "[:NO]", "Use debug info to print source file location for unresolved symbol" }, + { LNK_CmdSwitch_Rad_MemoryMapFiles, 0, "RAD_MEMORY_MAP_FILES", "[:NO]", "When enabled, files are memory-mapped instead of being read entirely on request." }, + { LNK_CmdSwitch_Rad_Debug, 0, "RAD_DEBUG", "[:NO]", "Emit RAD debug info file." }, + { LNK_CmdSwitch_Rad_DebugAltPath, 0, "RAD_DEBUGALTPATH", "", "" }, + { LNK_CmdSwitch_Rad_DebugName, 0, "RAD_DEBUG_NAME", ":FILENAME", "Sets file name for RAD debug info file." }, + { LNK_CmdSwitch_Rad_DelayBind, 0, "RAD_DELAY_BIND", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_DoMerge, 0, "RAD_DO_MERGE", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_EnvLib, 0, "RAD_ENV_LIB", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Exe, 0, "RAD_EXE", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Guid, 0, "RAD_GUID", ":{IMAGEBLAKE3|XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX}", "" }, + { LNK_CmdSwitch_Rad_LargePages, 0, "RAD_LARGE_PAGES", "[:NO]", "Disabled by default on Windows." }, + { LNK_CmdSwitch_Rad_LinkVer, 0, "RAD_LINK_VER", ":##,##", "" }, + { LNK_CmdSwitch_Rad_Log, 0, "RAD_LOG", ":{ALL,INPUT_OBJ,INPUT_LIB,IO,LINK_STATS,TIMERS}", "" }, + { LNK_CmdSwitch_Rad_MtPath, 0, "RAD_MT_PATH", ":EXEPATH", "Exe path to manifest tool, default: " LNK_MANIFEST_MERGE_TOOL_NAME }, + { LNK_CmdSwitch_Rad_OsVer, 0, "RAD_OS_VER", ":##,##", "" }, + { LNK_CmdSwitch_Rad_PageSize, 0, "RAD_PAGE_SIZE", ":#", "Must be power of two." }, + { LNK_CmdSwitch_Rad_PathStyle, 0, "RAD_PATH_STYLE", ":{WindowsAbsolute|UnixAbsolute}", "" }, + { LNK_CmdSwitch_Rad_PdbHashTypeNameLength, 0, "RAD_PDB_HASH_TYPE_NAME_LENGTH", ":#", "Number of hash bytes to use to replace type name. Default 8 bytes (Max 16)." }, + { LNK_CmdSwitch_Rad_PdbHashTypeNameMap, 0, "RAD_PDB_HASH_TYPE_NAME_MAP", ":FILENAME", "Produce map file with hash -> type name mappings." }, + { LNK_CmdSwitch_Rad_PdbHashTypeNames, 0, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." }, + { LNK_CmdSwitch_Rad_RemoveSection, 0, "RAD_REMOVE_SECTION", ":NAME", "Removes a section from output image." }, + { LNK_CmdSwitch_Rad_SharedThreadPool, 0, "RAD_SHARED_THREAD_POOL", "[:STRING]", "Default value \"" LNK_DEFAULT_THREAD_POOL_NAME "\"" }, + { LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, 0, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." }, + { LNK_CmdSwitch_Rad_SuppressError, 0, "RAD_SUPPRESS_ERROR", ":#", "" }, + { LNK_CmdSwitch_Rad_TargetOs, 0, "RAD_TARGET_OS", ":{WINDOWS,LINUX,MAC}" }, + { LNK_CmdSwitch_Rad_WriteTempFiles, 0, "RAD_WRITE_TEMP_FILES", "[:NO]", "When speicifed linker writes image and debug info to temporary files and renames after link is done." }, + { LNK_CmdSwitch_Rad_TimeStamp, 0, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." }, + { LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, 0, "RAD_UNRESOLVED_SYMBOL_LIMIT", ":#", "Limits number of unresolved symbol errors linker reports." }, + { LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, 0, "RAD_UNRESOLVED_SYMBOL_REF_LIMIT", ":#", "Limit number of unresolved symbol references linker reports." }, + { LNK_CmdSwitch_Rad_Version, 0, "RAD_VERSION", "", "Print version and exit." }, + { LNK_CmdSwitch_Rad_Workers, 0, "RAD_WORKERS", ":#", "Sets number of workers created in the pool. Number is capped at 1024. When /RAD_SHARED_THREAD_POOL is specified this number cant exceed /RAD_SHARED_THREAD_POOL_MAX_WORKERS." }, { LNK_CmdSwitch_Help, 0, "HELP", "", "" }, { LNK_CmdSwitch_Help, 0, "?", "", "" }, @@ -333,15 +335,20 @@ internal String8 lnk_error_check_and_strip_quotes(LNK_ErrorCode error_code, LNK_Obj *obj, LNK_CmdSwitchType cmd_switch, String8 string) { String8 result = string; - B32 starts_with_quote = str8_match_lit("\"", string, StringMatchFlag_RightSideSloppy); - if (starts_with_quote) { - if (str8_ends_with_lit(string, "\"", 0)) { - result = str8_skip(result, 1); - result = str8_chop(result, 1); - } else { - lnk_error_cmd_switch(error_code, obj, cmd_switch, "detected unmatched \" in \"%S\"", string); - } + + B32 starts_with_quote = str8_match(str8_substr(string, rng_1u64(0,1)), str8_lit("\""), 0); + B32 ends_with_quote = 0; + if (string.size > 2) { + ends_with_quote = str8_match(str8_substr(string, rng_1u64(string.size-1,string.size)), str8_lit("\""), 0); } + + if (starts_with_quote && ends_with_quote) { + result = str8_skip(result, 1); + result = str8_chop(result, 1); + } else if (starts_with_quote && !ends_with_quote) { + lnk_error_cmd_switch(error_code, obj, cmd_switch, "detected unmatched \" in \"%S\"", string); + } + return result; } @@ -736,16 +743,6 @@ lnk_parse_export_directive(Arena *arena, String8 directive, LNK_Obj *obj, PE_Exp return is_parsed; } -internal LNK_MergeDirectiveNode * -lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data) -{ - LNK_MergeDirectiveNode *node = push_array_no_zero(arena, LNK_MergeDirectiveNode, 1); - node->data = data; - SLLQueuePush(list->first, list->last, node); - list->count += 1; - return node; -} - internal B32 lnk_parse_merge_directive(String8 string, LNK_Obj *obj, LNK_MergeDirective *out) { @@ -763,6 +760,26 @@ lnk_parse_merge_directive(String8 string, LNK_Obj *obj, LNK_MergeDirective *out) return is_parse_ok; } +internal LNK_AltNameNode * +lnk_alt_name_list_push(Arena *arena, LNK_AltNameList *list, LNK_AltName v) +{ + LNK_AltNameNode *node = push_array(arena, LNK_AltNameNode, 1); + node->v = v; + SLLQueuePush(list->first, list->last, node); + list->count += 1; + return node; +} + +internal LNK_MergeDirectiveNode * +lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective v) +{ + LNK_MergeDirectiveNode *node = push_array_no_zero(arena, LNK_MergeDirectiveNode, 1); + node->v = v; + SLLQueuePush(list->first, list->last, node); + list->count += 1; + return node; +} + internal String8 lnk_get_image_name(LNK_Config *config) { @@ -933,6 +950,63 @@ lnk_is_section_removed(LNK_Config *config, String8 section_name) return is_removed; } +internal B32 +lnk_is_dll_delay_load(LNK_Config *config, String8 dll_name) +{ + return hash_table_search_path_u64(config->delay_load_ht, dll_name, 0); +} + +internal String8 +lnk_get_lib_name(String8 path) +{ + static String8 LIB_EXT = str8_lit_comp(".LIB"); + + // strip path + String8 name = str8_skip_last_slash(path); + + // strip extension + String8 name_ext = str8_postfix(name, LIB_EXT.size); + if (str8_match(name_ext, LIB_EXT, StringMatchFlag_CaseInsensitive)) { + name = str8_chop(name, LIB_EXT.size); + } + + return name; +} + +internal void +lnk_push_disallow_lib(LNK_Config *config, String8 path) +{ + String8 lib_name = lnk_get_lib_name(path); + hash_table_push_path_u64(config->arena, config->disallow_lib_ht, lib_name, 0); +} + +internal B32 +lnk_is_lib_disallowed(LNK_Config *config, String8 path) +{ + String8 lib_name = lnk_get_lib_name(path); + return hash_table_search_path(config->disallow_lib_ht, lib_name) != 0; +} + +internal void +lnk_include_symbol(LNK_Config *config, String8 name, LNK_Obj *obj) +{ + // is this a duplicate symbol? + if (hash_table_search_string_raw(config->include_symbol_ht, name)) { + return; + } + + name = push_str8_copy(config->arena, name); + + LNK_IncludeSymbolNode *node = push_array(config->arena, LNK_IncludeSymbolNode, 1); + node->v.name = name; + node->v.obj = obj; + + SLLQueuePush(config->include_symbol_list.first, config->include_symbol_list.last, node); + config->include_symbol_list.count += 1; + + hash_table_push_string_raw(config->arena, config->include_symbol_ht, name, node); +} + internal void lnk_print_build_info() { @@ -1057,9 +1131,9 @@ lnk_unwrap_rsp(Arena *arena, String8List arg_list) } internal void -lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_name, String8List value_strings, LNK_Obj *obj) +lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List value_strings, LNK_Obj *obj) { - Temp scratch = scratch_begin(&arena,1); + Temp scratch = scratch_begin(&config->arena, 1); LNK_CmdSwitchType cmd_switch = lnk_cmd_switch_type_from_string(cmd_name); @@ -1100,16 +1174,11 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam lnk_error_obj(LNK_Error_AlternateNameConflict, obj, "conflicting alternative name: existing '%S=%S' vs. new '%S=%S'", alt_name.from, to_extant, alt_name.from, alt_name.to); } } else { - hash_table_push_string_string(arena, config->alt_name_ht, alt_name.from, alt_name.to); + alt_name.from = push_str8_copy(config->arena, alt_name.from); + alt_name.to = push_str8_copy(config->arena, alt_name.to); - alt_name.from = push_str8_copy(arena, alt_name.from); - alt_name.to = push_str8_copy(arena, alt_name.to); - - LNK_AltNameNode *alt_name_n = push_array(arena, LNK_AltNameNode, 1); - alt_name_n->data = alt_name; - - SLLQueuePush(config->alt_name_list.first, config->alt_name_list.last, alt_name_n); - config->alt_name_list.count += 1; + lnk_alt_name_list_push(config->arena, &config->alt_name_list, alt_name); + hash_table_push_string_string(config->arena, config->alt_name_ht, alt_name.from, alt_name.to); } } } else { @@ -1149,6 +1218,10 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } } break; + case LNK_CmdSwitch_Brepro: { + // not supported -- ignore + } break; + case LNK_CmdSwitch_Debug: { if (value_strings.node_count == 0) { config->debug_mode = LNK_DebugMode_Full; @@ -1171,7 +1244,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_DefaultLib: { - String8List default_lib_list = str8_list_copy(arena, &value_strings); + String8List default_lib_list = str8_list_copy(config->arena, &value_strings); if (obj) { str8_list_concat_in_place(&config->input_obj_lib_list, &default_lib_list); } else { @@ -1195,8 +1268,12 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_DelayLoad: { - String8List delay_load_dll_list = str8_list_copy(arena, &value_strings); - str8_list_concat_in_place(&config->delay_load_dll_list, &delay_load_dll_list); + for (String8Node *name_n = value_strings.first; name_n != 0; name_n = name_n->next) { + if (hash_table_search_path_u64(config->delay_load_ht, name_n->string, 0)) { continue; } + String8 name = push_str8_copy(config->arena, name_n->string); + hash_table_push_path_u64(config->arena, config->delay_load_ht, name, 0); + str8_list_push(config->arena, &config->delay_load_dll_list, name); + } } break; case LNK_CmdSwitch_Dll: { @@ -1211,9 +1288,13 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unsupported switch; binary dump is done by passing /DUMP to link.exe"); } break; + case LNK_CmdSwitch_D2: { + // not supported -- ignore + } break; + case LNK_CmdSwitch_Entry: { String8 new_entry_point_name = {0}; - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &new_entry_point_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &new_entry_point_name); if (config->entry_point_name.size) { lnk_error_cmd_switch(LNK_Warning_Cmdl, obj, cmd_switch, "unable to redefine entry point \"%S\" to \"%S\"", config->entry_point_name, new_entry_point_name); @@ -1223,23 +1304,26 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam config->entry_point_name = new_entry_point_name; } break; + case LNK_CmdSwitch_ErrorReport: { + // not supported -- ignore + } break; + case LNK_CmdSwitch_Export: { PE_ExportParse export_parse = {0}; - if (lnk_parse_export_directive_ex(arena, value_strings, obj, &export_parse)) { - PE_ExportParseNode *exp_n = 0; - String8 export_name = pe_name_from_export_parse(&export_parse); - hash_table_search_string_raw(config->export_ht, export_name, &exp_n); + if (lnk_parse_export_directive_ex(config->arena, value_strings, obj, &export_parse)) { + String8 export_name = pe_name_from_export_parse(&export_parse); + PE_ExportParseNode *exp_n = hash_table_search_string_raw(config->export_ht, export_name); if (exp_n == 0) { // make sure export is defined if (!export_parse.is_forwarder) { - str8_list_push(arena, &config->include_symbol_list, export_parse.name); + lnk_include_symbol(config, export_parse.name, 0); } // push new export - exp_n = pe_export_parse_list_push(arena, &config->export_symbol_list, export_parse); + exp_n = pe_export_parse_list_push(config->arena, &config->export_symbol_list, export_parse); - hash_table_push_string_raw(arena, config->export_ht, export_name, exp_n); + hash_table_push_string_raw(config->arena, config->export_ht, export_name, exp_n); } else { B32 is_ambiguous = 1; PE_ExportParse *extant_export = &exp_n->data; @@ -1337,19 +1421,12 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_ImpLib: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->imp_lib_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->imp_lib_name); } break; case LNK_CmdSwitch_Include: { for (String8Node *value_n = value_strings.first; value_n != 0; value_n = value_n->next) { - // is this a duplicate symbol? - if (hash_table_search_string_raw(config->include_symbol_ht, value_n->string, 0)) { - continue; - } - - String8 include_symbol = push_str8_copy(arena, value_n->string); - hash_table_push_string_raw(arena, config->include_symbol_ht, include_symbol, 0); - str8_list_push(arena, &config->include_symbol_list, include_symbol); + lnk_include_symbol(config, value_n->string, obj); } } break; @@ -1371,7 +1448,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_LibPath: { - String8List lib_dir_list = str8_list_copy(arena, &value_strings); + String8List lib_dir_list = str8_list_copy(config->arena, &value_strings); for (String8Node *dir_n = lib_dir_list.first; dir_n != 0; dir_n = dir_n->next) { if (!os_folder_path_exists(dir_n->string)) { String8 full_path = os_full_path_from_path(scratch.arena, dir_n->string); @@ -1411,7 +1488,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (res_id_arr.count == 2) { U64 resource_id; if (try_u64_from_str8_c_rules(res_id_arr.v[1], &resource_id)) { - config->manifest_resource_id = push_u64(arena, resource_id); + config->manifest_resource_id = push_u64(config->arena, resource_id); } else { lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "unable to parse resource_id \"%S\"", res_id_arr.v[1]); } @@ -1440,7 +1517,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_ManifestDependency: { - String8List manifest_dependency_list = str8_list_copy(arena, &value_strings); + String8List manifest_dependency_list = str8_list_copy(config->arena, &value_strings); str8_list_concat_in_place(&config->manifest_dependency_list, &manifest_dependency_list); if (config->manifest_opt == LNK_ManifestOpt_Null) { @@ -1449,7 +1526,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_ManifestFile: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->manifest_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->manifest_name); } break; case LNK_CmdSwitch_ManifestInput: { @@ -1473,7 +1550,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam str8_match_lit("'requireAdministrator'", level, 0)) { // manifest level was parsed! config->manifest_uac = 1; - config->manifest_level = push_str8_copy(arena, level); + config->manifest_level = push_str8_copy(config->arena, level); if (param_arr.count > 1) { String8 ui_access_param = param_arr.v[1]; String8List ui_access_list = str8_split_by_string_chars(scratch.arena, ui_access_param, str8_lit("="), 0); @@ -1482,7 +1559,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (str8_match_lit("'true'", ui_access, 0) || str8_match_lit("'false'", ui_access, 0)) { // ui access was parsed! - config->manifest_ui_access = push_str8_copy(arena, ui_access); + config->manifest_ui_access = push_str8_copy(config->arena, ui_access); } else { lnk_error_invalid_uac_ui_access_param(LNK_Error_Cmdl, obj, cmd_switch, ui_access_param); } @@ -1516,9 +1593,9 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (value_strings.node_count == 1) { LNK_MergeDirective merge = {0}; if (lnk_parse_merge_directive(value_strings.first->string, obj, &merge)) { - merge.src = push_str8_copy(arena, merge.src); - merge.dst = push_str8_copy(arena, merge.dst); - lnk_merge_directive_list_push(arena, &config->merge_list, merge); + merge.src = push_str8_copy(config->arena, merge.src); + merge.dst = push_str8_copy(config->arena, merge.dst); + lnk_merge_directive_list_push(config->arena, &config->merge_list, merge); } } else { lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters %d", value_strings.node_count); @@ -1534,7 +1611,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } } - String8List natvis_list = str8_list_copy(arena, &value_strings); + String8List natvis_list = str8_list_copy(config->arena, &value_strings); str8_list_concat_in_place(&config->natvis_list, &natvis_list); } break; @@ -1543,8 +1620,13 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (value_strings.node_count == 0) { config->no_default_libs = 1; } else { - String8List no_default_lib_list = str8_list_copy(arena, &value_strings); - str8_list_concat_in_place(&config->disallow_lib_list, &no_default_lib_list); + for (String8Node *lib_n = value_strings.first; lib_n != 0; lib_n = lib_n->next) { + String8 lib_name = lnk_get_lib_name(lib_n->string); + if (hash_table_search_path_raw(config->disallow_lib_ht, lib_name)) { + continue; + } + hash_table_push_path_raw(config->arena, config->disallow_lib_ht, lib_name, 0); + } } } break; @@ -1599,16 +1681,16 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Out: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->image_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->image_name); } break; case LNK_CmdSwitch_Pdb: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->pdb_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->pdb_name); } break; case LNK_CmdSwitch_PdbAltPath: { // see :PdbAltPath - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->pdb_alt_path); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->pdb_alt_path); } break; case LNK_CmdSwitch_PdbPageSize: { @@ -1698,6 +1780,15 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam lnk_cmd_switch_parse_u32(obj, cmd_switch, value_strings, &config->age, 0); } break; + case LNK_CmdSwitch_Rad_AltPchDir: { + if (value_strings.node_count == 0) { + lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "missing parameters"); + break; + } + String8List dirs = str8_list_copy(config->arena, &value_strings); + str8_list_concat_in_place(&config->alt_pch_dirs, &dirs); + } break; + case LNK_CmdSwitch_Rad_BuildInfo: { lnk_print_build_info(); os_abort(0); @@ -1708,10 +1799,14 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Rad_Map: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->rad_chunk_map_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->rad_chunk_map_name); config->rad_chunk_map = LNK_SwitchState_Yes; } break; + case LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols: { + lnk_cmd_switch_parse_flag(obj, cmd_switch, value_strings, &config->map_lines_for_unresolved_symbols); + } break; + case LNK_CmdSwitch_Rad_MemoryMapFiles: { lnk_cmd_switch_set_flag_32(obj, cmd_switch, value_strings, &config->io_flags, LNK_IO_Flags_MemoryMapFiles); } break; @@ -1722,11 +1817,11 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam case LNK_CmdSwitch_Rad_DebugName: { // :Rad_DebugAltPath - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->rad_debug_name); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->rad_debug_name); } break; case LNK_CmdSwitch_Rad_DebugAltPath: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->rad_debug_alt_path); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->rad_debug_alt_path); } break; case LNK_CmdSwitch_Rad_DelayBind: { @@ -1824,7 +1919,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Rad_MtPath: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->mt_path); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->mt_path); } break; case LNK_CmdSwitch_Rad_OsVer: { @@ -1865,7 +1960,7 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } break; case LNK_CmdSwitch_Rad_PdbHashTypeNameMap: { - lnk_cmd_switch_parse_string_copy(arena, obj, cmd_switch, value_strings, &config->pdb_hash_type_name_map); + lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value_strings, &config->pdb_hash_type_name_map); } break; case LNK_CmdSwitch_Rad_PdbHashTypeNameLength: { @@ -1875,8 +1970,8 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam case LNK_CmdSwitch_Rad_RemoveSection: { String8 sect_name = {0}; if (lnk_cmd_switch_parse_string(obj, cmd_switch, value_strings, §_name)) { - sect_name = push_str8_copy(arena, sect_name); - str8_list_push(arena, &config->remove_sections, sect_name); + sect_name = push_str8_copy(config->arena, sect_name); + str8_list_push(config->arena, &config->remove_sections, sect_name); } } break; @@ -1919,19 +2014,6 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } } break; - case LNK_CmdSwitch_Rad_SymbolTableCapDefined: { - lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->symbol_table_cap_defined, 0); - } break; - case LNK_CmdSwitch_Rad_SymbolTableCapInternal: { - lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->symbol_table_cap_internal, 0); - } break; - case LNK_CmdSwitch_Rad_SymbolTableCapWeak: { - lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->symbol_table_cap_weak, 0); - } break; - case LNK_CmdSwitch_Rad_SymbolTableCapLib: { - lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->symbol_table_cap_lib, 0); - } break; - case LNK_CmdSwitch_Rad_TargetOs: { if (value_strings.node_count == 1) { String8 os_string = str8_list_first(&value_strings); @@ -1954,6 +2036,14 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam lnk_cmd_switch_parse_u32(obj, cmd_switch, value_strings, &config->time_stamp, 0); } break; + case LNK_CmdSwitch_Rad_UnresolvedSymbolLimit: { + lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->unresolved_symbol_limit, 0); + } break; + + case LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit: { + lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->unresolved_symbol_ref_limit, 0); + } break; + case LNK_CmdSwitch_Rad_Version: { fprintf(stdout, "%s\n", BUILD_TITLE_STRING_LITERAL); os_abort(0); @@ -1976,12 +2066,14 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam } internal LNK_Config * -lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd_line) +lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line) { ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); + Temp scratch = scratch_begin(0, 0); - LNK_Config *config = push_array(arena, LNK_Config, 1); + Arena *arena = arena_alloc(); + LNK_Config *config = push_array(arena, LNK_Config, 1); + config->arena = arena; config->raw_cmd_line = str8_list_copy(arena, &raw_cmd_line); config->work_dir = os_get_current_path(arena); config->build_imp_lib = 1; @@ -1996,10 +2088,12 @@ lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd config->export_ht = hash_table_init(arena, max_U16/2); config->alt_name_ht = hash_table_init(arena, 0x100); config->include_symbol_ht = hash_table_init(arena, 0x100); + config->delay_load_ht = hash_table_init(arena, 0x100); + config->disallow_lib_ht = hash_table_init(arena, 0x100); // process command line switches for (LNK_CmdOption *cmd = cmd_line.first_option; cmd != 0; cmd = cmd->next) { - lnk_apply_cmd_option_to_config(arena, config, cmd->string, cmd->value_strings, 0); + lnk_apply_cmd_option_to_config(config, cmd->string, cmd->value_strings, 0); } // :manifest_input diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index aa6cdba1..3638ea29 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -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 @@ -37,6 +37,7 @@ typedef enum LNK_CmdSwitch_AlternateName, LNK_CmdSwitch_AppContainer, LNK_CmdSwitch_Base, + LNK_CmdSwitch_Brepro, LNK_CmdSwitch_Debug, LNK_CmdSwitch_DefaultLib, LNK_CmdSwitch_Delay, @@ -44,7 +45,9 @@ typedef enum LNK_CmdSwitch_Dll, LNK_CmdSwitch_DynamicBase, LNK_CmdSwitch_Dump, + LNK_CmdSwitch_D2, LNK_CmdSwitch_Entry, + LNK_CmdSwitch_ErrorReport, LNK_CmdSwitch_Export, LNK_CmdSwitch_FastFail, LNK_CmdSwitch_FileAlign, @@ -101,7 +104,6 @@ typedef enum LNK_CmdSwitch_DisallowLib, LNK_CmdSwitch_EditAndContinue, LNK_CmdSwitch_EmitVolatileMetadata, - LNK_CmdSwitch_ErrorReport, LNK_CmdSwitch_ExportAdmin, LNK_CmdSwitch_FastGenProfile, LNK_CmdSwitch_FailIfMismatch, @@ -149,6 +151,7 @@ typedef enum LNK_CmdSwitch_Wx, LNK_CmdSwitch_Rad_Age, + LNK_CmdSwitch_Rad_AltPchDir, LNK_CmdSwitch_Rad_BuildInfo, LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, LNK_CmdSwitch_Rad_Debug, @@ -164,6 +167,7 @@ typedef enum LNK_CmdSwitch_Rad_Log, LNK_CmdSwitch_Rad_Logo, LNK_CmdSwitch_Rad_Map, + LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, LNK_CmdSwitch_Rad_MemoryMapFiles, LNK_CmdSwitch_Rad_MtPath, LNK_CmdSwitch_Rad_OsVer, @@ -176,12 +180,10 @@ typedef enum LNK_CmdSwitch_Rad_SharedThreadPool, LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, LNK_CmdSwitch_Rad_SuppressError, - LNK_CmdSwitch_Rad_SymbolTableCapDefined, - LNK_CmdSwitch_Rad_SymbolTableCapInternal, - LNK_CmdSwitch_Rad_SymbolTableCapLib, - LNK_CmdSwitch_Rad_SymbolTableCapWeak, LNK_CmdSwitch_Rad_TargetOs, LNK_CmdSwitch_Rad_TimeStamp, + LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, + LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, LNK_CmdSwitch_Rad_Version, LNK_CmdSwitch_Rad_Workers, LNK_CmdSwitch_Rad_WriteTempFiles, @@ -249,6 +251,25 @@ typedef enum LNK_ManifestOpt_No, } LNK_ManifestOpt; +typedef struct LNK_IncludeSymbol +{ + String8 name; + struct LNK_Obj *obj; +} LNK_IncludeSymbol; + +typedef struct LNK_IncludeSymbolNode +{ + struct LNK_IncludeSymbolNode *next; + LNK_IncludeSymbol v; +} LNK_IncludeSymbolNode; + +typedef struct LNK_IncludeSymbolList +{ + U64 count; + LNK_IncludeSymbolNode *first; + LNK_IncludeSymbolNode *last; +} LNK_IncludeSymbolList; + typedef struct LNK_AltName { String8 from; @@ -259,7 +280,7 @@ typedef struct LNK_AltName typedef struct LNK_AltNameNode { struct LNK_AltNameNode *next; - LNK_AltName data; + LNK_AltName v; } LNK_AltNameNode; typedef struct LNK_AltNameList @@ -278,7 +299,7 @@ typedef struct LNK_MergeDirective typedef struct LNK_MergeDirectiveNode { struct LNK_MergeDirectiveNode *next; - LNK_MergeDirective data; + LNK_MergeDirective v; } LNK_MergeDirectiveNode; typedef struct LNK_MergeDirectiveList @@ -304,6 +325,7 @@ typedef enum typedef struct LNK_Config { + Arena *arena; LNK_ConfigFlags flags; LNK_DebugMode debug_mode; LNK_SwitchState opt_ref; @@ -362,7 +384,6 @@ typedef struct LNK_Config String8List input_list[LNK_Input_Count]; String8List input_obj_lib_list; String8List input_default_lib_list; - String8List disallow_lib_list; String8List delay_load_dll_list; String8List natvis_list; String8 manifest_name; @@ -375,13 +396,9 @@ typedef struct LNK_Config String8 rad_chunk_map_name; String8 rad_debug_name; String8 rad_debug_alt_path; - String8List include_symbol_list; + LNK_IncludeSymbolList include_symbol_list; LNK_AltNameList alt_name_list; LNK_MergeDirectiveList merge_list; - U64 symbol_table_cap_defined; - U64 symbol_table_cap_internal; - U64 symbol_table_cap_weak; - U64 symbol_table_cap_lib; U64 data_dir_count; B32 build_imp_lib; B32 build_exp; @@ -390,11 +407,18 @@ typedef struct LNK_Config String8 temp_pdb_name; String8 temp_rad_debug_name; String8 temp_rad_chunk_map_name; + String8 delay_load_helper_name; String8List remove_sections; LNK_IO_Flags io_flags; HashTable *export_ht; HashTable *alt_name_ht; HashTable *include_symbol_ht; + HashTable *delay_load_ht; + HashTable *disallow_lib_ht; + U64 unresolved_symbol_limit; + U64 unresolved_symbol_ref_limit; + LNK_SwitchState map_lines_for_unresolved_symbols; + String8List alt_pch_dirs; } LNK_Config; // --- MSVC Error Codes -------------------------------------------------------- @@ -566,25 +590,31 @@ internal B32 lnk_parse_merge_directive (String8 string, struct LNK_Obj *obj, internal B32 lnk_parse_export_directive (Arena *arena, String8 directive, struct LNK_Obj *obj, PE_ExportParse *export_out); internal B32 lnk_parse_export_directive_ex(Arena *arena, String8List directive, struct LNK_Obj *obj, PE_ExportParse *export_out); -internal LNK_AltNameNode * lnk_alt_name_list_push(Arena *arena, LNK_AltNameList *list, LNK_AltName data); -internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data); +internal LNK_AltNameNode * lnk_alt_name_list_push(Arena *arena, LNK_AltNameList *list, LNK_AltName v); +internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective v); // --- Getters ----------------------------------------------------------------- -internal String8 lnk_get_image_name(LNK_Config *config); -internal U64 lnk_get_default_function_pad_min(COFF_MachineType machine); -internal U64 lnk_get_base_addr(LNK_Config *config); +internal String8 lnk_get_image_name (LNK_Config *config); +internal U64 lnk_get_default_function_pad_min (COFF_MachineType machine); +internal U64 lnk_get_base_addr (LNK_Config *config); internal Version lnk_get_default_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine); -internal Version lnk_get_min_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine); +internal Version lnk_get_min_subsystem_version (PE_WindowsSubsystem subsystem, COFF_MachineType machine); internal B32 lnk_do_debug_info (LNK_Config *config); internal B32 lnk_is_thread_pool_shared(LNK_Config *config); +internal B32 lnk_is_section_removed (LNK_Config *config, String8 section_name); +internal B32 lnk_is_dll_delay_load (LNK_Config *config, String8 dll_name); -internal B32 lnk_is_section_removed(LNK_Config *config, String8 section_name); +internal String8 lnk_get_lib_name (String8 path); +internal void lnk_push_disallow_lib(LNK_Config *config, String8 path); +internal B32 lnk_is_lib_disallowed(LNK_Config *config, String8 path); + +internal void lnk_include_symbol(LNK_Config *config, String8 name, struct LNK_Obj *obj); // --- Config ------------------------------------------------------------------ -internal void lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 name, String8List value_list, struct LNK_Obj *obj); +internal void lnk_apply_cmd_option_to_config(LNK_Config *config, String8 name, String8List value_list, struct LNK_Obj *obj); -internal LNK_Config * lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd_line); +internal LNK_Config * lnk_config_from_cmd_line(String8List raw_cmd_line, LNK_CmdLine cmd_line); diff --git a/src/linker/lnk_debug_helper.c b/src/linker/lnk_debug_helper.c index 9602f296..7d1b7017 100644 --- a/src/linker/lnk_debug_helper.c +++ b/src/linker/lnk_debug_helper.c @@ -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 String8 @@ -72,4 +72,3 @@ lnk_make_dll_import_debug_symbols(Arena *arena, COFF_MachineType machine, String return debug_symbols; } - diff --git a/src/linker/lnk_debug_helper.h b/src/linker/lnk_debug_helper.h index a0627d17..dd1df4b3 100644 --- a/src/linker/lnk_debug_helper.h +++ b/src/linker/lnk_debug_helper.h @@ -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 diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index ff4d2d72..d277bee8 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -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 @@ -140,7 +140,7 @@ THREAD_POOL_TASK_FUNC(lnk_parse_cv_symbols_task) } internal LNK_PchInfo * -lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj *obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols) +lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj **obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols, String8List alt_pch_dirs) { Temp scratch = scratch_begin(&arena, 1); @@ -155,16 +155,16 @@ lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj *obj_arr, CV_DebugT *debug_t_ if (debug_t->count && debug_p->count) { lnk_error_obj(LNK_Warning_MultipleDebugTAndDebugP, - &obj_arr[obj_idx], + obj_arr[obj_idx], "multiple sections with debug types detected, obj must have either .debug$T or .debug$P (using .debug$T for type server)"); continue; } if (debug_p->count) { - String8 obj_path = obj_arr[obj_idx].path; + String8 obj_path = obj_arr[obj_idx]->path; obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, obj_path, work_dir); if (hash_table_search_path(debug_p_ht, obj_path)) { - lnk_error_obj(LNK_Warning_DuplicateObjPath, &obj_arr[obj_idx], "duplicate obj path %S", obj_path); + lnk_error_obj(LNK_Warning_DuplicateObjPath, obj_arr[obj_idx], "duplicate obj path %S", obj_path); } else { hash_table_push_path_u64(scratch.arena, debug_p_ht, obj_path, obj_idx); } @@ -179,11 +179,21 @@ lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj *obj_arr, CV_DebugT *debug_t_ CV_PrecompInfo precomp = cv_precomp_info_from_leaf(precomp_leaf); String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, precomp.obj_name, work_dir); - + // map obj name in LF_PRECOMP to obj index - U64 debug_p_obj_idx; + U64 debug_p_obj_idx = max_U64; if (!hash_table_search_path_u64(debug_p_ht, obj_path, &debug_p_obj_idx)) { - lnk_error_obj(LNK_Error_PrecompObjNotFound, &obj_arr[obj_idx], "LF_PRECOMP references non-existent obj %S", obj_path); + String8 obj_name = str8_skip_last_slash(obj_path); + for EachNode(alt_dir_n, String8Node, alt_pch_dirs.first) { + String8 alt_obj_path = str8f(scratch.arena, "%S/%S", alt_dir_n->string, obj_name); + if (hash_table_search_path_u64(debug_p_ht, alt_obj_path, &debug_p_obj_idx)) { + break; + } + } + } + + if (debug_p_obj_idx == max_U64) { + lnk_error_obj(LNK_Error_PrecompObjNotFound, obj_arr[obj_idx], "LF_PRECOMP references non-existent obj %S", obj_path); lnk_exit(LNK_Error_PrecompObjNotFound); } @@ -194,34 +204,34 @@ lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj *obj_arr, CV_DebugT *debug_t_ // error check LF_PRECOMP if (precomp.start_index > CV_MinComplexTypeIndex) { - lnk_error_obj(LNK_Warning_AtypicalStartIndex, &obj_arr[obj_idx], "atypical start index 0x%X in LF_PRECOMP", precomp.start_index); + lnk_error_obj(LNK_Warning_AtypicalStartIndex, obj_arr[obj_idx], "atypical start index 0x%X in LF_PRECOMP", precomp.start_index); } if (precomp.start_index < CV_MinComplexTypeIndex) { - lnk_error_obj(LNK_Error_InvalidStartIndex, &obj_arr[obj_idx], "invalid start index 0x%X in LF_PRECOMP; must be >= 0x%X", precomp.start_index, CV_MinComplexTypeIndex); + lnk_error_obj(LNK_Error_InvalidStartIndex, obj_arr[obj_idx], "invalid start index 0x%X in LF_PRECOMP; must be >= 0x%X", precomp.start_index, CV_MinComplexTypeIndex); } if (precomp.leaf_count > debug_p.count) { - lnk_error_obj(LNK_Error_InvalidPrecompLeafCount, &obj_arr[obj_idx], "leaf count %u LF_PRECOMP exceeds leaf count %u in .debug$P in %S", precomp.leaf_count, debug_p.count, obj_arr[debug_p_obj_idx].path); + lnk_error_obj(LNK_Error_InvalidPrecompLeafCount, obj_arr[obj_idx], "leaf count %u LF_PRECOMP exceeds leaf count %u in .debug$P in %S", precomp.leaf_count, debug_p.count, obj_arr[debug_p_obj_idx]->path); } // error check LF_ENDPRECOMP if (endprecomp_leaf.kind != CV_LeafKind_ENDPRECOMP) { - lnk_error_obj(LNK_Error_EndprecompNotFound, &obj_arr[obj_idx], "unable to find LF_ENDPRECOMP @ 0x%X in %S", precomp.leaf_count, obj_arr[debug_p_obj_idx].path); + lnk_error_obj(LNK_Error_EndprecompNotFound, obj_arr[obj_idx], "unable to find LF_ENDPRECOMP @ 0x%X in %S", precomp.leaf_count, obj_arr[debug_p_obj_idx]->path); } if (endprecomp_leaf.data.size != sizeof(CV_LeafEndPreComp)) { - lnk_error_obj(LNK_Error_IllData, &obj_arr[obj_idx], "invalid size 0x%X for LF_ENDPRECOMP", endprecomp_leaf.data.size); + lnk_error_obj(LNK_Error_IllData, obj_arr[obj_idx], "invalid size 0x%X for LF_ENDPRECOMP", endprecomp_leaf.data.size); } if (endprecomp->sig != precomp.sig) { - lnk_error_obj(LNK_Error_PrecompSigMismatch, &obj_arr[obj_idx], "signature mismatch between LF_PRECOMP(0x%X) and LF_ENDPRECOMP(0x%X); precomp obj %S", precomp.sig, endprecomp->sig, obj_arr[debug_p_obj_idx].path); + lnk_error_obj(LNK_Error_PrecompSigMismatch, obj_arr[obj_idx], "signature mismatch between LF_PRECOMP(0x%X) and LF_ENDPRECOMP(0x%X); precomp obj %S", precomp.sig, endprecomp->sig, obj_arr[debug_p_obj_idx]->path); } { // check against S_OBJNAME sig in precompiled obj $$SYMBOLS CV_SymbolList symbol_list = parsed_symbols[debug_p_obj_idx].v[0]; if (symbol_list.count) { CV_ObjInfo obj_info = cv_obj_info_from_symbol(symbol_list.first->data); if (obj_info.sig != 0 && obj_info.sig != precomp.sig) { - lnk_error_obj(LNK_Error_PrecompSigMismatch, &obj_arr[obj_idx], "signature mismatch between LF_PRECOMP(0x%X) and S_OBJNAME(0x%X) in %S", precomp.sig, obj_info.sig, &obj_arr[debug_p_obj_idx].path); + lnk_error_obj(LNK_Error_PrecompSigMismatch, obj_arr[obj_idx], "signature mismatch between LF_PRECOMP(0x%X) and S_OBJNAME(0x%X) in %S", precomp.sig, obj_info.sig, obj_arr[debug_p_obj_idx]->path); } } else { - lnk_error_obj(LNK_Warning_PrecompObjSymbolsNotFound, &obj_arr[obj_idx], "symbols not found, unable to chceck LF_PRECOMP signature against S_OBJ"); + lnk_error_obj(LNK_Warning_PrecompObjSymbolsNotFound, obj_arr[obj_idx], "symbols not found, unable to chceck LF_PRECOMP signature against S_OBJ"); } } @@ -358,7 +368,7 @@ lnk_merge_debug_t_and_debug_p(Arena *arena, U64 obj_count, CV_DebugT *debug_t_ar } internal LNK_CodeViewInput -lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, U64 obj_count, LNK_Obj **obj_arr) +lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, String8List alt_pch_dirs, U64 obj_count, LNK_Obj **obj_arr) { ProfBegin("Extract CodeView"); Temp scratch = scratch_begin(0,0); @@ -406,7 +416,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla ProfBegin("Sort Type Servers"); U64 external_count = 0, internal_count = 0; - LNK_Obj *sorted_obj_arr = push_array_no_zero(tp_arena->v[0], LNK_Obj, obj_count); + LNK_Obj **sorted_obj_arr = push_array_no_zero(tp_arena->v[0], LNK_Obj *, obj_count); CV_DebugS *sorted_debug_s_arr = push_array_no_zero(tp_arena->v[0], CV_DebugS, obj_count); CV_DebugT *sorted_debug_t_arr = push_array_no_zero(tp_arena->v[0], CV_DebugT, obj_count); CV_DebugT *sorted_debug_p_arr = push_array_no_zero(tp_arena->v[0], CV_DebugT, obj_count); @@ -420,7 +430,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla // TODO: report error: somehow obj was compiled with /Zi and /Yc Assert(debug_p_arr[obj_idx].count == 0); - sorted_obj_arr[slot_idx] = *obj_arr[obj_idx]; + sorted_obj_arr[slot_idx] = obj_arr[obj_idx]; sorted_debug_s_arr[slot_idx] = debug_s_arr[obj_idx]; sorted_debug_t_arr[slot_idx] = debug_t_arr[obj_idx]; MemoryZeroStruct(&sorted_debug_p_arr[slot_idx]); @@ -429,7 +439,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla U64 slot_idx = internal_count; ++internal_count; - sorted_obj_arr[slot_idx] = *obj_arr[obj_idx]; + sorted_obj_arr[slot_idx] = obj_arr[obj_idx]; sorted_debug_s_arr[slot_idx] = debug_s_arr[obj_idx]; sorted_debug_t_arr[slot_idx] = debug_t_arr[obj_idx]; sorted_debug_p_arr[slot_idx] = debug_p_arr[obj_idx]; @@ -439,8 +449,8 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla ProfEnd(); // setup pointers to arrays - LNK_Obj *internal_obj_arr = sorted_obj_arr; - LNK_Obj *external_obj_arr = sorted_obj_arr + internal_count; + LNK_Obj **internal_obj_arr = sorted_obj_arr; + LNK_Obj **external_obj_arr = sorted_obj_arr + internal_count; CV_DebugS *internal_debug_s_arr = sorted_debug_s_arr; CV_DebugS *external_debug_s_arr = sorted_debug_s_arr + internal_count; CV_DebugT *internal_debug_t_arr = sorted_debug_t_arr; @@ -522,7 +532,8 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla internal_obj_arr, internal_debug_t_arr, internal_debug_p_arr, - internal_parsed_symbols); + internal_parsed_symbols, + alt_pch_dirs); CV_DebugT *merged_debug_t_p_arr = lnk_merge_debug_t_and_debug_p(tp_arena->v[0], internal_count, internal_debug_t_arr, internal_debug_p_arr); @@ -530,8 +541,8 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla String8Array ts_path_arr; Rng1U64 **external_ti_ranges; CV_DebugT **external_leaves; - U64 *obj_to_ts_idx_arr = push_array_no_zero(tp_arena->v[0], U64, external_count); - U64List *ts_to_obj_arr = push_array(tp_arena->v[0], U64List, external_count); + U64 *obj_to_ts_idx_arr = push_array_no_zero(tp_arena->v[0], U64, external_count + 1); + U64List *ts_to_obj_arr = push_array(tp_arena->v[0], U64List, external_count + 1); { HashTable *type_server_path_ht = hash_table_init(scratch.arena, 256); HashTable *ignored_path_ht = hash_table_init(scratch.arena, 256); @@ -539,6 +550,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla // push null CV_TypeServerInfoNode *null_ts_info = push_array(scratch.arena, CV_TypeServerInfoNode, 1); + null_ts_info->data.name = str8_lit("\0"); SLLQueuePush(ts_info_list.first, ts_info_list.last, null_ts_info); ++ts_info_list.count; @@ -549,29 +561,11 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla CV_TypeServerInfo ts = cv_type_server_info_from_leaf(leaf); // search disk for type server - String8List match_list = lnk_file_search(scratch.arena, lib_dir_list, ts.name); - - // chop file name from path and search on it - // - // TODO: check if ts.name is a path and in that case do file search - if (match_list.node_count == 0) { - String8 file_name = str8_skip_last_slash(ts.name); - match_list = lnk_file_search(scratch.arena, lib_dir_list, file_name); - } + String8 type_server_path = lnk_find_first_file(scratch.arena, lib_dir_list, ts.name); + // report no match B32 do_debug_info_discard = 0; - - // too many matches? - if (match_list.node_count > 1) { - if (!hash_table_search_path(ignored_path_ht, ts.name)) { - hash_table_push_path_u64(scratch.arena, ignored_path_ht, ts.name, 0); - lnk_error_obj(LNK_Warning_MultipleExternalTypeServers, obj_arr[obj_idx], "located multiple external type servers:"); - lnk_supplement_error_list(match_list); - } - do_debug_info_discard = 1; - } - // no match? - else if (match_list.node_count == 0) { + if (type_server_path.size == 0) { if (!hash_table_search_path(ignored_path_ht, ts.name)) { hash_table_push_string_u64(scratch.arena, ignored_path_ht, ts.name, 0); lnk_error_obj(LNK_Warning_MissingExternalTypeServer, obj_arr[obj_idx], "unable to open external type server %S", ts.name); @@ -585,7 +579,6 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla continue; } - String8 path = match_list.first->string; { struct HT_Value { CV_TypeServerInfo ts; @@ -594,7 +587,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla }; // was this type server queued? - KeyValuePair *is_path_queued = hash_table_search_path(type_server_path_ht, path); + KeyValuePair *is_path_queued = hash_table_search_path(type_server_path_ht, type_server_path); if (is_path_queued) { struct HT_Value *present = is_path_queued->value_raw; @@ -616,12 +609,12 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla // when we search matches on disk we store path on scratch, // make path copy in case we need it for error reporting - path = push_str8_copy(tp_arena->v[0], path); + type_server_path = push_str8_copy(tp_arena->v[0], type_server_path); // fill out type server info we read from obj CV_TypeServerInfoNode *ts_info_node = push_array(scratch.arena, CV_TypeServerInfoNode, 1); ts_info_node->data = ts; - ts_info_node->data.name = path; + ts_info_node->data.name = type_server_path; // push to type server info list SLLQueuePush(ts_info_list.first, ts_info_list.last, ts_info_node); @@ -640,7 +633,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla value->ts_idx = ts_idx; // update hash table - hash_table_push_path_raw(scratch.arena, type_server_path_ht, path, value); + hash_table_push_path_raw(scratch.arena, type_server_path_ht, type_server_path, value); } } } @@ -726,7 +719,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_fla str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t%S\n", ts_path_arr.v[ts_idx]); str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t\tDependent obj(s):\n"); for (U64Node *obj_idx_node = obj_idx_list.first; obj_idx_node != 0; obj_idx_node = obj_idx_node->next) { - String8 obj_path = external_obj_arr[obj_idx_node->data].path; + String8 obj_path = external_obj_arr[obj_idx_node->data]->path; str8_list_pushf(scratch.arena, &unopen_type_server_list, "\t\t\t%S\n", obj_path); } } @@ -1209,7 +1202,7 @@ lnk_hash_cv_leaf(Arena *arena, String8 leaf_kind_str = cv_string_from_leaf_kind(leaf.kind); String8 leaf_info = push_str8f(scratch.arena, "LF_%S(type_index: 0x%x) forward refs member type index 0x%x (leaf struct offset: 0x%llx)", leaf_kind_str, curr_ti, sub_ti, ti_n->offset); if (loc_type == LNK_LeafLocType_Internal) { - lnk_error_obj(LNK_Error_InvalidTypeIndex, input->internal_obj_arr+loc_idx, "%S", leaf_info); + lnk_error_obj(LNK_Error_InvalidTypeIndex, input->internal_obj_arr[loc_idx], "%S", leaf_info); } else if (loc_type == LNK_LeafLocType_External) { lnk_error(LNK_Error_InvalidTypeIndex, "%S: %S", input->type_server_path_arr[loc_idx], leaf_info); } else { @@ -1988,39 +1981,43 @@ lnk_assign_type_indices(TP_Context *tp, LNK_LeafBucketArray bucket_arr, CV_TypeI internal THREAD_POOL_TASK_FUNC(lnk_patch_symbols_task) { + ProfBeginFunction(); LNK_PatchSymbolTypesTask *task = raw_task; - Arena *fixed_arena = task->arena_arr[worker_id]; - LNK_CodeViewSymbolsInput symbol_input = task->input->symbol_inputs[task_id]; + Arena *fixed_arena = task->arena_arr[task_id]; - LNK_LeafLocType loc_type = lnk_loc_type_from_obj_idx(task->input, symbol_input.obj_idx); - U64 loc_idx = lnk_loc_idx_from_obj_idx(task->input, symbol_input.obj_idx); + for EachInRange(symbol_input_idx, task->ranges[task_id]) { + LNK_CodeViewSymbolsInput symbol_input = task->input->symbol_inputs[symbol_input_idx]; + LNK_LeafLocType loc_type = lnk_loc_type_from_obj_idx(task->input, symbol_input.obj_idx); + U64 loc_idx = lnk_loc_idx_from_obj_idx(task->input, symbol_input.obj_idx); - CV_TypeIndex ti_lo_arr[CV_TypeIndexSource_COUNT]; - ti_lo_arr[CV_TypeIndexSource_NULL] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_NULL); - ti_lo_arr[CV_TypeIndexSource_TPI ] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_TPI); - ti_lo_arr[CV_TypeIndexSource_IPI ] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_IPI); + CV_TypeIndex ti_lo_arr[CV_TypeIndexSource_COUNT]; + ti_lo_arr[CV_TypeIndexSource_NULL] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_NULL); + ti_lo_arr[CV_TypeIndexSource_TPI ] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_TPI); + ti_lo_arr[CV_TypeIndexSource_IPI ] = lnk_ti_lo_from_loc(task->input, loc_type, loc_idx, CV_TypeIndexSource_IPI); - for (CV_SymbolNode *symnode = symbol_input.symbol_list->first; symnode != 0; symnode = symnode->next) { - Temp temp = temp_begin(fixed_arena); + for (CV_SymbolNode *symnode = symbol_input.symbol_list->first; symnode != 0; symnode = symnode->next) { + Temp temp = temp_begin(fixed_arena); - // find type index offsets in symbol - CV_TypeIndexInfoList ti_list = cv_get_symbol_type_index_offsets(temp.arena, symnode->data.kind, symnode->data.data); - - // overwrite type indices in symbol - for (CV_TypeIndexInfo *ti_info = ti_list.first; ti_info != 0; ti_info = ti_info->next) { - CV_TypeIndex *ti_ptr = (CV_TypeIndex *) (symnode->data.data.str + ti_info->offset); - if (*ti_ptr >= ti_lo_arr[ti_info->source]) { - LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[ti_info->source]; - LNK_LeafRef leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(task->input, loc_type, ti_info->source, loc_idx, *ti_ptr); - LNK_LeafBucket *leaf_bucket = lnk_leaf_hash_table_search(leaf_ht, task->input, task->hashes, leaf_ref); + // find type index offsets in symbol + CV_TypeIndexInfoList ti_list = cv_get_symbol_type_index_offsets(temp.arena, symnode->data.kind, symnode->data.data); - // we overwrite section memory directly - *ti_ptr = leaf_bucket->type_index; + // overwrite type indices in symbol + for (CV_TypeIndexInfo *ti_info = ti_list.first; ti_info != 0; ti_info = ti_info->next) { + CV_TypeIndex *ti_ptr = (CV_TypeIndex *) (symnode->data.data.str + ti_info->offset); + if (*ti_ptr >= ti_lo_arr[ti_info->source]) { + LNK_LeafHashTable *leaf_ht = &task->leaf_ht_arr[ti_info->source]; + LNK_LeafRef leaf_ref = lnk_leaf_ref_from_loc_idx_and_ti(task->input, loc_type, ti_info->source, loc_idx, *ti_ptr); + LNK_LeafBucket *leaf_bucket = lnk_leaf_hash_table_search(leaf_ht, task->input, task->hashes, leaf_ref); + + // we overwrite section memory directly + *ti_ptr = leaf_bucket->type_index; + } } + + temp_end(temp); } - - temp_end(temp); } + ProfEnd(); } internal void @@ -2035,11 +2032,12 @@ lnk_patch_symbols(TP_Context *tp, U64 max_ti_list_size = sizeof(CV_TypeIndexInfo) * (max_U16 / sizeof(CV_TypeIndex)); LNK_PatchSymbolTypesTask task = {0}; + task.ranges = tp_divide_work(scratch.arena, input->total_symbol_input_count, tp->worker_count); task.input = input; task.hashes = hashes; task.leaf_ht_arr = leaf_ht_arr; task.arena_arr = alloc_fixed_size_arena_array(scratch.arena, tp->worker_count, max_ti_list_size, max_ti_list_size); - tp_for_parallel(tp, 0, input->total_symbol_input_count, lnk_patch_symbols_task, &task); + tp_for_parallel(tp, 0, tp->worker_count, lnk_patch_symbols_task, &task); scratch_end(scratch); ProfEnd(); @@ -2944,7 +2942,7 @@ THREAD_POOL_TASK_FUNC(lnk_push_dbi_sec_contrib_task) U64 obj_idx = task_id; LNK_PushDbiSecContribTaskData *task = raw_task; PDB_DbiModule *mod = task->mod_arr[obj_idx]; - LNK_Obj *obj = &task->obj_arr[obj_idx]; + LNK_Obj *obj = task->obj_arr[obj_idx]; COFF_SectionHeader *obj_section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; PDB_DbiSectionContribNode *sc_arr = push_array_no_zero(arena, PDB_DbiSectionContribNode, obj->header.section_count_no_null); @@ -2953,12 +2951,9 @@ THREAD_POOL_TASK_FUNC(lnk_push_dbi_sec_contrib_task) for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { COFF_SectionHeader *obj_sect_header = &obj_section_table[sect_idx]; - if (obj_sect_header->flags & COFF_SectionFlag_LnkRemove) { - continue; - } - if (lnk_is_coff_section_debug(obj, sect_idx)) { - continue; - } + if (obj_sect_header->flags & COFF_SectionFlag_LnkInfo) { continue; } + if (obj_sect_header->flags & COFF_SectionFlag_LnkRemove) { continue; } + if (obj_sect_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } U64 sect_number; String8 sect_data; @@ -3021,9 +3016,10 @@ THREAD_POOL_TASK_FUNC(lnk_build_pdb_public_symbols_defined_task) U64 node_idx = 0; for EachIndex(i, chunk->count) { LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); + LNK_ObjSymbolRef symbol_ref = lnk_ref_from_symbol(symbol); + COFF_ParsedSymbol symbol_parsed = lnk_parsed_from_symbol(symbol); - if (symbol_parsed.section_number == lnk_obj_get_removed_section_number(symbol->u.defined.obj)) { continue; } + if (symbol_parsed.section_number == lnk_obj_get_removed_section_number(symbol_ref.obj)) { continue; } COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); if (symbol_interp != COFF_SymbolValueInterp_Regular) { continue; } @@ -3072,8 +3068,8 @@ lnk_build_pdb_public_symbols(TP_Context *tp, ProfBegin("Defined"); LNK_BuildPublicSymbolsTask task = {0}; - task.pub_list_arr = push_array(scratch.arena, CV_SymbolList, tp->worker_count); - task.chunk_lists = symtab->chunk_lists[LNK_SymbolScope_Defined]; + task.pub_list_arr = push_array(scratch.arena, CV_SymbolList, tp->worker_count); + task.chunk_lists = symtab->chunks; tp_for_parallel(tp, arena, tp->worker_count, lnk_build_pdb_public_symbols_defined_task, &task); ProfEnd(); @@ -3094,7 +3090,7 @@ lnk_build_pdb(TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 obj_count, - LNK_Obj *obj_arr, + LNK_Obj **obj_arr, CV_DebugS *debug_s_arr, U64 total_symbol_input_count, LNK_CodeViewSymbolsInput *symbol_inputs, @@ -3132,7 +3128,7 @@ lnk_build_pdb(TP_Context *tp, ProfBegin("Reserve DBI Modules"); PDB_DbiModule **mod_arr = push_array(tp_arena->v[0], PDB_DbiModule *, obj_count); for (U64 obj_idx = 0; obj_idx < obj_count; ++obj_idx) { - LNK_Obj *obj = obj_arr + obj_idx; + LNK_Obj *obj = obj_arr[obj_idx]; mod_arr[obj_idx] = dbi_push_module(pdb->dbi, obj->path, lnk_obj_get_lib_path(obj)); // we don't support symbol append @@ -4201,7 +4197,7 @@ lnk_src_file_hash_table_lookup_slot(LNK_SourceFileBucket **buckets, if (buckets[bucket_idx] == 0) { break; } - if (rdib_source_file_match(buckets[bucket_idx]->src_file, &temp, operating_system_from_context())) { + if (rdib_source_file_match(buckets[bucket_idx]->src_file, &temp, OperatingSystem_CURRENT)) { return buckets[bucket_idx]; } bucket_idx = (bucket_idx + 1) % cap; @@ -4233,7 +4229,7 @@ lnk_src_file_insert_or_update(LNK_SourceFileBucket **buckets, U64 cap, U64 hash, // another thread took the bucket... goto retry; - } else if (rdib_source_file_match(curr_bucket->src_file, new_bucket->src_file, operating_system_from_context())) { + } else if (rdib_source_file_match(curr_bucket->src_file, new_bucket->src_file, OperatingSystem_CURRENT)) { // do we need to update value in the bucket? int cmp = u64_compar(&curr_bucket->obj_idx, &new_bucket->obj_idx); if (cmp <= 0) { @@ -4306,10 +4302,10 @@ THREAD_POOL_TASK_FUNC(lnk_insert_src_files_task) String8List raw_strtab_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_StringTable); if (raw_strtab_list.node_count > 1) { - lnk_error_obj(LNK_Warning_IllData, &task->obj_arr[obj_idx], "Multiple string table sub-sections, picking first one."); + lnk_error_obj(LNK_Warning_IllData, task->obj_arr[obj_idx], "Multiple string table sub-sections, picking first one."); } if (raw_chksms_list.node_count > 1) { - lnk_error_obj(LNK_Warning_IllData, &task->obj_arr[obj_idx], "Multiple file checksum sub-sections, picking first one."); + lnk_error_obj(LNK_Warning_IllData, task->obj_arr[obj_idx], "Multiple file checksum sub-sections, picking first one."); } String8 string_table = cv_string_table_from_debug_s(debug_s); @@ -4490,7 +4486,7 @@ THREAD_POOL_TASK_FUNC(lnk_find_obj_compiler_info_task) } exit:; - LNK_Obj *obj = &task->obj_arr[task_id]; + LNK_Obj *obj = task->obj_arr[task_id]; // fill out unit info U64 unit_chunk_idx = task_id / task->unit_chunk_cap; @@ -4517,7 +4513,7 @@ THREAD_POOL_TASK_FUNC(lnk_convert_line_tables_to_rdi_task) U64 unit_idx = task_id; LNK_ConvertUnitToRDITask *task = raw_task; - LNK_Obj *obj = &task->obj_arr[unit_idx]; + LNK_Obj *obj = task->obj_arr[unit_idx]; CV_DebugS debug_s = task->debug_s_arr[unit_idx]; U64 unit_chunk_idx = unit_idx / task->unit_chunk_cap; @@ -4614,7 +4610,7 @@ THREAD_POOL_TASK_FUNC(lnk_convert_symbols_to_rdi_task) LNK_ConvertUnitToRDITask *task = raw_task; LNK_CodeViewSymbolsInput symbols_input = task->symbol_inputs[task_id]; - LNK_Obj *obj = &task->obj_arr[symbols_input.obj_idx]; + LNK_Obj *obj = task->obj_arr[symbols_input.obj_idx]; LNK_CodeViewCompilerInfo comp_info = task->comp_info_arr[symbols_input.obj_idx]; CV_InlineeLinesAccel *inlinee_lines_accel = task->inlinee_lines_accel_arr[symbols_input.obj_idx]; @@ -5243,8 +5239,7 @@ THREAD_POOL_TASK_FUNC(lnk_convert_inline_site_line_tables_task) // prase checksum header CV_C13Checksum *checksum_header = (CV_C13Checksum *) (raw_file_chksms.str + lines.file_off); if (lines.file_off + sizeof(CV_C13Checksum) + checksum_header->len > raw_file_chksms.size) { - LNK_Obj *obj = task->obj_arr + obj_idx; - lnk_error_obj(LNK_Warning_IllData, obj, "Not enough bytes to read file checksum @ 0x%llx.", lines.file_off); + lnk_error_obj(LNK_Warning_IllData, task->obj_arr[obj_idx], "Not enough bytes to read file checksum @ 0x%llx.", lines.file_off); continue; } String8 file_path = str8_cstring_capped(raw_string_table.str + checksum_header->name_off, raw_string_table.str + raw_string_table.size); @@ -5255,8 +5250,7 @@ THREAD_POOL_TASK_FUNC(lnk_convert_inline_site_line_tables_task) U64 src_file_hash = lnk_src_file_hash_cv(normal_path, checksum_header->kind, checksum_bytes); LNK_SourceFileBucket *src_file_bucket = lnk_src_file_hash_table_lookup_slot(task->src_file_buckets, task->src_file_buckets_cap, src_file_hash, normal_path, checksum_header->kind, checksum_bytes); if (src_file_bucket == 0) { - LNK_Obj *obj = task->obj_arr + obj_idx; - lnk_error_obj(LNK_Error_UnexpectedCodePath, obj, "Unable to find source file in the hash table: \"%S\".", file_path); + lnk_error_obj(LNK_Error_UnexpectedCodePath, task->obj_arr[obj_idx], "Unable to find source file in the hash table: \"%S\".", file_path); continue; } RDIB_SourceFile *src_file = src_file_bucket->src_file; @@ -5290,7 +5284,7 @@ THREAD_POOL_TASK_FUNC(lnk_collect_obj_virtual_ranges_task) LNK_ConvertUnitToRDITask *task = raw_task; U64 unit_idx = task_id; - LNK_Obj *obj = &task->obj_arr[unit_idx]; + LNK_Obj *obj = task->obj_arr[unit_idx]; U64 unit_chunk_idx = unit_idx / task->unit_chunk_cap; U64 local_unit_idx = unit_idx - unit_chunk_idx * task->unit_chunk_cap; @@ -5329,7 +5323,7 @@ lnk_build_rad_debug_info(TP_Context *tp, String8 image_name, String8 image_data, U64 obj_count, - LNK_Obj *obj_arr, + LNK_Obj **obj_arr, CV_DebugS *debug_s_arr, U64 total_symbol_input_count, LNK_CodeViewSymbolsInput *symbol_inputs, diff --git a/src/linker/lnk_debug_info.h b/src/linker/lnk_debug_info.h index 36ff5923..5d93dd36 100644 --- a/src/linker/lnk_debug_info.h +++ b/src/linker/lnk_debug_info.h @@ -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 @@ -28,7 +28,7 @@ typedef struct LNK_CodeViewInput String8 *type_server_path_arr; // [type_server_count] String8 *type_server_data_arr; // [type_server_count] U64List *ts_to_obj_arr; // [type_server_count] - LNK_Obj *obj_arr; // [count] + LNK_Obj **obj_arr; // [count] LNK_PchInfo *pch_arr; // [count] CV_DebugS *debug_s_arr; // [count] CV_DebugT *debug_p_arr; // [count] @@ -39,7 +39,7 @@ typedef struct LNK_CodeViewInput LNK_CodeViewSymbolsInput *symbol_inputs; // [total_symbol_input_count] CV_SymbolListArray *parsed_symbols; // [count] - LNK_Obj *internal_obj_arr; // [internal_count] + LNK_Obj **internal_obj_arr; // [internal_count] CV_DebugS *internal_debug_s_arr; // [internal_count] CV_DebugT *internal_debug_t_arr; // [internal_count] CV_DebugT *internal_debug_p_arr; // [internal_count] @@ -47,7 +47,7 @@ typedef struct LNK_CodeViewInput LNK_CodeViewSymbolsInput *internal_symbol_inputs; // [internal_total_symbol_input_count] CV_SymbolListArray *internal_parsed_symbols; // [internal_count] - LNK_Obj *external_obj_arr; // [external_count] + LNK_Obj **external_obj_arr; // [external_count] CV_DebugS *external_debug_s_arr; // [external_count] CV_DebugT *external_debug_t_arr; // [external_count] CV_DebugT *external_debug_p_arr; // [external_count] @@ -236,6 +236,7 @@ typedef struct typedef struct { + Rng1U64 *ranges; LNK_CodeViewInput *input; LNK_LeafHashes *hashes; LNK_LeafHashTable *leaf_ht_arr; @@ -312,7 +313,7 @@ typedef struct typedef struct { - LNK_Obj *obj_arr; + LNK_Obj **obj_arr; PDB_DbiModule **mod_arr; PDB_DbiSectionContribList *sc_list; String8 image_data; @@ -436,7 +437,7 @@ typedef struct typedef struct { - LNK_Obj *obj_arr; + LNK_Obj **obj_arr; CV_DebugS *debug_s_arr; U64 total_src_file_count; LNK_SourceFileBucket **src_file_buckets; @@ -453,7 +454,7 @@ typedef struct typedef struct { COFF_SectionHeaderArray image_sects; - LNK_Obj *obj_arr; + LNK_Obj **obj_arr; CV_DebugS *debug_s_arr; CV_DebugT ipi; LNK_CodeViewSymbolsInput *symbol_inputs; @@ -496,9 +497,9 @@ typedef struct internal CV_DebugS * lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *sect_list_arr); internal CV_DebugT * lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8List *debug_t_list_arr); internal CV_SymbolList * lnk_cv_symbol_list_arr_from_debug_s_arr(TP_Context *tp, TP_Arena *arena, U64 obj_count, CV_DebugS *debug_s_arr); -internal LNK_PchInfo * lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj *obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols); +internal LNK_PchInfo * lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj **obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols, String8List alt_pch_dirs); -internal LNK_CodeViewInput lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, U64 objs_count, LNK_Obj **objs); +internal LNK_CodeViewInput lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_IO_Flags io_flags, String8List lib_dir_list, String8List alt_pch_dirs, U64 objs_count, LNK_Obj **objs); internal LNK_LeafRef lnk_leaf_ref(U32 idx, U32 leaf_idx); internal LNK_LeafRef lnk_obj_leaf_ref(U32 obj_idx, U32 leaf_idx); @@ -553,7 +554,7 @@ internal String8List lnk_build_rad_debug_info(TP_Context *tp, String8 image_name, String8 image_data, U64 obj_count, - LNK_Obj *obj_arr, + LNK_Obj **obj_arr, CV_DebugS *debug_s_arr, U64 total_symbol_input_count, LNK_CodeViewSymbolsInput *symbol_inputs, @@ -575,7 +576,7 @@ internal String8List lnk_build_pdb(TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 obj_count, - LNK_Obj *obj_arr, + LNK_Obj **obj_arr, CV_DebugS *debug_s_arr, U64 total_symbol_input_count, LNK_CodeViewSymbolsInput *symbol_inputs, diff --git a/src/linker/lnk_error.c b/src/linker/lnk_error.c index a0235975..f06a3b7e 100644 --- a/src/linker/lnk_error.c +++ b/src/linker/lnk_error.c @@ -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/) static LNK_ErrorMode g_error_mode_arr[LNK_Error_Count]; diff --git a/src/linker/lnk_error.h b/src/linker/lnk_error.h index 40e52eeb..7a7e4a28 100644 --- a/src/linker/lnk_error.h +++ b/src/linker/lnk_error.h @@ -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 @@ -37,7 +37,6 @@ typedef enum LNK_Error_IllegalSectionMerge, LNK_Error_IllegalRelocation, LNK_Error_CircularMerge, - LNK_Error_UnresolvedSymbol, LNK_Error_AssociativeLoop, LNK_Error_AlternateNameConflict, LNK_Error_RelocationAgainstRemovedSection, @@ -55,6 +54,7 @@ typedef enum LNK_Error_UndefinedIsWeak, LNK_Error_WeakCycle, LNK_Error_InvalidLib, + LNK_Error_UnresolvedSymbol, LNK_Error_Last, LNK_Warning_First, diff --git a/src/linker/lnk_input.c b/src/linker/lnk_input.c deleted file mode 100644 index a7f543bf..00000000 --- a/src/linker/lnk_input.c +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2025 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal void -lnk_error_input_obj(LNK_ErrorCode code, LNK_InputObj *input, char *fmt, ...) -{ - va_list args; va_start(args, fmt); - lnk_error_with_loc_fv(code, input->path, input->lib ? input->lib->path : str8_zero(), fmt, args); - va_end(args); -} - -internal String8 -lnk_string_from_input_source(LNK_InputSourceType input_source) -{ - String8 result = str8_zero(); - switch (input_source) { - case LNK_InputSource_CmdLine: result = str8_lit("CmdLine"); break; - case LNK_InputSource_Default: result = str8_lit("Default"); break; - case LNK_InputSource_Obj: result = str8_lit("Obj"); break; - default: InvalidPath; - } - return result; -} - -internal void -lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node) -{ - SLLQueuePush(list->first, list->last, node); - ++list->count; -} - -internal LNK_InputObj * -lnk_input_obj_list_push(Arena *arena, LNK_InputObjList *list) -{ - LNK_InputObj *node = push_array(arena, LNK_InputObj, 1); - lnk_input_obj_list_push_node(list, node); - return node; -} - -internal void -lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal LNK_InputObj ** -lnk_array_from_input_obj_list(Arena *arena, LNK_InputObjList list) -{ - LNK_InputObj **result = push_array_no_zero(arena, LNK_InputObj *, list.count); - U64 i = 0; - for (LNK_InputObj *n = list.first; n != 0; n = n->next, ++i) { - Assert(i < list.count); - result[i] = n; - } - return result; -} - -internal LNK_InputObj ** -lnk_thin_array_from_input_obj_list(Arena *arena, LNK_InputObjList list, U64 *count_out) -{ - for (LNK_InputObj *input = list.first; input != 0; input = input->next) { - if (input->is_thin) { *count_out += 1; } - } - LNK_InputObj **thin_inputs = push_array(arena, LNK_InputObj *, *count_out); - U64 input_idx = 0; - for (LNK_InputObj *input = list.first; input != 0; input = input->next) { - if (input->is_thin) { thin_inputs[input_idx++] = input; } - } - return thin_inputs; -} - -internal String8Array -lnk_path_array_from_input_obj_array(Arena *arena, LNK_InputObj **arr, U64 count) -{ - String8Array paths = {0}; - paths.count = count; - paths.v = push_array(arena, String8, count); - for (U64 i = 0; i < count; i += 1) { - paths.v[i] = arr[i]->path; - } - return paths; -} - -internal int -lnk_input_obj_compar(const void *raw_a, const void *raw_b) -{ - const LNK_InputObj **a = (const LNK_InputObj **) raw_a; - const LNK_InputObj **b = (const LNK_InputObj **) raw_b; - int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path); - return cmp; -} - -internal int -lnk_input_obj_compar_is_before(void *raw_a, void *raw_b) -{ - LNK_InputObj **a = raw_a; - LNK_InputObj **b = raw_b; - int cmp = str8_compar_case_sensitive(&(*a)->path, &(*b)->path); - int is_before = cmp < 0; - return is_before; -} - -internal LNK_InputObjList -lnk_list_from_input_obj_arr(LNK_InputObj **arr, U64 count) -{ - LNK_InputObjList list = {0}; - for (U64 i = 0; i < count; ++i) { - SLLQueuePush(list.first, list.last, arr[i]); - ++list.count; - } - return list; -} - -internal LNK_InputObjList -lnk_input_obj_list_from_string_list(Arena *arena, String8List list) -{ - LNK_InputObjList input_list = {0}; - for (String8Node *path = list.first; path != 0; path = path->next) { - LNK_InputObj *input = lnk_input_obj_list_push(arena, &input_list); - input->is_thin = 1; - input->dedup_id = path->string; - input->path = path->string; - } - return input_list; -} - -internal LNK_InputImportNode * -lnk_input_import_list_push(Arena *arena, LNK_InputImportList *list) -{ - LNK_InputImportNode *node = push_array(arena, LNK_InputImportNode, 1); - SLLQueuePush(list->first, list->last, node); - list->count += 1; - return node; -} - -internal void -lnk_input_import_list_concat_in_place(LNK_InputImportList *list, LNK_InputImportList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal LNK_InputImportNode ** -lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list) -{ - LNK_InputImportNode **result = push_array_no_zero(arena, LNK_InputImportNode *, list.count); - U64 idx = 0; - for (LNK_InputImportNode *node = list.first; node != 0; node = node->next) { - Assert(idx < list.count); - result[idx++] = node; - } - return result; -} - -internal LNK_InputImportList -lnk_list_from_input_import_arr(LNK_InputImportNode **arr, U64 count) -{ - LNK_InputImportList list = {0}; - for (U64 i = 0; i < count; i += 1) { - SLLQueuePush(list.first, list.last, arr[i]); - list.count += 1; - } - return list; -} - -int -lnk_input_import_is_before(void *raw_a, void *raw_b) -{ - LNK_InputImport *a = *(LNK_InputImport **)raw_a; - LNK_InputImport *b = *(LNK_InputImport **)raw_b; - return a->input_idx < b->input_idx; -} - -int -lnk_input_import_node_compar(const void *raw_a, const void *raw_b) -{ - LNK_InputImportNode * const *a = raw_a; - LNK_InputImportNode * const *b = raw_b; - return u64_compar(&(*a)->data.input_idx, &(*b)->data.input_idx); -} - diff --git a/src/linker/lnk_input.h b/src/linker/lnk_input.h deleted file mode 100644 index f139e39c..00000000 --- a/src/linker/lnk_input.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2025 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -typedef enum -{ - LNK_InputSource_CmdLine, // specified on command line - LNK_InputSource_Default, // specified through defaultlib switch - LNK_InputSource_Obj, // refrenced from objects - LNK_InputSource_Count -} LNK_InputSourceType; - -typedef String8Node LNK_InputLib; -typedef String8List LNK_InputLibList; - -typedef struct LNK_InputImport -{ - String8 coff_import; - U64 input_idx; - struct LNK_Lib *lib; -} LNK_InputImport; - -typedef struct LNK_InputImportNode -{ - struct LNK_InputImportNode *next; - LNK_InputImport data; -} LNK_InputImportNode; - -typedef struct LNK_InputImportList -{ - U64 count; - LNK_InputImportNode *first; - LNK_InputImportNode *last; -} LNK_InputImportList; - -typedef struct LNK_InputObj -{ - struct LNK_InputObj *next; - B32 is_thin; - B32 has_disk_read_failed; - String8 dedup_id; - String8 path; - String8 data; - struct LNK_Lib *lib; - U64 input_idx; -} LNK_InputObj; - -typedef struct LNK_InputObjList -{ - U64 count; - LNK_InputObj *first; - LNK_InputObj *last; -} LNK_InputObjList; - -//////////////////////////////// - -internal void lnk_error_input_obj(LNK_ErrorCode code, LNK_InputObj *input, char *fmt, ...); - -internal String8 lnk_string_from_input_source(LNK_InputSourceType input_source); - -internal void lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node); -internal LNK_InputObj * lnk_input_obj_list_push(Arena *arena, LNK_InputObjList *list); -internal void lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat); - -internal LNK_InputObj ** lnk_array_from_input_obj_list(Arena *arena, LNK_InputObjList list); -internal LNK_InputObj ** lnk_thin_array_from_input_obj_list(Arena *arena, LNK_InputObjList list, U64 *count_out); -internal String8Array lnk_path_array_from_input_obj_array(Arena *arena, LNK_InputObj **arr, U64 count); -internal LNK_InputObjList lnk_list_from_input_obj_arr(LNK_InputObj **arr, U64 count); -internal LNK_InputObjList lnk_input_obj_list_from_string_list(Arena *arena, String8List list); - -internal LNK_InputImportNode * lnk_input_import_list_push(Arena *arena, LNK_InputImportList *list); -internal void lnk_input_import_list_concat_in_place(LNK_InputImportList *list, LNK_InputImportList *to_concat); -internal LNK_InputImportNode ** lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list); -internal LNK_InputImportList lnk_list_from_input_import_arr(LNK_InputImportNode **arr, U64 count); - diff --git a/src/linker/lnk_io.c b/src/linker/lnk_io.c index c9626617..1125c884 100644 --- a/src/linker/lnk_io.c +++ b/src/linker/lnk_io.c @@ -48,51 +48,36 @@ lnk_write_file(void *raw_handle, uint64_t offset, void *buffer, uint64_t buffer_ return write_size; } -internal String8List -lnk_file_search(Arena *arena, String8List dir_list, String8 file_path) +internal String8 +lnk_find_first_file(Arena *arena, String8List dir_list, String8 path) { ProfBeginFunction(); Temp scratch = scratch_begin(&arena, 1); - String8List match_list; MemoryZeroStruct(&match_list); - - if (os_file_path_exists(file_path)) { - String8 str = push_str8_copy(arena, file_path); - str8_list_push(arena, &match_list, str); - } - - PathStyle file_path_style = path_style_from_str8(file_path); - B32 is_relative = file_path_style != PathStyle_WindowsAbsolute && - file_path_style != PathStyle_UnixAbsolute; - - if (is_relative) { - for (String8Node *i = dir_list.first; i != 0; i = i->next) { - String8List path_list = {0}; - str8_list_push(scratch.arena, &path_list, i->string); - str8_list_push(scratch.arena, &path_list, file_path); - String8 path = str8_path_list_join_by_style(scratch.arena, &path_list, PathStyle_SystemAbsolute); - B32 file_exists = os_file_path_exists(path); - if (file_exists) { - B32 is_unique = 1; - OS_FileID file_id = os_id_from_file_path(path); - for (String8Node *k = match_list.first; k != 0; k = k->next) { - OS_FileID test_id = os_id_from_file_path(k->string); - int cmp = os_file_id_compare(test_id, file_id) != 0; - if (cmp == 0) { - is_unique = 0; - break; - } - } - if (is_unique) { - String8 str = push_str8_copy(arena, path); - str8_list_push(arena, &match_list, str); - } + String8 result = {0}; + if (os_file_path_exists(path)) { + PathStyle path_style = path_style_from_str8(path); + if (path_style == PathStyle_Relative) { + String8 current_path = os_get_current_path(scratch.arena); + String8List l = {0}; + str8_list_push(scratch.arena, &l, current_path); + str8_list_push(scratch.arena, &l, path); + result = str8_path_list_join_by_style(arena, &l, PathStyle_SystemAbsolute); + } else { + result = path; + } + } else { + String8 file_name = str8_skip_last_slash(path); + for EachNode(n, String8Node, dir_list.first) { + String8 full_path = push_str8f(scratch.arena, "%S/%S", n->string, file_name); + if (os_file_path_exists(full_path)) { + result = push_str8_copy(arena, full_path); + break; } } } - scratch_end(scratch); ProfEnd(); - return match_list; + return result; } internal OS_Handle diff --git a/src/linker/lnk_io.h b/src/linker/lnk_io.h index 3e1b1705..76795427 100644 --- a/src/linker/lnk_io.h +++ b/src/linker/lnk_io.h @@ -28,8 +28,6 @@ shared_function uint64_t lnk_write_file(void *raw_handle, uint64_t offset, void // --- IO Functions ------------------------------------------------------------ -internal String8List lnk_file_search(Arena *arena, String8List dir_list, String8 file_path); - internal OS_Handle lnk_file_open_with_rename_permissions(String8 path); internal B32 lnk_file_set_delete_on_close(OS_Handle handle, B32 delete_file); internal B32 lnk_file_rename(OS_Handle handle, String8 new_name); diff --git a/src/linker/lnk_lib.c b/src/linker/lnk_lib.c index 14d69774..28ab3673 100644 --- a/src/linker/lnk_lib.c +++ b/src/linker/lnk_lib.c @@ -1,216 +1,270 @@ -// Copyright (c) 2025 Epic Games Tools +// Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal LNK_LibNode * -lnk_lib_list_pop_node_atomic(LNK_LibList *list) -{ - for (;;) { - LNK_LibNode *expected = list->first; - LNK_LibNode *current = ins_atomic_ptr_eval_cond_assign(&list->first, expected->next, expected); - if (expected == current) { - ins_atomic_u64_dec_eval(&list->count); - return expected; - } - } -} - -internal void -lnk_lib_list_push_node_atomic(LNK_LibList *list, LNK_LibNode *node) -{ - for (;;) { - LNK_LibNode *expected = list->first; - LNK_LibNode *current = ins_atomic_ptr_eval_cond_assign(&list->first, node, expected); - if (current == expected) { - node->next = expected; - ins_atomic_u64_inc_eval(&list->count); - return; - } - } -} - -internal void -lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node) -{ - SLLStackPush(list->first, node); - list->count += 1; -} - -internal LNK_LibList -lnk_lib_list_reserve(Arena *arena, U64 count) -{ - LNK_LibList result = {0}; - LNK_LibNode *nodes = push_array(arena, LNK_LibNode, count); - for (U64 i = 0; i < count; i += 1) { lnk_lib_list_push_node(&result, &nodes[i]); } - return result; -} - -internal LNK_LibNodeArray -lnk_array_from_lib_list(Arena *arena, LNK_LibList list) -{ - LNK_LibNodeArray result = {0}; - result.v = push_array(arena, LNK_LibNode, list.count); - for (LNK_LibNode *n = list.first; n != 0; n = n->next) { result.v[result.count++] = *n; } - return result; -} - -internal B32 -lnk_lib_from_data(Arena *arena, String8 data, String8 path, LNK_Lib *lib_out) -{ - ProfBeginFunction(); - - // is data archive? - COFF_ArchiveType type = coff_archive_type_from_data(data); - if (type == COFF_Archive_Null) { - return 0; - } - - COFF_ArchiveParse parse = coff_archive_parse_from_data(data); - if (parse.error.size) { - return 0; - } - - U64 symbol_count; - String8 string_table; - U32 *member_off_arr; - - // try to init library from optional second member - if (parse.second_member.member_count) { - COFF_ArchiveSecondMember second_member = parse.second_member; - Assert(second_member.symbol_count == second_member.symbol_index_count); - Assert(second_member.member_count == second_member.member_offset_count); - - symbol_count = second_member.symbol_count; - string_table = second_member.string_table; - member_off_arr = push_array_no_zero(arena, U32, symbol_count); - - // decompress member offsets - for (U64 symbol_idx = 0; symbol_idx < symbol_count; symbol_idx += 1) { - U16 off_number = second_member.symbol_indices[symbol_idx]; - if (0 < off_number && off_number <= second_member.member_count) { - member_off_arr[symbol_idx] = second_member.member_offsets[off_number - 1]; - } else { - // TODO: log bad offset - member_off_arr[symbol_idx] = max_U32; - } - } - } - // first member is deprecated however tools emit it for compatibility reasons - // and lld-link with /DLL emits only first member - else if (parse.first_member.symbol_count) { - COFF_ArchiveFirstMember first_member = parse.first_member; - Assert(first_member.symbol_count == first_member.member_offset_count); - - symbol_count = first_member.symbol_count; - string_table = first_member.string_table; - member_off_arr = first_member.member_offsets; - - // convert big endian offsets - for (U32 offset_idx = 0; offset_idx < symbol_count; offset_idx += 1) { - member_off_arr[offset_idx] = from_be_u32(member_off_arr[offset_idx]); - } - } else { - symbol_count = 0; - string_table = str8_zero(); - member_off_arr = 0; - } - - // parse string table - String8List symbol_name_list = str8_split_by_string_chars(arena, string_table, str8_lit("\0"), StringSplitFlag_KeepEmpties); - Assert(symbol_name_list.node_count >= symbol_count); - symbol_count = Min(symbol_count, symbol_name_list.node_count); - - // init lib - lib_out->path = push_str8_copy(arena, path); - lib_out->data = data; - lib_out->type = type; - lib_out->symbol_count = symbol_count; - lib_out->member_off_arr = member_off_arr; - lib_out->symbol_name_list = symbol_name_list; - lib_out->long_names = parse.long_names; - - ProfEnd(); - return 1; -} - -internal -THREAD_POOL_TASK_FUNC(lnk_lib_initer) -{ - LNK_LibIniter *task = raw_task; - - LNK_LibNode *lib_node = lnk_lib_list_pop_node_atomic(&task->free_libs); - lib_node->data.input_idx = task_id; - - B32 is_valid_lib = lnk_lib_from_data(arena, task->data_arr[task_id], task->path_arr[task_id], &lib_node->data); - if (is_valid_lib) { - lnk_lib_list_push_node_atomic(&task->valid_libs, lib_node); - } else { - lnk_lib_list_push_node_atomic(&task->invalid_libs, lib_node); - } -} - internal int lnk_lib_node_is_before(void *a, void *b) { return ((LNK_LibNode*)a)->data.input_idx < ((LNK_LibNode*)b)->data.input_idx; } +internal int +lnk_lib_node_ptr_is_before(void *raw_a, void *raw_b) +{ + return lnk_lib_node_is_before(*(LNK_Lib **)raw_a, *(LNK_Lib **)raw_b); +} + +internal B32 +lnk_first_member_sort_key_is_before(void *raw_a, void *raw_b) +{ + LNK_FirstMemberSortKey *a = raw_a, *b = raw_b; + return str8_is_before_case_sensitive(&a->symbol_name, &b->symbol_name); +} + +internal B32 +lnk_lib_from_data(Arena *arena, String8 data, String8 path, U64 input_idx, LNK_Lib *lib_out) +{ + // is data archive? + COFF_ArchiveType type = coff_archive_type_from_data(data); + if (type == COFF_Archive_Null) { + return 0; + } + + // TODO: report parse errors + COFF_ArchiveParse parse = coff_archive_parse_from_data(data); + if (parse.error.size) { + return 0; + } + + U32 member_count = 0; + U64 symbol_count = 0; + String8Array symbol_names = {0}; + U16 *symbol_indices = 0; + U32 *member_offsets = 0; + + // try to init library from optional second member + if (parse.second_member.member_count) { + COFF_ArchiveSecondMember second_member = parse.second_member; + Assert(second_member.symbol_count == second_member.symbol_index_count); + Assert(second_member.member_count == second_member.member_offset_count); + + member_count = second_member.member_count; + symbol_count = second_member.symbol_count; + member_offsets = second_member.member_offsets; + symbol_indices = second_member.symbol_indices; + + // parse symbol names + { + Temp scratch = scratch_begin(&arena, 1); + String8List symbol_name_list = str8_split_by_string_chars(scratch.arena, second_member.string_table, str8_lit("\0"), 0); + Assert(symbol_name_list.node_count >= symbol_count); + symbol_names = str8_array_from_list(arena, &symbol_name_list); + scratch_end(scratch); + } + } + // first member is deprecated however tools emit it for compatibility reasons + // and lld-link with /DLL emits only first member + else if (parse.first_member.symbol_count) { + Temp scratch = scratch_begin(&arena, 1); + + COFF_ArchiveFirstMember first_member = parse.first_member; + Assert(first_member.symbol_count == first_member.member_offset_count); + + symbol_count = first_member.symbol_count; + + // convert big endian offsets + for (U32 offset_idx = 0; offset_idx < symbol_count; offset_idx += 1) { + first_member.member_offsets[offset_idx] = from_be_u32(first_member.member_offsets[offset_idx]); + } + + // compress member offsets to match those from the second header + { + HashTable *member_off_ht = hash_table_init(scratch.arena, (U64)((F64)first_member.symbol_count * 1.3)); + for EachIndex(symbol_idx, symbol_count) { + if (!hash_table_search_u32_u32(member_off_ht, first_member.member_offsets[symbol_idx], 0)) { + hash_table_push_u32_u32(scratch.arena, member_off_ht, first_member.member_offsets[symbol_idx], member_off_ht->count); + } + } + + symbol_indices = push_array(arena, U16, first_member.symbol_count); + for EachIndex(symbol_idx, first_member.symbol_count) { + U32 member_off = first_member.member_offsets[symbol_idx]; + U32 member_off_idx = 0; + if (!hash_table_search_u32_u32(member_off_ht, member_off, &member_off_idx)) { + InvalidPath; + } + symbol_indices[symbol_idx] = member_off_idx+1; + } + + member_count = member_off_ht->count; + member_offsets = push_array_no_zero(arena, U32, member_count); + + for EachIndex(bucket_idx, member_off_ht->cap) { + BucketList *bucket = &member_off_ht->buckets[bucket_idx]; + for (BucketNode *n = bucket->first; n != 0; n = n->next) { + U32 member_off = n->v.key_u32; + U32 member_off_idx = n->v.value_u32; + member_offsets[member_off_idx] = member_off; + } + } + + // parse symbol names + { + Temp scratch = scratch_begin(&arena, 1); + String8List symbol_name_list = str8_split_by_string_chars(scratch.arena, first_member.string_table, str8_lit("\0"), 0); + Assert(symbol_name_list.node_count >= first_member.symbol_count); + symbol_names = str8_array_from_list(arena, &symbol_name_list); + scratch_end(scratch); + } + + // sort lexically symbol names + LNK_FirstMemberSortKey *sort_keys = push_array_no_zero(scratch.arena, LNK_FirstMemberSortKey, first_member.symbol_count); + for EachIndex(symbol_idx, first_member.symbol_count) { + sort_keys[symbol_idx].symbol_name = symbol_names.v[symbol_idx]; + sort_keys[symbol_idx].member_off_idx = symbol_indices[symbol_idx]; + } + radsort(sort_keys, first_member.symbol_count, lnk_first_member_sort_key_is_before); + + for EachIndex(symbol_idx, first_member.symbol_count) { + symbol_names.v[symbol_idx] = sort_keys[symbol_idx].symbol_name; + symbol_indices[symbol_idx] = sort_keys[symbol_idx].member_off_idx; + } + } + + scratch_end(scratch); + } + + // init lib + lib_out->path = push_str8_copy(arena, path); + lib_out->data = data; + lib_out->type = type; + lib_out->member_count = member_count; + lib_out->symbol_count = Min(symbol_count, symbol_names.count); // TODO: warn about mismatched number of symbol names and symbol count in the header + lib_out->member_offsets = member_offsets; + lib_out->symbol_indices = symbol_indices; + lib_out->member_links = push_array(arena, LNK_Symbol *, member_count); + lib_out->symbol_names = symbol_names; + lib_out->long_names = parse.long_names; + lib_out->input_idx = input_idx; + + return 1; +} + +internal +THREAD_POOL_TASK_FUNC(lnk_lib_initer) +{ + LNK_LibIniter *task = raw_task; + LNK_Input *input = task->inputs[task_id]; + + U64 lib_node_idx = ins_atomic_u64_inc_eval(&task->next_free_lib_idx)-1; + LNK_LibNode *lib_node = &task->free_libs[lib_node_idx]; + + B32 is_valid_lib = lnk_lib_from_data(arena, input->data, input->path, task->lib_id_base + task_id, &lib_node->data); + if (is_valid_lib) { + U64 valid_lib_idx = ins_atomic_u64_inc_eval(&task->valid_libs_count)-1; + task->valid_libs[valid_lib_idx] = lib_node; + } else { + U64 invalid_lib_idx = ins_atomic_u64_inc_eval(&task->invalid_libs_count); + task->invalid_libs[invalid_lib_idx] = lib_node; + } +} + +internal LNK_Lib ** +lnk_array_from_lib_list(Arena *arena, LNK_LibList list) +{ + LNK_Lib **arr = push_array_no_zero(arena, LNK_Lib *, list.count); + U64 idx = 0; + for (LNK_LibNode *node = list.first; node != 0; node = node->next, ++idx) { + arr[idx] = &node->data; + } + return arr; +} + +internal void +lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node) +{ + SLLQueuePush(list->first, list->last, node); + list->count += 1; +} + internal LNK_LibNodeArray -lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, String8Array data_arr, String8Array path_arr) +lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, U64 inputs_count, LNK_Input **inputs) { Temp scratch = scratch_begin(arena->v, arena->count); - Assert(data_arr.count == path_arr.count); - U64 lib_count = data_arr.count; + U64 lib_id_base = list->count; // parse libs in parallel LNK_LibIniter task = {0}; - task.free_libs = lnk_lib_list_reserve(scratch.arena, lib_count); - task.data_arr = data_arr.v; - task.path_arr = path_arr.v; - tp_for_parallel(tp, arena, lib_count, lnk_lib_initer, &task); + task.lib_id_base = list->count; + task.free_libs = push_array(arena->v[0], LNK_LibNode, inputs_count); + task.valid_libs = push_array(scratch.arena, LNK_LibNode *, inputs_count); + task.invalid_libs = push_array(scratch.arena, LNK_LibNode *, inputs_count); + task.inputs = inputs; + tp_for_parallel(tp, arena, inputs_count, lnk_lib_initer, &task); // report invalid libs - LNK_LibNodeArray invalid_libs = lnk_array_from_lib_list(scratch.arena, task.invalid_libs); - radsort(invalid_libs.v, invalid_libs.count, lnk_lib_node_is_before); - for (U64 i = 0; i < task.invalid_libs.count; i += 1) { - U64 input_idx = invalid_libs.v[i].data.input_idx; - lnk_error(LNK_Error_InvalidLib, "%S: failed to parse library", path_arr.v[input_idx]); + radsort(task.invalid_libs, task.invalid_libs_count, lnk_lib_node_ptr_is_before); + for EachIndex(i, task.invalid_libs_count) { + U64 input_idx = task.invalid_libs[i]->data.input_idx; + lnk_error(LNK_Error_InvalidLib, "%S: failed to parse library", inputs[input_idx]->path); } // push parsed libs - LNK_LibNodeArray result = lnk_array_from_lib_list(arena->v[0], task.valid_libs); - radsort(result.v, result.count, lnk_lib_node_is_before); - for (U64 i = result.count; i > 0; i -= 1) { - result.v[i-1].data.input_idx = list->count; - lnk_lib_list_push_node(list, &result.v[i-1]); + radsort(task.valid_libs, task.valid_libs_count, lnk_lib_node_ptr_is_before); + for EachIndex(i, task.valid_libs_count) { + lnk_lib_list_push_node(list, task.valid_libs[i]); } + LNK_LibNodeArray result = { .count = task.valid_libs_count, task.valid_libs }; + scratch_end(scratch); return result; } -internal -THREAD_POOL_TASK_FUNC(lnk_push_lib_symbols_task) +internal B32 +lnk_lib_set_link_symbol(LNK_Lib *lib, U32 member_idx, LNK_Symbol *link_symbol) { - LNK_SymbolPusher *task = raw_task; - LNK_SymbolTable *symtab = task->symtab; - LNK_Lib *lib = &task->u.libs.v[task_id].data; + local_persist LNK_Symbol null_symbol; - String8Node *name_node = lib->symbol_name_list.first; - for (U64 symbol_idx = 0; symbol_idx < lib->symbol_count; ++symbol_idx, name_node = name_node->next) { - LNK_Symbol *symbol = lnk_make_lib_symbol(arena, name_node->string, lib, lib->member_off_arr[symbol_idx]); - lnk_symbol_table_push_(symtab, arena, worker_id, LNK_SymbolScope_Lib, symbol); + LNK_Symbol *slot = ins_atomic_ptr_eval_assign(&lib->member_links[member_idx], &null_symbol); + + B32 was_linked = (slot == 0); + + for (LNK_Symbol *leader = link_symbol;;) { + // update slot symbol if it is empty or link symbol comes before symbol in the slot + if (slot && slot != &null_symbol) { + if (lnk_symbol_is_before(slot, leader)) { + leader = slot; + } + } else { + leader = link_symbol; + } + + // try to insert back updated slot symbol + LNK_Symbol *swap = ins_atomic_ptr_eval_cond_assign(&lib->member_links[member_idx], leader, &null_symbol); + + // exit if slot symbol was null + if (swap == &null_symbol) { + break; + } + + // reload slot symbol + slot = ins_atomic_ptr_eval_assign(&lib->member_links[member_idx], &null_symbol); } + + return was_linked; } -internal void -lnk_input_lib_symbols(TP_Context *tp, LNK_SymbolTable *symtab, LNK_LibNodeArray libs) +internal force_inline B32 +lnk_search_lib(LNK_Lib *lib, String8 symbol_name, U32 *member_idx_out) { - ProfBeginFunction(); - LNK_SymbolPusher task = {0}; - task.symtab = symtab; - task.u.libs = libs; - tp_for_parallel(tp, symtab->arena, libs.count, lnk_push_lib_symbols_task, &task); - ProfEnd(); + U64 symbol_idx = str8_array_bsearch(lib->symbol_names, symbol_name); + if (symbol_idx < lib->symbol_count) { + if (member_idx_out) { + *member_idx_out = lib->symbol_indices[symbol_idx]-1; + } + return 1; + } + return 0; } diff --git a/src/linker/lnk_lib.h b/src/linker/lnk_lib.h index 7b5f1c27..0cbdd673 100644 --- a/src/linker/lnk_lib.h +++ b/src/linker/lnk_lib.h @@ -1,20 +1,23 @@ -// Copyright (c) 2025 Epic Games Tools +// Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once typedef struct LNK_Lib { - String8 path; - String8 data; - COFF_ArchiveType type; - U32 symbol_count; - U32 *member_off_arr; - String8List symbol_name_list; - String8 long_names; - U64 input_idx; + String8 path; + String8 data; + COFF_ArchiveType type; + U32 member_count; + U32 symbol_count; + U32 *member_offsets; + U16 *symbol_indices; + LNK_Symbol **member_links; + String8Array symbol_names; + String8 long_names; + U64 input_idx; } LNK_Lib; - + typedef struct LNK_LibNode { LNK_Lib data; @@ -23,31 +26,48 @@ typedef struct LNK_LibNode typedef struct LNK_LibNodeArray { - U64 count; - LNK_LibNode *v; + U64 count; + LNK_LibNode **v; } LNK_LibNodeArray; typedef struct LNK_LibList { - U64 count; - struct LNK_LibNode *first; + U64 count; + LNK_LibNode *first; + LNK_LibNode *last; } LNK_LibList; + +typedef struct LNK_FirstMemberSortKey +{ + String8 symbol_name; + U16 member_off_idx; +} LNK_FirstMemberSortKey; + +// --- Workers Contexts -------------------------------------------------------- typedef struct { - String8 *data_arr; - String8 *path_arr; - LNK_LibList free_libs; - LNK_LibList valid_libs; - LNK_LibList invalid_libs; + struct LNK_Input **inputs; + U64 lib_id_base; + U64 next_free_lib_idx; + U64 valid_libs_count; + U64 invalid_libs_count; + LNK_LibNode *free_libs; + LNK_LibNode **valid_libs; + LNK_LibNode **invalid_libs; } LNK_LibIniter; -internal LNK_LibNode * lnk_lib_list_pop_node_atomic(LNK_LibList *list); -internal void lnk_lib_list_push_node_atomic(LNK_LibList *list, LNK_LibNode *node); +// ----------------------------------------------------------------------------- + +internal int lnk_lib_node_is_before(void *a, void *b); +internal int lnk_lib_node_ptr_is_before(void *raw_a, void *raw_b); + +internal B32 lnk_lib_from_data(Arena *arena, String8 data, String8 path, U64 input_idx, LNK_Lib *lib_out); +internal LNK_Lib ** lnk_array_from_lib_list(Arena *arena, LNK_LibList list); internal void lnk_lib_list_push_node(LNK_LibList *list, LNK_LibNode *node); -internal LNK_LibList lnk_lib_list_reserve(Arena *arena, U64 count); -internal LNK_LibNodeArray lnk_array_from_lib_list(Arena *arena, LNK_LibList list); +internal LNK_LibNodeArray lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, U64 inputs_count, struct LNK_Input **inputs); -internal B32 lnk_lib_from_data(Arena *arena, String8 data, String8 path, LNK_Lib *lib_out); -internal LNK_LibNodeArray lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, String8Array data_arr, String8Array path_arr); +internal B32 lnk_lib_set_link_symbol(LNK_Lib *lib, U32 member_idx, LNK_Symbol *link_symbol); + +internal B32 lnk_search_lib(LNK_Lib *lib, String8 symbol_name, U32 *member_idx_out); diff --git a/src/linker/lnk_log.c b/src/linker/lnk_log.c index 905b26ba..1904996c 100644 --- a/src/linker/lnk_log.c +++ b/src/linker/lnk_log.c @@ -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 void @@ -45,6 +45,7 @@ lnk_log_type_from_string(String8 string) "SizeBreakdown", LNK_Log_SizeBreakdown, "LinkStats", LNK_Log_LinkStats, "Timers", LNK_Log_Timers, + "Links", LNK_Log_Links, }; Assert(ArrayCount(map) == LNK_Log_Count); diff --git a/src/linker/lnk_log.h b/src/linker/lnk_log.h index a878803f..a1e24157 100644 --- a/src/linker/lnk_log.h +++ b/src/linker/lnk_log.h @@ -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 @@ -14,6 +14,7 @@ typedef enum LNK_Log_SizeBreakdown, LNK_Log_LinkStats, LNK_Log_Timers, + LNK_Log_Links, LNK_Log_Count } LNK_LogType; diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index 38cbce44..7a8e1a88 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -1,16 +1,40 @@ -// Copyright (c) 2025 Epic Games Tools +// Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +internal String8 +lnk_loc_from_obj(Arena *arena, LNK_Obj *obj) +{ + String8 obj_path = str8_skip_last_slash(obj ? obj->path : str8_lit("RADLINK")); + String8 lib_path = str8_skip_last_slash(lnk_obj_get_lib_path(obj)); + String8 result; + if (lib_path.size) { + result = push_str8f(arena, "%S(%S)", lib_path, obj_path); + } else { + result = push_str8_copy(arena, obj_path); + } + return result; +} + internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...) { va_list args; va_start(args, fmt); - String8 obj_path = obj ? obj->path : str8_lit("RADLINK"); + String8 obj_path = obj ? obj->path : str8_zero(); String8 lib_path = lnk_obj_get_lib_path(obj); lnk_error_with_loc_fv(code, obj_path, lib_path, fmt, args); va_end(args); } +internal void +lnk_error_input_obj(LNK_ErrorCode code, LNK_Input *input, char *fmt, ...) +{ + va_list args; va_start(args, fmt); + LNK_LibMemberRef *link_member = input->link_member; + LNK_Lib *link_lib = link_member ? link_member->lib : 0; + lnk_error_with_loc_fv(code, input->path, link_lib ? link_lib->path : str8_zero(), fmt, args); + va_end(args); +} + internal LNK_Obj ** lnk_array_from_obj_list(Arena *arena, LNK_ObjList list) { @@ -26,11 +50,10 @@ internal THREAD_POOL_TASK_FUNC(lnk_obj_initer) { LNK_ObjIniter *task = raw_task; - LNK_InputObj *input = task->inputs[task_id]; - LNK_Obj *obj = &task->objs.v[task_id].data; - U64 obj_idx = task->obj_id_base + task_id; + LNK_Input *input = task->inputs[task_id]; + LNK_Obj *obj = &task->objs[task_id].data; - ProfBeginV("Init Obj [%S%s%S]", input->lib_path, (input->lib_path.size ? ": " : 0), input->path); + //ProfBeginV("Init Obj [%S%s%S]", input->lib_path, (input->lib_path.size ? ": " : 0), input->path); // // parse obj header @@ -58,7 +81,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) String8 raw_coff_string_table = str8_substr(input->data, header.string_table_range); // - // error check: section table / symbol table / string table + // error check section table / symbol table / string table // if (raw_coff_section_table.size != dim_1u64(header.section_table_range)) { lnk_error_input_obj(LNK_Error_IllData, input, "corrupted file, unable to read section header table"); @@ -76,14 +99,10 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) COFF_SectionHeader *coff_section_table = (COFF_SectionHeader *)raw_coff_section_table.str; for (U64 sect_idx = 0; sect_idx < header.section_count_no_null; sect_idx += 1) { COFF_SectionHeader *coff_sect_header = &coff_section_table[sect_idx]; - - // read name - String8 sect_name = coff_name_from_section_header(raw_coff_string_table, coff_sect_header); - + String8 sect_name = coff_name_from_section_header(raw_coff_string_table, coff_sect_header); if (~coff_sect_header->flags & COFF_SectionFlag_CntUninitializedData) { if (coff_sect_header->fsize > 0) { Rng1U64 sect_range = rng_1u64(coff_sect_header->foff, coff_sect_header->foff + coff_sect_header->fsize); - if (contains_1u64(header.header_range, coff_sect_header->foff) || (coff_sect_header->fsize > 0 && contains_1u64(header.header_range, sect_range.max-1))) { lnk_error_input_obj(LNK_Error_IllData, input, "header (%S No. %#llx) defines out of bounds section data (file offsets point into file header)", sect_name, sect_idx+1); @@ -104,15 +123,13 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) } // - // error check symbols + // error check symbol table // { COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(input->data, header.section_table_range).str; - String8 string_table = str8_substr(input->data, header.string_table_range); - String8 symbol_table = str8_substr(input->data, header.symbol_table_range); COFF_ParsedSymbol symbol; for (U64 symbol_idx = 0; symbol_idx < header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = coff_parse_symbol(header, string_table, symbol_table, symbol_idx); + symbol = coff_parse_symbol(header, raw_coff_string_table, raw_coff_symbol_table, symbol_idx); COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); if (interp == COFF_SymbolValueInterp_Regular) { if (symbol.section_number == 0 || symbol.section_number > header.section_count_no_null) { @@ -140,11 +157,9 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) comdats = push_array_no_zero(arena, U32, header.section_count_no_null); MemorySet(comdats, 0xff, header.section_count_no_null * sizeof(comdats[0])); - String8 string_table = str8_substr(input->data, header.string_table_range); - String8 symbol_table = str8_substr(input->data, header.symbol_table_range); COFF_ParsedSymbol symbol; for (U64 symbol_idx = 0; symbol_idx < header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = coff_parse_symbol(header, string_table, symbol_table, symbol_idx); + symbol = coff_parse_symbol(header, raw_coff_string_table, raw_coff_symbol_table, symbol_idx); COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); if (interp == COFF_SymbolValueInterp_Regular) { @@ -180,8 +195,6 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) { Temp scratch = scratch_begin(&arena, 1); - String8 string_table = str8_substr(input->data, header.string_table_range); - String8 symbol_table = str8_substr(input->data, header.symbol_table_range); HashTable *visited_sections = hash_table_init(scratch.arena, 32); for (U64 sect_idx = 0; sect_idx < header.section_count_no_null; sect_idx += 1) { for (U32 curr_section = sect_idx;;) { @@ -193,7 +206,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) } // extract COMDAT info for current section - COFF_ParsedSymbol symbol = coff_parse_symbol(header, string_table, symbol_table, symbol_idx); + COFF_ParsedSymbol symbol = coff_parse_symbol(header, raw_coff_string_table, raw_coff_symbol_table, symbol_idx); COFF_ComdatSelectType select = COFF_ComdatSelect_Null; U32 section_number = 0; coff_parse_secdef(symbol, header.is_big_obj, &select, §ion_number, 0, 0); @@ -205,7 +218,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) // was section visited? -- loop found if (hash_table_search_u64(visited_sections, curr_section)) { - COFF_ParsedSymbol symbol = coff_parse_symbol(header, string_table, symbol_table, comdats[sect_idx]); + COFF_ParsedSymbol symbol = coff_parse_symbol(header, raw_coff_string_table, raw_coff_symbol_table, comdats[sect_idx]); lnk_error_input_obj(LNK_Error_AssociativeLoop, input, "section symbol %S (No. 0x%x) does not terminate on a non-associate COMDAT symbol", symbol.name, comdats[sect_idx]); break; } @@ -230,12 +243,9 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) // U32Node **associated_sections = push_array(arena, U32Node *, header.section_count_no_null + 1); { - String8 string_table = str8_substr(input->data, header.string_table_range); - String8 symbol_table = str8_substr(input->data, header.symbol_table_range); - COFF_ParsedSymbol symbol; for (U32 symbol_idx = 0; symbol_idx < header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - symbol = coff_parse_symbol(header, string_table, symbol_table, symbol_idx); + symbol = coff_parse_symbol(header, raw_coff_string_table, raw_coff_symbol_table, symbol_idx); COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol); if (interp == COFF_SymbolValueInterp_Regular && symbol.storage_class == COFF_SymStorageClass_Static && symbol.aux_symbol_count > 0) { COFF_ComdatSelectType selection = COFF_ComdatSelect_Null; @@ -249,36 +259,61 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) } } } + + // + // mark sections + // + { + for EachIndex(sect_idx, header.section_count_no_null) { + COFF_SectionHeader *sect_header = &coff_section_table[sect_idx]; + String8 sect_name = coff_name_from_section_header(raw_coff_string_table, sect_header); + + // debug info + if (str8_starts_with(sect_name, str8_lit(".debug$"))) { + sect_header->flags |= LNK_SECTION_FLAG_DEBUG; + } + + // function overrides + if (str8_ends_with(sect_name, str8_lit("$fo$"), 0) || + str8_ends_with(sect_name, str8_lit("$fo_rvas$"), 0) || + str8_ends_with(sect_name, str8_lit("$fo_bdd$"), 0)) { + sect_header->flags |= COFF_SectionFlag_LnkInfo; + } + } + } - // - // extract obj features from compile symbol in .debug$S - // B8 hotpatch = 0; if (header.machine == COFF_MachineType_X64) { hotpatch = 1; - } else { + } + // + // extract obj features from compile symbol in .debug$S + // + else { Temp scratch = scratch_begin(&arena, 1); CV_Symbol comp_symbol = {0}; - for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { + for EachIndex(sect_idx, header.section_count_no_null) { COFF_SectionHeader *sect_header = &coff_section_table[sect_idx]; - String8 name = str8_cstring_capped_reverse(sect_header->name, sect_header->name+sizeof(sect_header->name)); - if (str8_match(name, str8_lit(".debug$S"), 0)) { - Temp temp = temp_begin(scratch.arena); - String8 debug_s_data = str8_substr(input->data, rng_1u64(sect_header->foff, sect_header->foff+sect_header->fsize)); - CV_DebugS debug_s = cv_parse_debug_s(temp.arena, debug_s_data); - for (String8Node *symbols_n = debug_s.data_list[CV_C13SubSectionIdxKind_Symbols].first; symbols_n != 0; symbols_n = symbols_n->next) { - CV_SymbolList symbol_list = {0}; - cv_parse_symbol_sub_section_capped(scratch.arena, &symbol_list, 0, symbols_n->string, CV_SymbolAlign, 2); - if (symbol_list.first->data.kind == CV_SymKind_COMPILE3) { - comp_symbol = symbol_list.first->data; - goto found_comp_symbol; - } else if (symbol_list.last->data.kind == CV_SymKind_COMPILE3) { - comp_symbol = symbol_list.last->data; - goto found_comp_symbol; + if (sect_header->flags & LNK_SECTION_FLAG_DEBUG) { + String8 name = str8_cstring_capped(sect_header->name, sect_header->name+sizeof(sect_header->name)); + if (str8_match(name, str8_lit(".debug$S"), 0)) { + Temp temp = temp_begin(scratch.arena); + String8 debug_s_data = str8_substr(input->data, rng_1u64(sect_header->foff, sect_header->foff+sect_header->fsize)); + CV_DebugS debug_s = cv_parse_debug_s(temp.arena, debug_s_data); + for EachNode(symbols_n, String8Node, debug_s.data_list[CV_C13SubSectionIdxKind_Symbols].first) { + CV_SymbolList symbol_list = {0}; + cv_parse_symbol_sub_section_capped(scratch.arena, &symbol_list, 0, symbols_n->string, CV_SymbolAlign, 2); + if (symbol_list.first->data.kind == CV_SymKind_COMPILE3) { + comp_symbol = symbol_list.first->data; + goto found_comp_symbol; + } else if (symbol_list.last->data.kind == CV_SymKind_COMPILE3) { + comp_symbol = symbol_list.last->data; + goto found_comp_symbol; + } } + temp_end(temp); } - temp_end(temp); } } found_comp_symbol:; @@ -292,64 +327,62 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer) } // fill out obj - obj->data = input->data; - obj->path = push_str8_copy(arena, input->path); - obj->lib = input->lib; - obj->input_idx = obj_idx; - obj->header = header; - obj->comdats = comdats; - obj->hotpatch = hotpatch; - obj->associated_sections = associated_sections; - - ProfEnd(); + obj->data = input->data; + obj->path = push_str8_copy(arena, input->path); + obj->header = header; + obj->comdats = comdats; + obj->exclude_from_debug_info = input->exclude_from_debug_info; + obj->hotpatch = hotpatch; + obj->associated_sections = associated_sections; + obj->node = &task->objs[task_id]; + obj->link_member = input->link_member; } -internal LNK_ObjNodeArray -lnk_obj_list_push_parallel(TP_Context *tp, - TP_Arena *arena, - LNK_ObjList *list, - COFF_MachineType machine, - U64 input_count, - LNK_InputObj **inputs) +internal LNK_ObjNode * +lnk_obj_from_input_many(TP_Context *tp, TP_Arena *arena, COFF_MachineType machine, U64 inputs_count, LNK_Input **inputs) { - ProfBeginFunction(); - - // store base id - U64 obj_id_base = list->count; - - // reserve obj nodes - LNK_ObjNodeArray objs = {0}; - if (input_count > 0) { - objs.count = input_count; - objs.v = push_array(arena->v[0], LNK_ObjNode, input_count); - for (LNK_ObjNode *ptr = objs.v, *opl = objs.v + input_count; ptr < opl; ++ptr) { - SLLQueuePush(list->first, list->last, ptr); - } - list->count += input_count; + LNK_ObjNode *objs = 0; + if (inputs_count) { + objs = push_array(arena->v[0], LNK_ObjNode, inputs_count); + tp_for_parallel(tp, arena, inputs_count, lnk_obj_initer, &(LNK_ObjIniter){ .inputs = inputs, .objs = objs, .machine = machine }); } - - // fill out & run task - LNK_ObjIniter task = {0}; - task.inputs = inputs; - task.obj_id_base = obj_id_base; - task.objs = objs; - task.machine = machine; - tp_for_parallel(tp, arena, input_count, lnk_obj_initer, &task); - - ProfEnd(); return objs; } +internal LNK_ObjNode * +lnk_obj_from_input(Arena *arena, COFF_MachineType machine, LNK_Input *input) +{ + Temp scratch = scratch_begin(&arena, 1); + TP_Context *tp = tp_alloc(scratch.arena, 1, 1, str8_zero()); + TP_Arena tp_arena = { .count = 1, .v = &arena }; + LNK_ObjNode *result = lnk_obj_from_input_many(tp, &tp_arena, machine, 1, &input); + scratch_end(scratch); + return result; +} + +internal void +lnk_obj_list_push_node_many(LNK_ObjList *list, U64 count, LNK_ObjNode *nodes) +{ + for EachIndex(i, count) { + DLLPushBack(list->first, list->last, &nodes[i]); + } + list->count += count; +} + +internal void +lnk_obj_list_push_node(LNK_ObjList *list, LNK_ObjNode *node) +{ + lnk_obj_list_push_node_many(list, 1, node); +} + internal THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table) { LNK_InputCoffSymbolTable *task = raw_task; - LNK_Obj *obj = &task->objs.v[task_id].data; - + LNK_Obj *obj = task->objs[task_id]; COFF_ParsedSymbol symbol = {0}; for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); - COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol); switch (interp) { case COFF_SymbolValueInterp_Regular: { @@ -358,36 +391,28 @@ THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table) if (sect_header->flags & COFF_SectionFlag_LnkRemove) { break; } - LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); - lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); + LNK_Symbol *defn = lnk_make_symbol(arena, symbol.name, obj, symbol_idx); + lnk_symbol_table_push_(task->symtab, arena, worker_id, defn); } } break; case COFF_SymbolValueInterp_Weak: { - LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); - lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); - lnk_symbol_list_push(arena, &task->weak_lists[worker_id], defn); + LNK_Symbol *defn = lnk_make_symbol(arena, symbol.name, obj, symbol_idx); + lnk_symbol_table_push_(task->symtab, arena, worker_id, defn); + } break; + case COFF_SymbolValueInterp_Undefined: { + if (symbol.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_make_symbol(arena, symbol.name, obj, symbol_idx); + lnk_symbol_table_push_(task->symtab, arena, worker_id, defn); + } } break; case COFF_SymbolValueInterp_Common: { - LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); - lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); + LNK_Symbol *defn = lnk_make_symbol(arena, symbol.name, obj, symbol_idx); + lnk_symbol_table_push_(task->symtab, arena, worker_id, defn); } break; case COFF_SymbolValueInterp_Abs: { if (symbol.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); - lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); - } - } break; - case COFF_SymbolValueInterp_Undefined: { - LNK_Symbol *s = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); - if (s == 0) { - if (symbol.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *undef = lnk_make_undefined_symbol(arena, symbol.name, obj); - lnk_symbol_list_push(arena, &task->undef_lists[worker_id], undef); - } else if (symbol.storage_class == COFF_SymStorageClass_Section) { - // lookup is performed during image patching step - } else { - Assert(!"unexpected storage class on undefined symbol"); - } + LNK_Symbol *defn = lnk_make_symbol(arena, symbol.name, obj, symbol_idx); + lnk_symbol_table_push_(task->symtab, arena, worker_id, defn); } } break; case COFF_SymbolValueInterp_Debug: { @@ -410,7 +435,7 @@ lnk_symlinks_from_obj(Arena *arena, LNK_SymbolTable *symtab, LNK_Obj *obj) COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { if (symlinks[symbol.section_number] == 0 || symbol.value == 0) { - symlinks[symbol.section_number] = lnk_symbol_table_search_(symtab, LNK_SymbolScope_Defined, symbol.name); + symlinks[symbol.section_number] = lnk_symbol_table_search_(symtab, symbol.name); } } } @@ -422,63 +447,31 @@ internal THREAD_POOL_TASK_FUNC(lnk_assign_comdat_symlinks_task) { LNK_InputCoffSymbolTable *task = raw_task; - LNK_Obj *obj = &task->objs.v[task_id].data; + LNK_Obj *obj = task->objs[task_id]; obj->symlinks = lnk_symlinks_from_obj(arena, task->symtab, obj); } -internal LNK_SymbolInputResult -lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs) +internal void +lnk_push_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs) { ProfBeginFunction(); - Temp scratch = scratch_begin(arena->v, arena->count); - - LNK_InputCoffSymbolTable task = {0}; - task.symtab = symtab; - task.objs = objs; - task.weak_lists = push_array(scratch.arena, LNK_SymbolList, tp->worker_count); - task.undef_lists = push_array(scratch.arena, LNK_SymbolList, tp->worker_count); - tp_for_parallel(tp, arena, objs.count, lnk_input_coff_symbol_table, &task); - tp_for_parallel(tp, arena, objs.count, lnk_assign_comdat_symlinks_task, &task); - - LNK_SymbolInputResult result = {0}; - SLLConcatInPlaceArray(&result.weak_symbols, task.weak_lists, tp->worker_count); - SLLConcatInPlaceArray(&result.undef_symbols, task.undef_lists, tp->worker_count); - - scratch_end(scratch); + LNK_InputCoffSymbolTable task = { .symtab = symtab, .objs = objs }; + tp_for_parallel(tp, arena, objs_count, lnk_input_coff_symbol_table, &task); + tp_for_parallel(tp, arena, objs_count, lnk_assign_comdat_symlinks_task, &task); ProfEnd(); - return result; } internal COFF_ParsedSymbol lnk_obj_match_symbol(LNK_Obj *obj, String8 match_name) { - COFF_ParsedSymbol result = {0}; - - COFF_FileHeaderInfo coff_info = coff_file_header_info_from_data(obj->data); - - String8 raw_coff_symbol_table = str8_substr(obj->data, coff_info.symbol_table_range); - String8 raw_coff_string_table = str8_substr(obj->data, coff_info.string_table_range); - COFF_ParsedSymbol symbol; - for (U64 symbol_idx = 0; symbol_idx < coff_info.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { - void *symbol_ptr; - if (coff_info.is_big_obj) { - symbol_ptr = &((COFF_Symbol32 *)raw_coff_symbol_table.str)[symbol_idx]; - symbol = coff_parse_symbol32(raw_coff_string_table, symbol_ptr); - } else { - symbol_ptr = &((COFF_Symbol16 *)raw_coff_symbol_table.str)[symbol_idx]; - symbol = coff_parse_symbol16(raw_coff_string_table, symbol_ptr); - } - - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); if (str8_match(symbol.name, match_name, 0)) { - result = symbol; - break; + return symbol; } } - - return result; + return (COFF_ParsedSymbol){0}; } internal MSCRT_FeatFlags @@ -499,12 +492,19 @@ lnk_obj_get_vol_md(LNK_Obj *obj) return lnk_obj_match_symbol(obj, str8_lit("@vol.md")).value; } +internal LNK_Lib * +lnk_obj_get_lib(LNK_Obj *obj) +{ + return obj->link_member ? obj->link_member->lib : 0; +} + internal String8 lnk_obj_get_lib_path(LNK_Obj *obj) { String8 lib_path = {0}; - if (obj && obj->lib) { - lib_path = obj->lib->path; + if (obj) { + LNK_Lib *lib = lnk_obj_get_lib(obj); + lib_path = lib ? lib->path : str8_zero(); } return lib_path; } @@ -530,12 +530,33 @@ lnk_coff_section_header_from_section_number(LNK_Obj *obj, U64 section_number) return section_header; } +internal COFF_RelocArray +lnk_coff_relocs_from_section_header(LNK_Obj *obj, COFF_SectionHeader *section_header) +{ + COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, section_header); + COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); + COFF_RelocArray result = { .count = reloc_info.count, .v = relocs }; + return result; +} + internal COFF_SectionHeader * lnk_coff_section_table_from_obj(LNK_Obj *obj) { return (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; } +internal String8 +lnk_coff_string_table_from_obj(LNK_Obj *obj) +{ + return str8_substr(obj->data, obj->header.string_table_range); +} + +internal String8 +lnk_coff_symbol_table_from_obj(LNK_Obj *obj) +{ + return str8_substr(obj->data, obj->header.symbol_table_range); +} + internal COFF_RelocArray lnk_coff_reloc_info_from_section_number(LNK_Obj *obj, U64 section_number) { @@ -559,20 +580,6 @@ lnk_try_comdat_props_from_section_number(LNK_Obj *obj, U32 section_number, COFF_ return 0; } -internal B32 -lnk_is_coff_section_debug(LNK_Obj *obj, U64 sect_idx) -{ - String8 string_table = str8_substr(obj->data, obj->header.string_table_range); - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, sect_idx+1); - - String8 full_name = coff_name_from_section_header(string_table, section_header); - String8 name, postfix; - coff_parse_section_name(full_name, &name, &postfix); - - B32 is_debug = str8_match(name, str8_lit(".debug"), 0); - return is_debug; -} - internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff_symbol_idx(LNK_Obj *obj, U64 symbol_idx) { @@ -720,3 +727,47 @@ lnk_directive_info_from_raw_directives(Arena *arena, LNK_Obj *obj, String8List r return directive_info; } +internal CV_DebugS +lnk_debug_s_from_obj(Arena *arena, LNK_Obj *obj) +{ + Temp scratch = scratch_begin(&arena, 1); + + String8List raw_debug_s = {0}; + { + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + String8 string_table = lnk_coff_string_table_from_obj(obj); + for EachIndex(sect_idx, obj->header.section_count_no_null) { + COFF_SectionHeader *section_header = §ion_table[sect_idx]; + String8 section_name = coff_name_from_section_header(string_table, section_header); + if (str8_match(section_name, str8_lit(".debug$S"), 0)) { + String8 debug_s = str8_substr(obj->data, rng_1u64(section_header->foff, section_header->foff + section_header->fsize)); + str8_list_push(scratch.arena, &raw_debug_s, debug_s); + } + } + } + + CV_DebugS debug_s = {0}; + { + for (String8Node *node = raw_debug_s.first; node != 0; node = node->next) { + // parse & merge sub sections + CV_DebugS ds = cv_parse_debug_s(scratch.arena, node->string); + cv_debug_s_concat_in_place(&debug_s, &ds); + + // make sure there is one string table + String8List string_data_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_StringTable); + if (string_data_list.node_count > 1) { + break; + } + + // make sure there is one file checksum table + String8List checksum_data_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms); + if (checksum_data_list.node_count > 1) { + continue; + } + } + } + + scratch_end(scratch); + return debug_s; +} + diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index 78510b80..9fca1cb5 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -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 @@ -7,20 +7,25 @@ typedef struct LNK_Obj { - String8 data; - String8 path; - struct LNK_Lib *lib; - U32 input_idx; - COFF_FileHeaderInfo header; - U32 *comdats; - B8 hotpatch; - U32Node **associated_sections; - LNK_SymbolHashTrie **symlinks; + String8 path; + String8 data; + U32 input_idx; + COFF_FileHeaderInfo header; + U32 *comdats; + B8 hotpatch; + B8 exclude_from_debug_info; + U32Node **associated_sections; + LNK_SymbolHashTrie **symlinks; + + struct LNK_LibMemberRef *link_member; + + struct LNK_ObjNode *node; } LNK_Obj; typedef struct LNK_ObjNode { struct LNK_ObjNode *next; + struct LNK_ObjNode *prev; LNK_Obj data; } LNK_ObjNode; @@ -37,12 +42,6 @@ typedef struct LNK_ObjNodeArray LNK_ObjNode *v; } LNK_ObjNodeArray; -typedef struct LNK_SymbolInputResult -{ - LNK_SymbolList weak_symbols; - LNK_SymbolList undef_symbols; -} LNK_SymbolInputResult; - // --- Directive Parser -------------------------------------------------------- typedef struct LNK_Directive @@ -68,18 +67,16 @@ typedef struct LNK_DirectiveInfo typedef struct { - LNK_InputObj **inputs; - LNK_ObjNodeArray objs; - U64 obj_id_base; - U32 machine; + struct LNK_Input **inputs; + LNK_ObjNode *objs; + U64 obj_id_base; + U32 machine; } LNK_ObjIniter; typedef struct { - LNK_SymbolTable *symtab; - LNK_ObjNodeArray objs; - LNK_SymbolList *weak_lists; - LNK_SymbolList *undef_lists; + LNK_SymbolTable *symtab; + LNK_Obj **objs; } LNK_InputCoffSymbolTable; typedef struct @@ -92,31 +89,38 @@ typedef struct // --- Error ------------------------------------------------------------------- +internal String8 lnk_loc_from_obj(Arena *arena, LNK_Obj *obj); internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...); +internal void lnk_error_input_obj(LNK_ErrorCode code, struct LNK_Input *input, char *fmt, ...); // --- Input ------------------------------------------------------------------- -internal LNK_Obj ** lnk_array_from_obj_list(Arena *arena, LNK_ObjList list); -internal LNK_ObjNodeArray lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *tp_arena, LNK_ObjList *obj_list, COFF_MachineType machine, U64 input_count, LNK_InputObj **inputs); -internal LNK_SymbolInputResult lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs); +internal LNK_Obj ** lnk_array_from_obj_list(Arena *arena, LNK_ObjList list); +internal void lnk_obj_list_push_node_many(LNK_ObjList *list, U64 count, LNK_ObjNode *nodes); +internal void lnk_obj_list_push_node(LNK_ObjList *list, LNK_ObjNode *node); + +internal void lnk_inputer_push_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, U64 objs_count, LNK_ObjNode *objs); // --- Metadata ---------------------------------------------------------------- -internal U32 lnk_obj_get_features(LNK_Obj *obj); -internal U32 lnk_obj_get_comp_id(LNK_Obj *obj); -internal U32 lnk_obj_get_vol_md(LNK_Obj *obj); -internal String8 lnk_obj_get_lib_path(LNK_Obj *obj); -internal U32 lnk_obj_get_removed_section_number(LNK_Obj *obj); -internal LNK_Symbol * lnk_obj_get_comdat_symlink(LNK_Obj *obj, U64 section_number); +internal U32 lnk_obj_get_features(LNK_Obj *obj); +internal U32 lnk_obj_get_comp_id(LNK_Obj *obj); +internal U32 lnk_obj_get_vol_md(LNK_Obj *obj); +internal struct LNK_Lib * lnk_obj_get_lib(LNK_Obj *obj); +internal String8 lnk_obj_get_lib_path(LNK_Obj *obj); +internal U32 lnk_obj_get_removed_section_number(LNK_Obj *obj); +internal LNK_Symbol * lnk_obj_get_comdat_symlink(LNK_Obj *obj, U64 section_number); // --- Symbol & Section Helpers ------------------------------------------------ internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff(LNK_Obj *obj, void *coff_symbol); internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff_symbol_idx(LNK_Obj *obj, U64 symbol_idx); internal COFF_SectionHeader * lnk_coff_section_header_from_section_number(LNK_Obj *obj, U64 section_number); +internal COFF_RelocArray lnk_coff_relocs_from_section_header(LNK_Obj *obj, COFF_SectionHeader *section_header); internal COFF_SectionHeader * lnk_coff_section_table_from_obj(LNK_Obj *obj); +internal String8 lnk_coff_string_table_from_obj(LNK_Obj *obj); +internal String8 lnk_coff_symbol_table_from_obj(LNK_Obj *obj); internal B32 lnk_try_comdat_props_from_section_number(LNK_Obj *obj, U32 section_number, COFF_ComdatSelectType *select_out, U32 *section_number_out, U32 *section_length_out, U32 *check_sum_out); -internal B32 lnk_is_coff_section_debug(LNK_Obj *obj, U64 sect_idx); // --- Helpers ----------------------------------------------------------------- @@ -129,3 +133,7 @@ internal void lnk_parse_msvc_linker_directive(Arena *arena, LNK_Obj internal String8List lnk_raw_directives_from_obj(Arena *arena, LNK_Obj *obj); internal LNK_DirectiveInfo lnk_directive_info_from_raw_directives(Arena *arena, LNK_Obj *obj, String8List raw_directives); +// --- Debug Info -------------------------------------------------------------- + +internal CV_DebugS lnk_debug_s_from_obj(Arena *arena, LNK_Obj *obj); + diff --git a/src/linker/lnk_section_table.c b/src/linker/lnk_section_table.c index dd85728f..7c3a464e 100644 --- a/src/linker/lnk_section_table.c +++ b/src/linker/lnk_section_table.c @@ -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 LNK_SectionContrib * @@ -57,6 +57,20 @@ lnk_array_from_section_contrib_chunk_list(Arena *arena, LNK_SectionContribChunkL return result; } +internal void +lnk_section_list_push_node(LNK_SectionList *list, LNK_SectionNode *node) +{ + DLLPushBack(list->first, list->last, node); + list->count += 1; +} + +internal void +lnk_section_list_remove_node(LNK_SectionList *list, LNK_SectionNode *node) +{ + DLLRemove(list->first, list->last, node); + list->count -= 1; +} + internal LNK_SectionArray lnk_section_array_from_list(Arena *arena, LNK_SectionList list) { @@ -115,7 +129,7 @@ lnk_section_table_push(LNK_SectionTable *sectab, String8 name, COFF_SectionFlags sect->flags = flags; LNK_SectionList *sect_list = §ab->list; - SLLQueuePush(sect_list->first, sect_list->last, sect_node); + DLLPushBack(sect_list->first, sect_list->last, sect_node); sect_list->count += 1; String8 name_with_flags = lnk_make_name_with_flags(sectab->arena, name, flags); @@ -129,47 +143,31 @@ internal LNK_SectionNode * lnk_section_table_remove(LNK_SectionTable *sectab, String8 name) { ProfBeginFunction(); - - // find node LNK_SectionNode *node; for (node = sectab->list.first; node != 0; node = node->next) { if (str8_match(node->data.name, name, 0)) { + lnk_section_list_remove_node(§ab->list, node); break; } } - - // remove node - { - LNK_SectionList *list = §ab->list; - if (list->count > 0) { - if (list->first == node) { - list->first = list->first->next; - list->count -= 1; - - if (list->last == node) { - list->last = 0; - } - } else { - for (LNK_SectionNode *curr = list->first, *prev = 0; curr != 0; prev = curr, curr = curr->next) { - if (curr == node) { - prev->next = curr->next; - list->count -= 1; - - if (list->last == curr) { - list->last = prev; - } - - break; - } - } - } - } - } - ProfEnd(); return node; } +internal void +lnk_section_table_purge(LNK_SectionTable *sectab, String8 name) +{ + Temp scratch = scratch_begin(0,0); + + LNK_SectionNode *node = lnk_section_table_remove(sectab, name); + String8 name_with_flags = lnk_make_name_with_flags(scratch.arena, name, node->data.flags); + KeyValuePair *kv = hash_table_search_string(sectab->sect_ht, name_with_flags); + kv->key_string = str8_zero(); + kv->value_raw = 0; + + scratch_end(scratch); +} + internal LNK_Section * lnk_section_table_search(LNK_SectionTable *sectab, String8 full_or_partial_name, COFF_SectionFlags flags) { @@ -180,8 +178,7 @@ lnk_section_table_search(LNK_SectionTable *sectab, String8 full_or_partial_name, coff_parse_section_name(full_or_partial_name, &name, &postfix); String8 name_with_flags = lnk_make_name_with_flags(scratch.arena, name, flags); - LNK_Section *section = 0; - hash_table_search_string_raw(sectab->sect_ht, name_with_flags, §ion); + LNK_Section *section = hash_table_search_string_raw(sectab->sect_ht, name_with_flags); scratch_end(scratch); return section; @@ -224,7 +221,7 @@ lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_l Temp scratch = scratch_begin(0, 0); for (LNK_MergeDirectiveNode *merge_node = merge_list.first; merge_node != 0; merge_node = merge_node->next) { - LNK_MergeDirective *merge = &merge_node->data; + LNK_MergeDirective *merge = &merge_node->v; // guard against illegal merges { @@ -232,7 +229,7 @@ lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_l str8_lit_comp(".rsrc"), str8_lit_comp(".reloc"), }; - for (U64 i = 0; i < ArrayCount(illegal_merge_sections); i += 1) { + for EachIndex(i, ArrayCount(illegal_merge_sections)) { if (str8_match(merge->src, illegal_merge_sections[i], 0)) { lnk_error(LNK_Error_IllegalSectionMerge, "illegal to merge %S with %S", illegal_merge_sections[i], merge->dst); } @@ -244,20 +241,18 @@ lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_l // guard against circular merges { - if (str8_match(merge_node->data.dst, merge_node->data.src, 0)) { - lnk_error(LNK_Error_CircularMerge, "detected circular /MERGE:%S=%S", merge_node->data.src, merge_node->data.dst); + if (str8_match(merge_node->v.dst, merge_node->v.src, 0)) { + lnk_error(LNK_Error_CircularMerge, "detected circular /MERGE:%S=%S", merge_node->v.src, merge_node->v.dst); } for (LNK_SectionNode *sect_n = sectab->merge_list.first; sect_n != 0; sect_n = sect_n->next) { - if (str8_match(sect_n->data.name, merge_node->data.dst, 0) || - str8_match(sect_n->data.name, merge_node->data.src, 0)) { - lnk_error(LNK_Error_CircularMerge, "detected circular /MERGE:%S=%S", merge_node->data.src, merge_node->data.dst); + if (str8_match(sect_n->data.name, merge_node->v.dst, 0)) { + lnk_error(LNK_Error_CircularMerge, "detected circular /MERGE:%S=%S", merge_node->v.src, merge_node->v.dst); } } } // are we trying to merge section that was already merged? - LNK_Section *merge_sect = 0; - hash_table_search_string_raw(sectab->sect_ht, merge->src, &merge_sect); + LNK_Section *merge_sect = hash_table_search_string_raw(sectab->sect_ht, merge->src); if (merge_sect && merge_sect->merge_dst) { LNK_Section *dst = merge_sect->merge_dst; B32 is_ambiguous_merge = !str8_match(dst->name, merge->dst, 0); @@ -292,7 +287,7 @@ lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_l } } - for (U64 src_idx = 0; src_idx < src_matches.count; src_idx += 1) { + for EachIndex(src_idx, src_matches.count) { LNK_Section *src = src_matches.v[src_idx]; if (src->flags != dst->flags) { @@ -304,18 +299,33 @@ lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_l lnk_section_contrib_chunk_list_concat_in_place(&dst->contribs, &src->contribs); src->merge_dst = dst; - // remove from output section list + // remove node from output section list LNK_SectionNode *merge_node = lnk_section_table_remove(sectab, src->name); // move node to the merge list - SLLQueuePush(sectab->merge_list.first, sectab->merge_list.last, merge_node); - sectab->merge_list.count += 1; + lnk_section_list_push_node(§ab->merge_list, merge_node); } } scratch_end(scratch); ProfEnd(); } +internal U64 +lnk_section_table_total_fsize(LNK_SectionTable *sectab) +{ + U64 total_fsize = 0; + for EachNode(n, LNK_SectionNode, sectab->list.first) { total_fsize += n->data.fsize; } + return total_fsize; +} + +internal U64 +lnk_section_table_total_vsize(LNK_SectionTable *sectab) +{ + U64 total_vsize = 0; + for EachNode(n, LNK_SectionNode, sectab->list.first) { total_vsize += n->data.vsize; } + return total_vsize; +} + internal int lnk_section_contrib_chunk_is_before(void *raw_a, void *raw_b) { @@ -474,3 +484,4 @@ lnk_get_first_section_contrib_voff(COFF_SectionHeader **image_section_table, LNK LNK_SectionContrib *sc = lnk_get_first_section_contrib(sect); return lnk_voff_from_section_contrib(image_section_table, sc); } + diff --git a/src/linker/lnk_section_table.h b/src/linker/lnk_section_table.h index 4f08c3c1..071f7c9c 100644 --- a/src/linker/lnk_section_table.h +++ b/src/linker/lnk_section_table.h @@ -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 @@ -106,9 +106,12 @@ internal LNK_SectionTable * lnk_section_table_alloc(void); internal void lnk_section_table_release(LNK_SectionTable **sectab_ptr); internal LNK_Section * lnk_section_table_push(LNK_SectionTable *sectab, String8 name, COFF_SectionFlags flags); internal LNK_SectionNode * lnk_section_table_remove(LNK_SectionTable *sectab, String8 name); +internal void lnk_section_table_purge(LNK_SectionTable *sectab, String8 name); internal LNK_Section * lnk_section_table_search(LNK_SectionTable *sectab, String8 name, COFF_SectionFlags flags); internal LNK_SectionArray lnk_section_table_search_many(Arena *arena, LNK_SectionTable *sectab, String8 full_or_partial_name); internal void lnk_section_table_merge(LNK_SectionTable *sectab, LNK_MergeDirectiveList merge_list); +internal U64 lnk_section_table_total_fsize(LNK_SectionTable *sectab); +internal U64 lnk_section_table_total_vsize(LNK_SectionTable *sectab); // --- Section Finalization ---------------------------------------------------- diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index 7d5b5dda..dc9f37a0 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -1,47 +1,58 @@ -// Copyright (c) 2025 Epic Games Tools +// Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal LNK_Symbol * -lnk_make_defined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx) +lnk_make_symbol(Arena *arena, String8 name, LNK_Obj *obj, U32 symbol_idx) { - LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1); - symbol->name = name; - symbol->u.defined.obj = obj; - symbol->u.defined.symbol_idx = symbol_idx; + LNK_ObjSymbolRefNode *ref = push_array(arena, LNK_ObjSymbolRefNode, 1); + ref->v.obj = obj; + ref->v.symbol_idx = symbol_idx; + + LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1); + symbol->name = name; + symbol->refs = ref; + return symbol; } -internal LNK_Symbol * -lnk_make_lib_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset) +internal int +lnk_obj_symbol_ref_is_before(void *raw_a, void *raw_b) { - LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1); - symbol->name = name; - symbol->u.lib.lib = lib; - symbol->u.lib.member_offset = member_offset; - return symbol; + LNK_ObjSymbolRef *a_ref = raw_a; + LNK_ObjSymbolRef *b_ref = raw_b; + LNK_Lib *a_lib = lnk_obj_get_lib(a_ref->obj); + LNK_Lib *b_lib = lnk_obj_get_lib(b_ref->obj); + U32 a_lib_input_idx = a_lib ? a_lib->input_idx : 0; + U32 b_lib_input_idx = b_lib ? b_lib->input_idx : 0; + if (a_lib_input_idx == b_lib_input_idx) { + if (a_ref->obj->input_idx == b_ref->obj->input_idx) { + return a_ref->symbol_idx < b_ref->symbol_idx; + } + return a_ref->obj->input_idx < b_ref->obj->input_idx; + } + return a_lib_input_idx < b_lib_input_idx; } -internal LNK_Symbol * -lnk_make_undefined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj) +internal int +lnk_obj_symbol_ref_ptr_is_before(void *raw_a, void *raw_b) { - LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1); - symbol->name = name; - symbol->u.undef.obj = obj; - return symbol; + LNK_ObjSymbolRef **a = raw_a, **b = raw_b; + return lnk_obj_symbol_ref_is_before(*a, *b); } -internal B32 -lnk_symbol_defined_is_before(void *raw_a, void *raw_b) +internal int +lnk_symbol_is_before(void *raw_a, void *raw_b) { LNK_Symbol *a = raw_a, *b = raw_b; - return a->u.defined.obj->input_idx < b->u.defined.obj->input_idx; + LNK_ObjSymbolRef a_ref = lnk_ref_from_symbol(a); + LNK_ObjSymbolRef b_ref = lnk_ref_from_symbol(b); + return lnk_obj_symbol_ref_is_before(&a_ref, &b_ref); } -internal B32 -lnk_symbol_lib_is_before(void *raw_a, void *raw_b) +internal int +lnk_symbol_ptr_is_before(void *raw_a, void *raw_b) { - LNK_Symbol *a = raw_a, *b = raw_b; - return a->u.lib.lib->input_idx < b->u.lib.lib->input_idx; + return lnk_symbol_is_before(*(LNK_Symbol **)raw_a, *(LNK_Symbol **)raw_b); } internal void @@ -60,56 +71,6 @@ lnk_symbol_list_push(Arena *arena, LNK_SymbolList *list, LNK_Symbol *symbol) return node; } -internal void -lnk_symbol_list_concat_in_place(LNK_SymbolList *list, LNK_SymbolList *to_concat) -{ - SLLConcatInPlace(list, to_concat); -} - -internal void -lnk_symbol_concat_in_place_array(LNK_SymbolList *list, LNK_SymbolList *to_concat, U64 to_concat_count) -{ - SLLConcatInPlaceArray(list, to_concat, to_concat_count); -} - -internal LNK_SymbolList -lnk_symbol_list_from_array(Arena *arena, LNK_SymbolArray arr) -{ - LNK_SymbolList list = {0}; - LNK_SymbolNode *node_arr = push_array_no_zero(arena, LNK_SymbolNode, arr.count); - for (U64 i = 0; i < arr.count; i += 1) { - LNK_SymbolNode *node = &node_arr[i]; - node->next = 0; - node->data = &arr.v[i]; - lnk_symbol_list_push_node(&list, node); - } - return list; -} - -internal LNK_SymbolNodeArray -lnk_symbol_node_array_from_list(Arena *arena, LNK_SymbolList list) -{ - LNK_SymbolNodeArray result = {0}; - result.count = 0; - result.v = push_array_no_zero(arena, LNK_SymbolNode *, list.count); - for (LNK_SymbolNode *i = list.first; i != 0; i = i->next, ++result.count) { - result.v[result.count] = i; - } - return result; -} - -internal LNK_SymbolArray -lnk_symbol_array_from_list(Arena *arena, LNK_SymbolList list) -{ - LNK_SymbolArray arr = {0}; - arr.count = 0; - arr.v = push_array_no_zero(arena, LNK_Symbol, list.count); - for (LNK_SymbolNode *node = list.first; node != 0; node = node->next) { - arr.v[arr.count++] = *node->data; - } - return arr; -} - internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_chunk_list_push(Arena *arena, LNK_SymbolHashTrieChunkList *list, U64 cap) { @@ -125,218 +86,287 @@ lnk_symbol_hash_trie_chunk_list_push(Arena *arena, LNK_SymbolHashTrieChunkList * return result; } +internal void +lnk_symbol_hash_trie_chunk_list_concat_in_place(LNK_SymbolHashTrieChunkList *list, LNK_SymbolHashTrieChunkList *to_concat) +{ + SLLConcatInPlace(list, to_concat); +} + internal void lnk_error_multiply_defined_symbol(LNK_Symbol *dst, LNK_Symbol *src) { - lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx); + LNK_ObjSymbolRef dst_ref = lnk_ref_from_symbol(dst); + LNK_ObjSymbolRef src_ref = lnk_ref_from_symbol(src); + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst_ref.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst_ref.symbol_idx, src_ref.obj->path, src_ref.symbol_idx); } internal B32 -lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) +lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) { B32 can_replace = 0; - switch (scope) { - case LNK_SymbolScope_Defined: { - LNK_Obj *dst_obj = dst->u.defined.obj; - LNK_Obj *src_obj = src->u.defined.obj; - COFF_ParsedSymbol dst_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dst->u.defined.obj, dst->u.defined.symbol_idx); - COFF_ParsedSymbol src_parsed = lnk_parsed_symbol_from_coff_symbol_idx(src->u.defined.obj, src->u.defined.symbol_idx); - COFF_SymbolValueInterpType dst_interp = coff_interp_from_parsed_symbol(dst_parsed); - COFF_SymbolValueInterpType src_interp = coff_interp_from_parsed_symbol(src_parsed); - // regular vs abs - if (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs) { + COFF_ParsedSymbol dst_parsed = lnk_parsed_from_symbol(dst); + COFF_ParsedSymbol src_parsed = lnk_parsed_from_symbol(src); + COFF_SymbolValueInterpType dst_interp = lnk_interp_from_symbol(dst); + COFF_SymbolValueInterpType src_interp = lnk_interp_from_symbol(src); + LNK_ObjSymbolRef dst_ref = lnk_ref_from_symbol(dst); + LNK_ObjSymbolRef src_ref = lnk_ref_from_symbol(src); + LNK_Obj *dst_obj = dst_ref.obj; + LNK_Obj *src_obj = src_ref.obj; + + // undefined vs regular + if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Regular) { + can_replace = 1; + } + // (weak vs undefined) or (undefined vs weak) + else if ((dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Undefined) || (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Weak)) { + LNK_Symbol *weak, *undef; + COFF_ParsedSymbol weak_parsed; + if (dst_interp == COFF_SymbolValueInterp_Weak) { + weak = dst, undef = src; + weak_parsed = dst_parsed; + } else { + weak = src, undef = dst; + weak_parsed = src_parsed; + } + + LNK_ObjSymbolRef weak_symbol_ref = lnk_ref_from_symbol(weak); + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(weak_parsed, weak_symbol_ref.obj->header.is_big_obj); + if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { + // NOTE: MSVC does not let a weak symbol to replace an undefined one, + // but LLD links without errors or warnings, meaning undefined symbols + // are resolved to the weak, which can potentially change behaviour of + // the linked image + can_replace = dst_interp == COFF_SymbolValueInterp_Weak; + } else if (weak_ext->characteristics == COFF_WeakExt_NoLibrary) { + can_replace = dst_interp == COFF_SymbolValueInterp_Weak; + } else if (weak_ext->characteristics == COFF_WeakExt_SearchAlias) { + can_replace = dst_interp == COFF_SymbolValueInterp_Undefined; + } else { + can_replace = lnk_symbol_is_before(src, dst); + } + } + // undefined vs undefined + else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Undefined) { + can_replace = lnk_symbol_is_before(src, dst); + } + // undefined vs common + else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Common) { + can_replace = 1; + } + // undefined vs abs + else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Abs) { + can_replace = 1; + } + // undefined vs debug + else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Debug) { + can_replace = 1; + } + // regular/common/abs/debug vs undefined + else if (dst_interp != COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Undefined) { + can_replace = 0; + } + // regular vs abs + else if (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs) { + lnk_error_multiply_defined_symbol(dst, src); + } + // abs vs regular + else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Regular) { + lnk_error_multiply_defined_symbol(dst, src); + } + // abs vs common + else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Common) { + if (lnk_symbol_is_before(dst, src)) { + can_replace = 1; + } else { lnk_error_multiply_defined_symbol(dst, src); } - // abs vs regular - else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Regular) { + } + // common vs abs + else if (dst_interp == COFF_SymbolValueInterp_Common && src_interp == COFF_SymbolValueInterp_Abs) { + if (lnk_symbol_is_before(dst, src)) { lnk_error_multiply_defined_symbol(dst, src); } - // abs vs common - else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Common) { - if (lnk_symbol_defined_is_before(dst, src)) { + } + // abs vs abs + else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Abs) { + lnk_error_multiply_defined_symbol(dst, src); + } + // weak vs weak + else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Weak) { + COFF_SymbolWeakExt *dst_ext = coff_parse_weak_tag(dst_parsed, dst_ref.obj->header.is_big_obj); + COFF_SymbolWeakExt *src_ext = coff_parse_weak_tag(src_parsed, src_ref.obj->header.is_big_obj); + if ((dst_ext->characteristics == COFF_WeakExt_SearchAlias && src_ext->characteristics != COFF_WeakExt_SearchAlias)) { + if (lnk_symbol_is_before(dst, src) || src_ext->characteristics == COFF_WeakExt_AntiDependency) { + can_replace = 0; + } else { + lnk_error_multiply_defined_symbol(dst, src); + } + } else if (dst_ext->characteristics != COFF_WeakExt_SearchAlias && src_ext->characteristics == COFF_WeakExt_SearchAlias) { + if (lnk_symbol_is_before(src, dst) || dst_ext->characteristics == COFF_WeakExt_AntiDependency) { can_replace = 1; } else { lnk_error_multiply_defined_symbol(dst, src); } - } - // common vs abs - else if (dst_interp == COFF_SymbolValueInterp_Common && src_interp == COFF_SymbolValueInterp_Abs) { - if (lnk_symbol_defined_is_before(dst, src)) { - lnk_error_multiply_defined_symbol(dst, src); - } - } - // abs vs abs - else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Abs) { + } else if (dst_ext->characteristics == COFF_WeakExt_SearchAlias && src_ext->characteristics == COFF_WeakExt_SearchAlias) { lnk_error_multiply_defined_symbol(dst, src); + } else { + can_replace = lnk_symbol_is_before(src, dst); } - // weak vs weak - else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Weak) { - can_replace = lnk_symbol_defined_is_before(src, dst); + } + // weak vs regular/abs/common + else if (dst_interp == COFF_SymbolValueInterp_Weak && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Abs || src_interp == COFF_SymbolValueInterp_Common)) { + can_replace = 1; + } + // regular/abs/common vs weak + else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Abs || dst_interp == COFF_SymbolValueInterp_Common) && src_interp == COFF_SymbolValueInterp_Weak) { + can_replace = 0; + } + // regular/common vs regular/common + else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Common) && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common)) { + // parse dst symbol properties + B32 dst_is_comdat = 0; + COFF_ComdatSelectType dst_select; + U32 dst_section_length; + U32 dst_check_sum; + if (dst_interp == COFF_SymbolValueInterp_Regular) { + dst_is_comdat = lnk_try_comdat_props_from_section_number(dst_ref.obj, dst_parsed.section_number, &dst_select, 0, &dst_section_length, &dst_check_sum); + } else if (dst_interp == COFF_SymbolValueInterp_Common) { + dst_select = COFF_ComdatSelect_Largest; + dst_section_length = dst_parsed.value; + dst_check_sum = 0; + dst_is_comdat = 1; } - // weak vs regular/abs/common - else if (dst_interp == COFF_SymbolValueInterp_Weak && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Abs || src_interp == COFF_SymbolValueInterp_Common)) { - can_replace = 1; + + // parse src symbol properties + B32 src_is_comdat = 0; + COFF_ComdatSelectType src_select; + U32 src_section_length, src_checks; + U32 src_check_sum; + if (src_interp == COFF_SymbolValueInterp_Regular) { + src_is_comdat = lnk_try_comdat_props_from_section_number(src_ref.obj, src_parsed.section_number, &src_select, 0, &src_section_length, &src_check_sum); + } else if (src_interp == COFF_SymbolValueInterp_Common) { + src_select = COFF_ComdatSelect_Largest; + src_section_length = src_parsed.value; + src_check_sum = 0; + src_is_comdat = 1; } - // regular/abs/common vs weak - else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Abs || dst_interp == COFF_SymbolValueInterp_Common) && src_interp == COFF_SymbolValueInterp_Weak) { + + // regular non-comdat vs communal + if (dst_interp == COFF_SymbolValueInterp_Regular && !dst_is_comdat && src_interp == COFF_SymbolValueInterp_Common) { can_replace = 0; } - // regular/common vs regular/common - else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Common) && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common)) { - // parse dst symbol properties - B32 dst_is_comdat = 0; - COFF_ComdatSelectType dst_select; - U32 dst_section_length; - U32 dst_check_sum; - if (dst_interp == COFF_SymbolValueInterp_Regular) { - dst_is_comdat = lnk_try_comdat_props_from_section_number(dst->u.defined.obj, dst_parsed.section_number, &dst_select, 0, &dst_section_length, &dst_check_sum); - } else if (dst_interp == COFF_SymbolValueInterp_Common) { - dst_select = COFF_ComdatSelect_Largest; - dst_section_length = dst_parsed.value; - dst_check_sum = 0; - dst_is_comdat = 1; + // communal vs regular non-comdat + else if (dst_interp == COFF_SymbolValueInterp_Common && src_interp == COFF_SymbolValueInterp_Regular && !src_is_comdat) { + can_replace = 1; + } + // handle COMDATs + else if (dst_is_comdat && src_is_comdat) { + if ((src_select == COFF_ComdatSelect_Any && dst_select == COFF_ComdatSelect_Largest)) { + src_select = COFF_ComdatSelect_Largest; + } + if (src_select == COFF_ComdatSelect_Largest && dst_select == COFF_ComdatSelect_Any) { + dst_select = COFF_ComdatSelect_Largest; } - // parse src symbol properties - B32 src_is_comdat = 0; - COFF_ComdatSelectType src_select; - U32 src_section_length, src_checks; - U32 src_check_sum; - if (src_interp == COFF_SymbolValueInterp_Regular) { - src_is_comdat = lnk_try_comdat_props_from_section_number(src->u.defined.obj, src_parsed.section_number, &src_select, 0, &src_section_length, &src_check_sum); - } else if (src_interp == COFF_SymbolValueInterp_Common) { - src_select = COFF_ComdatSelect_Largest; - src_section_length = src_parsed.value; - src_check_sum = 0; - src_is_comdat = 1; - } - - // regular non-comdat vs communal - if (dst_interp == COFF_SymbolValueInterp_Regular && !dst_is_comdat && src_interp == COFF_SymbolValueInterp_Common) { - can_replace = 0; - } - // communal vs regular non-comdat - else if (dst_interp == COFF_SymbolValueInterp_Common && src_interp == COFF_SymbolValueInterp_Regular && !src_is_comdat) { - can_replace = 1; - } - // handle COMDATs - else if (dst_is_comdat && src_is_comdat) { - if ((src_select == COFF_ComdatSelect_Any && dst_select == COFF_ComdatSelect_Largest)) { - src_select = COFF_ComdatSelect_Largest; - } - if (src_select == COFF_ComdatSelect_Largest && dst_select == COFF_ComdatSelect_Any) { - dst_select = COFF_ComdatSelect_Largest; - } - - if (src_select == dst_select) { - switch (src_select) { - case COFF_ComdatSelect_Null: - case COFF_ComdatSelect_Any: { - if (src_section_length == dst_section_length) { - can_replace = lnk_obj_is_before(src_obj, dst_obj); - } else { - // both COMDATs are valid but to get smaller exe pick smallest - can_replace = src_section_length < dst_section_length; - } - } break; - case COFF_ComdatSelect_NoDuplicates: { + if (src_select == dst_select) { + switch (src_select) { + case COFF_ComdatSelect_Null: + case COFF_ComdatSelect_Any: { + can_replace = lnk_obj_is_before(src_obj, dst_obj); + } break; + case COFF_ComdatSelect_NoDuplicates: { + lnk_error_multiply_defined_symbol(dst, src); + } break; + case COFF_ComdatSelect_SameSize: { + if (dst_section_length == src_section_length) { + can_replace = lnk_obj_is_before(src_obj, dst_obj); + } else { lnk_error_multiply_defined_symbol(dst, src); - } break; - case COFF_ComdatSelect_SameSize: { - if (dst_section_length == src_section_length) { - can_replace = lnk_obj_is_before(src_obj, dst_obj); - } else { - lnk_error_multiply_defined_symbol(dst, src); - } - } break; - case COFF_ComdatSelect_ExactMatch: { - COFF_SectionHeader *dst_sect_header = lnk_coff_section_header_from_section_number(dst_obj, dst_parsed.section_number); - COFF_SectionHeader *src_sect_header = lnk_coff_section_header_from_section_number(src_obj, src_parsed.section_number); - String8 dst_data = str8_substr(dst_obj->data, rng_1u64(dst_sect_header->foff, dst_sect_header->foff + dst_sect_header->fsize)); - String8 src_data = str8_substr(src_obj->data, rng_1u64(src_sect_header->foff, src_sect_header->foff + src_sect_header->fsize)); - B32 is_exact_match = 0; - if (dst_check_sum != 0 && src_check_sum != 0) { - is_exact_match = dst_check_sum == src_check_sum && str8_match(dst_data, src_data, 0); - } else { - is_exact_match = str8_match(dst_data, src_data, 0); - } - - if (is_exact_match) { - can_replace = lnk_obj_is_before(src_obj, dst_obj); - } else { - lnk_error_multiply_defined_symbol(dst, src); - } - } break; - case COFF_ComdatSelect_Largest: { - if (dst_section_length == src_section_length) { - can_replace = lnk_obj_is_before(src_obj, dst_obj); - } else { - can_replace = dst_section_length < src_section_length; - } - } break; - case COFF_ComdatSelect_Associative: { /* ignore */ } break; - default: { InvalidPath; } break; } - } else { - lnk_error_obj(LNK_Warning_UnresolvedComdat, src_obj, - "%S: COMDAT selection conflict detected, current selection %S, leader selection %S from %S", - src->name, coff_string_from_comdat_select_type(src_select), coff_string_from_comdat_select_type(dst_select), dst_obj); + } break; + case COFF_ComdatSelect_ExactMatch: { + COFF_SectionHeader *dst_sect_header = lnk_coff_section_header_from_section_number(dst_obj, dst_parsed.section_number); + COFF_SectionHeader *src_sect_header = lnk_coff_section_header_from_section_number(src_obj, src_parsed.section_number); + String8 dst_data = str8_substr(dst_obj->data, rng_1u64(dst_sect_header->foff, dst_sect_header->foff + dst_sect_header->fsize)); + String8 src_data = str8_substr(src_obj->data, rng_1u64(src_sect_header->foff, src_sect_header->foff + src_sect_header->fsize)); + B32 is_exact_match = 0; + if (dst_check_sum != 0 && src_check_sum != 0) { + is_exact_match = dst_check_sum == src_check_sum && str8_match(dst_data, src_data, 0); + } else { + is_exact_match = str8_match(dst_data, src_data, 0); + } + + if (is_exact_match) { + can_replace = lnk_obj_is_before(src_obj, dst_obj); + } else { + lnk_error_multiply_defined_symbol(dst, src); + } + } break; + case COFF_ComdatSelect_Largest: { + if (dst_section_length == src_section_length) { + can_replace = lnk_obj_is_before(src_obj, dst_obj); + } else { + can_replace = dst_section_length < src_section_length; + } + } break; + case COFF_ComdatSelect_Associative: { /* ignore */ } break; + default: { InvalidPath; } break; } } else { - lnk_error_multiply_defined_symbol(dst, src); + lnk_error_obj(LNK_Warning_UnresolvedComdat, src_obj, + "%S: COMDAT selection conflict detected, current selection %S, leader selection %S from %S", + src->name, coff_string_from_comdat_select_type(src_select), coff_string_from_comdat_select_type(dst_select), dst_obj); } } else { - lnk_error(LNK_Error_InvalidPath, "unable to find a suitable replacement logic for symbol combination"); + lnk_error_multiply_defined_symbol(dst, src); } - } break; - case LNK_SymbolScope_Lib: { - // link.exe picks symbol from lib that is discovered first - can_replace = lnk_symbol_lib_is_before(src, dst); - } break; - default: { InvalidPath; } + } else { + lnk_error(LNK_Error_InvalidPath, "unable to find a suitable replacement logic for symbol combination"); } + return can_replace; } internal void -lnk_on_symbol_replace(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) +lnk_on_symbol_replace(LNK_Symbol *dst, LNK_Symbol *src) { - switch (scope) { - case LNK_SymbolScope_Defined: { - COFF_ParsedSymbol dst_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dst->u.defined.obj, dst->u.defined.symbol_idx); - COFF_SymbolValueInterpType dst_interp = coff_interp_from_parsed_symbol(dst_parsed); - if (dst_interp == COFF_SymbolValueInterp_Regular) { - // remove replaced section from the output - COFF_SectionHeader *dst_sect = lnk_coff_section_header_from_section_number(dst->u.defined.obj, dst_parsed.section_number); - dst_sect->flags |= COFF_SectionFlag_LnkRemove; + COFF_ParsedSymbol dst_parsed = lnk_parsed_from_symbol(dst); + COFF_SymbolValueInterpType dst_interp = lnk_interp_from_symbol(dst); + LNK_ObjSymbolRef dst_ref = lnk_ref_from_symbol(dst); - // remove associated sections from the output - for (U32Node *associated_section = dst->u.defined.obj->associated_sections[dst_parsed.section_number]; - associated_section != 0; - associated_section = associated_section->next) { - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(dst->u.defined.obj, associated_section->data); - section_header->flags |= COFF_SectionFlag_LnkRemove; - } - } + if (dst_interp == COFF_SymbolValueInterp_Regular) { + // remove replaced section from the output + COFF_SectionHeader *dst_sect = lnk_coff_section_header_from_section_number(dst_ref.obj, dst_parsed.section_number); + dst_sect->flags |= COFF_SectionFlag_LnkRemove; - // make sure leader section is not removed from the output -#if BUILD_DEBUG - { - COFF_ParsedSymbol src_parsed = lnk_parsed_symbol_from_coff_symbol_idx(src->u.defined.obj, src->u.defined.symbol_idx); - COFF_SymbolValueInterpType src_interp = coff_interp_from_parsed_symbol(src_parsed); - if (src_interp == COFF_SymbolValueInterp_Regular) { - COFF_SectionHeader *src_sect = lnk_coff_section_header_from_section_number(src->u.defined.obj, src_parsed.section_number); - AssertAlways(~src_sect->flags & COFF_SectionFlag_LnkRemove); - } + // remove associated sections from the output + for (U32Node *associated_section = dst_ref.obj->associated_sections[dst_parsed.section_number]; + associated_section != 0; + associated_section = associated_section->next) { + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(dst_ref.obj, associated_section->data); + section_header->flags |= COFF_SectionFlag_LnkRemove; } -#endif - } break; - case LNK_SymbolScope_Lib: { - // nothing to replace - } break; - default: { InvalidPath; } } + + // merge symbol refs + LNK_ObjSymbolRefNode *src_last_ref; + for (src_last_ref = src->refs; src_last_ref->next != 0; src_last_ref = src_last_ref->next); + src_last_ref->next = dst->refs; + + // assert leader section is live +#if BUILD_DEBUG + { + COFF_ParsedSymbol src_parsed = lnk_parsed_from_symbol(src); + COFF_SymbolValueInterpType src_interp = lnk_interp_from_symbol(src); + LNK_ObjSymbolRef src_ref = lnk_ref_from_symbol(src); + + if (src_interp == COFF_SymbolValueInterp_Regular) { + COFF_SectionHeader *src_sect = lnk_coff_section_header_from_section_number(src_ref.obj, src_parsed.section_number); + AssertAlways(~src_sect->flags & COFF_SectionFlag_LnkRemove); + } + } +#endif } internal void @@ -344,7 +374,6 @@ lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunks, LNK_SymbolHashTrie **trie, U64 hash, - LNK_SymbolScope scope, LNK_Symbol *symbol) { LNK_SymbolHashTrie **curr_trie_ptr = trie; @@ -354,7 +383,7 @@ lnk_symbol_hash_trie_insert_or_replace(Arena *arena, if (curr_trie == 0) { // init node - LNK_SymbolHashTrie *new_trie = lnk_symbol_hash_trie_chunk_list_push(arena, chunks, 512); + LNK_SymbolHashTrie *new_trie = lnk_symbol_hash_trie_chunk_list_push(arena, chunks, 0x1000); new_trie->name = &symbol->name; new_trie->symbol = symbol; MemoryZeroArray(new_trie->child); @@ -384,13 +413,13 @@ lnk_symbol_hash_trie_insert_or_replace(Arena *arena, // apply replacement if (leader) { - if (lnk_can_replace_symbol(scope, leader, src)) { + if (lnk_can_replace_symbol(leader, src)) { // discard leader - lnk_on_symbol_replace(scope, leader, src); + lnk_on_symbol_replace(leader, src); leader = src; } else { // discard source - lnk_on_symbol_replace(scope, src, leader); + lnk_on_symbol_replace(src, leader); src = leader; } } else { @@ -439,56 +468,124 @@ lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie) ins_atomic_ptr_eval_assign(&trie->symbol, 0); } -internal U64 -lnk_symbol_hash(String8 string) +internal LNK_SymbolHashTrieChunk ** +lnk_array_from_symbol_hash_trie_chunk_list(Arena *arena, LNK_SymbolHashTrieChunkList *lists, U64 lists_count, U64 *count_out) { - XXH3_state_t hasher; XXH3_64bits_reset(&hasher); - XXH3_64bits_update(&hasher, &string.size, sizeof(string.size)); - XXH3_64bits_update(&hasher, string.str, string.size); - XXH64_hash_t result = XXH3_64bits_digest(&hasher); - return result; + U64 chunks_count = 0; + for EachIndex(i, lists_count) { chunks_count += lists[i].count; } + + LNK_SymbolHashTrieChunk **chunks = push_array(arena, LNK_SymbolHashTrieChunk *, chunks_count); + U64 chunks_cursor = 0; + for EachIndex(i, lists_count) { + for (LNK_SymbolHashTrieChunk *chunk = lists[i].first; chunk != 0; chunk = chunk->next) { + chunks[chunks_cursor++] = chunk; + } + } + + if (count_out) { + *count_out = chunks_count; + } + + return chunks; +} + +internal LNK_ObjSymbolRef +lnk_ref_from_symbol(LNK_Symbol *symbol) +{ + return symbol->refs->v; +} + +internal U64 +lnk_ref_count_from_symbol(LNK_Symbol *symbol) +{ + U64 count = 0; + for (LNK_ObjSymbolRefNode *node = symbol->refs; node != 0; node = node->next, count += 1); + return count; +} + +internal LNK_ObjSymbolRef ** +lnk_ref_from_symbol_many(Arena *arena, LNK_Symbol *symbol, U64 *count_out) +{ + // TODO: would be simpler if we sorted refs on insert/update + U64 refs_count = lnk_ref_count_from_symbol(symbol); + LNK_ObjSymbolRef **refs = push_array(arena, LNK_ObjSymbolRef *, refs_count); + U64 i = 0; + for (LNK_ObjSymbolRefNode *node = symbol->refs; node != 0; node = node->next, i += 1) { + refs[i] = &node->v; + } + radsort(refs, refs_count, lnk_obj_symbol_ref_ptr_is_before); + if (count_out) { + *count_out = refs_count; + } + return refs; +} + +internal COFF_ParsedSymbol +lnk_parsed_from_symbol(LNK_Symbol *symbol) +{ + LNK_ObjSymbolRef ref = lnk_ref_from_symbol(symbol); + return lnk_parsed_symbol_from_coff_symbol_idx(ref.obj, ref.symbol_idx); +} + +internal COFF_SymbolValueInterpType +lnk_interp_from_symbol(LNK_Symbol *symbol) +{ + COFF_ParsedSymbol symbol_parsed = lnk_parsed_from_symbol(symbol); + return coff_interp_from_parsed_symbol(symbol_parsed); +} + +internal U64 +lnk_symbol_table_hasher(String8 string) +{ + return u64_hash_from_str8(string); } internal LNK_SymbolTable * lnk_symbol_table_init(TP_Arena *arena) { - LNK_SymbolTable *symtab = push_array(arena->v[0], LNK_SymbolTable, 1); - symtab->arena = arena; - for (U64 i = 0; i < LNK_SymbolScope_Count; ++i) { - symtab->chunk_lists[i] = push_array(arena->v[0], LNK_SymbolHashTrieChunkList, arena->count); - } + LNK_SymbolTable *symtab = push_array(arena->v[0], LNK_SymbolTable, 1); + symtab->arena = arena; + symtab->chunks = push_array(arena->v[0], LNK_SymbolHashTrieChunkList, arena->count); + symtab->search_chunks = push_array(arena->v[0], LNK_SymbolHashTrieChunkList, arena->count); return symtab; } internal void -lnk_symbol_table_push_(LNK_SymbolTable *symtab, Arena *arena, U64 worker_id, LNK_SymbolScope scope, LNK_Symbol *symbol) +lnk_symbol_table_push_(LNK_SymbolTable *symtab, Arena *arena, U64 worker_id, LNK_Symbol *symbol) { - U64 hash = lnk_symbol_hash(symbol->name); - lnk_symbol_hash_trie_insert_or_replace(arena, &symtab->chunk_lists[scope][worker_id], &symtab->scopes[scope], hash, scope, symbol); + U64 hash = lnk_symbol_table_hasher(symbol->name); + COFF_SymbolValueInterpType interp = lnk_interp_from_symbol(symbol); + LNK_SymbolHashTrieChunkList *chunks; + if (interp == COFF_SymbolValueInterp_Weak || interp == COFF_SymbolValueInterp_Undefined) { + chunks = &symtab->search_chunks[worker_id]; + } else { + chunks = &symtab->chunks[worker_id]; + } + lnk_symbol_hash_trie_insert_or_replace(arena, chunks, &symtab->root, hash, symbol); } internal void -lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_SymbolScope scope, LNK_Symbol *symbol) +lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_Symbol *symbol) { - lnk_symbol_table_push_(symtab, symtab->arena->v[0], 0, scope, symbol); + lnk_symbol_table_push_(symtab, symtab->arena->v[0], 0, symbol); } internal LNK_SymbolHashTrie * -lnk_symbol_table_search_(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name) +lnk_symbol_table_search_(LNK_SymbolTable *symtab, String8 name) { - U64 hash = lnk_symbol_hash(name); - return lnk_symbol_hash_trie_search(symtab->scopes[scope], hash, name); + U64 hash = lnk_symbol_table_hasher(name); + return lnk_symbol_hash_trie_search(symtab->root, hash, name); } internal LNK_Symbol * -lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name) +lnk_symbol_table_search(LNK_SymbolTable *symtab, String8 name) { - LNK_SymbolHashTrie *trie = lnk_symbol_table_search_(symtab, scope, name); + LNK_SymbolHashTrie *trie = lnk_symbol_table_search_(symtab, name); return trie ? trie->symbol : 0; } internal LNK_Symbol * -lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *fmt, ...) +lnk_symbol_table_searchf(LNK_SymbolTable *symtab, char *fmt, ...) { Temp scratch = scratch_begin(0, 0); @@ -496,221 +593,169 @@ lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *f String8 name = push_str8fv(scratch.arena, fmt, args); va_end(args); - LNK_Symbol *symbol = lnk_symbol_table_search(symtab, scope, name); + LNK_Symbol *symbol = lnk_symbol_table_search(symtab, name); scratch_end(scratch); return symbol; } -internal -THREAD_POOL_TASK_FUNC(lnk_check_anti_dependecy_task) -{ - LNK_FinalizeWeakSymbolsTask *task = raw_task; - LNK_SymbolTable *symtab = task->symtab; - LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id]; - - for EachIndex(i, chunk->count) { - LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); - if (symbol_interp == COFF_SymbolValueInterp_Weak) { - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol->u.defined.obj->header.is_big_obj); - if (weak_ext->characteristics == COFF_WeakExt_AntiDependency) { - COFF_ParsedSymbol default_symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, weak_ext->tag_index); - COFF_SymbolValueInterpType default_symbol_interp = coff_interp_from_parsed_symbol(default_symbol_parsed); - - COFF_SymbolValueInterpType actual_default_symbol_interp = default_symbol_interp; - if (default_symbol_interp == COFF_SymbolValueInterp_Undefined) { - LNK_Symbol *actual_default_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, default_symbol_parsed.name); - if (actual_default_symbol) { - COFF_ParsedSymbol actual_default_symbol_parsed = lnk_parsed_symbol_from_defined(actual_default_symbol); - actual_default_symbol_interp = coff_interp_from_parsed_symbol(actual_default_symbol_parsed); - } - } - - if (actual_default_symbol_interp == COFF_SymbolValueInterp_Weak) { - LNK_SymbolNode *symbol_n = push_array(arena, LNK_SymbolNode, 1); - symbol_n->data = symbol; - lnk_symbol_list_push_node(&task->anti_dependency_symbols[task_id], symbol_n); - } - } - } - } -} - -internal -THREAD_POOL_TASK_FUNC(lnk_finalize_weak_symbols_task) -{ - Temp scratch = scratch_begin(&arena,1); - - LNK_FinalizeWeakSymbolsTask *task = raw_task; - LNK_SymbolTable *symtab = task->symtab; - LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id]; - - for EachIndex(i, chunk->count) { - LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); - COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); - if (symbol_interp == COFF_SymbolValueInterp_Weak) { - struct LookupLocation { struct LookupLocation *next; LNK_SymbolDefined symbol; B32 is_anti_dependency; }; - struct LookupLocation *lookup_first = 0, *lookup_last = 0; - - LNK_SymbolDefined current_symbol = symbol->u.defined; - for (;;) { - // guard against self-referencing weak symbols - struct LookupLocation *was_visited = 0; - for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { - if (MemoryCompare(&l->symbol, ¤t_symbol, sizeof(LNK_SymbolDefined)) == 0) { was_visited = l; break; } - } - if (was_visited) { - Temp temp = temp_begin(scratch.arena); - - String8List ref_list = {0}; - for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { - COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->symbol.obj, l->symbol.symbol_idx); - str8_list_pushf(temp.arena, &ref_list, "\t%S Symbol %S (No. %#x) =>", l->symbol.obj->path, loc_symbol.name, l->symbol.symbol_idx); - } - COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(lookup_first->symbol.obj, lookup_first->symbol.symbol_idx); - str8_list_pushf(temp.arena, &ref_list, "\t%S Symbol %S (No. %#x)", lookup_first->symbol.obj->path, loc_symbol.name, lookup_first->symbol.symbol_idx); - - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); - String8 loc_string = str8_list_join(temp.arena, &ref_list, &(StringJoin){ .sep = str8_lit("\n") }); - lnk_error_obj(LNK_Error_WeakCycle, symbol->u.defined.obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", parsed_symbol.name, loc_string); - - MemoryZeroStruct(¤t_symbol); - - temp_end(temp); - break; - } - - COFF_ParsedSymbol current_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, current_symbol.symbol_idx); - COFF_SymbolValueInterpType current_interp = coff_interp_symbol(current_parsed.section_number, current_parsed.value, current_parsed.storage_class); - if (current_interp == COFF_SymbolValueInterp_Weak) { - // record visited symbol - struct LookupLocation *loc = push_array(scratch.arena, struct LookupLocation, 1); - loc->symbol = current_symbol; - SLLQueuePush(lookup_first, lookup_last, loc); - - // does weak symbol have a definition? - LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, current_parsed.name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn_symbol->u.defined.obj, defn_symbol->u.defined.symbol_idx); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class); - if (defn_interp != COFF_SymbolValueInterp_Weak) { - current_symbol = defn_symbol->u.defined; - break; - } - - // no definition fallback to the tag - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(current_parsed, current_symbol.obj->header.is_big_obj); - COFF_ParsedSymbol tag_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, weak_ext->tag_index); - COFF_SymbolValueInterpType tag_interp = coff_interp_symbol(tag_parsed.section_number, tag_parsed.value, tag_parsed.storage_class); - current_symbol = (LNK_SymbolDefined){ .obj = current_symbol.obj, .symbol_idx = weak_ext->tag_index }; - } else if (current_interp == COFF_SymbolValueInterp_Undefined) { - LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, current_parsed.name); - if (defn_symbol == 0) { - MemoryZeroStruct(¤t_symbol); - break; - } - current_symbol = defn_symbol->u.defined; - } else { - break; - } - } - - // replace weak symbol with it's tag - symbol->u.defined = current_symbol; - } - } - - scratch_end(scratch); -} - -internal void -lnk_finalize_weak_symbols(TP_Arena *arena, TP_Context *tp, LNK_SymbolTable *symtab) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(arena->v, arena->count); - - U64 chunks_count = 0; - for EachIndex(worker_id, tp->worker_count) { chunks_count += symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].count; } - - LNK_SymbolHashTrieChunk **chunks = push_array(scratch.arena, LNK_SymbolHashTrieChunk *, chunks_count); - U64 chunks_cursor = 0; - for EachIndex(worker_id, tp->worker_count) { - for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; chunk != 0; chunk = chunk->next) { - chunks[chunks_cursor++] = chunk; - } - } - - LNK_FinalizeWeakSymbolsTask task = { .symtab = symtab, .chunks = chunks }; - - { - TP_Temp temp = tp_temp_begin(arena); - task.anti_dependency_symbols = push_array(scratch.arena, LNK_SymbolList, tp->worker_count); - tp_for_parallel(tp, arena, chunks_count, lnk_check_anti_dependecy_task, &task); - - LNK_SymbolList anti_dependency_symbol_list = {0}; - lnk_symbol_concat_in_place_array(&anti_dependency_symbol_list, task.anti_dependency_symbols, tp->worker_count); - LNK_SymbolArray anti_dependency_symbols = lnk_symbol_array_from_list(scratch.arena, anti_dependency_symbol_list); - radsort(anti_dependency_symbols.v, anti_dependency_symbols.count, lnk_symbol_defined_is_before); - - for EachIndex(symbol_idx, anti_dependency_symbols.count) { - LNK_Symbol *s = &anti_dependency_symbols.v[symbol_idx]; - lnk_error_obj(LNK_Error_UnresolvedSymbol, s->u.defined.obj, "unresolved symbol %S", s->name); - } - - tp_temp_end(temp); - } - - tp_for_parallel(tp, 0, chunks_count, lnk_finalize_weak_symbols_task, &task); - - scratch_end(scratch); - ProfEnd(); -} - internal ISectOff lnk_sc_from_symbol(LNK_Symbol *symbol) { - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); - - ISectOff sc = {0}; - sc.isect = parsed_symbol.section_number; - sc.off = parsed_symbol.value; - + COFF_ParsedSymbol parsed_symbol = lnk_parsed_from_symbol(symbol); + ISectOff sc = { .isect = parsed_symbol.section_number, .off = parsed_symbol.value }; return sc; } internal U64 -lnk_isect_from_symbol(LNK_Symbol *symbol) -{ - return lnk_sc_from_symbol(symbol).isect; -} - -internal U64 -lnk_sect_off_from_symbol(LNK_Symbol *symbol) -{ - return lnk_sc_from_symbol(symbol).off; -} - -internal U64 -lnk_virt_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol) +lnk_voff_from_symbol(COFF_SectionHeader **image_section_table, LNK_Symbol *symbol) { ISectOff sc = lnk_sc_from_symbol(symbol); - U64 voff = section_table[sc.isect]->voff + sc.off; + U64 voff = image_section_table[sc.isect]->voff + sc.off; return voff; } internal U64 -lnk_file_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol) +lnk_foff_from_symbol(COFF_SectionHeader **image_section_table, LNK_Symbol *symbol) { ISectOff sc = lnk_sc_from_symbol(symbol); - U64 foff = section_table[sc.isect]->foff + sc.off; + U64 foff = image_section_table[sc.isect]->foff + sc.off; return foff; } -internal COFF_ParsedSymbol -lnk_parsed_symbol_from_defined(LNK_Symbol *symbol) +internal B32 +lnk_resolve_weak_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymbolRef *resolved_symbol_out) { - return lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + Temp scratch = scratch_begin(0,0); + + B32 is_resolved = 0; + + struct S { struct S *next; LNK_ObjSymbolRef symbol; B32 is_anti_dep; }; + struct S *sf = 0, *sl = 0; + + LNK_ObjSymbolRef current_symbol = symbol; + for (;;) { + // guard against self-referencing weak symbols + struct S *was_visited = 0; + for (struct S *s = sf; s != 0; s = s->next) { + if (MemoryCompare(&s->symbol, ¤t_symbol, sizeof(LNK_ObjSymbolRef)) == 0) { was_visited = s; break; } + } + if (was_visited) { + String8List chain = {0}; + for (struct S *s = sf; s != 0; s = s->next) { + COFF_ParsedSymbol s_parsed = lnk_parsed_symbol_from_coff_symbol_idx(s->symbol.obj, s->symbol.symbol_idx); + str8_list_pushf(scratch.arena, &chain, "\t%S Symbol %S (No. %#x) =>", s->symbol.obj->path, s_parsed.name, s->symbol.symbol_idx); + } + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol.obj, symbol.symbol_idx); + str8_list_pushf(scratch.arena, &chain, "\t%S Symbol %S (No. %#x)", sf->symbol.obj->path, symbol_parsed.name, sf->symbol.symbol_idx); + + String8 chain_string = str8_list_join(scratch.arena, &chain, &(StringJoin){ .sep = str8_lit("\n") }); + lnk_error_obj(LNK_Error_WeakCycle, symbol.obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", symbol_parsed.name, chain_string); + + goto exit; + } + + COFF_ParsedSymbol current_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, current_symbol.symbol_idx); + COFF_SymbolValueInterpType current_interp = coff_interp_symbol(current_parsed.section_number, current_parsed.value, current_parsed.storage_class); + if (current_interp == COFF_SymbolValueInterp_Weak) { + // record visited symbol + struct S *s = push_array(scratch.arena, struct S, 1); + s->symbol = current_symbol; + SLLQueuePush(sf, sl, s); + + // does weak symbol have a definition? + LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, current_parsed.name); + COFF_ParsedSymbol defn_parsed = lnk_parsed_from_symbol(defn_symbol); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class); + if (defn_interp != COFF_SymbolValueInterp_Weak) { + current_symbol = lnk_ref_from_symbol(defn_symbol); + break; + } + + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(current_parsed, current_symbol.obj->header.is_big_obj); + + // no definition -- fallback to default symbol + COFF_ParsedSymbol tag_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, weak_ext->tag_index); + COFF_SymbolValueInterpType tag_interp = coff_interp_symbol(tag_parsed.section_number, tag_parsed.value, tag_parsed.storage_class); + current_symbol = (LNK_ObjSymbolRef){ .obj = current_symbol.obj, .symbol_idx = weak_ext->tag_index }; + + if (weak_ext->characteristics == COFF_WeakExt_AntiDependency) { + if (tag_interp == COFF_SymbolValueInterp_Undefined || tag_interp == COFF_SymbolValueInterp_Weak) { + LNK_Symbol *dep_symbol = lnk_symbol_table_search(symtab, tag_parsed.name); + tag_interp = lnk_interp_from_symbol(dep_symbol); + } + if (tag_interp == COFF_SymbolValueInterp_Weak) { break; } + } + } else if (current_interp == COFF_SymbolValueInterp_Undefined) { + LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, current_parsed.name); + COFF_SymbolValueInterpType defn_interp = lnk_interp_from_symbol(defn_symbol); + + // unresolved undefined symbol + if (defn_interp == COFF_SymbolValueInterp_Undefined) { break; } + + // follow symbol definition + current_symbol = lnk_ref_from_symbol(defn_symbol); + } else { break; } + } + + if (resolved_symbol_out) { + *resolved_symbol_out = current_symbol; + } + is_resolved = 1; + +exit:; + scratch_end(scratch); + return is_resolved; +} + +internal +THREAD_POOL_TASK_FUNC(lnk_replace_weak_with_default_symbol_task) +{ + LNK_SymbolTable *symtab = raw_task; + for EachNode(c, LNK_SymbolHashTrieChunk, symtab->search_chunks[task_id].first) { + for EachIndex(i, c->count) { + LNK_Symbol *symbol = c->v[i].symbol; + LNK_ObjSymbolRef symbol_ref = lnk_ref_from_symbol(symbol); + COFF_ParsedSymbol symbol_parsed = lnk_parsed_from_symbol(symbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); + if (symbol_interp == COFF_SymbolValueInterp_Weak) { + LNK_ObjSymbolRef resolve = {0}; + if (lnk_resolve_weak_symbol(symtab, symbol_ref, &resolve)) { + COFF_ParsedSymbol resolve_parsed = lnk_parsed_symbol_from_coff_symbol_idx(resolve.obj, resolve.symbol_idx); + COFF_SymbolValueInterpType resolve_interp = coff_interp_from_parsed_symbol(resolve_parsed); + if (resolve_interp == COFF_SymbolValueInterp_Weak) { + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(resolve_parsed, symbol_ref.obj->header.is_big_obj); + if (symbol_ref.obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol_parsed.raw_symbol; + symbol32->section_number = COFF_Symbol_UndefinedSection; + symbol32->value = 0; + symbol32->storage_class = COFF_SymStorageClass_External; + } else { + COFF_Symbol16 *symbol16 = symbol_parsed.raw_symbol; + symbol16->section_number = COFF_Symbol_UndefinedSection; + symbol16->value = 0; + symbol16->storage_class = COFF_SymStorageClass_External; + } + } else { + symbol->refs->v = resolve; + } + } + } + } + } +} + +internal void +lnk_replace_weak_with_default_symbols(TP_Context *tp, LNK_SymbolTable *symtab) +{ + ProfBeginFunction(); + + tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_replace_weak_with_default_symbol_task, symtab, "Replace Weak With Default Symbols"); + + for EachIndex(i, tp->worker_count) { + lnk_symbol_hash_trie_chunk_list_concat_in_place(&symtab->chunks[i], &symtab->search_chunks[i]); + } + + ProfEnd(); } diff --git a/src/linker/lnk_symbol_table.h b/src/linker/lnk_symbol_table.h index f6fed011..7ca55350 100644 --- a/src/linker/lnk_symbol_table.h +++ b/src/linker/lnk_symbol_table.h @@ -1,42 +1,26 @@ -// Copyright (c) 2025 Epic Games Tools +// Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #pragma once // --- Symbol ------------------------------------------------------------------ -typedef enum -{ - LNK_SymbolScope_Defined, - LNK_SymbolScope_Lib, - LNK_SymbolScope_Count -} LNK_SymbolScope; - -typedef struct LNK_SymbolDefined +typedef struct LNK_ObjSymbolRef { struct LNK_Obj *obj; U32 symbol_idx; -} LNK_SymbolDefined; +} LNK_ObjSymbolRef; -typedef struct LNK_SymbolLib +typedef struct LNK_ObjSymbolRefNode { - struct LNK_Lib *lib; - U64 member_offset; -} LNK_SymbolLib; - -typedef struct LNK_SymbolUndefined -{ - struct LNK_Obj *obj; -} LNK_SymbolUndefined; + struct LNK_ObjSymbolRefNode *next; + LNK_ObjSymbolRef v; +} LNK_ObjSymbolRefNode; typedef struct LNK_Symbol { - String8 name; - union { - LNK_SymbolDefined defined; - LNK_SymbolLib lib; - LNK_SymbolUndefined undef; - } u; + String8 name; + LNK_ObjSymbolRefNode *refs; } LNK_Symbol; // --- Symbol Containers ------------------------------------------------------- @@ -54,12 +38,6 @@ typedef struct LNK_SymbolList LNK_SymbolNode *last; } LNK_SymbolList; -typedef struct LNK_SymbolNodeArray -{ - U64 count; - LNK_SymbolNode **v; -} LNK_SymbolNodeArray; - typedef struct LNK_SymbolArray { U64 count; @@ -95,59 +73,67 @@ typedef struct LNK_SymbolHashTrieChunkList typedef struct LNK_SymbolTable { TP_Arena *arena; - LNK_SymbolHashTrie *scopes[LNK_SymbolScope_Count]; - LNK_SymbolHashTrieChunkList *chunk_lists[LNK_SymbolScope_Count]; + LNK_SymbolHashTrie *root; + LNK_SymbolHashTrieChunkList *chunks; + LNK_SymbolHashTrieChunkList *search_chunks; } LNK_SymbolTable; -// --- Workers Contensts ------------------------------------------------------- +// --- Workers Contexts -------------------------------------------------------- typedef struct { LNK_SymbolTable *symtab; LNK_SymbolHashTrieChunk **chunks; - LNK_SymbolList *anti_dependency_symbols; -} LNK_FinalizeWeakSymbolsTask; +} LNK_ReplaceWeakSymbolsWithDefaultSymbolTask; -// --- Symbol Make ------------------------------------------------------------- +// --- Symbol ----------------------------------------------------------------- -internal LNK_Symbol * lnk_make_defined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx); -internal LNK_Symbol * lnk_make_lib_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset); -internal LNK_Symbol * lnk_make_undefined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj); +internal LNK_Symbol * lnk_make_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx); + +internal int lnk_obj_symbol_ref_is_before(void *raw_a, void *raw_b); +internal int lnk_obj_symbol_ref_ptr_is_before(void *raw_a, void *raw_b); +internal int lnk_symbol_is_before(void *raw_a, void *raw_b); +internal int lnk_symbol_ptr_is_before(void *raw_a, void *raw_b); // --- Symbol Containers ------------------------------------------------------ -internal void lnk_symbol_list_push_node(LNK_SymbolList *list, LNK_SymbolNode *node); -internal LNK_SymbolNode * lnk_symbol_list_push(Arena *arena, LNK_SymbolList *list, LNK_Symbol *symbol); -internal void lnk_symbol_list_concat_in_place(LNK_SymbolList *list, LNK_SymbolList *to_concat); -internal void lnk_symbol_concat_in_place_array(LNK_SymbolList *list, LNK_SymbolList *to_concat, U64 to_concat_count); -internal LNK_SymbolList lnk_symbol_list_from_array(Arena *arena, LNK_SymbolArray arr); -internal LNK_SymbolNodeArray lnk_symbol_node_array_from_list(Arena *arena, LNK_SymbolList list); -internal LNK_SymbolArray lnk_symbol_array_from_list(Arena *arena, LNK_SymbolList list); +internal void lnk_symbol_list_push_node(LNK_SymbolList *list, LNK_SymbolNode *node); +internal LNK_SymbolNode * lnk_symbol_list_push(Arena *arena, LNK_SymbolList *list, LNK_Symbol *symbol); // --- Symbol Hash Trie -------------------------------------------------------- -internal void lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunks, LNK_SymbolHashTrie **trie, U64 hash, LNK_SymbolScope scope, LNK_Symbol *symbol); -internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_search(LNK_SymbolHashTrie *trie, U64 hash, String8 name); -internal void lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie); +internal LNK_SymbolHashTrie * lnk_symbol_hash_tire_chunk_list_push(Arena *arena, LNK_SymbolHashTrieChunkList *list, U64 cap); +internal void lnk_symbol_hash_trie_chunk_list_concat_in_place(LNK_SymbolHashTrieChunkList *list, LNK_SymbolHashTrieChunkList *to_concat); +internal void lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunks, LNK_SymbolHashTrie **trie, U64 hash, LNK_Symbol *symbol); +internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_search(LNK_SymbolHashTrie *trie, U64 hash, String8 name); +internal void lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie); +internal LNK_SymbolHashTrieChunk ** lnk_array_from_symbol_hash_trie_chunk_list(Arena *arena, LNK_SymbolHashTrieChunkList *lists, U64 lists_count, U64 *count_out); + +// --- Symbol Helpers ---------------------------------------------------------- + +internal LNK_ObjSymbolRef lnk_ref_from_symbol(LNK_Symbol *symbol); +internal U64 lnk_ref_count_from_symbol(LNK_Symbol *symbol); +internal COFF_ParsedSymbol lnk_parsed_from_symbol(LNK_Symbol *symbol); +internal COFF_SymbolValueInterpType lnk_interp_from_symbol(LNK_Symbol *symbol); // --- Symbol Table ------------------------------------------------------------ -internal U64 lnk_symbol_hash(String8 string); +internal U64 lnk_symbol_table_hasher(String8 string); internal LNK_SymbolTable * lnk_symbol_table_init(TP_Arena *arena); -internal void lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_SymbolScope scope, LNK_Symbol *symbol); -internal LNK_Symbol * lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name); -internal LNK_Symbol * lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *fmt, ...); - -internal void lnk_finalize_weak_symbols(TP_Arena *arena, TP_Context *tp, LNK_SymbolTable *symtab); +internal void lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_Symbol *symbol); +internal LNK_Symbol * lnk_symbol_table_search(LNK_SymbolTable *symtab, String8 name); +internal LNK_Symbol * lnk_symbol_table_searchf(LNK_SymbolTable *symtab, char *fmt, ...); // --- Symbol Contrib Helpers -------------------------------------------------- internal ISectOff lnk_sc_from_symbol(LNK_Symbol *symbol); -internal U64 lnk_isect_from_symbol(LNK_Symbol *symbol); -internal U64 lnk_sect_off_from_symbol(LNK_Symbol *symbol); -internal U64 lnk_virt_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol); -internal U64 lnk_file_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol); +internal U64 lnk_voff_from_symbol(COFF_SectionHeader **image_section_table, LNK_Symbol *symbol); +internal U64 lnk_foff_from_symbol(COFF_SectionHeader **image_section_table, LNK_Symbol *symbol); -internal COFF_ParsedSymbol lnk_parsed_symbol_from_defined(LNK_Symbol *symbol); +// --- Weak Symbol ------------------------------------------------------------- + +internal B32 lnk_resolve_weak_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymbolRef *resolved_symbol_out); + +internal void lnk_replace_weak_with_default_symbols(TP_Context *tp, LNK_SymbolTable *symtab); diff --git a/src/linker/lnk_timer.c b/src/linker/lnk_timer.c index 51460d58..4ce4522e 100644 --- a/src/linker/lnk_timer.c +++ b/src/linker/lnk_timer.c @@ -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/) global LNK_Timer g_timers[LNK_Timer_Count]; diff --git a/src/linker/lnk_timer.h b/src/linker/lnk_timer.h index ade95ae2..9e586454 100644 --- a/src/linker/lnk_timer.h +++ b/src/linker/lnk_timer.h @@ -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 diff --git a/src/linker/pdb_ext/msf_builder.c b/src/linker/pdb_ext/msf_builder.c index 5a1cf862..b0e9bfc2 100644 --- a/src/linker/pdb_ext/msf_builder.c +++ b/src/linker/pdb_ext/msf_builder.c @@ -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 diff --git a/src/linker/pdb_ext/msf_builder.h b/src/linker/pdb_ext/msf_builder.h index a2d10322..e39ef838 100644 --- a/src/linker/pdb_ext/msf_builder.h +++ b/src/linker/pdb_ext/msf_builder.h @@ -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 diff --git a/src/linker/pdb_ext/pdb.c b/src/linker/pdb_ext/pdb.c index 3ddd666f..9ffadf35 100644 --- a/src/linker/pdb_ext/pdb.c +++ b/src/linker/pdb_ext/pdb.c @@ -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 diff --git a/src/linker/pdb_ext/pdb.h b/src/linker/pdb_ext/pdb.h index 6b31f4ee..87c2c5c3 100644 --- a/src/linker/pdb_ext/pdb.h +++ b/src/linker/pdb_ext/pdb.h @@ -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 diff --git a/src/linker/pdb_ext/pdb_builder.c b/src/linker/pdb_ext/pdb_builder.c index 103a2b4c..153bc759 100644 --- a/src/linker/pdb_ext/pdb_builder.c +++ b/src/linker/pdb_ext/pdb_builder.c @@ -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/) //////////////////////////////// @@ -345,6 +345,7 @@ pdb_hash_table_get_present_keys_and_values(Arena *arena, PDB_HashTable *ht, Stri //////////////////////////////// +internal PDB_HASH_TABLE_UNPACK_FUNC(pdb_named_stream_ht_unpack) { Assert(!ud); @@ -366,6 +367,7 @@ PDB_HASH_TABLE_UNPACK_FUNC(pdb_named_stream_ht_unpack) return 0; } +internal PDB_HASH_TABLE_UNPACK_FUNC(pdb_hash_adj_ht_unpack) { Assert(local_data.size == 0); @@ -391,6 +393,7 @@ PDB_HASH_TABLE_UNPACK_FUNC(pdb_hash_adj_ht_unpack) return 0; } +internal PDB_HASH_TABLE_UNPACK_FUNC(pdb_src_header_block_ht_unpack) { if (*key_value_cursor + sizeof(PDB_StringOffset) > key_value_data.size) { @@ -414,6 +417,7 @@ PDB_HASH_TABLE_UNPACK_FUNC(pdb_src_header_block_ht_unpack) return 0; } +internal PDB_HASH_TABLE_PACK_FUNC(pdb_named_stream_ht_pack) { Assert(!ud); @@ -427,6 +431,7 @@ PDB_HASH_TABLE_PACK_FUNC(pdb_named_stream_ht_pack) str8_serial_push_string(arena, key_value_srl, value); } +internal PDB_HASH_TABLE_PACK_FUNC(pdb_hash_adj_ht_pack) { Assert(value.size == sizeof(CV_TypeIndex)); @@ -443,6 +448,7 @@ PDB_HASH_TABLE_PACK_FUNC(pdb_hash_adj_ht_pack) str8_serial_push_string(arena, key_value_srl, value); } +internal PDB_HASH_TABLE_PACK_FUNC(pdb_src_header_block_ht_pack) { Assert(value.size == sizeof(PDB_SrcHeaderBlockEntry)); diff --git a/src/linker/pdb_ext/pdb_builder.h b/src/linker/pdb_ext/pdb_builder.h index 8ca8ed93..f682e31e 100644 --- a/src/linker/pdb_ext/pdb_builder.h +++ b/src/linker/pdb_ext/pdb_builder.h @@ -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 diff --git a/src/linker/pdb_ext/pdb_helpers.c b/src/linker/pdb_ext/pdb_helpers.c index 0f8dcc28..8bdef67e 100644 --- a/src/linker/pdb_ext/pdb_helpers.c +++ b/src/linker/pdb_ext/pdb_helpers.c @@ -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 diff --git a/src/linker/pdb_ext/pdb_helpers.h b/src/linker/pdb_ext/pdb_helpers.h index 7ba2d47b..007eb64f 100644 --- a/src/linker/pdb_ext/pdb_helpers.h +++ b/src/linker/pdb_ext/pdb_helpers.h @@ -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 diff --git a/src/linker/rdi/rdi_builder.c b/src/linker/rdi/rdi_builder.c index 623f0a9f..91a31d7a 100644 --- a/src/linker/rdi/rdi_builder.c +++ b/src/linker/rdi/rdi_builder.c @@ -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 RDIB_DataModel @@ -2201,11 +2201,13 @@ rdib_string_map_insert_name_map_item(Arena *arena, RDIB_CollectStringsTask *task rdib_string_map_insert_item(arena, task, task_id, string, node); } +internal RDIB_STRING_MAP_UPDATE_FUNC(rdib_string_map_update_null) { // null update } +internal RDIB_STRING_MAP_UPDATE_FUNC(rdib_string_map_update_concat_void_list_atomic) { node->next = ins_atomic_ptr_eval_assign(head, node); @@ -3549,6 +3551,7 @@ rdib_data_from_vmap(Arena *arena, U64 range_count, RDIB_VMapRange *ranges) return raw_vmap; } +internal THREAD_POOL_TASK_FUNC(rdib_fill_scope_vmaps_task) { ProfBeginFunction(); diff --git a/src/linker/rdi/rdi_builder.h b/src/linker/rdi/rdi_builder.h index 50bb1965..8afb0122 100644 --- a/src/linker/rdi/rdi_builder.h +++ b/src/linker/rdi/rdi_builder.h @@ -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 diff --git a/src/linker/rdi/rdi_coff.c b/src/linker/rdi/rdi_coff.c index 57072743..4db328bc 100644 --- a/src/linker/rdi/rdi_coff.c +++ b/src/linker/rdi/rdi_coff.c @@ -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 RDI_Arch diff --git a/src/linker/rdi/rdi_coff.h b/src/linker/rdi/rdi_coff.h index c68e4dc5..caefb833 100644 --- a/src/linker/rdi/rdi_coff.h +++ b/src/linker/rdi/rdi_coff.h @@ -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 diff --git a/src/linker/rdi/rdi_cv.c b/src/linker/rdi/rdi_cv.c index 0f8e9de2..e5f31251 100644 --- a/src/linker/rdi/rdi_cv.c +++ b/src/linker/rdi/rdi_cv.c @@ -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 RDI_Arch diff --git a/src/linker/rdi/rdi_cv.h b/src/linker/rdi/rdi_cv.h index fb6ca4da..98aa8244 100644 --- a/src/linker/rdi/rdi_cv.h +++ b/src/linker/rdi/rdi_cv.h @@ -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 diff --git a/src/linker/scripts/obj_paths_from_pdb.py b/src/linker/scripts/obj_paths_from_pdb.py index da7d2ab3..2bd11b50 100644 --- a/src/linker/scripts/obj_paths_from_pdb.py +++ b/src/linker/scripts/obj_paths_from_pdb.py @@ -5,12 +5,12 @@ import os def get_sorted_objs(pdb_path): result = subprocess.run(["llvm-pdbutil", "dump", "--modules", pdb_path], stdout=subprocess.PIPE, text=True) lines = result.stdout.strip().split('\n') - filtered_lines = [line for line in lines if line.startswith("Mod ")] + filtered_lines = [line for line in lines if line.lstrip().startswith("Mod ")] # sort by the obj_path portion (line format: "Mod ") def extract_path(line): return line.split(maxsplit=2)[2].lower() - sorted_lines = sorted(filtered_lines, key=extract_path) - return sorted_lines + #sorted_lines = sorted(filtered_lines, key=extract_path) + return filtered_lines if __name__ == "__main__": sorted_objs = get_sorted_objs(sys.argv[1]) - for l in sorted_objs: print(l) + for l in sorted_objs: print(l.lstrip()) diff --git a/src/linker/thread_pool/thread_pool.c b/src/linker/thread_pool/thread_pool.c index a99c821e..da2f7814 100644 --- a/src/linker/thread_pool/thread_pool.c +++ b/src/linker/thread_pool/thread_pool.c @@ -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 void @@ -63,9 +63,9 @@ tp_alloc(Arena *arena, U32 worker_count, U32 max_worker_count, String8 name) B32 is_shared = (name.size > 0); // alloc semaphores - OS_Handle main_semaphore = {0}; - OS_Handle task_semaphore = {0}; - OS_Handle exec_semaphore = {0}; + Semaphore main_semaphore = {0}; + Semaphore task_semaphore = {0}; + Semaphore exec_semaphore = {0}; if (worker_count > 1) { main_semaphore = os_semaphore_alloc(0, 1, str8_zero()); if (is_shared) { @@ -99,7 +99,7 @@ tp_alloc(Arena *arena, U32 worker_count, U32 max_worker_count, String8 name) // launch worker threads for (U64 i = 1; i < worker_count; i += 1) { TP_Worker *worker = &pool->worker_arr[i]; - worker->handle = os_thread_launch(worker_entry, worker, 0); + worker->handle = thread_launch(worker_entry, worker); } ProfEnd(); @@ -111,23 +111,23 @@ tp_release(TP_Context *pool) { pool->is_live = 0; - B32 is_shared = !os_handle_match(pool->exec_semaphore, os_handle_zero()); + B32 is_shared = pool->exec_semaphore.u64[0] != 0; if (is_shared) { for (U64 i = 0; i < pool->worker_count; ++i) { - os_semaphore_drop(pool->exec_semaphore); + semaphore_drop(pool->exec_semaphore); } } for (U64 i = 0; i < pool->worker_count; ++i) { - os_semaphore_drop(pool->task_semaphore); + semaphore_drop(pool->task_semaphore); } for (U64 i = 1; i < pool->worker_count; i += 1) { - os_thread_detach(pool->worker_arr[i].handle); + thread_detach(pool->worker_arr[i].handle); } if (is_shared) { - os_semaphore_release(pool->exec_semaphore); + semaphore_release(pool->exec_semaphore); } - os_semaphore_release(pool->task_semaphore); - os_semaphore_release(pool->main_semaphore); + semaphore_release(pool->task_semaphore); + semaphore_release(pool->main_semaphore); MemoryZeroStruct(pool); } @@ -209,7 +209,7 @@ tp_for_parallel(TP_Context *pool, TP_Arena *task_arena, U64 task_count, TP_TaskF U64 drop_count = Min(task_count, pool->worker_count); // if we are in shared mode ping local semaphore - if (!os_handle_match(pool->exec_semaphore, os_handle_zero())) { + if (pool->exec_semaphore.u64[0] != 0) { for (U64 worker_idx = 0; worker_idx < drop_count; worker_idx +=1) { os_semaphore_drop(pool->exec_semaphore); } diff --git a/src/linker/thread_pool/thread_pool.h b/src/linker/thread_pool/thread_pool.h index abea5cbd..abae8606 100644 --- a/src/linker/thread_pool/thread_pool.h +++ b/src/linker/thread_pool/thread_pool.h @@ -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 @@ -22,15 +22,15 @@ typedef struct TP_Worker { U64 id; struct TP_Context *pool; - OS_Handle handle; + Thread handle; } TP_Worker; typedef struct TP_Context { B32 is_live; - OS_Handle exec_semaphore; - OS_Handle task_semaphore; - OS_Handle main_semaphore; + Semaphore exec_semaphore; + Semaphore task_semaphore; + Semaphore main_semaphore; U32 worker_count; TP_Worker *worker_arr; diff --git a/src/metagen/metagen_base/metagen_base_profile.h b/src/metagen/metagen_base/metagen_base_profile.h index 098270f4..1e0d938b 100644 --- a/src/metagen/metagen_base/metagen_base_profile.h +++ b/src/metagen/metagen_base/metagen_base_profile.h @@ -21,6 +21,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 #endif @@ -44,25 +45,25 @@ # define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__) # define ProfColor(color) tmZoneColorSticky(color) # 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__); \ - tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \ - hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \ - TM_API_PTR->_tmEnterZoneFast_Core(0, 0, file_id, __LINE__, hash); \ - scratch_end(scratch); \ - } +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__); \ +tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \ +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) { \ - 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__); \ - tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \ - hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \ - TM_API_PTR->_tmMessageFast_Core(0, TMMF_ICON_NOTE, file_id, __LINE__, hash); \ - scratch_end(scratch); \ - } +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__); \ +tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \ +hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \ +TM_API_PTR->_tmMessageFast_Core(0, TMMF_ICON_NOTE, file_id, __LINE__, hash); \ +scratch_end(scratch); \ +} #endif //////////////////////////////// diff --git a/src/msf/msf_parse.c b/src/msf/msf_parse.c index 6fcd65fe..02912ab7 100644 --- a/src/msf/msf_parse.c +++ b/src/msf/msf_parse.c @@ -230,6 +230,7 @@ msf_raw_stream_table_from_data(Arena *arena, String8 msf_data) internal String8 msf_data_from_stream_number(Arena *arena, String8 msf_data, MSF_RawStreamTable *st, MSF_StreamNumber sn) { + ProfBeginFunction(); String8 result = {0}; if(sn < st->stream_count) { @@ -268,6 +269,7 @@ msf_data_from_stream_number(Arena *arena, String8 msf_data, MSF_RawStreamTable * result = str8(stream_buf, copy_size); } + ProfEnd(); return result; } diff --git a/src/msvc_crt/msvc_crt.c b/src/msvc_crt/msvc_crt.c index c251a7b7..aa2c54bc 100644 --- a/src/msvc_crt/msvc_crt.c +++ b/src/msvc_crt/msvc_crt.c @@ -1,6 +1,22 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +internal String8 +msvcrt_ctr_entry_from_user_entry(String8 user_entry) +{ + String8 crt_entry = {0}; + if (str8_match_lit("wmain", user_entry, 0)) { + crt_entry = str8_lit("wmainCRTStartup"); + } else if (str8_match_lit("main", user_entry, 0)) { + crt_entry = str8_lit("mainCRTStartup"); + } else if (str8_match_lit("WinMain", user_entry, 0)) { + crt_entry = str8_lit("WinMainCRTStartup"); + } else if (str8_match_lit("wWinMain", user_entry, 0)) { + crt_entry = str8_lit("wWinMainCRTStartup"); + } + return crt_entry; +} + internal String8 mscrt_delay_load_helper_name_from_machine(COFF_MachineType machine) { diff --git a/src/msvc_crt/msvc_crt.h b/src/msvc_crt/msvc_crt.h index 11000af2..4ad6354c 100644 --- a/src/msvc_crt/msvc_crt.h +++ b/src/msvc_crt/msvc_crt.h @@ -343,6 +343,10 @@ typedef struct MSCRT_ParsedFuncInfoV4 MSCRT_IP2State32V4 ip2state_map; } MSCRT_ParsedFuncInfoV4; +//- Entry Point + +internal String8 msvcrt_ctr_entry_from_user_entry(String8 user_entry_point); + //- Delay Load Helper internal String8 mscrt_delay_load_helper_name_from_machine(COFF_MachineType machine); diff --git a/src/mule/mule_main.cpp b/src/mule/mule_main.cpp index 203b6036..00a60072 100644 --- a/src/mule/mule_main.cpp +++ b/src/mule/mule_main.cpp @@ -345,6 +345,15 @@ enum{ Anonymous_D, }; +typedef uint32_t SizedKind; +enum SizedKindEnum +{ + SizedKind_A, + SizedKind_B, + SizedKind_C, + SizedKind_D, +}; + typedef Kind Alias1; typedef Flag Alias2; typedef Has_Enums Alias3; @@ -635,6 +644,8 @@ type_coverage_eval_tests(void) dynamic_array_vector.push_back(dynamic); dynamic_array_vector.push_back(dynamic); + SizedKind sized_kind = SizedKind_C; + int x = (int)(Anonymous_D); } diff --git a/src/mutable_text/mutable_text.c b/src/mutable_text/mutable_text.c index f53585c7..28a735ea 100644 --- a/src/mutable_text/mutable_text.c +++ b/src/mutable_text/mutable_text.c @@ -20,7 +20,7 @@ mtx_init(void) for(U64 idx = 0; idx < mtx_shared->stripes_count; idx += 1) { mtx_shared->stripes[idx].arena = arena_alloc(); - mtx_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc(); + mtx_shared->stripes[idx].rw_mutex = rw_mutex_alloc(); } mtx_shared->mut_threads_count = Min(os_get_system_info()->logical_processor_count, 4); mtx_shared->mut_threads = push_array(arena, MTX_MutThread, mtx_shared->mut_threads_count); @@ -28,9 +28,9 @@ mtx_init(void) { mtx_shared->mut_threads[idx].ring_size = KB(64); mtx_shared->mut_threads[idx].ring_base = push_array_no_zero(arena, U8, mtx_shared->mut_threads[idx].ring_size); - mtx_shared->mut_threads[idx].cv = os_condition_variable_alloc(); - mtx_shared->mut_threads[idx].mutex = os_mutex_alloc(); - mtx_shared->mut_threads[idx].thread = os_thread_launch(mtx_mut_thread__entry_point, &mtx_shared->mut_threads[idx], 0); + mtx_shared->mut_threads[idx].cv = cond_var_alloc(); + mtx_shared->mut_threads[idx].mutex = mutex_alloc(); + mtx_shared->mut_threads[idx].thread = thread_launch(mtx_mut_thread__entry_point, &mtx_shared->mut_threads[idx]); } } @@ -38,9 +38,9 @@ mtx_init(void) //~ rjf: Buffer Operations internal void -mtx_push_op(HS_Key buffer_key, MTX_Op op) +mtx_push_op(C_Key buffer_key, MTX_Op op) { - U64 hash = hs_little_hash_from_data(str8_struct(&buffer_key)); + U64 hash = u64_hash_from_str8(str8_struct(&buffer_key)); MTX_MutThread *thread = &mtx_shared->mut_threads[hash%mtx_shared->mut_threads_count]; mtx_enqueue_op(thread, buffer_key, op); } @@ -49,10 +49,10 @@ mtx_push_op(HS_Key buffer_key, MTX_Op op) //~ rjf: Mutation Threads internal void -mtx_enqueue_op(MTX_MutThread *thread, HS_Key buffer_key, MTX_Op op) +mtx_enqueue_op(MTX_MutThread *thread, C_Key buffer_key, MTX_Op op) { // TODO(rjf): if op.replace is too big, need to split into multiple edits - OS_MutexScope(thread->mutex) for(;;) + MutexScope(thread->mutex) for(;;) { U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos; U64 available_size = thread->ring_size - unconsumed_size; @@ -65,15 +65,15 @@ mtx_enqueue_op(MTX_MutThread *thread, HS_Key buffer_key, MTX_Op op) thread->ring_write_pos += ring_write(thread->ring_base, thread->ring_size, thread->ring_write_pos, op.replace.str, op.replace.size); break; } - os_condition_variable_wait(thread->cv, thread->mutex, max_U64); + cond_var_wait(thread->cv, thread->mutex, max_U64); } - os_condition_variable_broadcast(thread->cv); + cond_var_broadcast(thread->cv); } internal void -mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, HS_Key *buffer_key_out, MTX_Op *op_out) +mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, C_Key *buffer_key_out, MTX_Op *op_out) { - OS_MutexScope(thread->mutex) for(;;) + MutexScope(thread->mutex) for(;;) { U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos; if(unconsumed_size >= sizeof(*buffer_key_out) + sizeof(op_out->range) + sizeof(op_out->replace.size)) @@ -85,29 +85,29 @@ mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, HS_Key *buffer_key_out, MTX_ thread->ring_read_pos += ring_read(thread->ring_base, thread->ring_size, thread->ring_read_pos, op_out->replace.str, op_out->replace.size); break; } - os_condition_variable_wait(thread->cv, thread->mutex, max_U64); + cond_var_wait(thread->cv, thread->mutex, max_U64); } - os_condition_variable_broadcast(thread->cv); + cond_var_broadcast(thread->cv); } internal void mtx_mut_thread__entry_point(void *p) { MTX_MutThread *mut_thread = (MTX_MutThread *)p; - ThreadNameF("[mtx] mut thread #%I64u", (U64)(mut_thread - mtx_shared->mut_threads)); + ThreadNameF("mtx_mut_thread_%I64u", (U64)(mut_thread - mtx_shared->mut_threads)); for(;;) { Temp scratch = scratch_begin(0, 0); - HS_Scope *hs_scope = hs_scope_open(); + Access *access = access_open(); //- rjf: get next op - HS_Key buffer_key = {0}; + C_Key buffer_key = {0}; MTX_Op op = {0}; mtx_dequeue_op(scratch.arena, mut_thread, &buffer_key, &op); //- rjf: get buffer's current data - U128 hash = hs_hash_from_key(buffer_key, 0); - String8 data = hs_data_from_hash(hs_scope, hash); + U128 hash = c_hash_from_key(buffer_key, 0); + String8 data = c_data_from_hash(access, hash); //- rjf: clamp op by data op.range.min = Min(op.range.min, data.size); @@ -134,10 +134,10 @@ mtx_mut_thread__entry_point(void *p) MemoryCopy(new_data_base+pre_replace_data.size+op.replace.size, post_replace_data.str, post_replace_data.size); } String8 new_data = str8(new_data_base, new_data_size); - hs_submit_data(buffer_key, &arena, new_data); + c_submit_data(buffer_key, &arena, new_data); } - hs_scope_close(hs_scope); + access_close(access); scratch_end(scratch); } } diff --git a/src/mutable_text/mutable_text.h b/src/mutable_text/mutable_text.h index bb6826ac..1ff03ce9 100644 --- a/src/mutable_text/mutable_text.h +++ b/src/mutable_text/mutable_text.h @@ -27,7 +27,7 @@ struct MTX_Stripe { Arena *arena; MTX_Node *free_node; - OS_Handle rw_mutex; + RWMutex rw_mutex; }; //////////////////////////////// @@ -47,9 +47,9 @@ struct MTX_MutThread U8 *ring_base; U64 ring_read_pos; U64 ring_write_pos; - OS_Handle cv; - OS_Handle mutex; - OS_Handle thread; + CondVar cv; + Mutex mutex; + Thread thread; }; //////////////////////////////// @@ -84,13 +84,13 @@ internal void mtx_init(void); //////////////////////////////// //~ rjf: Buffer Operations -internal void mtx_push_op(HS_Key buffer_key, MTX_Op op); +internal void mtx_push_op(C_Key buffer_key, MTX_Op op); //////////////////////////////// //~ rjf: Mutation Threads -internal void mtx_enqueue_op(MTX_MutThread *thread, HS_Key buffer_key, MTX_Op op); -internal void mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, HS_Key *buffer_key_out, MTX_Op *op_out); +internal void mtx_enqueue_op(MTX_MutThread *thread, C_Key buffer_key, MTX_Op op); +internal void mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, C_Key *buffer_key_out, MTX_Op *op_out); internal void mtx_mut_thread__entry_point(void *p); #endif // MUTABLE_TEXT_H diff --git a/src/os/core/linux/os_core_linux.c b/src/os/core/linux/os_core_linux.c index d8ded967..8a22f460 100644 --- a/src/os/core/linux/os_core_linux.c +++ b/src/os/core/linux/os_core_linux.c @@ -121,12 +121,9 @@ internal void * os_lnx_thread_entry_point(void *ptr) { OS_LNX_Entity *entity = (OS_LNX_Entity *)ptr; - OS_ThreadFunctionType *func = entity->thread.func; + ThreadEntryPointFunctionType *func = entity->thread.func; void *thread_ptr = entity->thread.ptr; - TCTX tctx_; - tctx_init_and_equip(&tctx_); - func(thread_ptr); - tctx_release(); + supplement_thread_base_entry_point(func, thread_ptr); return 0; } @@ -777,13 +774,13 @@ os_process_launch(OS_ProcessLaunchParams *params) } internal B32 -os_process_join(OS_Handle handle, U64 endt_us) +os_process_join(OS_Handle handle, U64 endt_us, U64 *exit_code_out) { NotImplemented; } -internal B32 -os_process_join_exit_code(OS_Handle handle, U64 endt_us, int *exit_code_out) +internal U64 +os_process_array_join(OS_HandleArray processes, U64 endt_us, U64 *exit_code_out) { NotImplemented; } @@ -804,7 +801,7 @@ os_process_kill(OS_Handle handle) //~ rjf: @os_hooks Threads (Implemented Per-OS) internal OS_Handle -os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params) +os_thread_launch(ThreadEntryPointFunctionType *func, void *ptr, void *params) { OS_LNX_Entity *entity = os_lnx_entity_alloc(OS_LNX_EntityKind_Thread); entity->thread.func = func; @@ -914,31 +911,22 @@ os_rw_mutex_release(OS_Handle rw_mutex) } internal void -os_rw_mutex_take_r(OS_Handle rw_mutex) +os_rw_mutex_take(OS_Handle rw_mutex, B32 write_mode) { if(os_handle_match(rw_mutex, os_handle_zero())) { return; } OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; - pthread_rwlock_rdlock(&entity->rwmutex_handle); + if(write_mode) + { + pthread_rwlock_wrlock(&entity->rwmutex_handle); + } + else + { + pthread_rwlock_rdlock(&entity->rwmutex_handle); + } } internal void -os_rw_mutex_drop_r(OS_Handle rw_mutex) -{ - if(os_handle_match(rw_mutex, os_handle_zero())) { return; } - OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; - pthread_rwlock_unlock(&entity->rwmutex_handle); -} - -internal void -os_rw_mutex_take_w(OS_Handle rw_mutex) -{ - if(os_handle_match(rw_mutex, os_handle_zero())) { return; } - OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; - pthread_rwlock_wrlock(&entity->rwmutex_handle); -} - -internal void -os_rw_mutex_drop_w(OS_Handle rw_mutex) +os_rw_mutex_drop(OS_Handle rw_mutex, B32 write_mode) { if(os_handle_match(rw_mutex, os_handle_zero())) { return; } OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; @@ -948,7 +936,7 @@ os_rw_mutex_drop_w(OS_Handle rw_mutex) //- rjf: condition variables internal OS_Handle -os_condition_variable_alloc(void) +os_cond_var_alloc(void) { OS_LNX_Entity *entity = os_lnx_entity_alloc(OS_LNX_EntityKind_ConditionVariable); int init_result = pthread_cond_init(&entity->cv.cond_handle, 0); @@ -973,7 +961,7 @@ os_condition_variable_alloc(void) } internal void -os_condition_variable_release(OS_Handle cv) +os_cond_var_release(OS_Handle cv) { if(os_handle_match(cv, os_handle_zero())) { return; } OS_LNX_Entity *entity = (OS_LNX_Entity *)cv.u64[0]; @@ -983,7 +971,7 @@ os_condition_variable_release(OS_Handle cv) } internal B32 -os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us) +os_cond_var_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us) { if(os_handle_match(cv, os_handle_zero())) { return 0; } if(os_handle_match(mutex, os_handle_zero())) { return 0; } @@ -998,7 +986,7 @@ os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us) } internal B32 -os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) +os_cond_var_wait_rw(OS_Handle cv, OS_Handle mutex_rw, B32 write_mode, U64 endt_us) { // TODO(rjf): because pthread does not supply cv/rw natively, I had to hack // this together, but this would probably just be a lot better if we just @@ -1019,49 +1007,27 @@ os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &cv_entity->cv.rwlock_mutex_handle, &endt_timespec); if(wait_result != ETIMEDOUT) { - pthread_rwlock_rdlock(&rw_mutex_entity->rwmutex_handle); + if(write_mode) + { + pthread_rwlock_wrlock(&rw_mutex_entity->rwmutex_handle); + } + else + { + pthread_rwlock_rdlock(&rw_mutex_entity->rwmutex_handle); + } result = 1; break; } if(wait_result == ETIMEDOUT) { - pthread_rwlock_rdlock(&rw_mutex_entity->rwmutex_handle); - break; - } - } - pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle); - return result; -} - -internal B32 -os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) -{ - // TODO(rjf): because pthread does not supply cv/rw natively, I had to hack - // this together, but this would probably just be a lot better if we just - // implemented the primitives ourselves with e.g. futexes - // - if(os_handle_match(cv, os_handle_zero())) { return 0; } - if(os_handle_match(mutex_rw, os_handle_zero())) { return 0; } - OS_LNX_Entity *cv_entity = (OS_LNX_Entity *)cv.u64[0]; - OS_LNX_Entity *rw_mutex_entity = (OS_LNX_Entity *)mutex_rw.u64[0]; - struct timespec endt_timespec; - endt_timespec.tv_sec = endt_us/Million(1); - endt_timespec.tv_nsec = Thousand(1) * (endt_us - (endt_us/Million(1))*Million(1)); - B32 result = 0; - pthread_mutex_lock(&cv_entity->cv.rwlock_mutex_handle); - pthread_rwlock_unlock(&rw_mutex_entity->rwmutex_handle); - for(;;) - { - int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &cv_entity->cv.rwlock_mutex_handle, &endt_timespec); - if(wait_result != ETIMEDOUT) - { - pthread_rwlock_wrlock(&rw_mutex_entity->rwmutex_handle); - result = 1; - break; - } - if(wait_result == ETIMEDOUT) - { - pthread_rwlock_wrlock(&rw_mutex_entity->rwmutex_handle); + if(write_mode) + { + pthread_rwlock_wrlock(&rw_mutex_entity->rwmutex_handle); + } + else + { + pthread_rwlock_rdlock(&rw_mutex_entity->rwmutex_handle); + } break; } } @@ -1070,7 +1036,7 @@ os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) } internal void -os_condition_variable_signal(OS_Handle cv) +os_cond_var_signal(OS_Handle cv) { if(os_handle_match(cv, os_handle_zero())) { return; } OS_LNX_Entity *cv_entity = (OS_LNX_Entity *)cv.u64[0]; @@ -1078,7 +1044,7 @@ os_condition_variable_signal(OS_Handle cv) } internal void -os_condition_variable_broadcast(OS_Handle cv) +os_cond_var_broadcast(OS_Handle cv) { if(os_handle_match(cv, os_handle_zero())) { return; } OS_LNX_Entity *cv_entity = (OS_LNX_Entity *)cv.u64[0]; @@ -1091,14 +1057,18 @@ internal OS_Handle os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name) { OS_Handle result = {0}; - if (name.size > 0) { + if (name.size > 0) + { // TODO: we need to allocate shared memory to store sem_t // NotImplemented; - } else { + } + else + { sem_t *s = mmap(0, sizeof(*s), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); AssertAlways(s != MAP_FAILED); int err = sem_init(s, 0, initial_count); - if (err == 0) { + if(err == 0) + { result.u64[0] = (U64)s; } } @@ -1127,17 +1097,19 @@ os_semaphore_close(OS_Handle semaphore) internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us) { + // TODO(rjf): we need to use `sem_timedwait` here. AssertAlways(endt_us == max_U64); - for (;;) { + for(;;) + { int err = sem_wait((sem_t*)semaphore.u64[0]); - if (err == 0) { + if(err == 0) + { break; - } else { - if (errno == EAGAIN) { - continue; - } } - InvalidPath; + else if(errno == EAGAIN) + { + continue; + } break; } return 1; @@ -1146,20 +1118,50 @@ os_semaphore_take(OS_Handle semaphore, U64 endt_us) internal void os_semaphore_drop(OS_Handle semaphore) { - for (;;) { + for(;;) + { int err = sem_post((sem_t*)semaphore.u64[0]); - if (err == 0) { + if(err == 0) + { break; - } else { - if (errno == EAGAIN) { + } + else + { + if(errno == EAGAIN) + { continue; } } - InvalidPath; break; } } +//- rjf: barriers + +internal OS_Handle +os_barrier_alloc(U64 count) +{ + OS_LNX_Entity *entity = os_lnx_entity_alloc(OS_LNX_EntityKind_Barrier); + pthread_barrier_init(&entity->barrier, 0, count); + OS_Handle result = {IntFromPtr(entity)}; + return result; +} + +internal void +os_barrier_release(OS_Handle barrier) +{ + OS_LNX_Entity *entity = (OS_LNX_Entity*)PtrFromInt(barrier.u64[0]); + pthread_barrier_destroy(&entity->barrier); + os_lnx_entity_release(entity); +} + +internal void +os_barrier_wait(OS_Handle barrier) +{ + OS_LNX_Entity *entity = (OS_LNX_Entity*)PtrFromInt(barrier.u64[0]); + pthread_barrier_wait(&entity->barrier); +} + //////////////////////////////// //~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) @@ -1196,7 +1198,7 @@ os_library_close(OS_Handle lib) //~ rjf: @os_hooks Safe Calls (Implemented Per-OS) internal void -os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr) +os_safe_call(ThreadEntryPointFunctionType *func, ThreadEntryPointFunctionType *fail_handler, void *ptr) { // rjf: push handler to chain OS_LNX_SafeCallChain chain = {0}; @@ -1266,8 +1268,8 @@ main(int argc, char **argv) } //- rjf: set up thread context - local_persist TCTX tctx; - tctx_init_and_equip(&tctx); + TCTX *tctx = tctx_alloc(); + tctx_select(tctx); //- rjf: set up dynamically allocated state os_lnx_state.arena = arena_alloc(); diff --git a/src/os/core/linux/os_core_linux.h b/src/os/core/linux/os_core_linux.h index 1770d239..7b335fbb 100644 --- a/src/os/core/linux/os_core_linux.h +++ b/src/os/core/linux/os_core_linux.h @@ -54,7 +54,7 @@ typedef struct OS_LNX_SafeCallChain OS_LNX_SafeCallChain; struct OS_LNX_SafeCallChain { OS_LNX_SafeCallChain *next; - OS_ThreadFunctionType *fail_handler; + ThreadEntryPointFunctionType *fail_handler; void *ptr; }; @@ -67,6 +67,7 @@ typedef enum OS_LNX_EntityKind OS_LNX_EntityKind_Mutex, OS_LNX_EntityKind_RWMutex, OS_LNX_EntityKind_ConditionVariable, + OS_LNX_EntityKind_Barrier, } OS_LNX_EntityKind; @@ -80,7 +81,7 @@ struct OS_LNX_Entity struct { pthread_t handle; - OS_ThreadFunctionType *func; + ThreadEntryPointFunctionType *func; void *ptr; } thread; pthread_mutex_t mutex_handle; @@ -90,6 +91,7 @@ struct OS_LNX_Entity pthread_cond_t cond_handle; pthread_mutex_t rwlock_mutex_handle; } cv; + pthread_barrier_t barrier; }; }; diff --git a/src/os/core/linux/os_core_linux_old.c b/src/os/core/linux/os_core_linux_old.c index 91a457b0..4d492f06 100644 --- a/src/os/core/linux/os_core_linux_old.c +++ b/src/os/core/linux/os_core_linux_old.c @@ -783,7 +783,7 @@ lnx_free_entity(LNX_Entity *entity){ internal void* lnx_thread_base(void *ptr){ LNX_Entity *entity = (LNX_Entity*)ptr; - OS_ThreadFunctionType *func = entity->thread.func; + ThreadEntryPointFunctionType *func = entity->thread.func; void *thread_ptr = entity->thread.ptr; TCTX tctx_; @@ -1377,7 +1377,7 @@ os_launch_process(OS_LaunchOptions *options){ //~ rjf: @os_hooks Threads (Implemented Per-OS) internal OS_Handle -os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params){ +os_thread_launch(ThreadEntryPointFunctionType *func, void *ptr, void *params){ // entity LNX_Entity *entity = lnx_alloc_entity(LNX_EntityKind_Thread); entity->reference_mask = 0x3; @@ -1501,7 +1501,7 @@ os_rw_mutex_drop_w_(OS_Handle mutex) //- rjf: condition variables internal OS_Handle -os_condition_variable_alloc(void){ +os_cond_var_alloc(void){ // entity LNX_Entity *entity = lnx_alloc_entity(LNX_EntityKind_ConditionVariable); @@ -1521,7 +1521,7 @@ os_condition_variable_alloc(void){ } internal void -os_condition_variable_release(OS_Handle cv){ +os_cond_var_release(OS_Handle cv){ LNX_Entity *entity = (LNX_Entity*)PtrFromInt(cv.id); pthread_cond_destroy(&entity->cond); lnx_free_entity(entity); @@ -1642,7 +1642,7 @@ os_library_close(OS_Handle lib) //~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) internal void -os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr){ +os_safe_call(ThreadEntryPointFunctionType *func, ThreadEntryPointFunctionType *fail_handler, void *ptr){ LNX_SafeCallChain chain = {0}; SLLStackPush(lnx_safe_call_chain, &chain); chain.fail_handler = fail_handler; diff --git a/src/os/core/linux/os_core_linux_old.h b/src/os/core/linux/os_core_linux_old.h index efec7159..cf133b2a 100644 --- a/src/os/core/linux/os_core_linux_old.h +++ b/src/os/core/linux/os_core_linux_old.h @@ -48,7 +48,7 @@ struct LNX_Entity{ volatile U32 reference_mask; union{ struct{ - OS_ThreadFunctionType *func; + ThreadEntryPointFunctionType *func; void *ptr; pthread_t handle; } thread; @@ -62,7 +62,7 @@ struct LNX_Entity{ struct LNX_SafeCallChain{ LNX_SafeCallChain *next; - OS_ThreadFunctionType *fail_handler; + ThreadEntryPointFunctionType *fail_handler; void *ptr; }; diff --git a/src/os/core/os_core.c b/src/os/core/os_core.c index e4cb49e6..670f8486 100644 --- a/src/os/core/os_core.c +++ b/src/os/core/os_core.c @@ -40,21 +40,6 @@ os_handle_array_from_list(Arena *arena, OS_HandleList *list) return result; } -//////////////////////////////// -//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once) - -internal String8List -os_string_list_from_argcv(Arena *arena, int argc, char **argv) -{ - String8List result = {0}; - for(int i = 0; i < argc; i += 1) - { - String8 str = str8_cstring(argv[i]); - str8_list_push(arena, &result, str); - } - return result; -} - //////////////////////////////// //~ rjf: Filesystem Helpers (Helpers, Implemented Once) @@ -95,7 +80,6 @@ os_write_data_list_to_file_path(String8 path, String8List list) U64 write_buffer_write_pos = 0; U64 write_buffer_read_pos = 0; U64 file_off = 0; - U64 total_bytes_written = 0; { for(String8Node *n = list.first; n != 0; n = n->next) { @@ -105,7 +89,11 @@ os_write_data_list_to_file_path(String8 path, String8List list) U64 write_buffer_available_size = (write_buffer_size - write_buffer_unconsumed_size); if(write_buffer_available_size == 0) { - os_file_write(file, r1u64(file_off, file_off+write_buffer_size), write_buffer); + U64 file_write_size = os_file_write(file, r1u64(file_off, file_off+write_buffer_size), write_buffer); + if(file_write_size != write_buffer_size) + { + goto dbl_break; + } file_off += write_buffer_size; write_buffer_read_pos += write_buffer_size; } @@ -119,10 +107,12 @@ os_write_data_list_to_file_path(String8 path, String8List list) } if(write_buffer_write_pos > write_buffer_read_pos) { - total_bytes_written += os_file_write(file, r1u64(file_off, file_off + (write_buffer_write_pos-write_buffer_read_pos)), write_buffer); + U64 file_write_size = os_file_write(file, r1u64(file_off, file_off + (write_buffer_write_pos-write_buffer_read_pos)), write_buffer); + file_off += file_write_size; } } - good = (total_bytes_written == list.total_size); + dbl_break:; + good = (file_off == list.total_size); os_file_close(file); scratch_end(scratch); } diff --git a/src/os/core/os_core.h b/src/os/core/os_core.h index 415bb662..2731228f 100644 --- a/src/os/core/os_core.h +++ b/src/os/core/os_core.h @@ -128,11 +128,6 @@ struct OS_ProcessLaunchParams OS_Handle stdin_file; }; -//////////////////////////////// -//~ rjf: Thread Types - -typedef void OS_ThreadFunctionType(void *ptr); - //////////////////////////////// //~ rjf: Handle Type Functions (Helpers, Implemented Once) @@ -141,11 +136,6 @@ internal B32 os_handle_match(OS_Handle a, OS_Handle b); internal void os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle); internal OS_HandleArray os_handle_array_from_list(Arena *arena, OS_HandleList *list); -//////////////////////////////// -//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once) - -internal String8List os_string_list_from_argcv(Arena *arena, int argc, char **argv); - //////////////////////////////// //~ rjf: Filesystem Helpers (Helpers, Implemented Once) @@ -254,58 +244,53 @@ internal void os_sleep_milliseconds(U32 msec); //~ rjf: @os_hooks Child Processes (Implemented Per-OS) internal OS_Handle os_process_launch(OS_ProcessLaunchParams *params); -internal B32 os_process_join(OS_Handle handle, U64 endt_us); -internal B32 os_process_join_exit_code(OS_Handle handle, U64 endt_us, int *exit_code_out); +internal B32 os_process_join(OS_Handle handle, U64 endt_us, U64 *exit_code_out); internal void os_process_detach(OS_Handle handle); internal B32 os_process_kill(OS_Handle handle); //////////////////////////////// //~ rjf: @os_hooks Threads (Implemented Per-OS) -internal OS_Handle os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params); -internal B32 os_thread_join(OS_Handle handle, U64 endt_us); -internal void os_thread_detach(OS_Handle handle); +internal Thread os_thread_launch(ThreadEntryPointFunctionType *f, void *p); +internal B32 os_thread_join(Thread handle, U64 endt_us); +internal void os_thread_detach(Thread handle); //////////////////////////////// //~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS) //- rjf: recursive mutexes -internal OS_Handle os_mutex_alloc(void); -internal void os_mutex_release(OS_Handle mutex); -internal void os_mutex_take(OS_Handle mutex); -internal void os_mutex_drop(OS_Handle mutex); +internal Mutex os_mutex_alloc(void); +internal void os_mutex_release(Mutex mutex); +internal void os_mutex_take(Mutex mutex); +internal void os_mutex_drop(Mutex mutex); //- rjf: reader/writer mutexes -internal OS_Handle os_rw_mutex_alloc(void); -internal void os_rw_mutex_release(OS_Handle rw_mutex); -internal void os_rw_mutex_take_r(OS_Handle mutex); -internal void os_rw_mutex_drop_r(OS_Handle mutex); -internal void os_rw_mutex_take_w(OS_Handle mutex); -internal void os_rw_mutex_drop_w(OS_Handle mutex); +internal RWMutex os_rw_mutex_alloc(void); +internal void os_rw_mutex_release(RWMutex mutex); +internal void os_rw_mutex_take(RWMutex mutex, B32 write_mode); +internal void os_rw_mutex_drop(RWMutex mutex, B32 write_mode); //- rjf: condition variables -internal OS_Handle os_condition_variable_alloc(void); -internal void os_condition_variable_release(OS_Handle cv); +internal CondVar os_cond_var_alloc(void); +internal void os_cond_var_release(CondVar cv); // returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout -internal B32 os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us); -internal B32 os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us); -internal B32 os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us); -internal void os_condition_variable_signal(OS_Handle cv); -internal void os_condition_variable_broadcast(OS_Handle cv); +internal B32 os_cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us); +internal B32 os_cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us); +internal void os_cond_var_signal(CondVar cv); +internal void os_cond_var_broadcast(CondVar cv); //- rjf: cross-process semaphores -internal OS_Handle os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name); -internal void os_semaphore_release(OS_Handle semaphore); -internal OS_Handle os_semaphore_open(String8 name); -internal void os_semaphore_close(OS_Handle semaphore); -internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us); -internal void os_semaphore_drop(OS_Handle semaphore); +internal Semaphore os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name); +internal void os_semaphore_release(Semaphore semaphore); +internal Semaphore os_semaphore_open(String8 name); +internal void os_semaphore_close(Semaphore semaphore); +internal B32 os_semaphore_take(Semaphore semaphore, U64 endt_us); +internal void os_semaphore_drop(Semaphore semaphore); -//- rjf: scope macros -#define OS_MutexScope(mutex) DeferLoop(os_mutex_take(mutex), os_mutex_drop(mutex)) -#define OS_MutexScopeR(mutex) DeferLoop(os_rw_mutex_take_r(mutex), os_rw_mutex_drop_r(mutex)) -#define OS_MutexScopeW(mutex) DeferLoop(os_rw_mutex_take_w(mutex), os_rw_mutex_drop_w(mutex)) -#define OS_MutexScopeRWPromote(mutex) DeferLoop((os_rw_mutex_drop_r(mutex), os_rw_mutex_take_w(mutex)), (os_rw_mutex_drop_w(mutex), os_rw_mutex_take_r(mutex))) +//- rjf: barriers +internal Barrier os_barrier_alloc(U64 count); +internal void os_barrier_release(Barrier barrier); +internal void os_barrier_wait(Barrier barrier); //////////////////////////////// //~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) @@ -317,7 +302,7 @@ internal VoidProc *os_library_load_proc(OS_Handle lib, String8 name); //////////////////////////////// //~ rjf: @os_hooks Safe Calls (Implemented Per-OS) -internal void os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr); +internal void os_safe_call(ThreadEntryPointFunctionType *func, ThreadEntryPointFunctionType *fail_handler, void *ptr); //////////////////////////////// //~ rjf: @os_hooks GUIDs (Implemented Per-OS) @@ -333,7 +318,7 @@ internal Guid os_make_guid(void); #if BUILD_ENTRY_DEFINING_UNIT raddbg_entry_point(entry_point); -internal void entry_point(CmdLine *cmdline); +internal no_inline void entry_point(CmdLine *cmdline); #endif #endif // OS_CORE_H diff --git a/src/os/core/win32/os_core_win32.c b/src/os/core/win32/os_core_win32.c index 65d7926d..a2e8cd9f 100644 --- a/src/os/core/win32/os_core_win32.c +++ b/src/os/core/win32/os_core_win32.c @@ -8,6 +8,7 @@ typedef HRESULT W32_SetThreadDescription_Type(HANDLE hThread, PCWSTR lpThreadDescription); global W32_SetThreadDescription_Type *w32_SetThreadDescription_func = 0; +global RIO_EXTENSION_FUNCTION_TABLE w32_rio_functions = {0}; //////////////////////////////// //~ rjf: File Info Conversion Helpers @@ -143,12 +144,9 @@ internal DWORD os_w32_thread_entry_point(void *ptr) { OS_W32_Entity *entity = (OS_W32_Entity *)ptr; - OS_ThreadFunctionType *func = entity->thread.func; + ThreadEntryPointFunctionType *func = entity->thread.func; void *thread_ptr = entity->thread.ptr; - TCTX tctx_; - tctx_init_and_equip(&tctx_); - func(thread_ptr); - tctx_release(); + supplement_thread_base_entry_point(func, thread_ptr); return 0; } @@ -210,6 +208,7 @@ internal B32 os_commit(void *ptr, U64 size) { B32 result = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); + w32_rio_functions.RIODeregisterBuffer(w32_rio_functions.RIORegisterBuffer(ptr, size)); return result; } @@ -1062,28 +1061,21 @@ os_process_launch(OS_ProcessLaunchParams *params) } internal B32 -os_process_join(OS_Handle handle, U64 endt_us) +os_process_join(OS_Handle handle, U64 endt_us, U64 *exit_code_out) { HANDLE process = (HANDLE)(handle.u64[0]); DWORD sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); DWORD result = WaitForSingleObject(process, sleep_ms); - return (result == WAIT_OBJECT_0); -} - -internal B32 -os_process_join_exit_code(OS_Handle handle, U64 endt_us, int *exit_code_out) -{ - B32 result = 0; - if(os_process_join(handle, endt_us)) + B32 process_joined = (result == WAIT_OBJECT_0); + if(process_joined && exit_code_out) { - DWORD exit_code; - if(GetExitCodeProcess((HANDLE)handle.u64[0], &exit_code)) + DWORD exit_code = 0; + if(GetExitCodeProcess(process, &exit_code)) { *exit_code_out = exit_code; - result = 1; } } - return result; + return process_joined; } internal B32 @@ -1104,19 +1096,19 @@ os_process_detach(OS_Handle handle) //////////////////////////////// //~ rjf: @os_hooks Threads (Implemented Per-OS) -internal OS_Handle -os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params) +internal Thread +os_thread_launch(ThreadEntryPointFunctionType *f, void *p) { OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_Thread); - entity->thread.func = func; - entity->thread.ptr = ptr; + entity->thread.func = f; + entity->thread.ptr = p; entity->thread.handle = CreateThread(0, 0, os_w32_thread_entry_point, entity, 0, &entity->thread.tid); - OS_Handle result = {IntFromPtr(entity)}; + Thread result = {IntFromPtr(entity)}; return result; } internal B32 -os_thread_join(OS_Handle handle, U64 endt_us) +os_thread_join(Thread handle, U64 endt_us) { DWORD sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); OS_W32_Entity *entity = (OS_W32_Entity *)PtrFromInt(handle.u64[0]); @@ -1131,7 +1123,7 @@ os_thread_join(OS_Handle handle, U64 endt_us) } internal void -os_thread_detach(OS_Handle thread) +os_thread_detach(Thread thread) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(thread.u64[0]); if(entity != 0) @@ -1146,31 +1138,31 @@ os_thread_detach(OS_Handle thread) //- rjf: mutexes -internal OS_Handle +internal Mutex os_mutex_alloc(void) { OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_Mutex); InitializeCriticalSection(&entity->mutex); - OS_Handle result = {IntFromPtr(entity)}; + Mutex result = {IntFromPtr(entity)}; return result; } internal void -os_mutex_release(OS_Handle mutex) +os_mutex_release(Mutex mutex) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(mutex.u64[0]); os_w32_entity_release(entity); } internal void -os_mutex_take(OS_Handle mutex) +os_mutex_take(Mutex mutex) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(mutex.u64[0]); EnterCriticalSection(&entity->mutex); } internal void -os_mutex_drop(OS_Handle mutex) +os_mutex_drop(Mutex mutex) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(mutex.u64[0]); LeaveCriticalSection(&entity->mutex); @@ -1178,70 +1170,70 @@ os_mutex_drop(OS_Handle mutex) //- rjf: reader/writer mutexes -internal OS_Handle +internal RWMutex os_rw_mutex_alloc(void) { OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_RWMutex); InitializeSRWLock(&entity->rw_mutex); - OS_Handle result = {IntFromPtr(entity)}; + RWMutex result = {IntFromPtr(entity)}; return result; } internal void -os_rw_mutex_release(OS_Handle rw_mutex) +os_rw_mutex_release(RWMutex rw_mutex) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); os_w32_entity_release(entity); } internal void -os_rw_mutex_take_r(OS_Handle rw_mutex) +os_rw_mutex_take(RWMutex rw_mutex, B32 write_mode) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - AcquireSRWLockShared(&entity->rw_mutex); + if(write_mode) + { + AcquireSRWLockExclusive(&entity->rw_mutex); + } + else + { + AcquireSRWLockShared(&entity->rw_mutex); + } } internal void -os_rw_mutex_drop_r(OS_Handle rw_mutex) +os_rw_mutex_drop(RWMutex rw_mutex, B32 write_mode) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - ReleaseSRWLockShared(&entity->rw_mutex); -} - -internal void -os_rw_mutex_take_w(OS_Handle rw_mutex) -{ - OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - AcquireSRWLockExclusive(&entity->rw_mutex); -} - -internal void -os_rw_mutex_drop_w(OS_Handle rw_mutex) -{ - OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - ReleaseSRWLockExclusive(&entity->rw_mutex); + if(write_mode) + { + ReleaseSRWLockExclusive(&entity->rw_mutex); + } + else + { + ReleaseSRWLockShared(&entity->rw_mutex); + } } //- rjf: condition variables -internal OS_Handle -os_condition_variable_alloc(void) +internal CondVar +os_cond_var_alloc(void) { OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_ConditionVariable); InitializeConditionVariable(&entity->cv); - OS_Handle result = {IntFromPtr(entity)}; + CondVar result = {IntFromPtr(entity)}; return result; } internal void -os_condition_variable_release(OS_Handle cv) +os_cond_var_release(CondVar cv) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); os_w32_entity_release(entity); } internal B32 -os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us) +os_cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us) { U32 sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); BOOL result = 0; @@ -1255,7 +1247,7 @@ os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us) } internal B32 -os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) +os_cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us) { U32 sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); BOOL result = 0; @@ -1264,34 +1256,20 @@ os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); OS_W32_Entity *mutex_entity = (OS_W32_Entity*)PtrFromInt(mutex_rw.u64[0]); result = SleepConditionVariableSRW(&entity->cv, &mutex_entity->rw_mutex, sleep_ms, - CONDITION_VARIABLE_LOCKMODE_SHARED); - } - return result; -} - -internal B32 -os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) -{ - U32 sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); - BOOL result = 0; - if(sleep_ms > 0) - { - OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); - OS_W32_Entity *mutex_entity = (OS_W32_Entity*)PtrFromInt(mutex_rw.u64[0]); - result = SleepConditionVariableSRW(&entity->cv, &mutex_entity->rw_mutex, sleep_ms, 0); + write_mode ? 0 : CONDITION_VARIABLE_LOCKMODE_SHARED); } return result; } internal void -os_condition_variable_signal(OS_Handle cv) +os_cond_var_signal(CondVar cv) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); WakeConditionVariable(&entity->cv); } internal void -os_condition_variable_broadcast(OS_Handle cv) +os_cond_var_broadcast(CondVar cv) { OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); WakeAllConditionVariable(&entity->cv); @@ -1299,44 +1277,44 @@ os_condition_variable_broadcast(OS_Handle cv) //- rjf: cross-process semaphores -internal OS_Handle +internal Semaphore os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name) { Temp scratch = scratch_begin(0, 0); String16 name16 = str16_from_8(scratch.arena, name); HANDLE handle = CreateSemaphoreW(0, initial_count, max_count, (WCHAR *)name16.str); - OS_Handle result = {(U64)handle}; + Semaphore result = {(U64)handle}; scratch_end(scratch); return result; } internal void -os_semaphore_release(OS_Handle semaphore) +os_semaphore_release(Semaphore semaphore) { HANDLE handle = (HANDLE)semaphore.u64[0]; CloseHandle(handle); } -internal OS_Handle +internal Semaphore os_semaphore_open(String8 name) { Temp scratch = scratch_begin(0, 0); String16 name16 = str16_from_8(scratch.arena, name); - HANDLE handle = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS , 0, (WCHAR *)name16.str); - OS_Handle result = {(U64)handle}; + HANDLE handle = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, 0, (WCHAR *)name16.str); + Semaphore result = {(U64)handle}; scratch_end(scratch); return result; } internal void -os_semaphore_close(OS_Handle semaphore) +os_semaphore_close(Semaphore semaphore) { HANDLE handle = (HANDLE)semaphore.u64[0]; CloseHandle(handle); } internal B32 -os_semaphore_take(OS_Handle semaphore, U64 endt_us) +os_semaphore_take(Semaphore semaphore, U64 endt_us) { U32 sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); HANDLE handle = (HANDLE)semaphore.u64[0]; @@ -1346,12 +1324,38 @@ os_semaphore_take(OS_Handle semaphore, U64 endt_us) } internal void -os_semaphore_drop(OS_Handle semaphore) +os_semaphore_drop(Semaphore semaphore) { HANDLE handle = (HANDLE)semaphore.u64[0]; ReleaseSemaphore(handle, 1, 0); } +//- rjf: barriers + +internal Barrier +os_barrier_alloc(U64 count) +{ + OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_Barrier); + BOOL init_good = InitializeSynchronizationBarrier(&entity->sb, count, -1); + Barrier result = {IntFromPtr(entity)}; + return result; +} + +internal void +os_barrier_release(Barrier barrier) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(barrier.u64[0]); + DeleteSynchronizationBarrier(&entity->sb); + os_w32_entity_release(entity); +} + +internal void +os_barrier_wait(Barrier barrier) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(barrier.u64[0]); + EnterSynchronizationBarrier(&entity->sb, 0); +} + //////////////////////////////// //~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) @@ -1388,7 +1392,7 @@ os_library_close(OS_Handle lib) //~ rjf: @os_hooks Safe Calls (Implemented Per-OS) internal void -os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr) +os_safe_call(ThreadEntryPointFunctionType *func, ThreadEntryPointFunctionType *fail_handler, void *ptr) { __try { @@ -1693,6 +1697,18 @@ w32_entry_point_caller(int argc, WCHAR **wargv) } } + //- rjf: get RIO extension function table + { + // NOTE(mmozeiko): need to get function pointers to RIO functions, and that requires dummy socket + WSADATA WinSockData; + WSAStartup(MAKEWORD(2, 2), &WinSockData); + GUID guid = WSAID_MULTIPLE_RIO; + DWORD rio_byte = 0; + SOCKET Sock = socket(AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); + WSAIoctl(Sock, SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), (void**)&w32_rio_functions, sizeof(w32_rio_functions), &rio_byte, 0, 0); + closesocket(Sock); + } + //- rjf: get system info SYSTEM_INFO sysinfo = {0}; GetSystemInfo(&sysinfo); @@ -1750,8 +1766,8 @@ w32_entry_point_caller(int argc, WCHAR **wargv) } //- rjf: set up thread context - local_persist TCTX tctx; - tctx_init_and_equip(&tctx); + TCTX *tctx = tctx_alloc(); + tctx_select(tctx); //- rjf: set up dynamically-alloc'd state Arena *arena = arena_alloc(); diff --git a/src/os/core/win32/os_core_win32.h b/src/os/core/win32/os_core_win32.h index 22a21f80..777961c0 100644 --- a/src/os/core/win32/os_core_win32.h +++ b/src/os/core/win32/os_core_win32.h @@ -7,6 +7,8 @@ //////////////////////////////// //~ rjf: Includes / Libraries +#include +#include #include #include #include @@ -20,6 +22,7 @@ #pragma comment(lib, "rpcrt4") #pragma comment(lib, "shlwapi") #pragma comment(lib, "comctl32") +#pragma comment(lib, "ws2_32") #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") // this is required for loading correct comctl32 dll file //////////////////////////////// @@ -46,6 +49,7 @@ typedef enum OS_W32_EntityKind OS_W32_EntityKind_Mutex, OS_W32_EntityKind_RWMutex, OS_W32_EntityKind_ConditionVariable, + OS_W32_EntityKind_Barrier, } OS_W32_EntityKind; @@ -58,7 +62,7 @@ struct OS_W32_Entity { struct { - OS_ThreadFunctionType *func; + ThreadEntryPointFunctionType *func; void *ptr; HANDLE handle; DWORD tid; @@ -66,6 +70,7 @@ struct OS_W32_Entity CRITICAL_SECTION mutex; SRWLOCK rw_mutex; CONDITION_VARIABLE cv; + SYNCHRONIZATION_BARRIER sb; }; }; diff --git a/src/os/gfx/linux/os_gfx_linux.c b/src/os/gfx/linux/os_gfx_linux.c index 12a9aa56..9485225b 100644 --- a/src/os/gfx/linux/os_gfx_linux.c +++ b/src/os/gfx/linux/os_gfx_linux.c @@ -327,6 +327,23 @@ os_dpi_from_window(OS_Handle handle) return 96.f; } +//////////////////////////////// +//~ rjf: @os_hooks External Windows (Implemented Per-OS) + +internal OS_Handle +os_focused_external_window(void) +{ + OS_Handle result = {0}; + // TODO(rjf) + return result; +} + +internal void +os_focus_external_window(OS_Handle handle) +{ + // TODO(rjf) +} + //////////////////////////////// //~ rjf: @os_hooks Monitors (Implemented Per-OS) diff --git a/src/os/gfx/os_gfx.c b/src/os/gfx/os_gfx.c index ec0ccade..3b584ec0 100644 --- a/src/os/gfx/os_gfx.c +++ b/src/os/gfx/os_gfx.c @@ -35,13 +35,9 @@ internal String8List os_string_list_from_modifiers(Arena *arena, OS_Modifiers modifiers) { String8List result = {0}; - String8 modifier_strs[] = - { - str8_lit("Ctrl"), - str8_lit("Shift"), - str8_lit("Alt"), - }; - str8_list_from_flags(arena, &result, modifiers, modifier_strs, ArrayCount(modifier_strs)); + if(modifiers & OS_Modifier_Ctrl) { str8_list_push(arena, &result, str8_lit("Ctrl")); } + if(modifiers & OS_Modifier_Shift) { str8_list_push(arena, &result, str8_lit("Shift")); } + if(modifiers & OS_Modifier_Alt) { str8_list_push(arena, &result, str8_lit("Alt")); } return result; } diff --git a/src/os/gfx/os_gfx.h b/src/os/gfx/os_gfx.h index d56a71ca..8eabfd57 100644 --- a/src/os/gfx/os_gfx.h +++ b/src/os/gfx/os_gfx.h @@ -163,6 +163,12 @@ internal Rng2F32 os_rect_from_window(OS_Handle window); internal Rng2F32 os_client_rect_from_window(OS_Handle window); internal F32 os_dpi_from_window(OS_Handle window); +//////////////////////////////// +//~ rjf: @os_hooks External Windows (Implemented Per-OS) + +internal OS_Handle os_focused_external_window(void); +internal void os_focus_external_window(OS_Handle handle); + //////////////////////////////// //~ rjf: @os_hooks Monitors (Implemented Per-OS) diff --git a/src/os/gfx/stub/os_gfx_stub.c b/src/os/gfx/stub/os_gfx_stub.c index 3a095ab2..e1e9b805 100644 --- a/src/os/gfx/stub/os_gfx_stub.c +++ b/src/os/gfx/stub/os_gfx_stub.c @@ -149,6 +149,23 @@ os_dpi_from_window(OS_Handle window) return 96.f; } +//////////////////////////////// +//~ rjf: @os_hooks External Windows (Implemented Per-OS) + +internal OS_Handle +os_focused_external_window(void) +{ + OS_Handle result = {0}; + // TODO(rjf) + return result; +} + +internal void +os_focus_external_window(OS_Handle handle) +{ + // TODO(rjf) +} + //////////////////////////////// //~ rjf: @os_hooks Monitors (Implemented Per-OS) diff --git a/src/os/gfx/win32/os_gfx_win32.c b/src/os/gfx/win32/os_gfx_win32.c index dac8bde7..5b8ab880 100644 --- a/src/os/gfx/win32/os_gfx_win32.c +++ b/src/os/gfx/win32/os_gfx_win32.c @@ -1367,6 +1367,28 @@ os_dpi_from_window(OS_Handle handle) return result; } +//////////////////////////////// +//~ rjf: @os_hooks External Windows (Implemented Per-OS) + +internal OS_Handle +os_focused_external_window(void) +{ + HWND hwnd = GetForegroundWindow(); + OS_Handle result = {(U64)hwnd}; + return result; +} + +internal void +os_focus_external_window(OS_Handle handle) +{ + HWND hwnd = (HWND)handle.u64[0]; + if(hwnd != 0) + { + SetForegroundWindow(hwnd); + SetFocus(hwnd); + } +} + //////////////////////////////// //~ rjf: @os_hooks Monitors (Implemented Per-OS) diff --git a/src/pdb/pdb_parse.c b/src/pdb/pdb_parse.c index 0caff005..af3b52a5 100644 --- a/src/pdb/pdb_parse.c +++ b/src/pdb/pdb_parse.c @@ -215,18 +215,22 @@ pdb_strtbl_from_data(Arena *arena, String8 data){ return(result); } -internal PDB_DbiParsed* -pdb_dbi_from_data(Arena *arena, String8 data){ - ProfBegin("pdb_dbi_from_data"); +internal PDB_DbiParsed * +pdb_dbi_from_data(Arena *arena, String8 data) +{ + ProfBeginFunction(); + PDB_DbiParsed *result = push_array(arena, PDB_DbiParsed, 1);; - // get header + // rjf: extract header PDB_DbiHeader *header = 0; - if (sizeof(*header) <= data.size){ + if(sizeof(*header) <= data.size) + { header = (PDB_DbiHeader*)data.str; } - PDB_DbiParsed *result = 0; - if (header != 0 && header->sig == PDB_DbiHeaderSignature_V1){ + // rjf: parse + if(header != 0 && header->sig == PDB_DbiHeaderSignature_V1) + { // extract range sizes U64 range_size[PDB_DbiRange_COUNT]; range_size[PDB_DbiRange_ModuleInfo] = header->module_info_size; @@ -238,7 +242,6 @@ pdb_dbi_from_data(Arena *arena, String8 data){ range_size[PDB_DbiRange_DbgHeader] = header->dbg_header_size; // fill result - result = push_array(arena, PDB_DbiParsed, 1); result->data = data; result->machine_type = header->machine; result->gsi_sn = header->gsi_sn; @@ -249,7 +252,8 @@ pdb_dbi_from_data(Arena *arena, String8 data){ // fill result's range offsets { U64 cursor = sizeof(*header); - for (U64 i = 0; i < (U64)(PDB_DbiRange_COUNT); i += 1){ + for(U64 i = 0; i < (U64)(PDB_DbiRange_COUNT); i += 1) + { result->range_off[i] = cursor; cursor += range_size[i]; cursor = ClampTop(cursor, data.size); @@ -263,36 +267,36 @@ pdb_dbi_from_data(Arena *arena, String8 data){ U64 dbg_streams_size_raw = dbg_streams_max - dbg_streams_min; U64 dbg_streams_size = ClampTop(dbg_streams_size_raw, sizeof(result->dbg_streams)); MemoryCopy(result->dbg_streams, data.str + dbg_streams_min, dbg_streams_size); - if (dbg_streams_size < sizeof(result->dbg_streams)){ + if(dbg_streams_size < sizeof(result->dbg_streams)) + { U64 filled_count = dbg_streams_size/sizeof(MSF_StreamNumber); - MemorySet(result->dbg_streams + filled_count, 0xff, - (ArrayCount(result->dbg_streams) - filled_count)*sizeof(MSF_StreamNumber)); + MemorySet(result->dbg_streams + filled_count, 0xff, (ArrayCount(result->dbg_streams) - filled_count)*sizeof(MSF_StreamNumber)); } } - ProfEnd(); - - return(result); + return result; } -internal PDB_TpiParsed* -pdb_tpi_from_data(Arena *arena, String8 data){ - ProfBegin("pdb_tpi_from_data"); +internal PDB_TpiParsed * +pdb_tpi_from_data(Arena *arena, String8 data) +{ + ProfBeginFunction(); + PDB_TpiParsed *result = push_array(arena, PDB_TpiParsed, 1); - // get header + // rjf: extract header PDB_TpiHeader *header = 0; if (sizeof(*header) <= data.size){ header = (PDB_TpiHeader*)data.str; } - PDB_TpiParsed *result = 0; - if (header != 0 && header->version == PDB_TpiVersion_IMPV80){ + // rjf: parse + if(header != 0 && header->version == PDB_TpiVersion_IMPV80) + { U64 leaf_first_raw = header->header_size; U64 leaf_first = ClampTop(leaf_first_raw, data.size); U64 leaf_opl_raw = leaf_first + header->leaf_data_size; U64 leaf_opl = ClampTop(leaf_opl_raw, data.size); - result = push_array(arena, PDB_TpiParsed, 1); result->data = data; result->leaf_first = leaf_first; @@ -313,20 +317,18 @@ pdb_tpi_from_data(Arena *arena, String8 data){ } ProfEnd(); - - return(result); + return result; } internal PDB_TpiHashParsed* -pdb_tpi_hash_from_data(Arena *arena, PDB_Strtbl *strtbl, PDB_TpiParsed *tpi, String8 data, String8 aux_data){ - ProfBegin("pdb_tpi_hash_from_data"); - - PDB_TpiHashParsed *result = 0; - +pdb_tpi_hash_from_data(Arena *arena, PDB_Strtbl *strtbl, PDB_TpiParsed *tpi, String8 data, String8 aux_data) +{ + ProfBeginFunction(); + PDB_TpiHashParsed *result = push_array(arena, PDB_TpiHashParsed, 1); U32 stride = tpi->hash_key_size; U32 bucket_count = tpi->hash_bucket_count; - if (1 <= stride && stride <= 8 && bucket_count > 0 && data.str != 0){ - + if(1 <= stride && stride <= 8 && bucket_count > 0 && data.str != 0) + { // allocate buckets PDB_TpiHashBlock **buckets = push_array(arena, PDB_TpiHashBlock*, bucket_count); @@ -436,32 +438,33 @@ pdb_tpi_hash_from_data(Arena *arena, PDB_Strtbl *strtbl, PDB_TpiParsed *tpi, Str } // fill result - result = push_array(arena, PDB_TpiHashParsed, 1); result->data = data; result->aux_data = aux_data; result->buckets = buckets; result->bucket_count = bucket_count; result->bucket_mask = bucket_mask; } - ProfEnd(); - - return(result); + return result; } -internal PDB_GsiParsed* -pdb_gsi_from_data(Arena *arena, String8 data){ - ProfBegin("pdb_gsi_from_data"); +internal PDB_GsiParsed * +pdb_gsi_from_data(Arena *arena, String8 data) +{ + ProfBeginFunction(); + PDB_GsiParsed *result = push_array(arena, PDB_GsiParsed, 1); - // get header + // rjf: extract header PDB_GsiHeader *header = 0; - if (sizeof(*header) <= data.size){ + if(sizeof(*header) <= data.size) + { header = (PDB_GsiHeader*)data.str; } - PDB_GsiParsed *result = 0; - if (header != 0 && header->signature == PDB_GsiSignature_Basic && - header->version == PDB_GsiVersion_V70 && header->bucket_data_size != 0){ + // rjf: parse + if(header != 0 && header->signature == PDB_GsiSignature_Basic && + header->version == PDB_GsiVersion_V70 && header->bucket_data_size != 0) + { Temp scratch = scratch_begin(&arena, 1); // hash offset @@ -479,7 +482,8 @@ pdb_gsi_from_data(Arena *arena, String8 data){ // get bitmask & packed offset arrays U8 *bitmasks = 0; U8 *packed_offsets = 0; - if (bitmask_off + bitmask_byte_size <= data.size){ + if(bitmask_off + bitmask_byte_size <= data.size) + { bitmasks = (data.str + bitmask_off); packed_offsets = (data.str + offsets_off); } @@ -487,7 +491,8 @@ pdb_gsi_from_data(Arena *arena, String8 data){ // unpack U32 *unpacked_offsets = 0; - if (packed_offsets != 0){ + if(packed_offsets != 0) + { unpacked_offsets = push_array(scratch.arena, U32, slot_count); U32 *bitmask_ptr = (U32*)bitmasks; @@ -496,34 +501,38 @@ pdb_gsi_from_data(Arena *arena, String8 data){ U32 *src_opl = src_ptr + packed_offset_count; U32 *dst_ptr = unpacked_offsets; U32 *dst_opl = dst_ptr + slot_count; - for (; bitmask_ptr < bitmask_opl && src_ptr < src_opl; bitmask_ptr += 1){ + for(; bitmask_ptr < bitmask_opl && src_ptr < src_opl; bitmask_ptr += 1) + { U32 bits = *bitmask_ptr; U32 src_max = (U32)(src_opl - src_ptr); U32 dst_max = (U32)(dst_opl - dst_ptr); U32 k_max0 = ClampTop(32, dst_max); U32 k_max = ClampTop(k_max0, src_max); - for (U32 k = 0; k < k_max; k += 1){ - if ((bits & 1) == 1){ + for(U32 k = 0; k < k_max; k += 1) + { + if((bits & 1) == 1) + { *dst_ptr = *src_ptr; src_ptr += 1; } - else{ + else + { *dst_ptr = 0xFFFFFFFF; } dst_ptr += 1; bits >>= 1; } } - for (; dst_ptr < dst_opl; dst_ptr += 1){ + for(; dst_ptr < dst_opl; dst_ptr += 1) + { *dst_ptr = 0xFFFFFFFF; } } // construct table B32 bad_table = 0; - if (unpacked_offsets != 0){ - result = push_array(arena, PDB_GsiParsed, 1); - + if(unpacked_offsets != 0) + { // hash records PDB_GsiHashRecord *hash_records = (PDB_GsiHashRecord*)(data.str + hash_record_array_off); U32 hash_record_count = header->hash_record_arr_size/sizeof(PDB_GsiHashRecord); @@ -539,13 +548,16 @@ pdb_gsi_from_data(Arena *arena, String8 data){ // build table PDB_GsiHashRecord *hash_record_ptr = hash_records + hash_record_count - 1; U32 prev_n = hash_record_count; - for (U32 i = slot_count; i > 1;){ + for(U32 i = slot_count; i > 1;) + { i -= 1; - if (unpacked_offsets[i] != 0xFFFFFFFF){ + if(unpacked_offsets[i] != 0xFFFFFFFF) + { // determine hash record range to use // * The "12" here is the result of some really sloppy PDB magic. U32 n = unpacked_offsets[i]/12; - if (n > prev_n){ + if(n > prev_n) + { bad_table = 1; break; } @@ -553,7 +565,8 @@ pdb_gsi_from_data(Arena *arena, String8 data){ // fill this bucket U32 *bucket_offs = push_array_aligned(arena, U32, num_steps, 4); - for (U32 j = num_steps; j > 0;){ + for(U32 j = num_steps; j > 0;) + { j -= 1; // * The "- 1" is more sloppy PDB magic. bucket_offs[j] = hash_record_ptr->symbol_off - 1; @@ -568,13 +581,11 @@ pdb_gsi_from_data(Arena *arena, String8 data){ } } } - scratch_end(scratch); } ProfEnd(); - - return(result); + return result; } internal U64 @@ -703,18 +714,20 @@ pdb_comp_unit_array_from_data(Arena *arena, String8 data){ return(result); } -internal PDB_CompUnitContributionArray* +internal PDB_CompUnitContributionArray pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 data, COFF_SectionHeaderArray sections) { PDB_CompUnitContribution *contributions = 0; U64 count = 0; - if (data.size >= sizeof(PDB_DbiSectionContribVersion)){ + if(data.size >= sizeof(PDB_DbiSectionContribVersion)) + { PDB_DbiSectionContribVersion *version = (PDB_DbiSectionContribVersion*)data.str; // determine array layout from version U32 item_size = 0; U32 array_off = 0; - switch (*version){ + switch(*version) + { default: { // TODO(allen): do we have a test case for this? @@ -743,11 +756,12 @@ pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 data, COFF_Sect // fill array PDB_CompUnitContribution *contribution_ptr = contributions; U64 cursor = array_off; - for (; cursor + item_size <= data.size; cursor += item_size){ + for(; cursor + item_size <= data.size; cursor += item_size) + { PDB_DbiSectionContrib40 *sc = (PDB_DbiSectionContrib40*)(data.str + cursor); - if (sc->size > 0 && 1 <= sc->sec && sc->sec <= section_count){ + if(sc->size > 0 && 1 <= sc->sec && sc->sec <= section_count) + { U64 voff = section_headers[sc->sec - 1].voff + sc->sec_off; - contribution_ptr->mod = sc->mod; contribution_ptr->voff_first = voff; contribution_ptr->voff_opl = voff + sc->size; @@ -758,11 +772,10 @@ pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 data, COFF_Sect } // fill result - PDB_CompUnitContributionArray *result = push_array(arena, PDB_CompUnitContributionArray, 1); - result->contributions = contributions; - result->count = count; - - return(result); + PDB_CompUnitContributionArray result = {0}; + result.contributions = contributions; + result.count = count; + return result; } //////////////////////////////// @@ -810,207 +823,209 @@ pdb_leaf_data_from_tpi(PDB_TpiParsed *tpi){ } internal CV_TypeIdArray -pdb_tpi_itypes_from_name(Arena *arena, PDB_TpiHashParsed *tpi_hash, CV_LeafParsed *leaf, - String8 name, B32 compare_unique_name, U32 output_cap){ - U32 hash = pdb_hash_v1(name); - U32 bucket_idx = ((tpi_hash->bucket_mask != 0) ? - hash&tpi_hash->bucket_mask : - hash%tpi_hash->bucket_count); - - CV_TypeId itype_first = leaf->itype_first; - CV_TypeId itype_opl = leaf->itype_opl; - String8 data = leaf->data; - - Temp scratch = scratch_begin(&arena, 1); - struct Chain{ - struct Chain *next; - CV_TypeId itype; - }; - struct Chain *first = 0; - struct Chain *last = 0; - U32 count = 0; - - for (PDB_TpiHashBlock *block = tpi_hash->buckets[bucket_idx]; - block != 0; - block = block->next){ - U32 local_count = block->local_count; - CV_TypeId *itype_ptr = block->itypes; - for (U32 i = 0; i < local_count; i += 1, itype_ptr += 1){ - - String8 extracted_name = {0}; - - CV_TypeId itype = *itype_ptr; - if (itype_first <= itype && itype < itype_opl){ - CV_RecRange *range = &leaf->leaf_ranges.ranges[itype - leaf->itype_first]; - if (range->off + range->hdr.size <= data.size){ - U8 *first = data.str + range->off + 2; - U64 cap = range->hdr.size - 2; - - switch (range->hdr.kind){ - default:break; +pdb_tpi_itypes_from_name(Arena *arena, PDB_TpiHashParsed *tpi_hash, CV_LeafParsed *leaf, String8 name, B32 compare_unique_name, U32 output_cap) +{ + CV_TypeIdArray result = {0}; + if(tpi_hash->bucket_count != 0) + { + U32 hash = pdb_hash_v1(name); + U32 bucket_idx = ((tpi_hash->bucket_mask != 0) ? + hash&tpi_hash->bucket_mask : + hash%tpi_hash->bucket_count); + + CV_TypeId itype_first = leaf->itype_first; + CV_TypeId itype_opl = leaf->itype_opl; + String8 data = leaf->data; + + Temp scratch = scratch_begin(&arena, 1); + struct Chain + { + struct Chain *next; + CV_TypeId itype; + }; + struct Chain *first = 0; + struct Chain *last = 0; + U32 count = 0; + + for (PDB_TpiHashBlock *block = tpi_hash->buckets[bucket_idx]; + block != 0; + block = block->next){ + U32 local_count = block->local_count; + CV_TypeId *itype_ptr = block->itypes; + for (U32 i = 0; i < local_count; i += 1, itype_ptr += 1){ + + String8 extracted_name = {0}; + + CV_TypeId itype = *itype_ptr; + if (itype_first <= itype && itype < itype_opl){ + CV_RecRange *range = &leaf->leaf_ranges.ranges[itype - leaf->itype_first]; + if (range->off + range->hdr.size <= data.size){ + U8 *first = data.str + range->off + 2; + U64 cap = range->hdr.size - 2; - case CV_LeafKind_CLASS: - case CV_LeafKind_STRUCTURE: - { - if (sizeof(CV_LeafStruct) <= cap){ - CV_LeafStruct *lf_struct = (CV_LeafStruct*)first; - - if (!(lf_struct->props & CV_TypeProp_FwdRef)){ - // size - U8 *numeric_ptr = (U8*)(lf_struct + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + switch (range->hdr.kind){ + default:break; + + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + if (sizeof(CV_LeafStruct) <= cap){ + CV_LeafStruct *lf_struct = (CV_LeafStruct*)first; - // name - U8 *name_ptr = numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); - - // unique name - if (compare_unique_name){ - if (lf_struct->props & CV_TypeProp_HasUniqueName) { - U8 *unique_name_ptr = name_ptr + name.size + 1; - String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); - extracted_name = unique_name; + if (!(lf_struct->props & CV_TypeProp_FwdRef)){ + // size + U8 *numeric_ptr = (U8*)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + + // name + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + + // unique name + if (compare_unique_name){ + if (lf_struct->props & CV_TypeProp_HasUniqueName) { + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); + extracted_name = unique_name; + } + } + else{ + extracted_name = name; } } - else{ - extracted_name = name; - } } - } - }break; - - case CV_LeafKind_CLASS2: - case CV_LeafKind_STRUCT2: - { - if (sizeof(CV_LeafStruct2) <= cap){ - CV_LeafStruct2 *lf_struct = (CV_LeafStruct2*)first; - - if (!(lf_struct->props & CV_TypeProp_FwdRef)){ - // size - U8 *numeric_ptr = (U8*)(lf_struct + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + }break; + + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + if (sizeof(CV_LeafStruct2) <= cap){ + CV_LeafStruct2 *lf_struct = (CV_LeafStruct2*)first; - // name - U8 *name_ptr = numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); - - // unique name - if (compare_unique_name){ - if (lf_struct->props & CV_TypeProp_HasUniqueName) { - U8 *unique_name_ptr = name_ptr + name.size + 1; - String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); - extracted_name = unique_name; + if (!(lf_struct->props & CV_TypeProp_FwdRef)){ + // size + U8 *numeric_ptr = (U8*)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + + // name + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + + // unique name + if (compare_unique_name){ + if (lf_struct->props & CV_TypeProp_HasUniqueName) { + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); + extracted_name = unique_name; + } + } + else{ + extracted_name = name; } } - else{ - extracted_name = name; - } } - } - }break; - - case CV_LeafKind_UNION: - { - if (sizeof(CV_LeafUnion) <= cap){ - CV_LeafUnion *lf_union = (CV_LeafUnion*)first; - - if (!(lf_union->props & CV_TypeProp_FwdRef)){ - // size - U8 *numeric_ptr = (U8*)(lf_union + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + }break; + + case CV_LeafKind_UNION: + { + if (sizeof(CV_LeafUnion) <= cap){ + CV_LeafUnion *lf_union = (CV_LeafUnion*)first; - // name - U8 *name_ptr = numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); - - // unique name - if (compare_unique_name){ - if (lf_union->props & CV_TypeProp_HasUniqueName) { - U8 *unique_name_ptr = name_ptr + name.size + 1; - String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); - extracted_name = unique_name; + if (!(lf_union->props & CV_TypeProp_FwdRef)){ + // size + U8 *numeric_ptr = (U8*)(lf_union + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + + // name + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + + // unique name + if (compare_unique_name){ + if (lf_union->props & CV_TypeProp_HasUniqueName) { + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); + extracted_name = unique_name; + } + } + else{ + extracted_name = name; } } - else{ - extracted_name = name; - } } - } - }break; - - case CV_LeafKind_ENUM: - { - if (sizeof(CV_LeafEnum) <= cap){ - CV_LeafEnum *lf_enum = (CV_LeafEnum*)first; - - if (!(lf_enum->props & CV_TypeProp_FwdRef)){ - // name - U8 *name_ptr = (U8*)(lf_enum + 1); - String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + }break; + + case CV_LeafKind_ENUM: + { + if (sizeof(CV_LeafEnum) <= cap){ + CV_LeafEnum *lf_enum = (CV_LeafEnum*)first; - // unique name - if (compare_unique_name){ - if (lf_enum->props & CV_TypeProp_HasUniqueName) { - U8 *unique_name_ptr = name_ptr + name.size + 1; - String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); - extracted_name = unique_name; + if (!(lf_enum->props & CV_TypeProp_FwdRef)){ + // name + U8 *name_ptr = (U8*)(lf_enum + 1); + String8 name = str8_cstring_capped((char*)name_ptr, (char *)(first + cap)); + + // unique name + if (compare_unique_name){ + if (lf_enum->props & CV_TypeProp_HasUniqueName) { + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped((char*)unique_name_ptr, (char *)(first + cap)); + extracted_name = unique_name; + } + } + else{ + extracted_name = name; } } - else{ - extracted_name = name; - } } - } - }break; + }break; + } + } + } + + if (str8_match(extracted_name, name, 0)){ + struct Chain *chain = push_array(scratch.arena, struct Chain, 1); + SLLQueuePush(first, last, chain); + count += 1; + chain->itype = itype; + if (count == output_cap){ + goto dblbreak; } } } - - if (str8_match(extracted_name, name, 0)){ - struct Chain *chain = push_array(scratch.arena, struct Chain, 1); - SLLQueuePush(first, last, chain); - count += 1; - chain->itype = itype; - if (count == output_cap){ - goto dblbreak; - } + } + + dblbreak:; + + + // assemble result + CV_TypeId *itypes = push_array_aligned(arena, CV_TypeId, count, 8); + { + CV_TypeId *itype_ptr = itypes; + for (struct Chain *node = first; + node != 0; + node = node->next, itype_ptr += 1){ + *itype_ptr = node->itype; } } + result.itypes = itypes; + result.count = count; + + scratch_end(scratch); } - - dblbreak:; - - - // assemble result - CV_TypeId *itypes = push_array_aligned(arena, CV_TypeId, count, 8); - { - CV_TypeId *itype_ptr = itypes; - for (struct Chain *node = first; - node != 0; - node = node->next, itype_ptr += 1){ - *itype_ptr = node->itype; - } - } - CV_TypeIdArray result = {0}; - result.itypes = itypes; - result.count = count; - - scratch_end(scratch); - - return(result); + return result; } internal CV_TypeId -pdb_tpi_first_itype_from_name(PDB_TpiHashParsed *tpi_hash, CV_LeafParsed *tpi_leaf, - String8 name, B32 compare_unique_name){ +pdb_tpi_first_itype_from_name(PDB_TpiHashParsed *tpi_hash, CV_LeafParsed *tpi_leaf, String8 name, B32 compare_unique_name) +{ Temp scratch = scratch_begin(0, 0); - CV_TypeIdArray array = pdb_tpi_itypes_from_name(scratch.arena, tpi_hash, tpi_leaf, - name, compare_unique_name, 1); + CV_TypeIdArray array = pdb_tpi_itypes_from_name(scratch.arena, tpi_hash, tpi_leaf, name, compare_unique_name, 1); CV_TypeId result = 0; - if (array.count > 0){ + if(array.count > 0) + { result = array.itypes[0]; } - scratch_end(scratch); return(result); } diff --git a/src/pdb/pdb_parse.h b/src/pdb/pdb_parse.h index 9474c1a3..a01b2592 100644 --- a/src/pdb/pdb_parse.h +++ b/src/pdb/pdb_parse.h @@ -184,6 +184,11 @@ typedef struct PDB_GsiParsed PDB_GsiBucket buckets[4096]; } PDB_GsiParsed; +//////////////////////////////// +//~ rjf: Globals + +read_only global PDB_CompUnit pdb_comp_unit_nil = {0}; + //////////////////////////////// //~ PDB Parser Functions @@ -206,9 +211,7 @@ internal COFF_SectionHeaderArray pdb_coff_section_array_from_data(Arena *arena, internal PDB_CompUnitArray* pdb_comp_unit_array_from_data(Arena *arena, String8 module_info_data); -internal PDB_CompUnitContributionArray* -pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 seccontrib_data, - COFF_SectionHeaderArray sections); +internal PDB_CompUnitContributionArray pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 seccontrib_data, COFF_SectionHeaderArray sections); //////////////////////////////// //~ PDB Dbi Functions diff --git a/src/pe/pe.c b/src/pe/pe.c index 93da7636..1789d154 100644 --- a/src/pe/pe.c +++ b/src/pe/pe.c @@ -25,7 +25,7 @@ pe_slot_count_from_unwind_op_code(PE_UnwindOpCode opcode) return result; } -read_only struct +global read_only struct { String8 string; PE_WindowsSubsystem type; @@ -566,7 +566,7 @@ pe_bin_info_from_data(Arena *arena, String8 data) Assert(!"unable to read data directory"); } } - + // export virtual directory ranges data_dir_vranges = push_array(arena, Rng1U64, data_dir_count); for(U32 dir_idx = 0; dir_idx < data_dir_count; dir_idx += 1) @@ -582,7 +582,7 @@ pe_bin_info_from_data(Arena *arena, String8 data) Assert(!"unable to read data directory"); } } - + // export directory range data_dir_range = rng_1u64(optional_range.min + reported_data_dir_offset, optional_range.min + reported_data_dir_offset + data_dir_count * sizeof(PE_DataDirectory)); } @@ -1009,18 +1009,18 @@ pe_parsed_imports_from_data(Arena *arena, { PE_ParsedImport *imports = 0; U64 import_count = 0; - + U64 name_table_foff = coff_foff_from_voff(sections, section_count, name_table_voff); String8 entries = str8_substr(raw_data, rng_1u64(name_table_foff, raw_data.size)); if (is_pe32) { import_count = index_of_zero_u32((U32 *)entries.str, entries.size/sizeof(U32)); if (import_count == max_U64) { import_count = 0; } imports = push_array(arena, PE_ParsedImport, import_count); - + for (U64 imp_idx = 0; imp_idx < import_count; imp_idx += 1) { U32 raw_entry = 0; str8_deserial_read_struct(entries, imp_idx*sizeof(raw_entry), &raw_entry); - + B32 is_ordinal = ExtractBit(raw_entry, 31); if (is_ordinal) { // fill out ordinal import @@ -1048,11 +1048,11 @@ pe_parsed_imports_from_data(Arena *arena, import_count = index_of_zero_u64((U64 *)entries.str, entries.size/sizeof(U64)); if (import_count == max_U64) { import_count = 0; } imports = push_array(arena, PE_ParsedImport, import_count); - + for (U64 imp_idx = 0; imp_idx < import_count; imp_idx += 1) { U64 raw_entry = 0; str8_deserial_read_struct(entries, imp_idx*sizeof(raw_entry), &raw_entry); - + B32 is_ordinal = ExtractBit(raw_entry, 63); if (is_ordinal) { // fill out ordinal import @@ -1215,7 +1215,7 @@ pe_delay_imports_from_data(Arena *arena, raw_dll->name_table_voff, &import_count); - + // parse bound table Rng1U64 bound_table_range = {0}; if (raw_dll->bound_table_voff) { @@ -1224,7 +1224,7 @@ pe_delay_imports_from_data(Arena *arena, } U64 bound_table_count; U64 *bound_table = pe_array_from_null_term_addr(arena, is_pe32, raw_data, bound_table_range, &bound_table_count); - + // parse unload table Rng1U64 unload_table_range = {0}; if (raw_dll->unload_table_voff) { @@ -1233,7 +1233,7 @@ pe_delay_imports_from_data(Arena *arena, } U64 unload_table_count; U64 *unload_table = pe_array_from_null_term_addr(arena, is_pe32, raw_data, unload_table_range, &unload_table_count); - + // fill out DLL PE_ParsedDelayDLLImport *dll = dlls+dll_idx; dll->attributes = raw_dll->attributes; @@ -1782,12 +1782,12 @@ pe_has_plus_header(COFF_MachineType machine) { B32 has_plus_header = 0; switch (machine) { - case COFF_MachineType_X86: { - has_plus_header = 0; - } break; - case COFF_MachineType_X64: { - has_plus_header = 1; - } break; + case COFF_MachineType_X86: { + has_plus_header = 0; + } break; + case COFF_MachineType_X64: { + has_plus_header = 1; + } break; } return has_plus_header; } @@ -1806,13 +1806,13 @@ pe_pdata_sort(COFF_MachineType machine, String8 raw_pdata) { ProfBeginFunction(); switch (machine) { - case COFF_MachineType_Unknown: break; - case COFF_MachineType_X86: - case COFF_MachineType_X64: { - U64 count = raw_pdata.size / sizeof(PE_IntelPdata); - radsort((PE_IntelPdata *)raw_pdata.str, count, pe_pdata_is_before_x86_64); - } break; - default: { NotImplemented; } break; + case COFF_MachineType_Unknown: break; + case COFF_MachineType_X86: + case COFF_MachineType_X64: { + U64 count = raw_pdata.size / sizeof(PE_IntelPdata); + radsort((PE_IntelPdata *)raw_pdata.str, count, pe_pdata_is_before_x86_64); + } break; + default: { NotImplemented; } break; } ProfEnd(); } diff --git a/src/pe/pe_make_debug_dir.c b/src/pe/pe_make_debug_dir.c index 0da26141..fe716b7c 100644 --- a/src/pe/pe_make_debug_dir.c +++ b/src/pe/pe_make_debug_dir.c @@ -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 String8 diff --git a/src/pe/pe_make_debug_dir.h b/src/pe/pe_make_debug_dir.h index e63a64ca..5ab4d2f5 100644 --- a/src/pe/pe_make_debug_dir.h +++ b/src/pe/pe_make_debug_dir.h @@ -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 PE_MAKE_DEBUG_DIR_H diff --git a/src/pe/pe_make_export_table.c b/src/pe/pe_make_export_table.c index 761a61ae..27e5d9e8 100644 --- a/src/pe/pe_make_export_table.c +++ b/src/pe/pe_make_export_table.c @@ -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 String8 @@ -324,7 +324,7 @@ pe_make_edata_obj(Arena *arena, return obj; } -internal String8List +internal String8 pe_make_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, String8 debug_symbols, PE_ExportParseList export_list) { ProfBeginFunction(); @@ -354,7 +354,7 @@ pe_make_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_s } // serialize lib - String8List lib = coff_lib_writer_serialize(arena, lib_writer, COFF_TimeStamp_Max, 0, /* emit second member: */ 1); + String8 lib = coff_lib_writer_serialize(arena, lib_writer, COFF_TimeStamp_Max, 0, /* emit second member: */ 1); coff_lib_writer_release(&lib_writer); ProfEnd(); diff --git a/src/pe/pe_make_export_table.h b/src/pe/pe_make_export_table.h index 3249d815..6b3d1ff8 100644 --- a/src/pe/pe_make_export_table.h +++ b/src/pe/pe_make_export_table.h @@ -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 PE_MAKE_EXPORT_TABLE_H @@ -57,6 +57,6 @@ typedef struct PE_FinalizedExports internal PE_ExportParsePtrArray pe_array_from_export_list(Arena *arena, PE_ExportParseList list); internal PE_ExportParseNode * pe_export_parse_list_push(Arena *arena, PE_ExportParseList *list, PE_ExportParse data); -internal String8List pe_make_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, String8 debug_symbols, PE_ExportParseList export_list); +internal String8 pe_make_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, String8 debug_symbols, PE_ExportParseList export_list); #endif // COFF_EXPORT_TABLE_H diff --git a/src/pe/pe_make_import_table.c b/src/pe/pe_make_import_table.c index 2c1adcb9..450b4a60 100644 --- a/src/pe/pe_make_import_table.c +++ b/src/pe/pe_make_import_table.c @@ -1,6 +1,22 @@ -// Copyright (c) 2025 Epic Games Tools +// Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +internal void +pe_make_import_header_list_push_node(PE_MakeImportList *list, PE_MakeImportNode *node) +{ + SLLQueuePush(list->first, list->last, node); + list->count += 1; +} + +internal PE_MakeImportNode * +pe_make_import_header_list_push(Arena *arena, PE_MakeImportList *list, PE_MakeImport v) +{ + PE_MakeImportNode *node = push_array(arena, PE_MakeImportNode, 1); + node->v = v; + pe_make_import_header_list_push_node(list, node); + return node; +} + internal COFF_ObjSymbol * pe_make_indirect_jump_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *iat_symbol, String8 thunk_name) { @@ -17,7 +33,7 @@ pe_make_indirect_jump_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *cod static const U64 JMP_OPERAND_OFFSET = 2; coff_obj_writer_section_push_reloc(obj_writer, code_sect, jmp_data_offset + JMP_OPERAND_OFFSET, iat_symbol, COFF_Reloc_X64_Rel32); - COFF_ObjSymbol *jmp_thunk_symbol = coff_obj_writer_push_symbol_extern(obj_writer, thunk_name, jmp_data_offset, code_sect); + COFF_ObjSymbol *jmp_thunk_symbol = coff_obj_writer_push_symbol_extern_func(obj_writer, thunk_name, jmp_data_offset, code_sect); ProfEnd(); return jmp_thunk_symbol; @@ -48,7 +64,7 @@ pe_make_load_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, C // emit symbol String8 thunk_name = push_str8f(obj_writer->arena, "__imp_load_%S", func_name); - COFF_ObjSymbol *load_thunk_symbol = coff_obj_writer_push_symbol_extern(obj_writer, thunk_name, load_thunk_data_offset, code_sect); + COFF_ObjSymbol *load_thunk_symbol = coff_obj_writer_push_symbol_extern_func(obj_writer, thunk_name, load_thunk_data_offset, code_sect); ProfEnd(); return load_thunk_symbol; @@ -237,7 +253,7 @@ pe_make_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_ } internal String8 -pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 debug_symbols, String8List import_headers) +pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 debug_symbols, PE_MakeImportList import_headers) { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); @@ -252,7 +268,7 @@ pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mach COFF_ObjSection *iat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$5"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); COFF_ObjSection *int_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$6"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); COFF_ObjSection *dll_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$7"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); - COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$i"), PE_TEXT_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); + COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$zz"), PE_TEXT_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); COFF_ObjSymbol *ilt_symbol = coff_obj_writer_push_symbol_static(obj_writer, ilt_sect->name, 0, ilt_sect); COFF_ObjSymbol *iat_symbol = coff_obj_writer_push_symbol_static(obj_writer, iat_sect->name, 0, iat_sect); @@ -263,8 +279,8 @@ pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mach coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_ImportEntry, name_voff), dll_name_symbol); coff_obj_writer_section_push_reloc_voff(obj_writer, dll_sect, OffsetOf(PE_ImportEntry, import_addr_table_voff), iat_symbol); - for (String8Node *import_header_n = import_headers.first; import_header_n != 0; import_header_n = import_header_n->next) { - COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(import_header_n->string); + for (PE_MakeImportNode *import_header_n = import_headers.first; import_header_n != 0; import_header_n = import_header_n->next) { + COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(import_header_n->v.header); COFF_ObjSymbol *iat_symbol = 0; switch (import_header.import_by) { @@ -300,12 +316,15 @@ pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mach } // emit thunks - COFF_ObjSymbol *jmp_thunk_symbol = 0; - if (import_header.type == COFF_ImportHeader_Code) { - switch (import_header.machine) { - case COFF_MachineType_Unknown: {} break; - case COFF_MachineType_X64: { jmp_thunk_symbol = pe_make_indirect_jump_thunk_x64(obj_writer, code_sect, iat_symbol, import_header.func_name); } break; - default: { NotImplemented; } break; + if (import_header_n->v.make_jump_thunk) { + if (import_header.type == COFF_ImportHeader_Code) { + switch (import_header.machine) { + case COFF_MachineType_Unknown: {} break; + case COFF_MachineType_X64: { pe_make_indirect_jump_thunk_x64(obj_writer, code_sect, iat_symbol, import_header.func_name); } break; + default: { NotImplemented; } break; + } + } else { + Assert(0 && "unable to make a jump thunk for non-code target"); } } } @@ -314,12 +333,13 @@ pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mach str8_list_push(obj_writer->arena, &iat_sect->data, str8(0, coff_word_size_from_machine(machine))); String8 dll_obj = coff_obj_writer_serialize(arena, obj_writer); + coff_obj_writer_release(&obj_writer); return dll_obj; } internal String8 -pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 delay_load_helper_name, String8 debug_symbols, String8List import_headers, B32 emit_biat, B32 emit_uiat) +pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 delay_load_helper_name, String8 debug_symbols, PE_MakeImportList import_headers, B32 emit_biat, B32 emit_uiat) { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine); @@ -385,11 +405,10 @@ pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mac default: { NotImplemented; } break; } - for (String8Node *import_header_n = import_headers.first; import_header_n != 0; import_header_n = import_header_n->next) { - COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(import_header_n->string); + for (PE_MakeImportNode *import_header_n = import_headers.first; import_header_n != 0; import_header_n = import_header_n->next) { + COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(import_header_n->v.header); // emit thunks - COFF_ObjSymbol *jmp_thunk_symbol = 0; COFF_ObjSymbol *load_thunk_symbol = 0; if (import_header.type == COFF_ImportHeader_Code) { switch (machine) { @@ -398,8 +417,11 @@ pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mac String8 iat_symbol_name = push_str8f(obj_writer->arena, "__imp_%S", import_header.func_name); iat_symbol = coff_obj_writer_push_symbol_extern(obj_writer, iat_symbol_name, iat_sect->data.total_size, iat_sect); - // emit thunks - jmp_thunk_symbol = pe_make_indirect_jump_thunk_x64(obj_writer, code_sect, iat_symbol, import_header.func_name); + if (import_header_n->v.make_jump_thunk) { + pe_make_indirect_jump_thunk_x64(obj_writer, code_sect, iat_symbol, import_header.func_name); + } + + // emit load thunk load_thunk_symbol = pe_make_load_thunk_x64(obj_writer, code_sect, iat_symbol, tail_merge_symbol, import_header.func_name); } break; default: { NotImplemented; } break; diff --git a/src/pe/pe_make_import_table.h b/src/pe/pe_make_import_table.h index 5ff7006e..c1a48c38 100644 --- a/src/pe/pe_make_import_table.h +++ b/src/pe/pe_make_import_table.h @@ -1,15 +1,39 @@ -// Copyright (c) 2025 Epic Games Tools +// Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) #ifndef PE_MAKE_IMPORT_TABLE_H #define PE_MAKE_IMPORT_TABLE_H +typedef struct PE_MakeImport +{ + String8 header; + B32 make_jump_thunk; +} PE_MakeImport; + +typedef struct PE_MakeImportNode +{ + PE_MakeImport v; + struct PE_MakeImportNode *next; +} PE_MakeImportNode; + +typedef struct PE_MakeImportList +{ + U64 count; + PE_MakeImportNode *first; + PE_MakeImportNode *last; +} PE_MakeImportList; + +// ----------------------------------------------------------------------------- + +internal void pe_make_import_header_list_push_node(PE_MakeImportList *list, PE_MakeImportNode *node); +internal PE_MakeImportNode * pe_make_import_header_list_push(Arena *arena, PE_MakeImportList *list, PE_MakeImport header); + internal COFF_ObjSymbol * pe_make_indirect_jump_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *iat_symbol, String8 thunk_name); internal COFF_ObjSymbol * pe_make_load_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, COFF_ObjSymbol *imp_addr_ptr, COFF_ObjSymbol *tail_merge, String8 func_name); internal COFF_ObjSymbol * pe_make_tail_merge_thunk_x64(COFF_ObjWriter *obj_writer, COFF_ObjSection *code_sect, String8 dll_name, String8 delay_load_helper_name, COFF_ObjSymbol *dll_import_descriptor); -internal String8 pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stmap, COFF_MachineType machine, String8 dll_name, String8 debug_symbols, String8List import_headers); -internal String8 pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 delay_load_helper_name, String8 debug_symbols, String8List import_headers, B32 emit_biat, B32 emit_uiat); +internal String8 pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stmap, COFF_MachineType machine, String8 dll_name, String8 debug_symbols, PE_MakeImportList import_headers); +internal String8 pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 dll_name, String8 delay_load_helper_name, String8 debug_symbols, PE_MakeImportList import_headers, B32 emit_biat, B32 emit_uiat); internal String8 pe_make_import_entry_obj_delayed(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols); internal String8 pe_make_null_import_descriptor_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols); @@ -19,4 +43,4 @@ internal String8 pe_make_import_entry_obj(Arena *arena, String8 dll_name, COFF_T internal String8 pe_make_null_import_descriptor_obj(Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols); internal String8 pe_make_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 debug_symbols); -#endif // PE_MAKE_IMPORT_TABLE_H \ No newline at end of file +#endif // PE_MAKE_IMPORT_TABLE_H diff --git a/src/pe/pe_section_flags.h b/src/pe/pe_section_flags.h index b6adfff1..cd835b38 100644 --- a/src/pe/pe_section_flags.h +++ b/src/pe/pe_section_flags.h @@ -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 PE_SECTION_FLAGS_H diff --git a/src/ptr_graph_cache/ptr_graph_cache.c b/src/ptr_graph_cache/ptr_graph_cache.c deleted file mode 100644 index 943f563c..00000000 --- a/src/ptr_graph_cache/ptr_graph_cache.c +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void -ptg_init(void) -{ - Arena *arena = arena_alloc(); - ptg_shared = push_array(arena, PTG_Shared, 1); - ptg_shared->arena = arena; - ptg_shared->slots_count = 1024; - ptg_shared->stripes_count = Min(ptg_shared->slots_count, os_get_system_info()->logical_processor_count); - ptg_shared->slots = push_array(arena, PTG_GraphSlot, ptg_shared->slots_count); - ptg_shared->stripes = push_array(arena, PTG_GraphStripe, ptg_shared->stripes_count); - for(U64 idx = 0; idx < ptg_shared->stripes_count; idx += 1) - { - ptg_shared->stripes[idx].arena = arena_alloc(); - ptg_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc(); - ptg_shared->stripes[idx].cv = os_condition_variable_alloc(); - } - ptg_shared->u2b_ring_size = KB(64); - ptg_shared->u2b_ring_base = push_array_no_zero(arena, U8, ptg_shared->u2b_ring_size); - ptg_shared->u2b_ring_cv = os_condition_variable_alloc(); - ptg_shared->u2b_ring_mutex = os_mutex_alloc(); - ptg_shared->builder_thread_count = Clamp(1, os_get_system_info()->logical_processor_count-1, 4); - ptg_shared->builder_threads = push_array(arena, OS_Handle, ptg_shared->builder_thread_count); - for(U64 idx = 0; idx < ptg_shared->builder_thread_count; idx += 1) - { - ptg_shared->builder_threads[idx] = os_thread_launch(ptg_builder_thread__entry_point, (void *)idx, 0); - } - ptg_shared->evictor_thread = os_thread_launch(ptg_evictor_thread__entry_point, 0, 0); -} - -//////////////////////////////// -//~ rjf: User Clock - -internal void -ptg_user_clock_tick(void) -{ - ins_atomic_u64_inc_eval(&ptg_shared->user_clock_idx); -} - -internal U64 -ptg_user_clock_idx(void) -{ - return ins_atomic_u64_eval(&ptg_shared->user_clock_idx); -} - -//////////////////////////////// -//~ rjf: Scoped Access - -internal PTG_Scope * -ptg_scope_open(void) -{ - if(ptg_tctx == 0) - { - Arena *arena = arena_alloc(); - ptg_tctx = push_array(arena, PTG_TCTX, 1); - ptg_tctx->arena = arena; - } - PTG_Scope *scope = ptg_tctx->free_scope; - if(scope) - { - SLLStackPop(ptg_tctx->free_scope); - } - else - { - scope = push_array_no_zero(ptg_tctx->arena, PTG_Scope, 1); - } - MemoryZeroStruct(scope); - return scope; -} - -internal void -ptg_scope_close(PTG_Scope *scope) -{ - for(PTG_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next) - { - next = touch->next; - ins_atomic_u64_dec_eval(&touch->node->scope_ref_count); - SLLStackPush(ptg_tctx->free_touch, touch); - } - SLLStackPush(ptg_tctx->free_scope, scope); -} - -internal void -ptg_scope_touch_node__stripe_r_guarded(PTG_Scope *scope, PTG_GraphNode *node) -{ - PTG_Touch *touch = ptg_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, ptg_user_clock_idx()); - if(touch != 0) - { - SLLStackPop(ptg_tctx->free_touch); - } - else - { - touch = push_array_no_zero(ptg_tctx->arena, PTG_Touch, 1); - } - MemoryZeroStruct(touch); - touch->node = node; - SLLStackPush(scope->top_touch, touch); -} - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal PTG_Graph * -ptg_graph_from_key(PTG_Scope *scope, PTG_Key *key) -{ - PTG_Graph *g = 0; - return g; -} - -//////////////////////////////// -//~ rjf: Transfer Threads - -internal B32 -ptg_u2b_enqueue_req(PTG_Key *key, U64 endt_us) -{ - B32 good = 0; - OS_MutexScope(ptg_shared->u2b_ring_mutex) for(;;) - { - U64 unconsumed_size = ptg_shared->u2b_ring_write_pos-ptg_shared->u2b_ring_read_pos; - U64 available_size = ptg_shared->u2b_ring_size-unconsumed_size; - if(available_size >= sizeof(key)) - { - good = 1; - ptg_shared->u2b_ring_write_pos += ring_write_struct(ptg_shared->u2b_ring_base, ptg_shared->u2b_ring_size, ptg_shared->u2b_ring_write_pos, &key); - break; - } - if(os_now_microseconds() >= endt_us) - { - break; - } - os_condition_variable_wait(ptg_shared->u2b_ring_cv, ptg_shared->u2b_ring_mutex, endt_us); - } - if(good) - { - os_condition_variable_broadcast(ptg_shared->u2b_ring_cv); - } - return good; -} - -internal void -ptg_u2b_dequeue_req(PTG_Key *key_out) -{ - OS_MutexScope(ptg_shared->u2b_ring_mutex) for(;;) - { - U64 unconsumed_size = ptg_shared->u2b_ring_write_pos-ptg_shared->u2b_ring_read_pos; - if(unconsumed_size >= sizeof(*key_out)) - { - ptg_shared->u2b_ring_read_pos += ring_read_struct(ptg_shared->u2b_ring_base, ptg_shared->u2b_ring_size, ptg_shared->u2b_ring_read_pos, key_out); - break; - } - os_condition_variable_wait(ptg_shared->u2b_ring_cv, ptg_shared->u2b_ring_mutex, max_U64); - } - os_condition_variable_broadcast(ptg_shared->u2b_ring_cv); -} - -internal void -ptg_builder_thread__entry_point(void *p) -{ - for(;;) - { - HS_Scope *scope = hs_scope_open(); - - //- rjf: get next key - PTG_Key key = {0}; - ptg_u2b_dequeue_req(&key); - - //- rjf: unpack hash - U64 slot_idx = key.root_hash.u64[1]%ptg_shared->slots_count; - U64 stripe_idx = slot_idx%ptg_shared->stripes_count; - PTG_GraphSlot *slot = &ptg_shared->slots[slot_idx]; - PTG_GraphStripe *stripe = &ptg_shared->stripes[stripe_idx]; - - //- rjf: take task - B32 got_task = 0; - OS_MutexScopeR(stripe->rw_mutex) - { - for(PTG_GraphNode *n = slot->first; n != 0; n = n->next) - { - if(MemoryMatchStruct(&n->key, &key)) - { - got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0); - break; - } - } - } - - //- rjf: do task - if(got_task) - { - - } - - - //- rjf: commit results to cache - if(got_task) OS_MutexScopeW(stripe->rw_mutex) - { - for(PTG_GraphNode *n = slot->first; n != 0; n = n->next) - { - if(MemoryMatchStruct(&n->key, &key)) - { - - ins_atomic_u32_eval_assign(&n->is_working, 0); - ins_atomic_u64_inc_eval(&n->load_count); - break; - } - } - } - - hs_scope_close(scope); - } -} - -//////////////////////////////// -//~ rjf: Evictor Threads - -internal void -ptg_evictor_thread__entry_point(void *p) -{ - for(;;) - { - U64 check_time_us = os_now_microseconds(); - U64 check_time_user_clocks = ptg_user_clock_idx(); - U64 evict_threshold_us = 10*1000000; - U64 evict_threshold_user_clocks = 10; - for(U64 slot_idx = 0; slot_idx < ptg_shared->slots_count; slot_idx += 1) - { - U64 stripe_idx = slot_idx%ptg_shared->stripes_count; - PTG_GraphSlot *slot = &ptg_shared->slots[slot_idx]; - PTG_GraphStripe *stripe = &ptg_shared->stripes[stripe_idx]; - B32 slot_has_work = 0; - OS_MutexScopeR(stripe->rw_mutex) - { - for(PTG_GraphNode *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(PTG_GraphNode *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); - arena_clear(n->arena); - SLLStackPush(stripe->free_node, n); - } - } - } - os_sleep_milliseconds(5); - } - os_sleep_milliseconds(1000); - } -} diff --git a/src/ptr_graph_cache/ptr_graph_cache.h b/src/ptr_graph_cache/ptr_graph_cache.h deleted file mode 100644 index 96193eae..00000000 --- a/src/ptr_graph_cache/ptr_graph_cache.h +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef PTR_GRAPH_CACHE_H -#define PTR_GRAPH_CACHE_H - -//////////////////////////////// -//~ rjf: Graph Search Key - -typedef struct PTG_Key PTG_Key; -struct PTG_Key -{ - U128 root_hash; - U64 link_offsets[8]; - U64 link_offsets_count; -}; - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct PTG_Node PTG_Node; -struct PTG_Node -{ - U64 value; -}; - -typedef struct PTG_Link PTG_Link; -struct PTG_Link -{ - U32 from; - U32 to; -}; - -typedef struct PTG_NodeChunkNode PTG_NodeChunkNode; -struct PTG_NodeChunkNode -{ - PTG_NodeChunkNode *next; - PTG_Node *v; - U64 count; - U64 cap; -}; - -typedef struct PTG_NodeChunkList PTG_NodeChunkList; -struct PTG_NodeChunkList -{ - PTG_NodeChunkNode *first; - PTG_NodeChunkNode *last; - U64 chunk_count; - U64 total_count; -}; - -typedef struct PTG_NodeArray PTG_NodeArray; -struct PTG_NodeArray -{ - PTG_Node *v; - U64 count; -}; - -typedef struct PTG_LinkChunkNode PTG_LinkChunkNode; -struct PTG_LinkChunkNode -{ - PTG_LinkChunkNode *next; - PTG_Link *v; - U64 count; - U64 cap; -}; - -typedef struct PTG_LinkChunkList PTG_LinkChunkList; -struct PTG_LinkChunkList -{ - PTG_LinkChunkNode *first; - PTG_LinkChunkNode *last; - U64 chunk_count; - U64 total_count; -}; - -typedef struct PTG_LinkArray PTG_LinkArray; -struct PTG_LinkArray -{ - PTG_Link *v; - U64 count; -}; - -typedef struct PTG_Graph PTG_Graph; -struct PTG_Graph -{ - PTG_NodeArray nodes; - PTG_LinkArray links; -}; - -typedef struct PTG_GraphNode PTG_GraphNode; -struct PTG_GraphNode -{ - // rjf: links - PTG_GraphNode *next; - PTG_GraphNode *prev; - - // rjf: key - PTG_Key key; - - // rjf: metadata - U64 scope_ref_count; - U64 last_time_touched_us; - U64 last_user_clock_idx_touched; - U64 load_count; - B32 is_working; - - // rjf: content - Arena *arena; - PTG_Graph graph; -}; - -typedef struct PTG_GraphSlot PTG_GraphSlot; -struct PTG_GraphSlot -{ - PTG_GraphNode *first; - PTG_GraphNode *last; -}; - -typedef struct PTG_GraphStripe PTG_GraphStripe; -struct PTG_GraphStripe -{ - Arena *arena; - OS_Handle rw_mutex; - OS_Handle cv; - PTG_GraphNode *free_node; -}; - -//////////////////////////////// -//~ rjf: Scoped Access Types - -typedef struct PTG_Touch PTG_Touch; -struct PTG_Touch -{ - PTG_Touch *next; - PTG_GraphNode *node; -}; - -typedef struct PTG_Scope PTG_Scope; -struct PTG_Scope -{ - PTG_Scope *next; - PTG_Touch *top_touch; -}; - -//////////////////////////////// -//~ rjf: Thread Context - -typedef struct PTG_TCTX PTG_TCTX; -struct PTG_TCTX -{ - Arena *arena; - PTG_Scope *free_scope; - PTG_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State - -typedef struct PTG_Shared PTG_Shared; -struct PTG_Shared -{ - Arena *arena; - - // rjf: user clock - U64 user_clock_idx; - - // rjf: cache - U64 slots_count; - U64 stripes_count; - PTG_GraphSlot *slots; - PTG_GraphStripe *stripes; - - // rjf: user -> xfer thread - U64 u2b_ring_size; - U8 *u2b_ring_base; - U64 u2b_ring_write_pos; - U64 u2b_ring_read_pos; - OS_Handle u2b_ring_cv; - OS_Handle u2b_ring_mutex; - - // rjf: builder threads - U64 builder_thread_count; - OS_Handle *builder_threads; - - // rjf: evictor thread - OS_Handle evictor_thread; -}; - -//////////////////////////////// -//~ rjf: Globals - -thread_static PTG_TCTX *ptg_tctx = 0; -global PTG_Shared *ptg_shared = 0; - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void ptg_init(void); - -//////////////////////////////// -//~ rjf: User Clock - -internal void ptg_user_clock_tick(void); -internal U64 ptg_user_clock_idx(void); - -//////////////////////////////// -//~ rjf: Scoped Access - -internal PTG_Scope *ptg_scope_open(void); -internal void ptg_scope_close(PTG_Scope *scope); -internal void ptg_scope_touch_node__stripe_r_guarded(PTG_Scope *scope, PTG_GraphNode *node); - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal PTG_Graph *ptg_graph_from_key(PTG_Scope *scope, PTG_Key *key); - -//////////////////////////////// -//~ rjf: Transfer Threads - -internal B32 ptg_u2b_enqueue_req(PTG_Key *key, U64 endt_us); -internal void ptg_u2b_dequeue_req(PTG_Key *key_out); -internal void ptg_builder_thread__entry_point(void *p); - -//////////////////////////////// -//~ rjf: Evictor Threads - -internal void ptg_evictor_thread__entry_point(void *p); - -#endif // PTR_GRAPH_CACHE_H diff --git a/src/radbin/radbin.c b/src/radbin/radbin.c index 63bb4d84..77d59968 100644 --- a/src/radbin/radbin.c +++ b/src/radbin/radbin.c @@ -7,22 +7,68 @@ #include "radbin/generated/radbin.meta.c" //////////////////////////////// -//~ rjf: Top-Level Entry Point +//~ rjf: Top-Level Entry Points internal void rb_entry_point(CmdLine *cmdline) { + Temp scratch = scratch_begin(0, 0); + U64 threads_count = os_get_system_info()->logical_processor_count; + String8 threads_count_from_cmdline_string = cmd_line_string(cmdline, str8_lit("thread_count")); + if(threads_count_from_cmdline_string.size != 0) + { + U64 threads_count_from_cmdline = 0; + if(try_u64_from_str8_c_rules(threads_count_from_cmdline_string, &threads_count_from_cmdline)) + { + threads_count = threads_count_from_cmdline; + } + } + Thread *threads = push_array(scratch.arena, Thread, threads_count); + RB_ThreadParams *threads_params = push_array(scratch.arena, RB_ThreadParams, threads_count); + Barrier barrier = barrier_alloc(threads_count); + U64 broadcast_val = 0; + for EachIndex(idx, threads_count) + { + threads_params[idx].cmdline = cmdline; + threads_params[idx].lane_ctx.lane_idx = idx; + threads_params[idx].lane_ctx.lane_count = threads_count; + threads_params[idx].lane_ctx.barrier = barrier; + threads_params[idx].lane_ctx.broadcast_memory = &broadcast_val; + threads[idx] = thread_launch(rb_thread_entry_point, &threads_params[idx]); + } + for EachIndex(idx, threads_count) + { + thread_join(threads[idx], max_U64); + } + scratch_end(scratch); +} + +internal void +rb_thread_entry_point(void *p) +{ + RB_ThreadParams *params = (RB_ThreadParams *)p; + CmdLine *cmdline = params->cmdline; + LaneCtx lctx = params->lane_ctx; + ThreadNameF("radbin_thread_%I64u", lctx.lane_idx); + lane_ctx(lctx); Arena *arena = arena_alloc(); - ASYNC_Root *async_root = async_root_alloc(); Log *log = log_alloc(); log_select(log); log_scope_begin(); + ////////////////////////////// + //- rjf: set up shared state + // + if(lane_idx() == 0) + { + rb_shared = push_array(arena, RB_Shared, 1); + } + lane_sync(); + ////////////////////////////// //- rjf: analyze & load command line input files // - RB_FileList input_files = {0}; - ProfScope("analyze & load command line input files") + ProfScope("analyze & load command line input files") if(lane_idx() == 0) { String8List input_file_path_tasks = str8_list_copy(arena, &cmdline->inputs); for(String8Node *n = input_file_path_tasks.first; n != 0; n = n->next) @@ -34,7 +80,7 @@ rb_entry_point(CmdLine *cmdline) RB_FileFormatFlags file_format_flags = 0; ProfScope("do thin analysis of file") { - OS_Handle file = os_file_open(OS_AccessFlag_Read, n->string); + OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, n->string); FileProperties props = os_properties_from_file(file); //- rjf: PDB magic -> PDB input @@ -318,23 +364,29 @@ rb_entry_point(CmdLine *cmdline) f->data = file_data; RB_FileNode *file_n = push_array(arena, RB_FileNode, 1); file_n->v = f; - SLLQueuePush(input_files.first, input_files.last, file_n); - input_files.count += 1; + SLLQueuePush(rb_shared->input_files.first, rb_shared->input_files.last, file_n); + rb_shared->input_files.count += 1; } } } + lane_sync(); + RB_FileList input_files = rb_shared->input_files; ////////////////////////////// //- rjf: bucket input files by format // - RB_FileList input_files_from_format_table[RB_FileFormat_COUNT] = {0}; - for(RB_FileNode *n = input_files.first; n != 0; n = n->next) + ProfScope("bucket input files by format") if(lane_idx() == 0) { - RB_FileNode *file_n = push_array(arena, RB_FileNode, 1); - file_n->v = n->v; - SLLQueuePush(input_files_from_format_table[n->v->format].first, input_files_from_format_table[n->v->format].last, file_n); - input_files_from_format_table[n->v->format].count += 1; + for(RB_FileNode *n = input_files.first; n != 0; n = n->next) + { + RB_FileNode *file_n = push_array(arena, RB_FileNode, 1); + file_n->v = n->v; + SLLQueuePush(rb_shared->input_files_from_format_table[n->v->format].first, rb_shared->input_files_from_format_table[n->v->format].last, file_n); + rb_shared->input_files_from_format_table[n->v->format].count += 1; + } } + lane_sync(); + RB_FileList *input_files_from_format_table = rb_shared->input_files_from_format_table; ////////////////////////////// //- rjf: unpack which kind of output we're producing, and to where @@ -400,17 +452,21 @@ rb_entry_point(CmdLine *cmdline) ////////////////////////////// //- rjf: print help preamble // - if(output_kind == OutputKind_Null || cmdline->inputs.node_count == 0) + if(lane_idx() == 0) { - fprintf(stderr, "%s\n", BUILD_TITLE); - fprintf(stderr, "%s\n\n", BUILD_VERSION_STRING_LITERAL); - if(output_kind != OutputKind_Null) + if(output_kind == OutputKind_Null || cmdline->inputs.node_count == 0) { - fprintf(stderr, "%.*s Help\n", str8_varg(output_kind_info[output_kind].title)); - fprintf(stderr, "To see top-level options for radbin, run the binary with no arguments.\n\n"); + fprintf(stderr, "%s\n", BUILD_TITLE); + fprintf(stderr, "%s\n\n", BUILD_VERSION_STRING_LITERAL); + if(output_kind != OutputKind_Null) + { + fprintf(stderr, "%.*s Help\n", str8_varg(output_kind_info[output_kind].title)); + fprintf(stderr, "To see top-level options for radbin, run the binary with no arguments.\n\n"); + } + fprintf(stderr, "-------------------------------------------------------------------------------\n\n"); } - fprintf(stderr, "-------------------------------------------------------------------------------\n\n"); } + lane_sync(); ////////////////////////////// //- rjf: perform operation based on output kind @@ -423,6 +479,7 @@ rb_entry_point(CmdLine *cmdline) // default: case OutputKind_Null: + if(lane_idx() == 0) { fprintf(stderr, "USAGE EXAMPLES\n\n"); @@ -479,7 +536,7 @@ rb_entry_point(CmdLine *cmdline) case OutputKind_Breakpad: { //- rjf: no inputs => help - if(cmdline->inputs.node_count == 0) switch(output_kind) + if(lane_idx() == 0 && cmdline->inputs.node_count == 0) switch(output_kind) { default: case OutputKind_RDI: @@ -541,7 +598,7 @@ rb_entry_point(CmdLine *cmdline) }break; case OutputKind_Breakpad: { - subset_flags = RDIM_SubsetFlag_All & ~(RDIM_SubsetFlag_Types|RDIM_SubsetFlag_UDTs); + subset_flags = (RDIM_SubsetFlag_Units|RDIM_SubsetFlag_Procedures|RDIM_SubsetFlag_Scopes|RDIM_SubsetFlag_LineInfo|RDIM_SubsetFlag_InlineLineInfo); }break; } @@ -638,7 +695,7 @@ rb_entry_point(CmdLine *cmdline) convert_params.subset_flags = subset_flags; convert_params.deterministic = cmd_line_has_flag(cmdline, str8_lit("deterministic")); } - ProfScope("convert") bake_params = d2r_convert(arena, async_root, &convert_params); + ProfScope("convert") bake_params = d2r_convert(arena, &convert_params); // rjf: no output path? -> pick one based on debug if(output_path.size == 0) @@ -672,7 +729,7 @@ rb_entry_point(CmdLine *cmdline) convert_params.subset_flags = subset_flags; convert_params.deterministic = cmd_line_has_flag(cmdline, str8_lit("deterministic")); } - ProfScope("convert") bake_params = p2r_convert(arena, async_root, &convert_params); + ProfScope("convert") bake_params = p2r_convert(arena, &convert_params); // rjf: no output path? -> pick one based on PDB if(output_path.size == 0) switch(output_kind) @@ -696,6 +753,13 @@ rb_entry_point(CmdLine *cmdline) log_user_errorf("Could not load debug info from the specified inputs. You must provide either a valid PDB file or an executable image (PE, ELF) file with DWARF debug info."); } + //- rjf: bake + RDIM_BakeResults bake_results = {0}; + if(convert_done) ProfScope("bake") + { + bake_results = rdim_bake(arena, &bake_params); + } + //- rjf: convert done => generate output if(convert_done) switch(output_kind) { @@ -704,10 +768,6 @@ rb_entry_point(CmdLine *cmdline) //- rjf: generate RDI blobs case OutputKind_RDI: { - // rjf: bake - RDIM_BakeResults bake_results = {0}; - ProfScope("bake") bake_results = rdim_bake(arena, async_root, &bake_params); - // rjf: serialize RDIM_SerializedSectionBundle serialized_section_bundle = {0}; ProfScope("serialize") serialized_section_bundle = rdim_serialized_section_bundle_from_bake_results(&bake_results); @@ -727,89 +787,178 @@ rb_entry_point(CmdLine *cmdline) //- rjf: generate breakpad text case OutputKind_Breakpad: { - p2b_async_root = async_root; - String8List dump = {0}; - - //- rjf: kick off unit vmap baking - P2B_BakeUnitVMapIn bake_unit_vmap_in = {&bake_params.units}; - ASYNC_Task *bake_unit_vmap_task = async_task_launch(arena, p2b_bake_unit_vmap_work, .input = &bake_unit_vmap_in); - - //- rjf: kick off line-table baking - P2B_BakeLineTablesIn bake_line_tables_in = {&bake_params.line_tables}; - ASYNC_Task *bake_line_tables_task = async_task_launch(arena, p2b_bake_line_table_work, .input = &bake_line_tables_in); - - //- rjf: build unit -> line table idx array - U64 unit_count = bake_params.units.total_count; - U32 *unit_line_table_idxs = push_array(arena, U32, unit_count+1); + //- rjf: set up shared state + typedef struct P2B_Shared P2B_Shared; + struct P2B_Shared { - U64 dst_idx = 1; - for(RDIM_UnitChunkNode *n = bake_params.units.first; n != 0; n = n->next) - { - for(U64 n_idx = 0; n_idx < n->count; n_idx += 1, dst_idx += 1) - { - unit_line_table_idxs[dst_idx] = rdim_idx_from_line_table(n->v[n_idx].line_table); - } - } + String8List dump; + String8List *lane_chunk_file_dumps; + String8List *lane_chunk_func_dumps; + }; + local_persist P2B_Shared *p2b_shared = 0; + if(lane_idx() == 0) + { + p2b_shared = push_array(arena, P2B_Shared, 1); + p2b_shared->lane_chunk_file_dumps = push_array(arena, String8List, lane_count()*bake_params.src_files.chunk_count); + p2b_shared->lane_chunk_func_dumps = push_array(arena, String8List, lane_count()*bake_params.procedures.chunk_count); } + lane_sync(); //- rjf: dump MODULE record - str8_list_pushf(arena, &dump, "MODULE windows x86_64 %I64x %S\n", bake_params.top_level_info.exe_hash, bake_params.top_level_info.exe_name); + if(lane_idx() == 0) + { + // rjf: pick name to identify module + String8 module_name_string = bake_params.top_level_info.exe_name; + if(module_name_string.size == 0 && input_files.first != 0) + { + module_name_string = input_files.first->v->path; + } + + // rjf: pick string for unique code + String8 unique_identifier_string = {0}; + if(unique_identifier_string.size == 0 && bake_params.top_level_info.exe_hash != 0) + { + unique_identifier_string = str8f(arena, "%I64x", bake_params.top_level_info.exe_hash); + } + if(unique_identifier_string.size == 0 && input_files.first != 0 && input_files.first->v->format == RB_FileFormat_PDB) + { + Temp scratch = scratch_begin(&arena, 1); + String8 msf_data = input_files.first->v->data; + MSF_RawStreamTable *st = msf_raw_stream_table_from_data(scratch.arena, msf_data); + String8 info_data = msf_data_from_stream_number(scratch.arena, msf_data, st, PDB_FixedStream_Info); + PDB_Info *info = pdb_info_from_data(scratch.arena, info_data); + if(info != 0 && info_data.size >= sizeof(PDB_InfoHeader)) + { + PDB_InfoHeader *info_header = (PDB_InfoHeader *)info_data.str; + Guid guid = info->auth_guid; + unique_identifier_string = str8f(arena, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%u", + guid.data1, + guid.data2, + guid.data3, + guid.data4[0], + guid.data4[1], + guid.data4[2], + guid.data4[3], + guid.data4[4], + guid.data4[5], + guid.data4[6], + guid.data4[7], + info_header->age); + } + scratch_end(scratch); + } + + // rjf: push record + str8_list_pushf(arena, &p2b_shared->dump, "MODULE windows x86_64 %S %S\n", unique_identifier_string, module_name_string); + } //- rjf: dump FILE records ProfScope("dump FILE records") { - for(RDIM_SrcFileChunkNode *n = bake_params.src_files.first; n != 0; n = n->next) + U64 chunk_idx = 0; + for EachNode(n, RDIM_SrcFileChunkNode, bake_params.src_files.first) { - for(U64 idx = 0; idx < n->count; idx += 1) + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) { U64 file_idx = rdim_idx_from_src_file(&n->v[idx]); String8 src_path = n->v[idx].path; - str8_list_pushf(arena, &dump, "FILE %I64u %S\n", file_idx, src_path); + str8_list_pushf(arena, &p2b_shared->lane_chunk_file_dumps[lane_idx()*bake_params.src_files.chunk_count + chunk_idx], "FILE %I64u %S\n", file_idx, src_path); + } + chunk_idx += 1; + } + } + + //- rjf: dump FUNC records + ProfScope("dump FUNC records") + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_SymbolChunkNode, bake_params.procedures.first) + { + String8List *out = &p2b_shared->lane_chunk_func_dumps[lane_idx()*bake_params.procedures.chunk_count + chunk_idx]; + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) + { + // NOTE(rjf): breakpad does not support multiple voff ranges per procedure. + RDIM_Symbol *proc = &n->v[idx]; + RDIM_Scope *root_scope = proc->root_scope; + if(root_scope != 0 && root_scope->voff_ranges.first != 0) + { + // rjf: dump function record + RDIM_Rng1U64 voff_range = root_scope->voff_ranges.first->v; + str8_list_pushf(arena, out, "FUNC %I64x %I64x %I64x %S\n", voff_range.min, voff_range.max-voff_range.min, 0ull, proc->name); + + // rjf: dump function lines + U64 unit_idx = rdi_vmap_idx_from_voff(bake_results.unit_vmap.vmap.vmap, bake_results.unit_vmap.vmap.count, voff_range.min); + if(0 < unit_idx && unit_idx <= bake_results.units.units_count) + { + U32 line_table_idx = bake_results.units.units[unit_idx].line_table_idx; + if(0 < line_table_idx && line_table_idx <= bake_results.line_tables.line_tables_count) + { + // rjf: unpack unit line info + RDI_LineTable *line_table = &bake_results.line_tables.line_tables[line_table_idx]; + RDI_ParsedLineTable line_info = + { + bake_results.line_tables.line_table_voffs + line_table->voffs_base_idx, + bake_results.line_tables.line_table_lines + line_table->lines_base_idx, + 0, + line_table->lines_count, + 0 + }; + for(U64 voff = voff_range.min, last_voff = 0; + voff < voff_range.max && voff > last_voff;) + { + RDI_U64 line_info_idx = rdi_line_info_idx_from_voff(&line_info, voff); + if(line_info_idx < line_info.count) + { + RDI_Line *line = &line_info.lines[line_info_idx]; + U64 line_voff_min = line_info.voffs[line_info_idx]; + U64 line_voff_opl = line_info.voffs[line_info_idx+1]; + if(line->file_idx != 0) + { + str8_list_pushf(arena, out, "%I64x %I64x %I64u %I64u\n", + line_voff_min, + line_voff_opl-line_voff_min, + (U64)line->line_num, + (U64)line->file_idx); + } + last_voff = voff; + voff = line_voff_opl; + } + else + { + break; + } + } + } + } + } + } + chunk_idx += 1; + } + } + + //- rjf: join + lane_sync(); + if(lane_idx() == 0) + { + for EachIndex(chunk_idx, bake_params.src_files.chunk_count) + { + for EachIndex(ln_idx, lane_count()) + { + str8_list_concat_in_place(&p2b_shared->dump, &p2b_shared->lane_chunk_file_dumps[ln_idx*bake_params.src_files.chunk_count + chunk_idx]); + } + } + for EachIndex(chunk_idx, bake_params.procedures.chunk_count) + { + for EachIndex(ln_idx, lane_count()) + { + str8_list_concat_in_place(&p2b_shared->dump, &p2b_shared->lane_chunk_func_dumps[ln_idx*bake_params.procedures.chunk_count + chunk_idx]); } } } - - //- rjf: join unit vmap - ProfBegin("join unit vmap"); - RDIM_UnitVMapBakeResult *bake_unit_vmap_out = async_task_join_struct(bake_unit_vmap_task, RDIM_UnitVMapBakeResult); - RDI_VMapEntry *unit_vmap = bake_unit_vmap_out->vmap.vmap; - U32 unit_vmap_count = bake_unit_vmap_out->vmap.count; - ProfEnd(); - - //- rjf: join line tables - ProfBegin("join line table"); - RDIM_LineTableBakeResult *bake_line_tables_out = async_task_join_struct(bake_line_tables_task, RDIM_LineTableBakeResult); - ProfEnd(); - - //- rjf: kick off FUNC & line record dump tasks - P2B_DumpProcChunkIn *dump_proc_chunk_in = push_array(arena, P2B_DumpProcChunkIn, bake_params.procedures.chunk_count); - ASYNC_Task **dump_proc_chunk_tasks = push_array(arena, ASYNC_Task *, bake_params.procedures.chunk_count); - ProfScope("kick off FUNC & line record dump tasks") - { - U64 task_idx = 0; - for(RDIM_SymbolChunkNode *n = bake_params.procedures.first; n != 0; n = n->next, task_idx += 1) - { - dump_proc_chunk_in[task_idx].unit_vmap = unit_vmap; - dump_proc_chunk_in[task_idx].unit_vmap_count = unit_vmap_count; - dump_proc_chunk_in[task_idx].unit_line_table_idxs = unit_line_table_idxs; - dump_proc_chunk_in[task_idx].unit_count = unit_count; - dump_proc_chunk_in[task_idx].line_tables_bake = bake_line_tables_out; - dump_proc_chunk_in[task_idx].chunk = n; - dump_proc_chunk_tasks[task_idx] = async_task_launch(arena, p2b_dump_proc_chunk_work, .input = &dump_proc_chunk_in[task_idx]); - } - } - - //- rjf: join FUNC & line record dump tasks - ProfScope("join FUNC & line record dump tasks") - { - for(U64 idx = 0; idx < bake_params.procedures.chunk_count; idx += 1) - { - String8List *out = async_task_join_struct(dump_proc_chunk_tasks[idx], String8List); - str8_list_concat_in_place(&dump, out); - } - } - - str8_list_concat_in_place(&output_blobs, &dump); + lane_sync(); + output_blobs = p2b_shared->dump; }break; } }break; @@ -822,7 +971,7 @@ rb_entry_point(CmdLine *cmdline) B32 deterministic = cmd_line_has_flag(cmdline, str8_lit("deterministic")); //- rjf: no inputs => help - if(cmdline->inputs.node_count == 0) + if(lane_idx() == 0 && cmdline->inputs.node_count == 0) { fprintf(stderr, "All input files specified on the command line will be dumped. Currently, only\n"); fprintf(stderr, "RDI files are supported.\n\n"); @@ -894,7 +1043,11 @@ rb_entry_point(CmdLine *cmdline) for(RB_FileNode *n = input_files.first; n != 0; n = n->next) { RB_File *f = n->v; - str8_list_pushf(arena, &output_blobs, "// %S (%S)\n\n", deterministic ? str8_skip_last_slash(f->path) : f->path, f->format ? rb_file_format_display_name_table[f->format] : str8_lit("Unsupported format")); + if(lane_idx() == 0) + { + str8_list_pushf(arena, &output_blobs, "// %S (%S)\n\n", deterministic ? str8_skip_last_slash(f->path) : f->path, f->format ? rb_file_format_display_name_table[f->format] : str8_lit("Unsupported format")); + } + lane_sync(); //- rjf: unpack file parses Arch arch = Arch_Null; @@ -979,18 +1132,19 @@ rb_entry_point(CmdLine *cmdline) { RDI_Parsed rdi = {0}; RDI_ParseStatus rdi_status = rdi_parse(f->data.str, f->data.size, &rdi); - String8 error = {0}; switch(rdi_status) { default:{}break; case RDI_ParseStatus_HeaderDoesNotMatch: {log_user_errorf("RDI parse failure: header does not match\n");}break; case RDI_ParseStatus_UnsupportedVersionNumber:{log_user_errorf("RDI parse failure: unsupported version\n");}break; case RDI_ParseStatus_InvalidDataSecionLayout: {log_user_errorf("RDI parse failure: invalid data section layout\n");}break; - case RDI_ParseStatus_MissingRequiredSection: {log_user_errorf("RDI parse failure: missing required section\n");}break; case RDI_ParseStatus_Good: { String8List dump = rdi_dump_list_from_parsed(arena, &rdi, rdi_dump_subset_flags); - str8_list_concat_in_place(&output_blobs, &dump); + if(lane_idx() == 0) + { + str8_list_concat_in_place(&output_blobs, &dump); + } }break; } }break; @@ -999,10 +1153,17 @@ rb_entry_point(CmdLine *cmdline) //- rjf: dump file extension info if(f->format_flags & RB_FileFormatFlag_HasDWARF) { - str8_list_pushf(arena, &output_blobs, "// %S (%S) (DWARF)\n\n", deterministic ? str8_skip_last_slash(f->path) : f->path, f->format ? rb_file_format_display_name_table[f->format] : str8_lit("Unsupported format")); + if(lane_idx() == 0) + { + str8_list_pushf(arena, &output_blobs, "// %S (%S) (DWARF)\n\n", deterministic ? str8_skip_last_slash(f->path) : f->path, f->format ? rb_file_format_display_name_table[f->format] : str8_lit("Unsupported format")); + } + lane_sync(); { String8List dump = dw_dump_list_from_sections(arena, &dw, arch, dw_dump_subset_flags); - str8_list_concat_in_place(&output_blobs, &dump); + if(lane_idx() == 0) + { + str8_list_concat_in_place(&output_blobs, &dump); + } } } } @@ -1012,43 +1173,57 @@ rb_entry_point(CmdLine *cmdline) ////////////////////////////// //- rjf: write outputs // - if(output_path.size != 0) ProfScope("write outputs [file]") + if(lane_idx() == 0) { - os_write_data_list_to_file_path(output_path, output_blobs); - log_infof("Results written to %S", output_path); - } - else ProfScope("write outputs [stdout]") - { - for(String8Node *n = output_blobs.first; n != 0; n = n->next) + if(output_path.size != 0) ProfScope("write outputs [file]") { - for(U64 off = 0; off < n->string.size;) + B32 is_written = os_write_data_list_to_file_path(output_path, output_blobs); + if(is_written) { - U64 size_to_write = Min(n->string.size - off, GB(2)); - fwrite(n->string.str + off, size_to_write, 1, stdout); - off += size_to_write; + log_infof("Results written to %S", output_path); + } + else + { + log_user_errorf("ERROR: failed to write file %S\n", output_path); } } - log_info(str8_lit("Results written to stdout")); + else ProfScope("write outputs [stdout]") + { + for(String8Node *n = output_blobs.first; n != 0; n = n->next) + { + for(U64 off = 0; off < n->string.size;) + { + U64 size_to_write = Min(n->string.size - off, GB(2)); + fwrite(n->string.str + off, size_to_write, 1, stdout); + off += size_to_write; + } + } + log_info(str8_lit("Results written to stdout")); + } } + lane_sync(); ////////////////////////////// //- rjf: write info & errors // LogScopeResult log_scope = log_scope_end(arena); - if(cmd_line_has_flag(cmdline, str8_lit("verbose")) && log_scope.strings[LogMsgKind_Info].size != 0) + if(lane_idx() == 0) { - String8List lines = wrapped_lines_from_string(arena, log_scope.strings[LogMsgKind_Info], 80, 80, 0); - for(String8Node *n = lines.first; n != 0; n = n->next) + if(cmd_line_has_flag(cmdline, str8_lit("verbose")) && log_scope.strings[LogMsgKind_Info].size != 0) { - fprintf(stderr, "%.*s\n", str8_varg(n->string)); + String8List lines = wrapped_lines_from_string(arena, log_scope.strings[LogMsgKind_Info], 80, 80, 0); + for(String8Node *n = lines.first; n != 0; n = n->next) + { + fprintf(stderr, "%.*s\n", str8_varg(n->string)); + } } - } - if(log_scope.strings[LogMsgKind_UserError].size != 0) - { - String8List lines = wrapped_lines_from_string(arena, log_scope.strings[LogMsgKind_UserError], 80, 80, 0); - for(String8Node *n = lines.first; n != 0; n = n->next) + if(log_scope.strings[LogMsgKind_UserError].size != 0) { - fprintf(stderr, "%.*s\n", str8_varg(n->string)); + String8List lines = wrapped_lines_from_string(arena, log_scope.strings[LogMsgKind_UserError], 80, 80, 0); + for(String8Node *n = lines.first; n != 0; n = n->next) + { + fprintf(stderr, "%.*s\n", str8_varg(n->string)); + } } } } diff --git a/src/radbin/radbin.h b/src/radbin/radbin.h index fc98e130..7e7265fc 100644 --- a/src/radbin/radbin.h +++ b/src/radbin/radbin.h @@ -9,6 +9,16 @@ #include "radbin/generated/radbin.meta.h" +//////////////////////////////// +//~ rjf: Thread Parameters + +typedef struct RB_ThreadParams RB_ThreadParams; +struct RB_ThreadParams +{ + CmdLine *cmdline; + LaneCtx lane_ctx; +}; + //////////////////////////////// //~ rjf: File Types @@ -46,8 +56,24 @@ read_only global RB_File rb_file_nil = {0}; #define rb_file_list_first(list) ((list)->first ? (list)->first->v : &rb_file_nil) //////////////////////////////// -//~ rjf: Top-Level Entry Point +//~ rjf: Cross-Thread State + +typedef struct RB_Shared RB_Shared; +struct RB_Shared +{ + RB_FileList input_files; + RB_FileList input_files_from_format_table[RB_FileFormat_COUNT]; +}; + +//////////////////////////////// +//~ rjf: Globals + +global RB_Shared *rb_shared = 0; + +//////////////////////////////// +//~ rjf: Top-Level Entry Points internal void rb_entry_point(CmdLine *cmdline); +internal void rb_thread_entry_point(void *p); #endif //RADBIN_H diff --git a/src/radbin/radbin_main.c b/src/radbin/radbin_main.c index f0f6f8b4..f72180de 100644 --- a/src/radbin/radbin_main.c +++ b/src/radbin/radbin_main.c @@ -32,7 +32,6 @@ #include "rdi_from_coff/rdi_from_coff.h" #include "rdi_from_elf/rdi_from_elf.h" #include "rdi_from_pdb/rdi_from_pdb.h" -#include "rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.h" #include "rdi_from_dwarf/rdi_from_dwarf.h" #include "radbin/radbin.h" @@ -58,7 +57,6 @@ #include "rdi_from_coff/rdi_from_coff.c" #include "rdi_from_elf/rdi_from_elf.c" #include "rdi_from_pdb/rdi_from_pdb.c" -#include "rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.c" #include "rdi_from_dwarf/rdi_from_dwarf.c" #include "radbin/radbin.c" diff --git a/src/radcon/radcon.c b/src/radcon/radcon.c deleted file mode 100644 index 1cd0643b..00000000 --- a/src/radcon/radcon.c +++ /dev/null @@ -1,481 +0,0 @@ -internal String8 -rc_data_from_file_path(Arena *arena, String8 path) -{ - String8 data = os_data_from_file_path(arena, path); - if (data.size == 0) { - fprintf(stderr, "error: unable to read file %.*s\n", str8_varg(path)); - os_abort(1); - } - return data; -} - -internal RC_Context -rc_context_from_cmd_line(Arena *arena, CmdLine *cmdl) -{ - Temp scratch = scratch_begin(&arena, 1); - - if (cmdl->inputs.node_count > 2) { - fprintf(stderr, "error: too many input files on the command line.\n"); - os_abort(1); - } - - B32 is_pe_present = 0; - B32 is_pdb_present = 0; - B32 is_elf_present = 0; - B32 is_elf_debug_present = 0; - String8 pe_name = {0}; - String8 pe_data = {0}; - String8 pdb_name = {0}; - String8 pdb_data = {0}; - String8 elf_name = {0}; - String8 elf_data = {0}; - String8 elf_debug_name = {0}; - String8 elf_debug_data = {0}; - - // - // Set typed inputs - // - if (cmd_line_has_flag(cmdl, str8_lit("pe"))) { - pe_name = cmd_line_string(cmdl, str8_lit("pe")); - pe_data = rc_data_from_file_path(arena, pe_name); - if (!pe_check_magic(pe_data)) { - fprintf(stderr, "error: -pe:%.*s is not of PE format\n", str8_varg(pe_name)); - os_abort(1); - } - is_pe_present = 1; - } - if (cmd_line_has_flag(cmdl, str8_lit("pdb"))) { - pdb_name = cmd_line_string(cmdl, str8_lit("pdb")); - pdb_data = rc_data_from_file_path(arena, pdb_name); - if (!msf_check_magic_20(pdb_data) && !msf_check_magic_70(pdb_data)) { - fprintf(stderr, "error: -pdb:%.*s is not of PDB format\n", str8_varg(pdb_name)); - os_abort(1); - } - is_pdb_present = 1; - } - if (cmd_line_has_flag(cmdl, str8_lit("elf"))) { - elf_name = cmd_line_string(cmdl, str8_lit("elf")); - elf_data = rc_data_from_file_path(arena, elf_name); - if (!elf_check_magic(elf_data)) { - fprintf(stderr, "error: -elf:%.*s is not of ELF format\n", str8_varg(elf_name)); - os_abort(1); - } - is_elf_present = 1; - } - if (cmd_line_has_flag(cmdl, str8_lit("elf_debug"))) { - elf_debug_name = cmd_line_string(cmdl, str8_lit("elf_debug")); - elf_debug_data = rc_data_from_file_path(arena, elf_debug_name); - if (!elf_check_magic(elf_debug_data)) { - fprintf(stderr, "error: -elf_debug:%.*s is not of ELF format\n", str8_varg(elf_debug_name)); - os_abort(1); - } - is_elf_debug_present = 1; - } - - // - // Pick conversion driver - // - RC_Driver driver = RC_Driver_Null; - if (cmd_line_has_flag(cmdl, str8_lit("driver"))) { - String8 driver_name = cmd_line_string(cmdl, str8_lit("driver")); - if (str8_match(driver_name, str8_lit("dwarf"), StringMatchFlag_CaseInsensitive)) { - driver = RC_Driver_Dwarf; - } else if (str8_match(driver_name, str8_lit("pdb"), StringMatchFlag_CaseInsensitive)) { - driver = RC_Driver_Pdb; - } else { - fprintf(stderr, "error: unknown driver \"%.*s\"\n", str8_varg(driver_name)); - os_abort(1); - } - } - - // - // Load inputs - // - for (String8Node *input_n = cmdl->inputs.first; input_n != 0; input_n = input_n->next) { - String8 input_data = os_data_from_file_path(arena, input_n->string); - - if (input_data.size == 0) { - fprintf(stderr, "unable to read input %.*s\n", str8_varg(input_n->string)); - os_abort(1); - } - - if (pe_check_magic(input_data)) { - if (is_pe_present) { - fprintf(stderr, "error: too many PE files are specified on the command line\n"); - fprintf(stderr, " selected: %.*s\n", str8_varg(pe_name)); - fprintf(stderr, " current: %.*s\n", str8_varg(input_n->string)); - os_abort(1); - } - pe_data = input_data; - pe_name = input_n->string; - is_pe_present = 1; - } else if (elf_check_magic(input_data)) { - ELF_Bin elf = elf_bin_from_data(input_data); - B32 is_dwarf_present = dw_is_dwarf_present_from_bin(input_data, &elf); - if (is_dwarf_present) { - if (is_elf_debug_present) { - fprintf(stderr, "error: ambiguous input, both ELFs have DWARF debug sections, please use --elf: --elf_debug: to clarify inputs.\n"); - os_abort(1); - } - elf_debug_name = input_n->string; - elf_debug_data = input_data; - is_elf_debug_present = 1; - } else { - elf_name = input_n->string; - elf_data = input_data; - is_elf_present = 1; - } - } else if (msf_check_magic_20(input_data) || msf_check_magic_70(input_data)) { - if (is_pdb_present) { - fprintf(stderr, "error: too many PDB files are specified on the command line\n"); - fprintf(stderr, " selected: %.*s\n", str8_varg(pdb_name)); - fprintf(stderr, " current: %.*s\n", str8_varg(input_n->string)); - continue; - } - pdb_name = input_n->string; - pdb_data = input_data; - is_pdb_present = 1; - } else { - fprintf(stderr, "error: unknown file format %.*s\n", str8_varg(input_n->string)); - } - } - - // - // Validate input combos - // - if ((is_pe_present || is_pdb_present) && (is_elf_present || is_elf_debug_present)) { - fprintf(stderr, "error: invalid combination of inputs provided, we convert only (PE|PDB) or (ELF|ELF_DEBUG) at a time.\n"); - if (is_pe_present) { - fprintf(stderr, " PE: %.*s\n", str8_varg(pe_name)); - } - if (is_pdb_present) { - fprintf(stderr, " PDB: %.*s\n", str8_varg(pdb_name)); - } - if (is_elf_present) { - fprintf(stderr, " ELF: %.*s\n", str8_varg(elf_name)); - } - if (is_elf_debug_present) { - fprintf(stderr, " ELF Debug: %.*s\n", str8_varg(elf_debug_name)); - } - os_abort(1); - } - - if (is_pe_present && (is_elf_present || is_elf_debug_present)) { - fprintf(stderr, "error: command line has too many image types specified.\n"); - os_abort(1); - } - - - ExecutableImageKind image = ExecutableImageKind_Null; - String8 image_name = {0}; - String8 image_data = {0}; - String8 debug_name = {0}; - String8 debug_data = {0}; - - B32 check_guid = 0; - Guid pe_pdb_guid = {0}; - - B32 elf_has_debug_link = 0; - ELF_GnuDebugLink debug_link = {0}; - - // - // Input has PE/COFF - // - if (is_pe_present) { - image = ExecutableImageKind_CoffPe; - image_name = pe_name; - image_data = pe_data; - - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, pe_data); - String8 raw_debug_dir = str8_substr(pe_data, pe.data_dir_franges[PE_DataDirectoryIndex_DEBUG]); - PE_DebugInfoList debug_dir = pe_debug_info_list_from_raw_debug_dir(scratch.arena, pe_data, raw_debug_dir); - for (PE_DebugInfoNode *debug_n = debug_dir.first; debug_n != 0; debug_n = debug_n->next) { - PE_DebugInfo *debug = &debug_n->v; - if (debug->header.type == PE_DebugDirectoryType_CODEVIEW) { - if (debug->u.codeview.magic == PE_CODEVIEW_PDB70_MAGIC) { - check_guid = 1; - pe_pdb_guid = debug->u.codeview.pdb70.header.guid; - - if (!is_pdb_present) { - pdb_name = debug->u.codeview.pdb70.path; - pdb_data = rc_data_from_file_path(arena, pdb_name); - is_pdb_present = 1; - } - - break; - } - } - } - - if (driver == RC_Driver_Null || driver == RC_Driver_Dwarf) { - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, pe_data); - String8 raw_section_table = str8_substr(pe_data, pe.section_table_range); - String8 string_table = str8_substr(pe_data, pe.string_table_range); - U64 section_count = raw_section_table.size / sizeof(COFF_SectionHeader); - COFF_SectionHeader *section_table = (COFF_SectionHeader *)raw_section_table.str; - if (dw_is_dwarf_present_coff_section_table(pe_data, string_table, section_count, section_table)) { - driver = RC_Driver_Dwarf; - debug_name = pe_name; - debug_data = pe_data; - goto driver_found; - } else if (driver == RC_Driver_Dwarf) { - fprintf(stderr, "error: image doesn't have DWARF debug sections.\n"); - os_abort(1); - } - } - } - - if (is_elf_present || is_elf_debug_present) { - if (driver != RC_Driver_Null && driver != RC_Driver_Dwarf) { - fprintf(stderr, "error: ELF inputs are only supported when using DWARF driver.\n"); - os_abort(1); - } - - // - // Load image ELF - // - ELF_Bin elf = elf_bin_from_data(elf_data); - B32 has_elf_dwarf = dw_is_dwarf_present_from_elf_bin(elf_data, &elf); - - // - // ELF doesn't have debug info and no .debug was specified on command line, - // try to load .debug via debug link - // - if (is_elf_present && !is_elf_debug_present) { - elf_has_debug_link = elf_parse_debug_link(elf_data, &elf, &debug_link); - } - if (elf_has_debug_link) { - elf_debug_data = rc_data_from_file_path(arena, debug_link.path); - is_elf_debug_present = 1; - } - - // - // Load .debug ELF - // - ELF_Bin elf_debug = elf_bin_from_data(elf_debug_data); - B32 has_elf_debug_dwarf = dw_is_dwarf_present_from_elf_bin(elf_debug_data, &elf_debug); - - // - // Input is image ELF and .debug ELF - // - B32 is_split_elf = is_elf_present && is_elf_debug_present && !has_elf_dwarf && has_elf_debug_dwarf; - if (is_split_elf) { - driver = RC_Driver_Dwarf; - image = ELF_HdrIs64Bit(elf.hdr.e_ident) ? ExecutableImageKind_Elf64 : ExecutableImageKind_Elf32; - image_name = elf_name; - image_data = elf_data; - debug_name = elf_debug_name; - debug_data = elf_debug_data; - goto driver_found; - } - - // - // Input ELF is image with debug info - // - B32 is_monolithic_elf = is_elf_present && !is_elf_debug_present && has_elf_dwarf; - if (is_monolithic_elf) { - driver = RC_Driver_Dwarf; - image = ELF_HdrIs64Bit(elf.hdr.e_ident) ? ExecutableImageKind_Elf64 : ExecutableImageKind_Elf32; - image_name = elf_name; - image_data = elf_data; - debug_name = elf_name; - debug_data = elf_data; - goto driver_found; - } - - // - // Input ELF is .debug - // - B32 is_debug_elf = !is_elf_present && is_elf_debug_present && has_elf_debug_dwarf; - if (is_debug_elf) { - driver = RC_Driver_Dwarf; - image = ELF_HdrIs64Bit(elf_debug.hdr.e_ident) ? ExecutableImageKind_Elf64 : ExecutableImageKind_Elf32; - debug_name = elf_debug_name; - debug_data = elf_debug_data; - goto driver_found; - } - } - - // - // Input is PDB - // - if (is_pdb_present) { - if (driver == RC_Driver_Null || driver == RC_Driver_Pdb) { - driver = RC_Driver_Pdb; - debug_name = pdb_name; - debug_data = pdb_data; - goto driver_found; - } else if (driver == RC_Driver_Dwarf) { - fprintf(stderr, "error: unable to select DWARF conversion driver because convert doesn't support PDB as input format.\n"); - os_abort(1); - } else { - InvalidPath; - } - } - - driver_found:; - - // - // Handle -out param - // - String8 out_name = {0}; - if (cmd_line_has_flag(cmdl, str8_lit("out"))) { - out_name = cmd_line_string(cmdl, str8_lit("out")); - if (out_name.size == 0) { - fprintf(stderr, "error: -out parameter doesn't have a value\n"); - os_abort(1); - } - } else { - if (image_name.size) { - out_name = path_replace_file_extension(arena, image_name, str8_lit("rdi")); - } else { - out_name = path_replace_file_extension(arena, debug_name, str8_lit("rdi")); - } - } - - - // - // Validate driver input - // - if (driver == RC_Driver_Pdb && - !is_pdb_present && (is_elf_present || is_elf_debug_present)) { - fprintf(stderr, "error: DWARF is an invalid input for PDB driver\n"); - os_abort(1); - } - - - RC_Context ctx = {0}; - ctx.driver = driver; - ctx.image = image; - ctx.image_name = image_name; - ctx.image_data = image_data; - ctx.debug_name = debug_name; - ctx.debug_data = debug_data; - ctx.flags = RC_Flag_Strings| - RC_Flag_IndexRuns| - RC_Flag_BinarySections| - RC_Flag_Units| - RC_Flag_Procedures| - RC_Flag_GlobalVariables| - RC_Flag_ThreadVariables| - RC_Flag_Scopes| - RC_Flag_Locals| - RC_Flag_Types| - RC_Flag_UDTs| - RC_Flag_LineInfo| - RC_Flag_GlobalVariableNameMap| - RC_Flag_ThreadVariableNameMap| - RC_Flag_ProcedureNameMap| - RC_Flag_TypeNameMap| - RC_Flag_LinkNameProcedureNameMap| - RC_Flag_NormalSourcePathNameMap; - if (check_guid) { - ctx.flags |= RC_Flag_CheckPdbGuid; - ctx.guid = pe_pdb_guid; - } - if (elf_has_debug_link) { - ctx.flags |= RC_Flag_CheckElfChecksum; - ctx.debug_link = debug_link; - } - ctx.out_name = out_name; - - scratch_end(scratch); - return ctx; -} - -internal String8List -rc_run(Arena *arena, RC_Context *rc) -{ - Temp scratch = scratch_begin(&arena, 1); - - ProfBegin("Convert"); - RDIM_LocalState *local_state = rdim_local_init(); - RDIM_BakeParams *convert2bake = 0; - switch (rc->driver) { - case RC_Driver_Null: break; - case RC_Driver_Dwarf: convert2bake = d2r_convert(scratch.arena, local_state, rc); break; - case RC_Driver_Pdb: convert2bake = p2r_convert(scratch.arena, local_state, rc); break; - } - ProfEnd(); - - if (rc->errors.node_count) { - NotImplemented; - } - - ProfBegin("Bake"); - RDIM_BakeResults bake2srlz = rdim_bake(local_state, convert2bake); - ProfEnd(); - - ProfBegin("Serialize Bake"); - RDIM_SerializedSectionBundle srlz2file = rdim_serialized_section_bundle_from_bake_results(&bake2srlz); - ProfEnd(); - - RDIM_SerializedSectionBundle srlz2file_compressed = srlz2file; - if (rc->flags & RC_Flag_Compress) { - ProfBegin("Compress"); - srlz2file_compressed = rdim_compress(scratch.arena, &srlz2file); - ProfEnd(); - } - - ProfBegin("Serialize"); - String8List raw_rdi = rdim_file_blobs_from_section_bundle(scratch.arena, &srlz2file_compressed); - ProfEnd(); - - scratch_end(scratch); - return raw_rdi; -} - -internal String8 -rc_rdi_from_cmd_line(Arena *arena, CmdLine *cmdl) -{ - Temp scratch = scratch_begin(&arena, 1); - RC_Context rc = rc_context_from_cmd_line(scratch.arena, cmdl); - String8List raw_rdi = rc_run(scratch.arena, &rc); - String8 result = str8_list_join(arena, &raw_rdi, 0); - scratch_end(scratch); - return result; -} - -internal void -rc_main(CmdLine *cmdl) -{ - B32 do_help = (cmd_line_has_flag(cmdl, str8_lit("help")) || - cmd_line_has_flag(cmdl, str8_lit("h")) || - cmd_line_has_flag(cmdl, str8_lit("?")) || - cmdl->argc == 1); - if (do_help) { - fprintf(stderr, "--- Help ---------------------------------------------------------------------\n"); - fprintf(stderr, " %s\n\n", BUILD_TITLE_STRING_LITERAL); - fprintf(stderr, " Usage: radcon [Options] [Files]\n\n"); - fprintf(stderr, " Options:\n"); - fprintf(stderr, " -pe: Path to Win32 executable image\n"); - fprintf(stderr, " -pdb: Path to PDB\n"); - fprintf(stderr, " -elf: Path to ELF\n"); - fprintf(stderr, " -elf_debug: Path to ELF with debug info\n"); - fprintf(stderr, " -out: Path at which the output RDI debug info will be written\n"); - fprintf(stderr, " -driver: Sets converter for debug info\n"); - } else { - Temp scratch = scratch_begin(0,0); - - // make converter context - RC_Context rc = rc_context_from_cmd_line(scratch.arena, cmdl); - - // make RDI from context - String8List raw_rdi = rc_run(scratch.arena, &rc); - - // output RDI - if (rc.errors.node_count == 0) { - if (!os_write_data_list_to_file_path(rc.out_name, raw_rdi)) { - str8_list_pushf(scratch.arena, &rc.errors, "no write access to path %.*s", str8_varg(rc.out_name)); - } - } - - // report any errors - for (String8Node *error_n = rc.errors.first; error_n != 0; error_n = error_n->next) { - fprintf(stderr, "error: %.*s\n", str8_varg(error_n->string)); - } - - scratch_end(scratch); - } -} - diff --git a/src/radcon/radcon.h b/src/radcon/radcon.h deleted file mode 100644 index 06458d85..00000000 --- a/src/radcon/radcon.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RADCON_H -#define RADCON_H - -typedef U32 RC_Flags; -enum -{ - RC_Flag_Strings = (1 << 0), - RC_Flag_IndexRuns = (1 << 1), - RC_Flag_BinarySections = (1 << 2), - RC_Flag_Units = (1 << 3), - RC_Flag_Procedures = (1 << 4), - RC_Flag_GlobalVariables = (1 << 5), - RC_Flag_ThreadVariables = (1 << 6), - RC_Flag_Scopes = (1 << 7), - RC_Flag_Locals = (1 << 8), - RC_Flag_Types = (1 << 9), - RC_Flag_UDTs = (1 << 10), - RC_Flag_LineInfo = (1 << 11), - RC_Flag_GlobalVariableNameMap = (1 << 12), - RC_Flag_ThreadVariableNameMap = (1 << 13), - RC_Flag_ProcedureNameMap = (1 << 14), - RC_Flag_TypeNameMap = (1 << 15), - RC_Flag_LinkNameProcedureNameMap= (1 << 16), - RC_Flag_NormalSourcePathNameMap = (1 << 17), - RC_Flag_Compress = (1 << 18), - RC_Flag_StrictDwarfParse = (1 << 19), - RC_Flag_Deterministic = (1 << 20), - RC_Flag_CheckPdbGuid = (1 << 21), - RC_Flag_CheckElfChecksum = (1 << 22), - RC_Flag_All = 0xffffffff, -}; - -typedef enum -{ - RC_Driver_Null, - RC_Driver_Dwarf, - RC_Driver_Pdb, -} RC_Driver; - -typedef struct RC_Context -{ - ExecutableImageKind image; - RC_Driver driver; - String8 image_name; - String8 image_data; - String8 debug_name; - String8 debug_data; - String8 out_name; - RC_Flags flags; - Guid guid; - ELF_GnuDebugLink debug_link; - String8List errors; -} RC_Context; - -//////////////////////////////// - -internal RC_Context rc_context_from_cmd_line(Arena *arena, CmdLine *cmdl); -internal String8List rc_run(Arena *arena, RC_Context *rc); -internal String8 rc_rdi_from_cmd_line(Arena *arena, CmdLine *cmdl); -internal void rc_main(CmdLine *cmdl); - -#endif // RADCON_H - diff --git a/src/radcon/radcon_main.c b/src/radcon/radcon_main.c deleted file mode 100644 index 0681750f..00000000 --- a/src/radcon/radcon_main.c +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#define BUILD_TITLE "Epic Games Tools (R) RAD Debug Info Converter" -#define BUILD_CONSOLE_INTERFACE 1 - -//////////////////////////////// -// Third Party - -#include "third_party/rad_lzb_simple/rad_lzb_simple.h" -#include "third_party/rad_lzb_simple/rad_lzb_simple.c" -#define XXH_STATIC_LINKING_ONLY -#include "third_party/xxHash/xxhash.c" -#include "third_party/xxHash/xxhash.h" -#define SINFL_IMPLEMENTATION -#include "third_party/sinfl/sinfl.h" -#include "third_party/radsort/radsort.h" - -//////////////////////////////// -// RDI Format Library - -#include "lib_rdi_format/rdi_format.h" -#include "lib_rdi_format/rdi_format.c" - -//////////////////////////////// -// Headers - -#include "base/base_inc.h" -#include "os/os_inc.h" -#include "async/async.h" -#include "rdi_make/rdi_make_local.h" -#include "linker/hash_table.h" -#include "coff/coff.h" -#include "coff/coff_parse.h" -#include "pe/pe.h" -#include "elf/elf.h" -#include "elf/elf_parse.h" -#include "codeview/codeview.h" -#include "codeview/codeview_parse.h" -#include "dwarf/dwarf.h" -#include "dwarf/dwarf_parse.h" -#include "dwarf/dwarf_coff.h" -#include "dwarf/dwarf_elf.h" -#include "msf/msf.h" -#include "msf/msf_parse.h" -#include "pdb/pdb.h" -#include "pdb/pdb_parse.h" -#include "pdb/pdb_stringize.h" -#include "radcon.h" -#include "radcon_coff.h" -#include "radcon_elf.h" -#include "radcon_cv.h" -#include "radcon_dwarf.h" -#include "radcon_pdb.h" - -//////////////////////////////// -// Implementations - -#include "base/base_inc.c" -#include "os/os_inc.c" -#include "async/async.c" -#include "rdi_make/rdi_make_local.c" -#include "linker/hash_table.c" -#include "coff/coff.c" -#include "coff/coff_parse.c" -#include "pe/pe.c" -#include "elf/elf.c" -#include "elf/elf_parse.c" -#include "codeview/codeview.c" -#include "codeview/codeview_parse.c" -#include "msf/msf.c" -#include "msf/msf_parse.c" -#include "pdb/pdb.c" -#include "pdb/pdb_parse.c" -#include "pdb/pdb_stringize.c" -#include "dwarf/dwarf.c" -#include "dwarf/dwarf_parse.c" -#include "dwarf/dwarf_coff.c" -#include "dwarf/dwarf_elf.c" -#include "radcon.c" -#include "radcon_coff.c" -#include "radcon_elf.c" -#include "radcon_cv.c" -#include "radcon_dwarf.c" -#include "radcon_pdb.c" - -//////////////////////////////// - -internal void -entry_point(CmdLine *cmdl) -{ - rc_main(cmdl); -} - diff --git a/src/raddbg/generated/raddbg.meta.c b/src/raddbg/generated/raddbg.meta.c index 0be16561..140275af 100644 --- a/src/raddbg/generated/raddbg.meta.c +++ b/src/raddbg/generated/raddbg.meta.c @@ -424,7 +424,7 @@ RD_NameSchemaInfo rd_name_schema_info_table[24] = {str8_lit_comp("window"), str8_lit_comp("x:\n{\n //- rjf: text rasterization settings\n @default(1) @display_name('Smooth UI Text') @description(\"Controls whether or not UI text is fully anti-aliased, for a smoother appearance.\")\n 'smooth_ui_text': bool,\n @default(1) @display_name('Hint UI Text') @description(\"Controls whether or not UI text is hinted, for better text readability at small sizes.\")\n 'hint_ui_text': bool,\n @default(0) @display_name('Smooth Code Text') @description(\"Controls whether or not code text is fully anti-aliased, for a smoother appearance.\")\n 'smooth_code_text': bool,\n @default(1) @display_name('Hint Code Text') @description(\"Controls whether or not code text is hinted, for better text readability at small sizes.\")\n 'hint_code_text': bool,\n @default(11) @display_name('Window Font Size') @description(\"Controls the window's default font size. Does not apply to tabs with their own font size set.\")\n 'font_size': @range[6, 72] u64,\n\n //- rjf: size settings\n @default(3.f) @display_name('Window Row Height') @description(\"Controls the window's default row height, in multiples of the font size. Does not apply to tabs with their own row height set.\")\n 'row_height': @range[1.75f, 5.f] f32,\n @default(3.f) @description(\"Controls the height of tabs, in multiples of the font size.\")\n 'tab_height': @range[1.75f, 5.f] f32,\n\n //- rjf: theme settings\n @default(1) @display_name('Use Project Theme') @description(\"Prefer using the project theme for this window, if any. If off, only the user's theme settings will be used.\")\n 'use_project_theme': bool,\n}\n")}, {str8_lit_comp("tab"), str8_lit_comp("@row_commands(@file copy_tab_full_path, @file show_file_in_explorer, duplicate_tab, close_tab)\nx:\n{\n @override @display_name('Tab Font Size') @description(\"Controls the tab's font size.\") @no_callee_helper\n 'font_size': @range[6, 72] u64,\n}\n")}, {str8_lit_comp("watch"), str8_lit_comp("@inherit(tab) x:\n{\n @override @display_name('Tab Row Height') @description(\"Controls the tab's row height, in multiples of the font size.\")\n 'row_height': @range[1.75f, 5.f] f32,\n 'label': code_string,\n @description(\"The root expression which is evaluated to produce the watch window.\")\n 'expression': expr_string,\n @no_expand 'watches': query,\n}\n")}, -{str8_lit_comp("text"), str8_lit_comp("@inherit(tab) @expand_commands(@output clear_output) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change':bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")}, +{str8_lit_comp("text"), str8_lit_comp("@inherit(tab) @expand_commands(@output clear_output) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(1) @display_name('Line Wrapping') @description(\"Splits textual lines into multiple visual lines, so that all text is within the visible area.\")\n 'line_wrapping': bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change': bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")}, {str8_lit_comp("disasm"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression to describe the base address or offset of the disassembly.\")\n 'expression': expr_string,\n 'arch': code_string,\n 'syntax': code_string,\n 'size': expr_string,\n @no_callee_helper @default(1) @description(\"Controls whether or not addresses are shown in the disassembly text.\")\n 'show_addresses': bool,\n @no_callee_helper @default(0) @description(\"Controls whether or not code bytes are shown in the disassembly text.\")\n 'show_code_bytes': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not source lines, corresponding to disassembly instruction ranges, are shown in the disassembly text.\")\n 'show_source_lines': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not disassembly text is decorated with symbol names.\")\n 'show_symbol_names': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers': bool,\n\n}\n")}, {str8_lit_comp("memory"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as memory.\")\n 'expression': expr_string,\n @display_name(\"Address Range Size\") @description(\"The number of bytes of the viewed memory range.\")\n 'size': expr_string,\n @display_name(\"Cursor Address\") @description(\"The address of the cursor.\")\n 'cursor': expr_string,\n @display_name(\"Cursor Size\") @description(\"The size, in bytes, of the cursor.\")\n 'cursor_size': @range[1, 16] u64,\n @default(16) @description(\"The number of columns to build before building new rows.\")\n 'num_columns': @range[1, 64] u64,\n @default(1) @display_name(\"Track Mark To Cursor\") @description(\"Ensures that the mark always follows the cursor, if the cursor value is updated.\")\n 'track_mark_to_cursor': bool,\n}\n")}, {str8_lit_comp("bitmap"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as a bitmap.\")\n 'expression': expr_string,\n @description(\"An expression describing the width of the bitmap, in pixels.\") @order(0) 'w': u64,\n @description(\"An expression describing the height of the bitmap, in pixels.\") @order(1) 'h': u64,\n @display_name(\"Bitmap Format\") @description(\"The pixel format that the bitmap data should be interpreted as being within.\")\n 'fmt': code_string,\n}\n")}, @@ -517,7 +517,7 @@ Rng1U64 rd_reg_slot_range_table[47] = {OffsetOf(RD_Regs, file_path), OffsetOf(RD_Regs, file_path) + sizeof(String8)}, {OffsetOf(RD_Regs, cursor), OffsetOf(RD_Regs, cursor) + sizeof(TxtPt)}, {OffsetOf(RD_Regs, mark), OffsetOf(RD_Regs, mark) + sizeof(TxtPt)}, -{OffsetOf(RD_Regs, text_key), OffsetOf(RD_Regs, text_key) + sizeof(HS_Key)}, +{OffsetOf(RD_Regs, text_key), OffsetOf(RD_Regs, text_key) + sizeof(C_Key)}, {OffsetOf(RD_Regs, lang_kind), OffsetOf(RD_Regs, lang_kind) + sizeof(TXT_LangKind)}, {OffsetOf(RD_Regs, lines), OffsetOf(RD_Regs, lines) + sizeof(D_LineList)}, {OffsetOf(RD_Regs, dbgi_key), OffsetOf(RD_Regs, dbgi_key) + sizeof(DI_Key)}, diff --git a/src/raddbg/generated/raddbg.meta.h b/src/raddbg/generated/raddbg.meta.h index d54928ee..14e419f0 100644 --- a/src/raddbg/generated/raddbg.meta.h +++ b/src/raddbg/generated/raddbg.meta.h @@ -463,7 +463,7 @@ U64 inline_depth; String8 file_path; TxtPt cursor; TxtPt mark; -HS_Key text_key; +C_Key text_key; TXT_LangKind lang_kind; D_LineList lines; DI_Key dbgi_key; diff --git a/src/raddbg/raddbg.mdesk b/src/raddbg/raddbg.mdesk index 0e0d27c2..b1175fde 100644 --- a/src/raddbg/raddbg.mdesk +++ b/src/raddbg/raddbg.mdesk @@ -481,8 +481,10 @@ RD_VocabTable: 'lang': code_string, @default(1) @description("Controls whether or not line numbers are shown.") 'show_line_numbers':bool, + @no_callee_helper @default(1) @display_name('Line Wrapping') @description("Splits textual lines into multiple visual lines, so that all text is within the visible area.") + 'line_wrapping': bool, @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description("Scrolls to the bottom if the text is changed.") - 'scroll_to_bottom_on_change':bool, + 'scroll_to_bottom_on_change': bool, @no_callee_helper @no_revert @default(0) @display_name('Transient') @description("Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.") 'auto': bool, } @@ -731,7 +733,7 @@ RD_RegTable: {String8 file_path FilePath } {TxtPt cursor Cursor } {TxtPt mark Mark } - {HS_Key text_key TextKey } + {C_Key text_key TextKey } {TXT_LangKind lang_kind LangKind } {D_LineList lines Lines } {DI_Key dbgi_key DbgiKey } diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 08186ae4..e59dc2e3 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -1746,13 +1746,13 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) //- rjf: reads from hash store key case E_SpaceKind_HashStoreKey: { - HS_Root root = {space.u64_0}; - HS_ID id = {space.u128}; - HS_Key key = hs_key_make(root, id); - U128 hash = hs_hash_from_key(key, 0); - HS_Scope *scope = hs_scope_open(); + C_Root root = {space.u64_0}; + C_ID id = {space.u128}; + C_Key key = c_key_make(root, id); + U128 hash = c_hash_from_key(key, 0); + Access *access = access_open(); { - String8 data = hs_data_from_hash(scope, hash); + String8 data = c_data_from_hash(access, hash); Rng1U64 legal_range = r1u64(0, data.size); Rng1U64 read_range = intersect_1u64(range, legal_range); if(read_range.min < read_range.max) @@ -1761,7 +1761,7 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) MemoryCopy(out, data.str + read_range.min, dim_1u64(read_range)); } } - hs_scope_close(scope); + access_close(access); }break; //- rjf: file reads @@ -1779,13 +1779,13 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) containing_range.max -= containing_range.max%chunk_size; // rjf: map to hash - HS_Key key = fs_key_from_path_range(file_path, containing_range, 0); - U128 hash = hs_hash_from_key(key, 0); + C_Key key = fs_key_from_path_range(file_path, containing_range, 0); + U128 hash = c_hash_from_key(key, 0); // rjf: look up from hash store - HS_Scope *scope = hs_scope_open(); + Access *access = access_open(); { - String8 data = hs_data_from_hash(scope, hash); + String8 data = c_data_from_hash(access, hash); Rng1U64 legal_range = r1u64(containing_range.min, containing_range.min + data.size); Rng1U64 read_range = intersect_1u64(range, legal_range); if(read_range.min < read_range.max) @@ -1794,7 +1794,7 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) MemoryCopy(out, data.str + read_range.min - containing_range.min, dim_1u64(read_range)); } } - hs_scope_close(scope); + access_close(access); }break; //- rjf: interior control entity reads (inside process address space or thread register block) @@ -1816,8 +1816,8 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) }break; case CTRL_EntityKind_Thread: { - CTRL_Scope *ctrl_scope = ctrl_scope_open(); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, 1, rd_state->frame_eval_memread_endt_us); + Access *access = access_open(); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(access, entity->handle, 1, rd_state->frame_eval_memread_endt_us); U64 concrete_frame_idx = e_interpret_ctx->reg_unwind_count; if(concrete_frame_idx < call_stack.concrete_frames_count) { @@ -1829,7 +1829,7 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) MemoryCopy(out, (U8 *)f->regs + read_range.min, read_size); result = (read_size == dim_1u64(range)); } - ctrl_scope_close(ctrl_scope); + access_close(access); }break; } }break; @@ -2137,17 +2137,17 @@ rd_eval_space_write(void *u, E_Space space, void *in, Rng1U64 range) //- rjf: asynchronous streamed reads -> hashes from spaces -internal HS_Key +internal C_Key rd_key_from_eval_space_range(E_Space space, Rng1U64 range, B32 zero_terminated) { - HS_Key result = {0}; + C_Key result = {0}; switch(space.kind) { case E_SpaceKind_HashStoreKey: { - HS_Root root = {space.u64_0}; - HS_ID id = {space.u128}; - result = hs_key_make(root, id); + C_Root root = {space.u64_0}; + C_ID id = {space.u128}; + result = c_key_make(root, id); }break; case E_SpaceKind_File: { @@ -2177,16 +2177,16 @@ rd_whole_range_from_eval_space(E_Space space) { case E_SpaceKind_HashStoreKey: { - HS_Root root = {space.u64_0}; - HS_ID id = {space.u128}; - HS_Key key = hs_key_make(root, id); - U128 hash = hs_hash_from_key(key, 0); - HS_Scope *hs_scope = hs_scope_open(); + C_Root root = {space.u64_0}; + C_ID id = {space.u128}; + C_Key key = c_key_make(root, id); + U128 hash = c_hash_from_key(key, 0); + Access *access = access_open(); { - String8 data = hs_data_from_hash(hs_scope, hash); + String8 data = c_data_from_hash(access, hash); result = r1u64(0, data.size); } - hs_scope_close(hs_scope); + access_close(access); }break; case E_SpaceKind_File: { @@ -2872,17 +2872,17 @@ rd_view_ui(Rng2F32 rect) // rjf: unpack view's target expression & hash E_Eval eval = e_eval_from_string(expr_string); Rng1U64 range = r1u64(0, 1024); - HS_Key key = rd_key_from_eval_space_range(eval.space, range, 0); - U128 hash = hs_hash_from_key(key, 0); + C_Key key = rd_key_from_eval_space_range(eval.space, range, 0); + U128 hash = c_hash_from_key(key, 0); // rjf: determine if hash's blob is ready, and which viewer to use B32 data_is_ready = 0; String8 new_view_name = {0}; { - HS_Scope *hs_scope = hs_scope_open(); + Access *access = access_open(); if(!u128_match(hash, u128_zero())) { - String8 data = hs_data_from_hash(hs_scope, hash); + String8 data = c_data_from_hash(access, hash); U64 num_utf8_bytes = 0; U64 num_unknown_bytes = 0; for(U64 idx = 0; idx < data.size && idx < range.max;) @@ -2911,7 +2911,7 @@ rd_view_ui(Rng2F32 rect) new_view_name = str8_lit("memory"); } } - hs_scope_close(hs_scope); + access_close(access); } // rjf: if we don't have a viewer, just use the memory viewer. @@ -5679,7 +5679,7 @@ rd_arch_from_eval(E_Eval eval) Arch arch = process->arch; if(arch == Arch_Null) { - arch = arch_from_context(); + arch = Arch_CURRENT; } // rjf: try arch arguments @@ -5993,7 +5993,7 @@ rd_window_frame(void) //- rjf: @window_frame_part compute window's theme // { - HS_Scope *hs_scope = hs_scope_open(); + Access *access = access_open(); //- rjf: try to find theme settings from the project, then the user. RD_CfgList colors_cfgs = {0}; @@ -6039,7 +6039,7 @@ rd_window_frame(void) } //- rjf: map the theme config to the associated tree (either from a preset, or from a file) - MD_Node *theme_tree = rd_theme_tree_from_name(scratch.arena, hs_scope, theme_cfg->first->string); + MD_Node *theme_tree = rd_theme_tree_from_name(scratch.arena, access, theme_cfg->first->string); if(colors_cfgs.count == 0 && theme_tree == &md_nil_node) { theme_tree = rd_state->theme_preset_trees[RD_ThemePreset_DefaultDark]; @@ -6110,7 +6110,7 @@ rd_window_frame(void) } } - hs_scope_close(hs_scope); + access_close(access); } ////////////////////////////// @@ -6464,12 +6464,12 @@ rd_window_frame(void) // rjf: unwind if(ctrl_entity->kind == CTRL_EntityKind_Thread) RD_Font(RD_FontSlot_Code) { - CTRL_Scope *ctrl_scope = ctrl_scope_open(); + Access *access = access_open(); Vec4F32 code_color = ui_color_from_name(str8_lit("code_default")); Vec4F32 symbol_color = ui_color_from_name(str8_lit("code_symbol")); CTRL_Entity *process = ctrl_entity_ancestor_from_kind(ctrl_entity, CTRL_EntityKind_Process); B32 call_stack_high_priority = ctrl_handle_match(ctrl_entity->handle, rd_base_regs()->thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, ctrl_entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(access, ctrl_entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); if(call_stack.frames_count != 0) { ui_spacer(ui_em(1.5f, 1.f)); @@ -6486,7 +6486,7 @@ rd_window_frame(void) String8 rip_value_string = rd_value_string_from_eval(scratch.arena, str8_zero(), &string_params, ui_top_font(), ui_top_font_size(), ui_top_font_size()*40.f, rip_eval); rd_code_label(1, 0, code_color, rip_value_string); } - ctrl_scope_close(ctrl_scope); + access_close(access); } }break; @@ -10205,7 +10205,7 @@ rd_set_autocomp_regs_(E_Eval dst_eval, RD_Regs *regs) //- rjf: colors internal MD_Node * -rd_theme_tree_from_name(Arena *arena, HS_Scope *scope, String8 theme_name) +rd_theme_tree_from_name(Arena *arena, Access *access, String8 theme_name) { Temp scratch = scratch_begin(&arena, 1); MD_Node *theme_tree = &md_nil_node; @@ -10228,7 +10228,7 @@ rd_theme_tree_from_name(Arena *arena, HS_Scope *scope, String8 theme_name) endt_us = os_now_microseconds()+50000; } U128 hash = fs_hash_from_path_range(path, r1u64(0, max_U64), endt_us); - String8 data = hs_data_from_hash(scope, hash); + String8 data = c_data_from_hash(access, hash); theme_tree = md_tree_from_string(arena, data); } } @@ -10864,10 +10864,10 @@ rd_init(CmdLine *cmdln) rd_state->user_path_arena = arena_alloc(); rd_state->project_path_arena = arena_alloc(); rd_state->theme_path_arena = arena_alloc(); - rd_state->user_cfg_string_key = hs_key_make(hs_root_alloc(), hs_id_make(0, 0)); - rd_state->project_cfg_string_key = hs_key_make(hs_root_alloc(), hs_id_make(0, 0)); - rd_state->cmdln_cfg_string_key = hs_key_make(hs_root_alloc(), hs_id_make(0, 0)); - rd_state->transient_cfg_string_key = hs_key_make(hs_root_alloc(), hs_id_make(0, 0)); + rd_state->user_cfg_string_key = c_key_make(c_root_alloc(), c_id_make(0, 0)); + rd_state->project_cfg_string_key = c_key_make(c_root_alloc(), c_id_make(0, 0)); + rd_state->cmdln_cfg_string_key = c_key_make(c_root_alloc(), c_id_make(0, 0)); + rd_state->transient_cfg_string_key = c_key_make(c_root_alloc(), c_id_make(0, 0)); for(U64 idx = 0; idx < ArrayCount(rd_state->frame_arenas); idx += 1) { rd_state->frame_arenas[idx] = arena_alloc(); @@ -11272,7 +11272,7 @@ rd_frame(void) { struct { - HS_Key key; + C_Key key; String8 name; } table[] = @@ -11288,7 +11288,7 @@ rd_frame(void) String8 data = rd_string_from_cfg_tree(arena, str8_zero(), rd_cfg_child_from_string(rd_state->root_cfg, table[idx].name)); - hs_submit_data(table[idx].key, &arena, data); + c_submit_data(table[idx].key, &arena, data); } } #endif @@ -11495,10 +11495,11 @@ rd_frame(void) ////////////////////////////// //- rjf: push frame scopes // + Access *frame_access_restore = rd_state->frame_access; DI_Scope *frame_di_scope_restore = rd_state->frame_di_scope; - CTRL_Scope *frame_ctrl_scope_restore = rd_state->frame_ctrl_scope; + rd_state->frame_access = access_open(); rd_state->frame_di_scope = di_scope_open(); - rd_state->frame_ctrl_scope = ctrl_scope_open(); + rd_state->got_frame_call_stack_tree = 0; ////////////////////////////// //- rjf: calculate avg length in us of last many frames @@ -12296,6 +12297,28 @@ rd_frame(void) e_string2typekey_map_insert(rd_frame_arena(), rd_state->meta_name2type_map, collection_name, collection_type_key); } + //- rjf: add macro for call stack tree +#if 0 + { + String8 collection_name = str8_lit("call_stack_tree"); + E_TypeKey collection_type_key = e_type_key_cons(.kind = E_TypeKind_Set, + .name = collection_name, + .flags = E_TypeFlag_StubSingleLineExpansion, + .access = E_TYPE_ACCESS_FUNCTION_NAME(call_stack_tree), + .expand = + { + .info = E_TYPE_EXPAND_INFO_FUNCTION_NAME(call_stack_tree), + .range = E_TYPE_EXPAND_RANGE_FUNCTION_NAME(call_stack_tree) + }); + E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafValue, r1u64(0, 0)); + expr->value.u64 = 1; + expr->type_key = collection_type_key; + expr->space = e_space_make(RD_EvalSpaceKind_MetaCallStackTree); + e_string2expr_map_insert(scratch.arena, macro_map, collection_name, expr); + e_string2typekey_map_insert(rd_frame_arena(), rd_state->meta_name2type_map, collection_name, collection_type_key); + } +#endif + //- rjf: add macro / lookup rules for unattached processes { String8 collection_name = str8_lit("unattached_processes"); @@ -12436,10 +12459,10 @@ rd_frame(void) //- rjf: add macro for output log { - HS_Scope *hs_scope = hs_scope_open(); - HS_Key key = d_state->output_log_key; - U128 hash = hs_hash_from_key(key, 0); - String8 data = hs_data_from_hash(hs_scope, hash); + Access *access = access_open(); + C_Key key = d_state->output_log_key; + U128 hash = c_hash_from_key(key, 0); + String8 data = c_data_from_hash(access, hash); E_Space space = e_space_make(E_SpaceKind_HashStoreKey); space.u64_0 = key.root.u64[0]; space.u128 = key.id.u128[0]; @@ -12448,7 +12471,7 @@ rd_frame(void) expr->mode = E_Mode_Offset; expr->type_key = e_type_key_cons_array(e_type_key_basic(E_TypeKind_U8), data.size, 0); e_string2expr_map_insert(scratch.arena, macro_map, str8_lit("output"), expr); - hs_scope_close(hs_scope); + access_close(access); } //- rjf: (DEBUG) add macro for cfg strings @@ -12456,7 +12479,7 @@ rd_frame(void) { struct { - HS_Key key; + C_Key key; String8 name; } table[] = @@ -12468,10 +12491,10 @@ rd_frame(void) }; for EachElement(idx, table) { - HS_Scope *hs_scope = hs_scope_open(); - HS_Key key = table[idx].key; - U128 hash = hs_hash_from_key(key, 0); - String8 data = hs_data_from_hash(hs_scope, hash); + Access *access = access_open(); + C_Key key = table[idx].key; + U128 hash = c_hash_from_key(key, 0); + String8 data = c_data_from_hash(access, hash); E_Space space = e_space_make(E_SpaceKind_HashStoreKey); E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, r1u64(0, 0)); space.u64_0 = key.root.u64[0]; @@ -12480,7 +12503,7 @@ rd_frame(void) expr->mode = E_Mode_Offset; expr->type_key = e_type_key_cons_array(e_type_key_basic(E_TypeKind_U8), data.size, 0); e_string2expr_map_insert(scratch.arena, macro_map, table[idx].name, expr); - hs_scope_close(hs_scope); + access_close(access); } } #endif @@ -12700,6 +12723,7 @@ rd_frame(void) ctx->reg_unwind_count = unwind_count; ctx->module_base = push_array(scratch.arena, U64, 1); ctx->module_base[0] = module->vaddr_range.min; + ctx->frame_base = push_array(scratch.arena, U64, 1); ctx->tls_base = push_array(scratch.arena, U64, 1); ctx->tls_base[0] = d_query_cached_tls_base_vaddr_from_process_root_rip(process, tls_root_vaddr, rip_vaddr); } @@ -12784,6 +12808,12 @@ rd_frame(void) } } + // rjf: run -> refocus pre-stop focused window + if(kind == RD_CmdKind_Run) + { + os_focus_external_window(rd_state->prestop_focused_window); + } + // rjf: run -> no active targets, no processes, but we only have one target? -> just launch it, then select it if((kind == RD_CmdKind_Run || kind == RD_CmdKind_StepInto || @@ -15907,10 +15937,10 @@ rd_frame(void) }break; case RD_CmdKind_AddThemeColor: { - HS_Scope *hs_scope = hs_scope_open(); + Access *access = access_open(); RD_Cfg *parent = rd_cfg_from_id(rd_regs()->cfg); RD_Cfg *theme = rd_cfg_child_from_string_or_alloc(parent, str8_lit("theme")); - MD_Node *theme_tree = rd_theme_tree_from_name(scratch.arena, hs_scope, theme->first->string); + MD_Node *theme_tree = rd_theme_tree_from_name(scratch.arena, access, theme->first->string); if(theme_tree == &md_nil_node) { rd_cfg_new_replace(theme, rd_theme_preset_display_string_table[RD_ThemePreset_DefaultDark]); @@ -15919,11 +15949,11 @@ rd_frame(void) rd_cfg_new(color, str8_lit("tags")); RD_Cfg *value = rd_cfg_new(color, str8_lit("value")); rd_cfg_new(value, str8_lit("0xffffffff")); - hs_scope_close(hs_scope); + access_close(access); }break; case RD_CmdKind_ForkTheme: { - HS_Scope *hs_scope = hs_scope_open(); + Access *access = access_open(); RD_Cfg *parent = rd_cfg_from_id(rd_regs()->cfg); RD_CfgList colors = rd_cfg_child_list_from_string(scratch.arena, parent, str8_lit("theme_color")); for(RD_CfgNode *n = colors.first; n != 0; n = n->next) @@ -15932,7 +15962,7 @@ rd_frame(void) } RD_Cfg *theme_cfg = rd_cfg_child_from_string(parent, str8_lit("theme")); String8 theme_name = theme_cfg->first->string; - MD_Node *theme_tree = rd_theme_tree_from_name(scratch.arena, hs_scope, theme_name); + MD_Node *theme_tree = rd_theme_tree_from_name(scratch.arena, access, theme_name); if(theme_tree == &md_nil_node) { theme_tree = rd_state->theme_preset_trees[RD_ThemePreset_DefaultDark]; @@ -15949,7 +15979,7 @@ rd_frame(void) } } rd_cfg_release(theme_cfg); - hs_scope_close(hs_scope); + access_close(access); }break; case RD_CmdKind_SaveTheme: case RD_CmdKind_SaveAndSetTheme: @@ -16062,15 +16092,14 @@ rd_frame(void) case RD_CmdKind_GoToNameAtCursor: case RD_CmdKind_ToggleWatchExpressionAtCursor: { - HS_Scope *hs_scope = hs_scope_open(); - TXT_Scope *txt_scope = txt_scope_open(); + Access *access = access_open(); RD_Regs *regs = rd_regs(); - HS_Key text_key = regs->text_key; + C_Key text_key = regs->text_key; TXT_LangKind lang_kind = regs->lang_kind; TxtRng range = txt_rng(regs->cursor, regs->mark); U128 hash = {0}; - TXT_TextInfo info = txt_text_info_from_key_lang(txt_scope, text_key, lang_kind, &hash); - String8 data = hs_data_from_hash(hs_scope, hash); + TXT_TextInfo info = txt_text_info_from_key_lang(access, text_key, lang_kind, &hash); + String8 data = c_data_from_hash(access, hash); Rng1U64 expr_off_range = {0}; if(range.min.column != range.max.column) { @@ -16085,8 +16114,7 @@ rd_frame(void) kind == RD_CmdKind_ToggleWatchExpressionAtCursor ? RD_CmdKind_ToggleWatchExpression : RD_CmdKind_GoToName), .string = expr); - txt_scope_close(txt_scope); - hs_scope_close(hs_scope); + access_close(access); }break; case RD_CmdKind_SetNextStatement: { @@ -16229,9 +16257,9 @@ rd_frame(void) }break; case RD_CmdKind_SelectUnwind: { - CTRL_Scope *ctrl_scope = ctrl_scope_open(); + Access *access = access_open(); CTRL_Entity *thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_base_regs()->thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(access, thread->handle, 1, os_now_microseconds()+10000); CTRL_CallStackFrame *frame = ctrl_call_stack_frame_from_unwind_and_inline_depth(&call_stack, rd_regs()->unwind_count, rd_regs()->inline_depth); if(frame == 0) { @@ -16243,14 +16271,14 @@ rd_frame(void) rd_state->base_regs.v.inline_depth = rd_regs()->inline_depth; } rd_cmd(RD_CmdKind_FindThread, .thread = thread->handle, .unwind_count = rd_state->base_regs.v.unwind_count, .inline_depth = rd_state->base_regs.v.inline_depth); - ctrl_scope_close(ctrl_scope); + access_close(access); }break; case RD_CmdKind_UpOneFrame: case RD_CmdKind_DownOneFrame: { - CTRL_Scope *ctrl_scope = ctrl_scope_open(); + Access *access = access_open(); CTRL_Entity *thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_base_regs()->thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(access, thread->handle, 1, os_now_microseconds()+10000); CTRL_CallStackFrame *current_frame = ctrl_call_stack_frame_from_unwind_and_inline_depth(&call_stack, rd_regs()->unwind_count, rd_regs()->inline_depth); CTRL_CallStackFrame *next_frame = current_frame; if(current_frame != 0) switch(kind) @@ -16274,7 +16302,7 @@ rd_frame(void) .unwind_count = next_frame->unwind_count, .inline_depth = next_frame->inline_depth); } - ctrl_scope_close(ctrl_scope); + access_close(access); }break; //- rjf: meta controls @@ -17048,6 +17076,7 @@ rd_frame(void) } if(ws != &rd_nil_window_state) { + rd_state->prestop_focused_window = os_focused_external_window(); os_window_set_minimized(ws->os, 0); os_window_bring_to_front(ws->os); os_window_focus(ws->os); @@ -17246,7 +17275,7 @@ rd_frame(void) { next = ws->hash_next; RD_Cfg *cfg = rd_cfg_from_id(ws->cfg_id); - if(cfg == &rd_nil_cfg || ws->last_frame_index_touched < rd_state->frame_index) + if(cfg == &rd_nil_cfg || ws->last_frame_index_touched < rd_state->frame_index || rd_state->quit) { ui_state_release(ws->ui); r_window_unequip(ws->os, ws->r); @@ -17295,10 +17324,10 @@ rd_frame(void) // will sleep for vsync, and we do not want to hold handles for long, // since eviction threads may be waiting to get rid of stuff. // + access_close(rd_state->frame_access); di_scope_close(rd_state->frame_di_scope); - ctrl_scope_close(rd_state->frame_ctrl_scope); + rd_state->frame_access = frame_access_restore; rd_state->frame_di_scope = frame_di_scope_restore; - rd_state->frame_ctrl_scope = frame_ctrl_scope_restore; ////////////////////////////// //- rjf: submit rendering to all windows diff --git a/src/raddbg/raddbg_core.h b/src/raddbg/raddbg_core.h index adc9f532..c58aff29 100644 --- a/src/raddbg/raddbg_core.h +++ b/src/raddbg/raddbg_core.h @@ -88,6 +88,7 @@ enum RD_EvalSpaceKind_MetaTheme, RD_EvalSpaceKind_MetaCtrlEntity, RD_EvalSpaceKind_MetaUnattachedProcess, + RD_EvalSpaceKind_MetaCallStackTree, }; //////////////////////////////// @@ -570,10 +571,10 @@ struct RD_State F32 tooltip_animation_rate; // rjf: serialized config debug string keys - HS_Key user_cfg_string_key; - HS_Key project_cfg_string_key; - HS_Key cmdln_cfg_string_key; - HS_Key transient_cfg_string_key; + C_Key user_cfg_string_key; + C_Key project_cfg_string_key; + C_Key cmdln_cfg_string_key; + C_Key transient_cfg_string_key; // rjf: schema table MD_NodePtrList *schemas; @@ -598,8 +599,10 @@ struct RD_State // rjf: frame parameters F32 frame_dt; + Access *frame_access; DI_Scope *frame_di_scope; - CTRL_Scope *frame_ctrl_scope; + CTRL_CallStackTree frame_call_stack_tree; + B32 got_frame_call_stack_tree; // rjf: dbgi match store DI_MatchStore *match_store; @@ -702,6 +705,9 @@ struct RD_State B32 bind_change_active; RD_CfgID bind_change_binding_id; String8 bind_change_cmd_name; + + // rjf: pre-stop focused window + OS_Handle prestop_focused_window; }; //////////////////////////////// @@ -892,7 +898,7 @@ internal B32 rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range internal B32 rd_eval_space_write(void *u, E_Space space, void *in, Rng1U64 range); //- rjf: asynchronous streamed reads -> hashes from spaces -internal HS_Key rd_key_from_eval_space_range(E_Space space, Rng1U64 range, B32 zero_terminated); +internal C_Key rd_key_from_eval_space_range(E_Space space, Rng1U64 range, B32 zero_terminated); //- rjf: space -> entire range internal Rng1U64 rd_whole_range_from_eval_space(E_Space space); @@ -982,7 +988,7 @@ internal void rd_set_autocomp_regs_(E_Eval dst_eval, RD_Regs *regs); //~ rjf: Colors, Fonts, Config //- rjf: colors -internal MD_Node *rd_theme_tree_from_name(Arena *arena, HS_Scope *scope, String8 theme_name); +internal MD_Node *rd_theme_tree_from_name(Arena *arena, Access *access, String8 theme_name); internal Vec4F32 rd_rgba_from_code_color_slot(RD_CodeColorSlot slot); internal RD_CodeColorSlot rd_code_color_slot_from_txt_token_kind(TXT_TokenKind kind); internal RD_CodeColorSlot rd_code_color_slot_from_txt_token_kind_lookup_string(TXT_TokenKind kind, String8 string); diff --git a/src/raddbg/raddbg_eval.c b/src/raddbg/raddbg_eval.c index 20d9e521..7b4f8088 100644 --- a/src/raddbg/raddbg_eval.c +++ b/src/raddbg/raddbg_eval.c @@ -968,7 +968,7 @@ E_TYPE_IREXT_FUNCTION_DEF(call_stack) B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread); accel->arch = entity->arch; accel->process = ctrl_process_from_entity(entity)->handle; - accel->call_stack = ctrl_call_stack_from_thread(rd_state->frame_ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); + accel->call_stack = ctrl_call_stack_from_thread(rd_state->frame_access, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); } scratch_end(scratch); } @@ -1538,6 +1538,95 @@ E_TYPE_EXPAND_RANGE_FUNCTION_DEF(ctrl_entities) } } +//////////////////////////////// +//~ rjf: Call Stack Tree Type Hooks + +E_TYPE_ACCESS_FUNCTION_DEF(call_stack_tree) +{ + E_IRTreeAndType result = {&e_irnode_nil}; + if(expr->kind == E_ExprKind_MemberAccess) + { + String8 id_string = expr->first->next->string; + U64 id = u64_from_str8(str8_skip(id_string, 1), 16); + if(id != 0) + { + result.type_key = lhs_irtree->type_key; + result.mode = E_Mode_Value; + result.root = e_irtree_set_space(arena, e_space_make(RD_EvalSpaceKind_MetaCallStackTree), e_irtree_const_u(arena, id)); + } + } + return result; +} + +typedef struct RD_CallStackTreeExpandAccel RD_CallStackTreeExpandAccel; +struct RD_CallStackTreeExpandAccel +{ + CTRL_CallStackTreeNode *node; + CTRL_CallStackTreeNode **children; + CTRL_HandleArray threads; +}; + +E_TYPE_EXPAND_INFO_FUNCTION_DEF(call_stack_tree) +{ + if(!rd_state->got_frame_call_stack_tree) + { + rd_state->got_frame_call_stack_tree = 1; + rd_state->frame_call_stack_tree = ctrl_call_stack_tree(rd_state->frame_access, 0); + } + RD_CallStackTreeExpandAccel *accel = push_array(arena, RD_CallStackTreeExpandAccel, 1); + accel->node = &ctrl_call_stack_tree_node_nil; + U64 id = e_value_eval_from_eval(eval).value.u64; + if(rd_state->frame_call_stack_tree.slots_count != 0) + { + for(CTRL_CallStackTreeNode *n = rd_state->frame_call_stack_tree.slots[id%rd_state->frame_call_stack_tree.slots_count]; + n != 0; + n = n->hash_next) + { + if(n->id == id) + { + accel->node = n; + break; + } + } + } + accel->children = push_array(arena, CTRL_CallStackTreeNode *, accel->node->child_count); + { + U64 idx = 0; + for(CTRL_CallStackTreeNode *n = accel->node->first; + n != &ctrl_call_stack_tree_node_nil; + n = n->next, idx += 1) + { + accel->children[idx] = n; + } + } + accel->threads = ctrl_handle_array_from_list(arena, &accel->node->threads); + E_TypeExpandInfo result = {accel}; + result.expr_count = accel->node->child_count + accel->threads.count; + return result; +} + +E_TYPE_EXPAND_RANGE_FUNCTION_DEF(call_stack_tree) +{ + Temp scratch = scratch_begin(&arena, 1); + RD_CallStackTreeExpandAccel *accel = (RD_CallStackTreeExpandAccel *)user_data; + CTRL_CallStackTreeNode *node = accel->node; + U64 needed_row_count = dim_1u64(idx_range); + for EachIndex(idx, needed_row_count) + { + E_Eval eval = e_eval_nil; + if(idx < node->child_count) + { + eval = e_eval_from_stringf("query:call_stack_tree.$%I64x", accel->children[idx]->id); + } + else if(node->child_count <= idx && idx < node->child_count + accel->threads.count) + { + eval = e_eval_from_stringf("query:control.%S", ctrl_string_from_handle(scratch.arena, accel->threads.v[idx - node->child_count])); + } + evals_out[idx] = eval; + } + scratch_end(scratch); +} + //////////////////////////////// //~ rjf: Debug Info Tables Eval Hooks diff --git a/src/raddbg/raddbg_eval.h b/src/raddbg/raddbg_eval.h index b5980a03..b2269be9 100644 --- a/src/raddbg/raddbg_eval.h +++ b/src/raddbg/raddbg_eval.h @@ -100,6 +100,13 @@ E_TYPE_ACCESS_FUNCTION_DEF(ctrl_entities); E_TYPE_EXPAND_INFO_FUNCTION_DEF(ctrl_entities); E_TYPE_EXPAND_RANGE_FUNCTION_DEF(ctrl_entities); +//////////////////////////////// +//~ rjf: Call Stack Tree Type Hooks + +E_TYPE_ACCESS_FUNCTION_DEF(call_stack_tree); +E_TYPE_EXPAND_INFO_FUNCTION_DEF(call_stack_tree); +E_TYPE_EXPAND_RANGE_FUNCTION_DEF(call_stack_tree); + //////////////////////////////// //~ rjf: Debug Info Tables Eval Hooks diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index 9795474f..4d7a2865 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -5,7 +5,14 @@ //~ rjf: post-0.9.20 TODO notes // //- urgent fixes +// [ ] (use msvc assert as an example) show fastfail exception info (code, name, etc.) - comes from ExceptionInformation @fastfail +// [ ] stepping w/ spoofs & shadow stack enabled - writing spoof will send a stack buffer overrun event @shadow_stack_step // [ ] hardware breakpoints regression (global eval in ctrl) +// [ ] native filesystem dialog, resizing raddbg window -> crash! +// [ ] stdout/stderr path target setting is now busted >:( +// [ ] target ui entry point should override built-in entry point +// [ ] list of all tabs in palette +// [ ] u64 + (ptr - ptr) seems to produce unexpected results - double check with C rules? // //- memory view // [ ] have smaller visible range than entire memory @@ -196,9 +203,11 @@ #define BUILD_TITLE "The RAD Debugger" #define OS_FEATURE_GRAPHICAL 1 +#define DMN_INIT_MANUAL 1 +#define CTRL_INIT_MANUAL 1 +#define OS_GFX_INIT_MANUAL 1 +#define FP_INIT_MANUAL 1 #define R_INIT_MANUAL 1 -#define TEX_INIT_MANUAL 1 -#define GEO_INIT_MANUAL 1 #define FNT_INIT_MANUAL 1 #define D_INIT_MANUAL 1 #define RD_INIT_MANUAL 1 @@ -211,12 +220,13 @@ #include "linker/hash_table.h" #include "os/os_inc.h" #include "async/async.h" +#include "artifact_cache/artifact_cache.h" #include "rdi/rdi_local.h" #include "rdi_make/rdi_make_local.h" #include "mdesk/mdesk.h" -#include "hash_store/hash_store.h" +#include "content/content.h" #include "file_stream/file_stream.h" -#include "text_cache/text_cache.h" +#include "text/text.h" #include "mutable_text/mutable_text.h" #include "coff/coff.h" #include "coff/coff_parse.h" @@ -235,21 +245,18 @@ #include "rdi_from_elf/rdi_from_elf.h" #include "rdi_from_pdb/rdi_from_pdb.h" #include "rdi_from_dwarf/rdi_from_dwarf.h" -#include "rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.h" #include "radbin/radbin.h" #include "regs/regs.h" #include "regs/rdi/regs_rdi.h" -#include "dbgi/dbgi.h" -#include "dasm_cache/dasm_cache.h" +#include "dbg_info/dbg_info.h" +#include "dbg_info/dbg_info2.h" +#include "disasm/disasm.h" #include "demon/demon_inc.h" #include "eval/eval_inc.h" #include "eval_visualization/eval_visualization_inc.h" #include "ctrl/ctrl_inc.h" #include "font_provider/font_provider_inc.h" #include "render/render_inc.h" -#include "ptr_graph_cache/ptr_graph_cache.h" -#include "texture_cache/texture_cache.h" -#include "geo_cache/geo_cache.h" #include "font_cache/font_cache.h" #include "draw/draw.h" #include "ui/ui_inc.h" @@ -261,12 +268,13 @@ #include "linker/hash_table.c" #include "os/os_inc.c" #include "async/async.c" +#include "artifact_cache/artifact_cache.c" #include "rdi/rdi_local.c" #include "rdi_make/rdi_make_local.c" #include "mdesk/mdesk.c" -#include "hash_store/hash_store.c" +#include "content/content.c" #include "file_stream/file_stream.c" -#include "text_cache/text_cache.c" +#include "text/text.c" #include "mutable_text/mutable_text.c" #include "coff/coff.c" #include "coff/coff_parse.c" @@ -285,21 +293,18 @@ #include "rdi_from_elf/rdi_from_elf.c" #include "rdi_from_pdb/rdi_from_pdb.c" #include "rdi_from_dwarf/rdi_from_dwarf.c" -#include "rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.c" #include "radbin/radbin.c" #include "regs/regs.c" #include "regs/rdi/regs_rdi.c" -#include "dbgi/dbgi.c" -#include "dasm_cache/dasm_cache.c" +#include "dbg_info/dbg_info.c" +#include "dbg_info/dbg_info2.c" +#include "disasm/disasm.c" #include "demon/demon_inc.c" #include "eval/eval_inc.c" #include "eval_visualization/eval_visualization_inc.c" #include "ctrl/ctrl_inc.c" #include "font_provider/font_provider_inc.c" #include "render/render_inc.c" -#include "ptr_graph_cache/ptr_graph_cache.c" -#include "texture_cache/texture_cache.c" -#include "geo_cache/geo_cache.c" #include "font_cache/font_cache.c" #include "draw/draw.c" #include "ui/ui_inc.c" @@ -330,17 +335,17 @@ struct IPCInfo //- rjf: IPC resources #define IPC_SHARED_MEMORY_BUFFER_SIZE MB(4) StaticAssert(IPC_SHARED_MEMORY_BUFFER_SIZE > sizeof(IPCInfo), ipc_buffer_size_requirement); -global OS_Handle ipc_sender2main_signal_semaphore = {0}; -global OS_Handle ipc_sender2main_lock_semaphore = {0}; +global Semaphore ipc_sender2main_signal_semaphore = {0}; +global Semaphore ipc_sender2main_lock_semaphore = {0}; global U8 *ipc_sender2main_shared_memory_base = 0; -global OS_Handle ipc_main2sender_signal_semaphore = {0}; -global OS_Handle ipc_main2sender_lock_semaphore = {0}; +global Semaphore ipc_main2sender_signal_semaphore = {0}; +global Semaphore ipc_main2sender_lock_semaphore = {0}; global U8 *ipc_main2sender_shared_memory_base = 0; global U8 ipc_s2m_ring_buffer[MB(4)] = {0}; global U64 ipc_s2m_ring_write_pos = 0; global U64 ipc_s2m_ring_read_pos = 0; -global OS_Handle ipc_s2m_ring_mutex = {0}; -global OS_Handle ipc_s2m_ring_cv = {0}; +global Mutex ipc_s2m_ring_mutex = {0}; +global CondVar ipc_s2m_ring_cv = {0}; //////////////////////////////// //~ rjf: IPC Signaler Thread @@ -348,7 +353,7 @@ global OS_Handle ipc_s2m_ring_cv = {0}; internal void ipc_signaler_thread__entry_point(void *p) { - ThreadNameF("[rd] ipc signaler thread"); + ThreadNameF("rd_ipc_signaler_thread"); for(;;) { if(os_semaphore_take(ipc_sender2main_signal_semaphore, max_U64)) @@ -358,7 +363,7 @@ ipc_signaler_thread__entry_point(void *p) IPCInfo *ipc_info = (IPCInfo *)ipc_sender2main_shared_memory_base; String8 msg = str8((U8 *)(ipc_info+1), ipc_info->msg_size); msg.size = Min(msg.size, IPC_SHARED_MEMORY_BUFFER_SIZE - sizeof(IPCInfo)); - OS_MutexScope(ipc_s2m_ring_mutex) for(;;) + MutexScope(ipc_s2m_ring_mutex) for(;;) { U64 unconsumed_size = ipc_s2m_ring_write_pos - ipc_s2m_ring_read_pos; U64 available_size = (sizeof(ipc_s2m_ring_buffer) - unconsumed_size); @@ -368,9 +373,9 @@ ipc_signaler_thread__entry_point(void *p) ipc_s2m_ring_write_pos += ring_write(ipc_s2m_ring_buffer, sizeof(ipc_s2m_ring_buffer), ipc_s2m_ring_write_pos, msg.str, msg.size); break; } - os_condition_variable_wait(ipc_s2m_ring_cv, ipc_s2m_ring_mutex, max_U64); + cond_var_wait(ipc_s2m_ring_cv, ipc_s2m_ring_mutex, max_U64); } - os_condition_variable_broadcast(ipc_s2m_ring_cv); + cond_var_broadcast(ipc_s2m_ring_cv); os_send_wakeup_event(); ipc_info->msg_size = 0; os_semaphore_drop(ipc_sender2main_lock_semaphore); @@ -438,9 +443,6 @@ entry_point(CmdLine *cmd_line) jit_attach = (jit_addr != 0); } - //- rjf: set up layers - ctrl_set_wakeup_hook(wakeup_hook_ctrl); - //- rjf: dispatch to top-level codepath based on execution mode switch(exec_mode) { @@ -486,12 +488,15 @@ entry_point(CmdLine *cmd_line) //- rjf: manual layer initialization { + dmn_init(); + ctrl_init(); + os_gfx_init(); + fp_init(); r_init(cmd_line); - tex_init(); - geo_init(); fnt_init(); d_init(); rd_init(cmd_line); + ctrl_set_wakeup_hook(wakeup_hook_ctrl); } //- rjf: set up shared resources for ipc to this instance; launch IPC signaler thread @@ -505,8 +510,8 @@ entry_point(CmdLine *cmd_line) String8 ipc_sender2main_lock_semaphore_name = push_str8f(scratch.arena, "_raddbg_ipc_sender2main_lock_semaphore_%i_", instance_pid); OS_Handle ipc_sender2main_shared_memory = os_shared_memory_alloc(IPC_SHARED_MEMORY_BUFFER_SIZE, ipc_sender2main_shared_memory_name); ipc_sender2main_shared_memory_base = (U8 *)os_shared_memory_view_open(ipc_sender2main_shared_memory, r1u64(0, IPC_SHARED_MEMORY_BUFFER_SIZE)); - ipc_sender2main_signal_semaphore = os_semaphore_alloc(0, 1, ipc_sender2main_signal_semaphore_name); - ipc_sender2main_lock_semaphore = os_semaphore_alloc(1, 1, ipc_sender2main_lock_semaphore_name); + ipc_sender2main_signal_semaphore = semaphore_alloc(0, 1, ipc_sender2main_signal_semaphore_name); + ipc_sender2main_lock_semaphore = semaphore_alloc(1, 1, ipc_sender2main_lock_semaphore_name); // rjf: set up cross-process main -> sender ring buffer String8 ipc_main2sender_shared_memory_name = push_str8f(scratch.arena, "_raddbg_ipc_main2sender_shared_memory_%i_", instance_pid); @@ -514,17 +519,17 @@ entry_point(CmdLine *cmd_line) String8 ipc_main2sender_lock_semaphore_name = push_str8f(scratch.arena, "_raddbg_ipc_main2sender_lock_semaphore_%i_", instance_pid); OS_Handle ipc_main2sender_shared_memory = os_shared_memory_alloc(IPC_SHARED_MEMORY_BUFFER_SIZE, ipc_main2sender_shared_memory_name); ipc_main2sender_shared_memory_base = (U8 *)os_shared_memory_view_open(ipc_main2sender_shared_memory, r1u64(0, IPC_SHARED_MEMORY_BUFFER_SIZE)); - ipc_main2sender_signal_semaphore = os_semaphore_alloc(0, 1, ipc_main2sender_signal_semaphore_name); - ipc_main2sender_lock_semaphore = os_semaphore_alloc(1, 1, ipc_main2sender_lock_semaphore_name); + ipc_main2sender_signal_semaphore = semaphore_alloc(0, 1, ipc_main2sender_signal_semaphore_name); + ipc_main2sender_lock_semaphore = semaphore_alloc(1, 1, ipc_main2sender_lock_semaphore_name); // rjf: set up ipc-receiver -> main thread ring buffer; launch signaler thread - ipc_s2m_ring_mutex = os_mutex_alloc(); - ipc_s2m_ring_cv = os_condition_variable_alloc(); + ipc_s2m_ring_mutex = mutex_alloc(); + ipc_s2m_ring_cv = cond_var_alloc(); IPCInfo *ipc_info = (IPCInfo *)ipc_sender2main_shared_memory_base; if(ipc_sender2main_shared_memory_base != 0) { MemoryZeroStruct(ipc_info); - os_thread_launch(ipc_signaler_thread__entry_point, 0, 0); + thread_launch(ipc_signaler_thread__entry_point, 0); } scratch_end(scratch); @@ -540,7 +545,7 @@ entry_point(CmdLine *cmd_line) Temp scratch = scratch_begin(0, 0); B32 consumed = 0; String8 msg = {0}; - OS_MutexScope(ipc_s2m_ring_mutex) + MutexScope(ipc_s2m_ring_mutex) { U64 unconsumed_size = ipc_s2m_ring_write_pos - ipc_s2m_ring_read_pos; if(unconsumed_size >= sizeof(U64)) @@ -555,7 +560,7 @@ entry_point(CmdLine *cmd_line) } if(consumed) { - os_condition_variable_broadcast(ipc_s2m_ring_cv); + cond_var_broadcast(ipc_s2m_ring_cv); } if(msg.size != 0) { @@ -700,7 +705,13 @@ entry_point(CmdLine *cmd_line) IPCInfo *ipc_info = (IPCInfo *)ipc_sender2main_shared_memory_base; U8 *buffer = (U8 *)(ipc_info+1); U64 buffer_max = IPC_SHARED_MEMORY_BUFFER_SIZE - sizeof(IPCInfo); - String8List parts = os_string_list_from_argcv(scratch.arena, cmd_line->argc - 1, cmd_line->argv + 1); + String8List parts = {0}; + { + for EachIndex(idx, cmd_line->argc-1) + { + str8_list_push(scratch.arena, &parts, str8_cstring(cmd_line->argv[idx+1])); + } + } StringJoin join = {str8_lit(""), str8_lit(" "), str8_lit("")}; String8 msg = str8_list_join(scratch.arena, &parts, &join); ipc_info->msg_size = Min(buffer_max, msg.size); @@ -740,6 +751,7 @@ entry_point(CmdLine *cmd_line) case ExecMode_BinaryUtility: { rb_entry_point(cmd_line); + di2_signal_completion(); }break; //- rjf: help message box diff --git a/src/raddbg/raddbg_views.c b/src/raddbg/raddbg_views.c index 1379a785..b1ced1c3 100644 --- a/src/raddbg/raddbg_views.c +++ b/src/raddbg/raddbg_views.c @@ -104,6 +104,21 @@ rd_code_view_build(Arena *arena, RD_CodeViewState *cv, RD_CodeViewBuildFlags fla } } + ////////////////////////////// + //- rjf: set up wrap cache + // + if(cv->wrap_arena == 0) + { + cv->wrap_arena = rd_push_view_arena(); + } + if(cv->wrap_total_vline_count == 0) + { + arena_clear(cv->wrap_arena); + cv->wrap_total_vline_count = text_info->lines_count; + cv->wrap_cache_slots_count = text_info->lines_count/64; + cv->wrap_cache_slots = push_array(cv->wrap_arena, RD_CodeViewTLineWrapCacheSlot, cv->wrap_cache_slots_count); + } + ////////////////////////////// //- rjf: determine visible line range / count // @@ -1011,11 +1026,11 @@ rd_watch_row_info_from_row(Arena *arena, EV_Row *row) CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(block_eval.space); if(entity->kind == CTRL_EntityKind_Thread) { - CTRL_Scope *ctrl_scope = ctrl_scope_open(); + Access *access = access_open(); info.callstack_thread = entity; U64 frame_num = ev_block_num_from_id(block, key.child_id); B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(access, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); if(1 <= frame_num && frame_num <= call_stack.frames_count) { CTRL_CallStackFrame *f = &call_stack.frames[frame_num-1]; @@ -1023,7 +1038,7 @@ rd_watch_row_info_from_row(Arena *arena, EV_Row *row) info.callstack_inline_depth = f->inline_depth; info.callstack_vaddr = regs_rip_from_arch_block(entity->arch, f->regs); } - ctrl_scope_close(ctrl_scope); + access_close(access); } } @@ -1446,8 +1461,7 @@ rd_watch_row_info_from_row(Arena *arena, EV_Row *row) F32 next_pct = 0; #define take_pct() (next_pct = (F32)f64_from_str8(w_cfg->string), w_cfg = w_cfg->next, next_pct) rd_watch_cell_list_push_new(arena, &info.cells, RD_WatchCellKind_CallStackFrame, row->eval, .default_pct = 0.05f, .pct = take_pct()); - rd_watch_cell_list_push_new(arena, &info.cells, RD_WatchCellKind_Eval, row->eval, .default_pct = 0.55f, .pct = take_pct()); - rd_watch_cell_list_push_new(arena, &info.cells, RD_WatchCellKind_Eval, e_eval_wrapf(row->eval, "hex((uint64)$)"), .default_pct = 0.20f, .pct = take_pct()); + rd_watch_cell_list_push_new(arena, &info.cells, RD_WatchCellKind_Eval, row->eval, .default_pct = 0.75f, .pct = take_pct()); rd_watch_cell_list_push_new(arena, &info.cells, RD_WatchCellKind_Eval, (module == &ctrl_entity_nil ? (E_Eval)zero_struct : module_eval), .default_pct = 0.20f, .pct = take_pct()); #undef take_pct @@ -1929,8 +1943,8 @@ rd_info_from_watch_row_cell(Arena *arena, EV_Row *row, EV_StringFlags string_fla dr_fstrs_push_new(arena, &fstrs, ¶ms, str8_lit(" ")); dr_fstrs_push_new(arena, &fstrs, ¶ms, name); { - HS_Scope *hs_scope = hs_scope_open(); - MD_Node *theme_tree = rd_theme_tree_from_name(scratch.arena, hs_scope, name); + Access *access = access_open(); + MD_Node *theme_tree = rd_theme_tree_from_name(scratch.arena, access, name); U64 color_idx = 0; for(MD_Node *n = theme_tree; color_idx < 4 && !md_node_is_nil(n); n = md_node_rec_depth_first_pre(n, theme_tree).next) { @@ -1958,7 +1972,7 @@ rd_info_from_watch_row_cell(Arena *arena, EV_Row *row, EV_StringFlags string_fla } } } - hs_scope_close(hs_scope); + access_close(access); } result.eval_fstrs = fstrs; }break; @@ -2010,8 +2024,7 @@ RD_VIEW_UI_FUNCTION_DEF(text) RD_CodeViewState *cv = rd_view_state(RD_CodeViewState); rd_code_view_init(cv); Temp scratch = scratch_begin(0, 0); - HS_Scope *hs_scope = hs_scope_open(); - TXT_Scope *txt_scope = txt_scope_open(); + Access *access = access_open(); ////////////////////////////// //- rjf: set up invariants @@ -2075,8 +2088,8 @@ RD_VIEW_UI_FUNCTION_DEF(text) rd_regs()->lang_kind = txt_lang_kind_from_extension(lang); } U128 hash = {0}; - TXT_TextInfo info = txt_text_info_from_key_lang(txt_scope, rd_regs()->text_key, rd_regs()->lang_kind, &hash); - String8 data = hs_data_from_hash(hs_scope, hash); + TXT_TextInfo info = txt_text_info_from_key_lang(access, rd_regs()->text_key, rd_regs()->lang_kind, &hash); + String8 data = c_data_from_hash(access, hash); B32 file_is_missing = (rd_regs()->file_path.size != 0 && os_properties_from_file_path(rd_regs()->file_path).modified == 0); B32 key_has_data = !u128_match(hash, u128_zero()) && info.lines_count; ProfEnd(); @@ -2163,7 +2176,7 @@ RD_VIEW_UI_FUNCTION_DEF(text) B32 file_is_out_of_date = 0; String8 out_of_date_dbgi_name = {0}; { - U64 file_timestamp = fs_properties_from_path(rd_regs()->file_path).modified; + U64 file_timestamp = os_properties_from_file_path(rd_regs()->file_path).modified; if(file_timestamp != 0) { for(DI_KeyNode *n = dbgi_keys.first; n != 0; n = n->next) @@ -2235,8 +2248,7 @@ RD_VIEW_UI_FUNCTION_DEF(text) rd_store_view_param_s64(str8_lit("mark_line"), rd_regs()->mark.line); rd_store_view_param_s64(str8_lit("mark_column"), rd_regs()->mark.column); - txt_scope_close(txt_scope); - hs_scope_close(hs_scope); + access_close(access); scratch_end(scratch); } @@ -2276,9 +2288,7 @@ RD_VIEW_UI_FUNCTION_DEF(disasm) } RD_CodeViewState *cv = &dv->cv; Temp scratch = scratch_begin(0, 0); - HS_Scope *hs_scope = hs_scope_open(); - DASM_Scope *dasm_scope = dasm_scope_open(); - TXT_Scope *txt_scope = txt_scope_open(); + Access *access = access_open(); ////////////////////////////// //- rjf: if disassembly views are not parameterized by anything, they @@ -2384,7 +2394,7 @@ RD_VIEW_UI_FUNCTION_DEF(disasm) syntax = DASM_Syntax_ATT; } } - HS_Key dasm_key = rd_key_from_eval_space_range(space, range, 0); + C_Key dasm_key = rd_key_from_eval_space_range(space, range, 0); U128 dasm_data_hash = {0}; DASM_Params dasm_params = {0}; { @@ -2395,12 +2405,12 @@ RD_VIEW_UI_FUNCTION_DEF(disasm) dasm_params.base_vaddr = base_vaddr; dasm_params.dbgi_key = dbgi_key; } - DASM_Info dasm_info = dasm_info_from_key_params(dasm_scope, dasm_key, &dasm_params, &dasm_data_hash); + DASM_Info dasm_info = dasm_info_from_key_params(access, dasm_key, &dasm_params, &dasm_data_hash); rd_regs()->text_key = dasm_info.text_key; rd_regs()->lang_kind = txt_lang_kind_from_arch(arch); U128 dasm_text_hash = {0}; - TXT_TextInfo dasm_text_info = txt_text_info_from_key_lang(txt_scope, rd_regs()->text_key, rd_regs()->lang_kind, &dasm_text_hash); - String8 dasm_text_data = hs_data_from_hash(hs_scope, dasm_text_hash); + TXT_TextInfo dasm_text_info = txt_text_info_from_key_lang(access, rd_regs()->text_key, rd_regs()->lang_kind, &dasm_text_hash); + String8 dasm_text_data = c_data_from_hash(access, dasm_text_hash); B32 has_disasm = (dasm_info.lines.count != 0 && dasm_text_info.lines_count != 0); B32 is_loading = (!has_disasm && dim_1u64(range) != 0 && eval.msgs.max_kind == E_MsgKind_Null && (space.kind != RD_EvalSpaceKind_CtrlEntity || space_entity != &ctrl_entity_nil)); @@ -2480,9 +2490,7 @@ RD_VIEW_UI_FUNCTION_DEF(disasm) dv->cursor = rd_regs()->cursor; dv->mark = rd_regs()->mark; - txt_scope_close(txt_scope); - dasm_scope_close(dasm_scope); - hs_scope_close(hs_scope); + access_close(access); scratch_end(scratch); } @@ -2921,10 +2929,10 @@ RD_VIEW_UI_FUNCTION_DEF(memory) }; AnnotationList *visible_memory_annotations = push_array(scratch.arena, AnnotationList, visible_memory_size); { - CTRL_Scope *ctrl_scope = ctrl_scope_open(); + Access *access = access_open(); CTRL_Entity *selected_thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_regs()->thread); CTRL_Entity *selected_process = ctrl_entity_ancestor_from_kind(selected_thread, CTRL_EntityKind_Process); - CTRL_CallStack selected_call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, selected_thread, 1, 0); + CTRL_CallStack selected_call_stack = ctrl_call_stack_from_thread(access, selected_thread->handle, 1, 0); CTRL_Entity *eval_process = &ctrl_entity_nil; if(eval.space.kind == RD_EvalSpaceKind_CtrlEntity) { @@ -3194,7 +3202,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) } } - ctrl_scope_close(ctrl_scope); + access_close(access); } ////////////////////////////// @@ -3715,6 +3723,13 @@ EV_EXPAND_RULE_INFO_FUNCTION_DEF(graph) //////////////////////////////// //~ rjf: bitmap @view_hook_impl +typedef struct RD_BitmapTopology RD_BitmapTopology; +struct RD_BitmapTopology +{ + Vec2S16 dim; + R_Tex2DFormat fmt; +}; + typedef struct RD_BitmapBoxDrawData RD_BitmapBoxDrawData; struct RD_BitmapBoxDrawData { @@ -3733,6 +3748,46 @@ struct RD_BitmapCanvasBoxDrawData F32 zoom; }; +internal AC_Artifact +rd_bitmap_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out) +{ + Access *access = access_open(); + + //- rjf: unpack key + U128 hash = {0}; + RD_BitmapTopology top = {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, &top); + } + String8 data = c_data_from_hash(access, hash); + + //- rjf: create texture + R_Handle texture = {0}; + if(top.dim.x > 0 && top.dim.y > 0 && + data.size >= (U64)top.dim.x*(U64)top.dim.y*(U64)r_tex2d_format_bytes_per_pixel_table[top.fmt]) + { + texture = r_tex2d_alloc(R_ResourceKind_Static, v2s32(top.dim.x, top.dim.y), top.fmt, data.str); + } + + //- rjf: bundle as artifact + AC_Artifact artifact = {0}; + StaticAssert(sizeof(artifact) >= sizeof(texture), tex_artifact_size_check); + MemoryCopy(&artifact, &texture, Min(sizeof(texture), sizeof(artifact))); + + access_close(access); + return artifact; +} + +internal void +rd_bitmap_artifact_destroy(AC_Artifact artifact) +{ + R_Handle texture = {0}; + MemoryCopy(&texture, &artifact, Min(sizeof(texture), sizeof(artifact))); + r_tex2d_release(texture); +} + internal Vec2F32 rd_bitmap_screen_from_canvas_pos(Vec2F32 view_center_pos, F32 zoom, Rng2F32 rect, Vec2F32 cvs) { @@ -3811,8 +3866,7 @@ EV_EXPAND_RULE_INFO_FUNCTION_DEF(bitmap) RD_VIEW_UI_FUNCTION_DEF(bitmap) { Temp scratch = scratch_begin(0, 0); - HS_Scope *hs_scope = hs_scope_open(); - TEX_Scope *tex_scope = tex_scope_open(); + Access *access = access_open(); ////////////////////////////// //- rjf: evaluate expression @@ -3859,11 +3913,31 @@ RD_VIEW_UI_FUNCTION_DEF(bitmap) ////////////////////////////// //- rjf: map expression artifacts -> texture // - HS_Key texture_key = rd_key_from_eval_space_range(eval.space, offset_range, 0); - TEX_Topology topology = tex_topology_make(dim, fmt); + C_Key texture_key = rd_key_from_eval_space_range(eval.space, offset_range, 0); + RD_BitmapTopology topology = {v2s16(dim.x, dim.y), fmt}; U128 data_hash = {0}; - R_Handle texture = tex_texture_from_key_topology(tex_scope, texture_key, topology, &data_hash); - String8 data = hs_data_from_hash(hs_scope, data_hash); + R_Handle texture = {0}; + for EachIndex(rewind_idx, C_KEY_HASH_HISTORY_COUNT) + { + U128 hash = c_hash_from_key(texture_key, rewind_idx); + struct + { + U128 hash; + RD_BitmapTopology top; + } + key_data = {hash, topology}; + String8 key = str8_struct(&key_data); + AC_Artifact artifact = ac_artifact_from_key(access, key, rd_bitmap_artifact_create, rd_bitmap_artifact_destroy, 0); + R_Handle texture_candidate = {0}; + MemoryCopy(&texture_candidate, &artifact, Min(sizeof(texture_candidate), sizeof(artifact))); + if(!r_handle_match(texture_candidate, r_handle_zero())) + { + data_hash = hash; + texture = texture_candidate; + break; + } + } + String8 data = c_data_from_hash(access, data_hash); ////////////////////////////// //- rjf: equip loading info @@ -4016,8 +4090,7 @@ RD_VIEW_UI_FUNCTION_DEF(bitmap) rd_store_view_param_f32(str8_lit("x"), view_center_pos.x); rd_store_view_param_f32(str8_lit("y"), view_center_pos.y); - hs_scope_close(hs_scope); - tex_scope_close(tex_scope); + access_close(access); scratch_end(scratch); } @@ -4289,6 +4362,51 @@ struct RD_Geo3DBoxDrawData R_Handle index_buffer; }; +internal AC_Artifact +rd_geo3d_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out) +{ + Access *access = access_open(); + U128 hash = {0}; + str8_deserial_read_struct(key, 0, &hash); + String8 data = c_data_from_hash(access, hash); + R_Handle buffer = {0}; + if(data.size != 0) + { + buffer = r_buffer_alloc(R_ResourceKind_Static, data.size, data.str); + } + AC_Artifact artifact = {0}; + MemoryCopy(&artifact, &buffer, Min(sizeof(artifact), sizeof(buffer))); + access_close(access); + return artifact; +} + +internal void +rd_geo3d_artifact_destroy(AC_Artifact artifact) +{ + R_Handle buffer = {0}; + MemoryCopy(&buffer, &artifact, Min(sizeof(buffer), sizeof(artifact))); + r_buffer_release(buffer); +} + +internal R_Handle +rd_geo3d_buffer_from_key(Access *access, C_Key key) +{ + R_Handle result = {0}; + for EachIndex(rewind_idx, C_KEY_HASH_HISTORY_COUNT) + { + U128 hash = c_hash_from_key(key, rewind_idx); + AC_Artifact artifact = ac_artifact_from_key(access, str8_struct(&hash), rd_geo3d_artifact_create, rd_geo3d_artifact_destroy, 0); + R_Handle buffer = {0}; + MemoryCopy(&buffer, &artifact, Min(sizeof(buffer), sizeof(artifact))); + if(!r_handle_match(buffer, r_handle_zero())) + { + result = buffer; + break; + } + } + return result; +} + internal UI_BOX_CUSTOM_DRAW(rd_geo3d_box_draw) { RD_Geo3DBoxDrawData *draw_data = (RD_Geo3DBoxDrawData *)user_data; @@ -4329,7 +4447,7 @@ EV_EXPAND_RULE_INFO_FUNCTION_DEF(geo3d) RD_VIEW_UI_FUNCTION_DEF(geo3d) { Temp scratch = scratch_begin(0, 0); - GEO_Scope *geo_scope = geo_scope_open(); + Access *access = access_open(); RD_Geo3DViewState *state = rd_view_state(RD_Geo3DViewState); ////////////////////////////// @@ -4349,10 +4467,10 @@ RD_VIEW_UI_FUNCTION_DEF(geo3d) U64 base_offset = eval_range.min; Rng1U64 idxs_range = r1u64(base_offset, base_offset+count*sizeof(U32)); Rng1U64 vtxs_range = r1u64(vtx_base_off, vtx_base_off+vtx_size); - HS_Key idxs_key = rd_key_from_eval_space_range(eval.space, idxs_range, 0); - HS_Key vtxs_key = rd_key_from_eval_space_range(eval.space, vtxs_range, 0); - R_Handle idxs_buffer = geo_buffer_from_key(geo_scope, idxs_key); - R_Handle vtxs_buffer = geo_buffer_from_key(geo_scope, vtxs_key); + C_Key idxs_key = rd_key_from_eval_space_range(eval.space, idxs_range, 0); + C_Key vtxs_key = rd_key_from_eval_space_range(eval.space, vtxs_range, 0); + R_Handle idxs_buffer = rd_geo3d_buffer_from_key(access, idxs_key); + R_Handle vtxs_buffer = rd_geo3d_buffer_from_key(access, vtxs_key); ////////////////////////////// //- rjf: equip loading info @@ -4436,6 +4554,6 @@ RD_VIEW_UI_FUNCTION_DEF(geo3d) rd_store_view_param_f32(str8_lit("pitch"), pitch_target); rd_store_view_param_f32(str8_lit("zoom"), zoom_target); - geo_scope_close(geo_scope); + access_close(access); scratch_end(scratch); } diff --git a/src/raddbg/raddbg_views.h b/src/raddbg/raddbg_views.h index 3b6d5c2a..f13b2ecc 100644 --- a/src/raddbg/raddbg_views.h +++ b/src/raddbg/raddbg_views.h @@ -14,6 +14,30 @@ enum RD_CodeViewBuildFlag_All = 0xffffffff, }; +typedef struct RD_CodeViewTLineSplitNode RD_CodeViewTLineSplitNode; +struct RD_CodeViewTLineSplitNode +{ + RD_CodeViewTLineSplitNode *next; + U64 off; +}; + +typedef struct RD_CodeViewTLineWrapCacheNode RD_CodeViewTLineWrapCacheNode; +struct RD_CodeViewTLineWrapCacheNode +{ + RD_CodeViewTLineWrapCacheNode *hash_next; + RD_CodeViewTLineWrapCacheNode *hash_prev; + S64 line_num; + RD_CodeViewTLineSplitNode *first_split; + RD_CodeViewTLineSplitNode *last_split; +}; + +typedef struct RD_CodeViewTLineWrapCacheSlot RD_CodeViewTLineWrapCacheSlot; +struct RD_CodeViewTLineWrapCacheSlot +{ + RD_CodeViewTLineWrapCacheNode *first; + RD_CodeViewTLineWrapCacheNode *last; +}; + typedef struct RD_CodeViewState RD_CodeViewState; struct RD_CodeViewState { @@ -32,6 +56,12 @@ struct RD_CodeViewState Arena *find_text_arena; String8 find_text_fwd; String8 find_text_bwd; + + // rjf: line wrapping cache & info + Arena *wrap_arena; + RD_CodeViewTLineWrapCacheSlot *wrap_cache_slots; + U64 wrap_cache_slots_count; + U64 wrap_total_vline_count; }; typedef struct RD_CodeViewBuildResult RD_CodeViewBuildResult; diff --git a/src/raddbg/raddbg_widgets.c b/src/raddbg/raddbg_widgets.c index 286a38cc..77cde640 100644 --- a/src/raddbg/raddbg_widgets.c +++ b/src/raddbg/raddbg_widgets.c @@ -547,12 +547,12 @@ rd_title_fstrs_from_ctrl_entity(Arena *arena, CTRL_Entity *entity, B32 include_e { Vec4F32 symbol_color = ui_color_from_name(str8_lit("code_symbol")); dr_fstrs_push_new(arena, &result, ¶ms, str8_lit(" ")); - CTRL_Scope *ctrl_scope = ctrl_scope_open(); + Access *access = access_open(); DI_Scope *di_scope = di_scope_open(); CTRL_Entity *process = ctrl_entity_ancestor_from_kind(entity, CTRL_EntityKind_Process); Arch arch = entity->arch; B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(access, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); B32 did_first_known = 0; for(U64 idx = 0, limit = 10; idx < call_stack.frames_count && idx < limit; @@ -592,7 +592,7 @@ rd_title_fstrs_from_ctrl_entity(Arena *arena, CTRL_Entity *entity, B32 include_e } } di_scope_close(di_scope); - ctrl_scope_close(ctrl_scope); + access_close(access); } //- rjf: modules get debug info status extras @@ -2384,17 +2384,24 @@ rd_code_slice(RD_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *prefe // if(do_scope_lines && cursor_scope_node != &txt_scope_node_nil) { - Vec4F32 scope_line_color = highlight_color; - scope_line_color.w *= 0.25f; + F32 scope_line_thickness = params->font_size*0.1f; + scope_line_thickness = Max(scope_line_thickness, 1.f); DR_Bucket *bucket = dr_bucket_make(); DR_BucketScope(bucket) { Vec2F32 text_base_pos = v2f32(text_container_box->rect.x0 + params->line_num_width_px + line_num_padding_px, text_container_box->rect.y0); + F32 ancestor_chain_depth = 0; for(TXT_ScopeNode *scope_n = cursor_scope_node; scope_n != &txt_scope_node_nil; - scope_n = txt_scope_node_from_info_num(params->text_info, scope_n->parent_num)) + scope_n = txt_scope_node_from_info_num(params->text_info, scope_n->parent_num), ancestor_chain_depth += 1) { + Vec4F32 scope_line_color = highlight_color; + F32 scope_line_color_target = highlight_color.w; + scope_line_color_target *= 1 - ancestor_chain_depth / 6.f; + scope_line_color_target = Max(0.2f, scope_line_color_target); + F32 scope_line_color_t = ui_anim(ui_key_from_stringf(text_container_box->key, "###scope_depth_%I64x_%I64x", scope_n->token_idx_range.min, scope_n->token_idx_range.max), scope_line_color_target, .rate = rd_state->menu_animation_rate__slow); + scope_line_color.w = scope_line_color_t*0.5f; Rng1U64 token_idx_range = scope_n->token_idx_range; Rng1U64 off_range = r1u64(params->text_info->tokens.v[token_idx_range.min].range.min, params->text_info->tokens.v[token_idx_range.max].range.min); TxtRng txt_range = txt_rng(txt_pt_from_info_off__linear_scan(params->text_info, off_range.min), txt_pt_from_info_off__linear_scan(params->text_info, off_range.max)); @@ -2422,7 +2429,7 @@ rd_code_slice(RD_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *prefe underline_clip.y1 = 10000; DR_ClipScope(underline_clip) { - dr_rect(underline_rect, scope_line_color, params->font_size*0.1f, 1.f, 1.f); + dr_rect(underline_rect, scope_line_color, params->font_size*0.1f, scope_line_thickness, 1.f); } } @@ -2460,7 +2467,7 @@ rd_code_slice(RD_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *prefe } DR_ClipScope(scope_clip_rect) { - dr_rect(scope_rect, scope_line_color, params->font_size*0.1f, 1.f, 1.f); + dr_rect(scope_rect, scope_line_color, params->font_size*0.1f, scope_line_thickness, 1.f); } } } diff --git a/src/raddump/raddump.c b/src/raddump/raddump.c index edb6a2f4..04192a6d 100644 --- a/src/raddump/raddump.c +++ b/src/raddump/raddump.c @@ -275,7 +275,7 @@ rd_format_preamble(Arena *arena, String8List *out, String8 indent, String8 input DateTime universal_dt = os_now_universal_time(); DateTime local_dt = os_local_time_from_universal(&universal_dt); - String8 time = push_date_time_string(scratch.arena, &local_dt); + String8 time = string_from_date_time(scratch.arena, &local_dt); String8 full_path = os_full_path_from_path(scratch.arena, input_path); rd_printf("# %S, [%S] %S", time, input_type_string, full_path); diff --git a/src/rdi/rdi.mdesk b/src/rdi/rdi.mdesk index 7dcd4eb0..8abb24e8 100644 --- a/src/rdi/rdi.mdesk +++ b/src/rdi/rdi.mdesk @@ -62,7 +62,7 @@ ""; "// \"raddbg\\0\\0\""; "#define RDI_MAGIC_CONSTANT 0x0000676264646172"; - "#define RDI_ENCODING_VERSION 12"; + "#define RDI_ENCODING_VERSION 13"; ""; "////////////////////////////////////////////////////////////////"; "//~ Format Types & Functions"; @@ -117,49 +117,49 @@ RDI_HeaderMemberTable: //////////////////////////////// //~ rjf: Format Section Tables -@table(name name_lower element_type value is_required index_base_type desc) +@table(name name_lower element_type value index_base_type desc) RDI_SectionTable: { - {NULL null RDI_U8 0x0000 - - ""} - {TopLevelInfo top_level_info RDI_TopLevelInfo 0x0001 - - ""} - {StringData string_data RDI_U8 0x0002 x - ""} - {StringTable string_table RDI_U32 0x0003 x U32 ""} - {IndexRuns index_runs RDI_U32 0x0004 x U32 ""} - {BinarySections binary_sections RDI_BinarySection 0x0005 - U32 ""} - {FilePathNodes file_path_nodes RDI_FilePathNode 0x0006 - U32 ""} - {SourceFiles source_files RDI_SourceFile 0x0007 - U32 ""} - {LineTables line_tables RDI_LineTable 0x0008 - U32 ""} - {LineInfoVOffs line_info_voffs RDI_U64 0x0009 - U32 ""} - {LineInfoLines line_info_lines RDI_Line 0x000A - U32 ""} - {LineInfoColumns line_info_columns RDI_Column 0x000B - U32 ""} - {SourceLineMaps source_line_maps RDI_SourceLineMap 0x000C - U32 ""} - {SourceLineMapNumbers source_line_map_numbers RDI_U32 0x000D - U32 ""} - {SourceLineMapRanges source_line_map_ranges RDI_U32 0x000E - U32 ""} - {SourceLineMapVOffs source_line_map_voffs RDI_U64 0x000F - U32 ""} - {Units units RDI_Unit 0x0010 - U32 ""} - {UnitVMap unit_vmap RDI_VMapEntry 0x0011 - - ""} - {TypeNodes type_nodes RDI_TypeNode 0x0012 - U32 ""} - {UDTs udts RDI_UDT 0x0013 - U32 ""} - {Members members RDI_Member 0x0014 - U32 ""} - {EnumMembers enum_members RDI_EnumMember 0x0015 - U32 ""} - {GlobalVariables global_variables RDI_GlobalVariable 0x0016 - U32 ""} - {GlobalVMap global_vmap RDI_VMapEntry 0x0017 - - ""} - {ThreadVariables thread_variables RDI_ThreadVariable 0x0018 - U32 ""} - {Constants constants RDI_Constant 0x0019 - U32 ""} - {Procedures procedures RDI_Procedure 0x001A - U32 ""} - {Scopes scopes RDI_Scope 0x001B - U32 ""} - {ScopeVOffData scope_voff_data RDI_U64 0x001C - U32 ""} - {ScopeVMap scope_vmap RDI_VMapEntry 0x001D - - ""} - {InlineSites inline_sites RDI_InlineSite 0x001E - U32 ""} - {Locals locals RDI_Local 0x001F - U32 ""} - {LocationBlocks location_blocks RDI_LocationBlock 0x0020 - U32 ""} - {LocationData location_data RDI_U8 0x0021 - U32 ""} - {ConstantValueData constant_value_data RDI_U8 0x0022 - U32 ""} - {ConstantValueTable constant_value_table RDI_U32 0x0023 - U32 ""} - {NameMaps name_maps RDI_NameMap 0x0024 - U32 ""} - {NameMapBuckets name_map_buckets RDI_NameMapBucket 0x0025 - U32 ""} - {NameMapNodes name_map_nodes RDI_NameMapNode 0x0026 - U32 ""} - {COUNT count RDI_U8 0x0027 - - ""} + {NULL null RDI_U8 0x0000 - ""} + {TopLevelInfo top_level_info RDI_TopLevelInfo 0x0001 - ""} + {StringData string_data RDI_U8 0x0002 - ""} + {StringTable string_table RDI_U32 0x0003 U32 ""} + {IndexRuns index_runs RDI_U32 0x0004 U32 ""} + {BinarySections binary_sections RDI_BinarySection 0x0005 U32 ""} + {FilePathNodes file_path_nodes RDI_FilePathNode 0x0006 U32 ""} + {SourceFiles source_files RDI_SourceFile 0x0007 U32 ""} + {LineTables line_tables RDI_LineTable 0x0008 U32 ""} + {LineInfoVOffs line_info_voffs RDI_U64 0x0009 U32 ""} + {LineInfoLines line_info_lines RDI_Line 0x000A U32 ""} + {LineInfoColumns line_info_columns RDI_Column 0x000B U32 ""} + {SourceLineMaps source_line_maps RDI_SourceLineMap 0x000C U32 ""} + {SourceLineMapNumbers source_line_map_numbers RDI_U32 0x000D U32 ""} + {SourceLineMapRanges source_line_map_ranges RDI_U32 0x000E U32 ""} + {SourceLineMapVOffs source_line_map_voffs RDI_U64 0x000F U32 ""} + {Units units RDI_Unit 0x0010 U32 ""} + {UnitVMap unit_vmap RDI_VMapEntry 0x0011 - ""} + {TypeNodes type_nodes RDI_TypeNode 0x0012 U32 ""} + {UDTs udts RDI_UDT 0x0013 U32 ""} + {Members members RDI_Member 0x0014 U32 ""} + {EnumMembers enum_members RDI_EnumMember 0x0015 U32 ""} + {GlobalVariables global_variables RDI_GlobalVariable 0x0016 U32 ""} + {GlobalVMap global_vmap RDI_VMapEntry 0x0017 - ""} + {ThreadVariables thread_variables RDI_ThreadVariable 0x0018 U32 ""} + {Constants constants RDI_Constant 0x0019 U32 ""} + {Procedures procedures RDI_Procedure 0x001A U32 ""} + {Scopes scopes RDI_Scope 0x001B U32 ""} + {ScopeVOffData scope_voff_data RDI_U64 0x001C U32 ""} + {ScopeVMap scope_vmap RDI_VMapEntry 0x001D - ""} + {InlineSites inline_sites RDI_InlineSite 0x001E U32 ""} + {Locals locals RDI_Local 0x001F U32 ""} + {LocationBlocks location_blocks RDI_LocationBlock 0x0020 U32 ""} + {LocationData location_data RDI_U8 0x0021 U32 ""} + {ConstantValueData constant_value_data RDI_U8 0x0022 U32 ""} + {ConstantValueTable constant_value_table RDI_U32 0x0023 U32 ""} + {NameMaps name_maps RDI_NameMap 0x0024 U32 ""} + {NameMapBuckets name_map_buckets RDI_NameMapBucket 0x0025 U32 ""} + {NameMapNodes name_map_nodes RDI_NameMapNode 0x0026 U32 ""} + {COUNT count RDI_U8 0x0027 - ""} } @table(name value) @@ -232,11 +232,6 @@ RDI_SectionMemberTable: @expand(RDI_SectionTable a) `sizeof($(a.element_type))`; } -@data(RDI_U8) rdi_section_is_required_table: -{ - @expand(RDI_SectionTable a) `$(a.is_required == 'x' -> 1)$(a.is_required != 'x' -> 0)`; -} - //////////////////////////////// //~ rjf: Common Type Tables @@ -438,6 +433,8 @@ RDI_RegCodeX64Table: {fip 98} {fdp 99} {mxcsr_mask 100} + {cetmsr 101} + {cetssp 102} } @enum(RDI_U32) RDI_Arch: @@ -1059,8 +1056,6 @@ RDI_ScopeMemberTable: {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 RDI_U32 ""} } @@ -1497,11 +1492,15 @@ RDI_PROC RDI_U8 *rdi_explanation_string_from_eval_conversion_kind(RDI_EvalConver 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; } diff --git a/src/rdi/rdi_local.c b/src/rdi/rdi_local.c index b67ccdca..8473578c 100644 --- a/src/rdi/rdi_local.c +++ b/src/rdi/rdi_local.c @@ -167,7 +167,7 @@ rdi_string_from_eval_op(Arena *arena, RDI_EvalOp op) switch(op) { default:{result = push_str8f(arena, "%#x", op);}break; -#define X(name) case RDI_EvalOp_##name:{result = str8_lit("#name");}break; +#define X(name) case RDI_EvalOp_##name:{result = str8_lit(#name);}break; RDI_EvalOp_XList #undef X } @@ -181,7 +181,7 @@ rdi_string_from_eval_type_group(Arena *arena, RDI_EvalTypeGroup eval_type_group) switch(eval_type_group) { default:{result = push_str8f(arena, "%#x", eval_type_group);}break; -#define X(name) case RDI_EvalTypeGroup_##name:{result = str8_lit("#name");}break; +#define X(name) case RDI_EvalTypeGroup_##name:{result = str8_lit(#name);}break; RDI_EvalTypeGroup_XList #undef X } @@ -512,25 +512,52 @@ rdi_strings_from_locations(Arena *arena, RDI_Parsed *rdi, RDI_Arch arch, Rng1U64 internal String8List rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags flags) { - String8List strings = {0}; + ProfBeginFunction(); + Temp scratch = scratch_begin(&arena, 1); String8 indent = str8_lit(" "); -#define dump(str) str8_list_push(arena, &strings, (str)) -#define dumpf(...) str8_list_pushf(arena, &strings, __VA_ARGS__) -#define DumpSubset(name) if(flags & RDI_DumpSubsetFlag_##name) DeferLoop(dumpf("////////////////////////////////\n//~ %S\n\n%S:\n{", rdi_name_title_from_dump_subset_table[RDI_DumpSubset_##name], rdi_name_lowercase_from_dump_subset_table[RDI_DumpSubset_##name]), dump(str8_lit("}\n\n"))) + + ////////////////////////////// + //- rjf: set up + // + typedef struct DumpSubsetOutputNode DumpSubsetOutputNode; + struct DumpSubsetOutputNode + { + DumpSubsetOutputNode *next; + RDI_DumpSubset subset; + String8List *lane_strings; + }; + local_persist DumpSubsetOutputNode *first_output_node = 0; + local_persist DumpSubsetOutputNode *last_output_node = 0; + local_persist String8List result_strings = {0}; + String8List *strings = 0; +#define dump(str) str8_list_push(arena, strings, (str)) +#define dumpf(...) str8_list_pushf(arena, strings, __VA_ARGS__) +#define DumpSubset(name) \ +if(lane_idx() == 0)\ +{\ +DumpSubsetOutputNode *n = push_array(scratch.arena, DumpSubsetOutputNode, 1);\ +SLLQueuePush(first_output_node, last_output_node, n);\ +n->subset = RDI_DumpSubset_##name;\ +n->lane_strings = push_array(scratch.arena, String8List, lane_count());\ +}\ +lane_sync();\ +strings = &last_output_node->lane_strings[lane_idx()];\ +lane_sync(); if(flags & RDI_DumpSubsetFlag_##name) ProfScope(#name) ////////////////////////////// //- rjf: dump data sections // DumpSubset(DataSections) { - dumpf("\n"); - for EachIndex(idx, rdi->sections_count) + if(lane_idx() == 0) { dumpf("\n"); } + Rng1U64 range = lane_range(rdi->sections_count); + for EachInRange(idx, range) { Temp scratch = scratch_begin(&arena, 1); RDI_SectionKind kind = (RDI_SectionKind)idx; RDI_Section *section = &rdi->sections[idx]; String8 kind_str = rdi_string_from_data_section_kind(scratch.arena, kind); - dumpf(" {%#08llx %7u %7u %*s} // data_section[%I64u]\n", section->off, section->encoded_size, section->unpacked_size, 24, kind_str.str, idx); + dumpf(" {%#010llx %10u %10u %*s} // data_section[%I64u]\n", section->off, section->encoded_size, section->unpacked_size, 24, kind_str.str, idx); scratch_end(scratch); } } @@ -540,14 +567,17 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla // DumpSubset(TopLevelInfo) { - RDI_TopLevelInfo *tli = rdi_element_from_name_idx(rdi, TopLevelInfo, 0); - Temp scratch = scratch_begin(&arena, 1); - dumpf("\n"); - dumpf(" arch: %S\n", rdi_string_from_arch(scratch.arena, tli->arch)); - dumpf(" exe_name: '%S'\n", str8_from_rdi_string_idx(rdi, tli->exe_name_string_idx)); - dumpf(" voff_max: %#08llx\n", tli->voff_max); - dumpf(" producer_name: '%S'\n", str8_from_rdi_string_idx(rdi, tli->producer_name_string_idx)); - scratch_end(scratch); + if(lane_idx() == 0) + { + RDI_TopLevelInfo *tli = rdi_element_from_name_idx(rdi, TopLevelInfo, 0); + Temp scratch = scratch_begin(&arena, 1); + dumpf("\n"); + dumpf(" arch: %S\n", rdi_string_from_arch(scratch.arena, tli->arch)); + dumpf(" exe_name: '%S'\n", str8_from_rdi_string_idx(rdi, tli->exe_name_string_idx)); + dumpf(" voff_max: %#08llx\n", tli->voff_max); + dumpf(" producer_name: '%S'\n", str8_from_rdi_string_idx(rdi, tli->producer_name_string_idx)); + scratch_end(scratch); + } } ////////////////////////////// @@ -555,10 +585,14 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla // DumpSubset(BinarySections) { + if(lane_idx() == 0) + { + dumpf("\n // %-16s %-16s %-12s %-12s %-12s %s\n", "name", "flags", "voff_first", "voff_opl", "foff_first", "foff_opl"); + } U64 count = 0; RDI_BinarySection *v = rdi_table_from_name(rdi, BinarySections, &count); - dumpf("\n // %-16s %-16s %-12s %-12s %-12s %s\n", "name", "flags", "voff_first", "voff_opl", "foff_first", "foff_opl"); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { Temp scratch = scratch_begin(&arena, 1); RDI_BinarySection *bin_section = &v[idx]; @@ -581,11 +615,12 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla // DumpSubset(FilePaths) { - dumpf("\n"); + if(lane_idx() == 0) { dumpf("\n"); } U64 count = 0; RDI_FilePathNode *v = rdi_table_from_name(rdi, FilePathNodes, &count); RDI_FilePathNode *nil = &v[0]; - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_FilePathNode *root = &v[idx]; if(root->parent_path_node != 0) { continue; } @@ -636,7 +671,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_SourceFile *v = rdi_table_from_name(rdi, SourceFiles, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_SourceFile *source_file = &v[idx]; dumpf("\n { file_path_node_idx: %4u, source_line_map: %4u, path: %-192S } // source_file[%I64u]", @@ -645,7 +681,7 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla push_str8f(arena, "'%S'", str8_from_rdi_string_idx(rdi, source_file->normal_full_path_string_idx)), idx); } - dumpf("\n"); + if(lane_idx() == lane_count()-1) { dumpf("\n"); } } ////////////////////////////// @@ -655,7 +691,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_Unit *v = rdi_table_from_name(rdi, Units, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_Unit *unit = &v[idx]; Temp scratch = scratch_begin(&arena, 1); @@ -678,10 +715,11 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla // DumpSubset(UnitVMap) { + if(lane_idx() == 0) { dumpf("\n"); } U64 count = 0; RDI_VMapEntry *v = rdi_table_from_name(rdi, UnitVMap, &count); - dumpf("\n"); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { dumpf(" {0x%I64x => %I64u}\n", v[idx].voff, v[idx].idx); } @@ -694,7 +732,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_LineTable *v = rdi_table_from_name(rdi, LineTables, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_LineTable *line_table = &v[idx]; RDI_ParsedLineTable parsed_line_table = {0}; @@ -730,7 +769,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_SourceLineMap *v = rdi_table_from_name(rdi, SourceLineMaps, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { Temp scratch = scratch_begin(&arena, 1); RDI_ParsedSourceLineMap line_map = {0}; @@ -762,7 +802,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_TypeNode *v = rdi_table_from_name(rdi, TypeNodes, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { Temp scratch = scratch_begin(&arena, 1); RDI_TypeNode *type = &v[idx]; @@ -798,7 +839,7 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla str8_list_pushf(scratch.arena, ¶m_idx_strings, "%u", param_idx_array[param_idx]); } String8 param_idx_str = str8_list_join(scratch.arena, ¶m_idx_strings, &(StringJoin){.pre = str8_lit("["), .sep = str8_lit(", "), .post = str8_lit("]")}); - dumpf(" constructed__params: %S\n", param_idx_str); + dumpf(" constructed__params: %S // idx_run[%u]\n", param_idx_str, type->constructed.param_idx_run_first); dumpf(" return_type: %u\n", type->constructed.direct_type_idx); } else if(type->kind == RDI_TypeKind_Method) @@ -818,8 +859,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla str8_list_pushf(scratch.arena, ¶m_idx_strings, "%u", param_idx_array[param_idx]); } String8 param_idx_str = str8_list_join(scratch.arena, ¶m_idx_strings, &(StringJoin){.pre = str8_lit("["), .sep = str8_lit(", "), .post = str8_lit("]")}); - dumpf(" constructed__this_type: %S\n", this_type_str); - dumpf(" constructed__params: %S\n", param_idx_str); + dumpf(" constructed__this_type: %S // idx_run[%u]\n", this_type_str, type->constructed.param_idx_run_first); + dumpf(" constructed__params: %S // idx_run[%u]\n", param_idx_str, type->constructed.param_idx_run_first); dumpf(" return_type: %u\n", type->constructed.direct_type_idx); } else if(RDI_TypeKind_FirstConstructed <= type->kind && type->kind <= RDI_TypeKind_LastConstructed) @@ -853,7 +894,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla RDI_Member *all_members = rdi_table_from_name(rdi, Members, &all_members_count); U64 all_enum_members_count = 0; RDI_EnumMember *all_enum_members = rdi_table_from_name(rdi, EnumMembers, &all_enum_members_count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_UDT *udt = &v[idx]; Temp scratch = scratch_begin(&arena, 1); @@ -910,7 +952,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_GlobalVariable *v = rdi_table_from_name(rdi, GlobalVariables, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_GlobalVariable *gvar = &v[idx]; Temp scratch = scratch_begin(&arena, 1); @@ -929,10 +972,11 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla // DumpSubset(GlobalVariablesVMap) { + if(lane_idx() == 0) { dumpf("\n"); } U64 count = 0; RDI_VMapEntry *v = rdi_table_from_name(rdi, GlobalVMap, &count); - dumpf("\n"); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { dumpf(" {0x%I64x => %I64u}\n", v[idx].voff, v[idx].idx); } @@ -945,7 +989,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_ThreadVariable *v = rdi_table_from_name(rdi, ThreadVariables, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_ThreadVariable *tvar = &v[idx]; Temp scratch = scratch_begin(&arena, 1); @@ -966,7 +1011,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_Constant *v = rdi_table_from_name(rdi, Constants, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_Constant *cnst = &v[idx]; dumpf("\n '%S': // constant[%I64u]\n {\n", str8_from_rdi_string_idx(rdi, cnst->name_string_idx), idx); @@ -983,24 +1029,28 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla RDI_TopLevelInfo *tli = rdi_element_from_name_idx(rdi, TopLevelInfo, 0); U64 count = 0; RDI_Procedure *v = rdi_table_from_name(rdi, Procedures, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_Procedure *proc = &v[idx]; Temp scratch = scratch_begin(&arena, 1); - String8List frame_base_location_strings = rdi_strings_from_locations(scratch.arena, rdi, tli->arch, r1u64(proc->frame_base_location_first, proc->frame_base_location_opl)); dumpf("\n '%S': // procedure[%I64u]\n {\n", str8_from_rdi_string_idx(rdi, proc->name_string_idx), idx); dumpf(" link_name: '%S'\n", str8_from_rdi_string_idx(rdi, proc->link_name_string_idx)); dumpf(" link_flags: `%S`\n", rdi_string_from_link_flags(scratch.arena, proc->link_flags)); dumpf(" type_idx: %u\n", proc->type_idx); dumpf(" root_scope_idx: %u\n", proc->root_scope_idx); dumpf(" container_idx: %u\n", proc->container_idx); - dumpf(" frame_base: // (first: %u, opl: %u)\n", proc->frame_base_location_first, proc->frame_base_location_opl); - dumpf(" {\n"); - for(String8Node *n = frame_base_location_strings.first; n != 0; n = n->next) + if(proc->frame_base_location_first != 0) { - dumpf(" %S\n", n->string); + String8List frame_base_location_strings = rdi_strings_from_locations(scratch.arena, rdi, tli->arch, r1u64(proc->frame_base_location_first, proc->frame_base_location_opl)); + dumpf(" frame_base: // (first: %u, opl: %u)\n", proc->frame_base_location_first, proc->frame_base_location_opl); + dumpf(" {\n"); + for(String8Node *n = frame_base_location_strings.first; n != 0; n = n->next) + { + dumpf(" %S\n", n->string); + } + dumpf(" }\n"); } - dumpf(" }\n"); dumpf(" }\n"); scratch_end(scratch); } @@ -1011,7 +1061,7 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla // DumpSubset(Scopes) { - dumpf("\n"); + if(lane_idx() == 0) { dumpf("\n"); } RDI_TopLevelInfo *tli = rdi_element_from_name_idx(rdi, TopLevelInfo, 0); U64 scope_voffs_count = 0; U64 *scope_voffs = rdi_table_from_name(rdi, ScopeVOffData, &scope_voffs_count); @@ -1020,7 +1070,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla U64 count = 0; RDI_Scope *v = rdi_table_from_name(rdi, Scopes, &count); RDI_Scope *nil = &v[0]; - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { if(v[idx].parent_scope_idx != 0) { continue; } RDI_Scope *root = &v[idx]; @@ -1129,10 +1180,11 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla // DumpSubset(ScopeVMap) { + if(lane_idx() == 0) { dumpf("\n"); } U64 count = 0; RDI_VMapEntry *v = rdi_table_from_name(rdi, ScopeVMap, &count); - dumpf("\n"); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { dumpf(" {0x%I64x => %I64u}\n", v[idx].voff, v[idx].idx); } @@ -1145,7 +1197,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; RDI_InlineSite *v = rdi_table_from_name(rdi, InlineSites, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_InlineSite *inline_site = &v[idx]; Temp scratch = scratch_begin(&arena, 1); @@ -1162,7 +1215,7 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla inline_site_idx); scratch_end(scratch); } - dumpf("\n"); + if(lane_idx() == lane_count()-1) { dumpf("\n"); } } ////////////////////////////// @@ -1173,7 +1226,8 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla Temp scratch = scratch_begin(&arena, 1); U64 count = 0; RDI_NameMap *v = rdi_table_from_name(rdi, NameMaps, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { RDI_ParsedNameMap name_map = {0}; rdi_parsed_from_name_map(rdi, &v[idx], &name_map); @@ -1201,9 +1255,10 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla for(U32 idx_i = 0; idx_i < idx_count; idx_i += 1) { U32 idx = idx_array[idx_i]; - str8_list_pushf(temp.arena, &idx_strings, "%u"); + str8_list_pushf(temp.arena, &idx_strings, "%u", idx); } - indices = str8_list_join(scratch.arena, &idx_strings, &(StringJoin){.sep = str8_lit(", ")}); + String8 extra = push_str8f(temp.arena, " // idx_run[%u]", node_ptr->match_idx_or_idx_run_first); + indices = str8_list_join(scratch.arena, &idx_strings, &(StringJoin){.sep = str8_lit(", "), .post = extra}); } dumpf(" \"%S\": %S\n", str, indices); temp_end(temp); @@ -1222,15 +1277,41 @@ rdi_dump_list_from_parsed(Arena *arena, RDI_Parsed *rdi, RDI_DumpSubsetFlags fla { U64 count = 0; U32 *v = rdi_table_from_name(rdi, StringTable, &count); - for EachIndex(idx, count) + Rng1U64 range = lane_range(count); + for EachInRange(idx, range) { dumpf("\n \"%S\" // string[%I64u]", str8_from_rdi_string_idx(rdi, idx), idx); } - dumpf("\n"); + if(lane_idx() == lane_count()-1) { dumpf("\n"); } } + ////////////////////////////// + //- rjf: join results + // + lane_sync(); + if(lane_idx() == 0) + { + for EachNode(n, DumpSubsetOutputNode, first_output_node) + { + String8List subset_strings = {0}; + for EachIndex(idx, lane_count()) + { + str8_list_concat_in_place(&subset_strings, &n->lane_strings[idx]); + } + if(subset_strings.total_size != 0) + { + str8_list_pushf(arena, &result_strings, "////////////////////////////////\n//~ %S\n\n%S:\n{", rdi_name_title_from_dump_subset_table[n->subset], rdi_name_lowercase_from_dump_subset_table[n->subset]); + str8_list_concat_in_place(&result_strings, &subset_strings); + str8_list_pushf(arena, &result_strings, "}\n\n"); + } + } + } + lane_sync(); + #undef DumpSubset #undef dumpf #undef dump - return strings; + scratch_end(scratch); + ProfEnd(); + return result_strings; } diff --git a/src/rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.c b/src/rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.c deleted file mode 100644 index e60dd482..00000000 --- a/src/rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.c +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Baking Tasks - -//- rjf: unit vmap baking - -ASYNC_WORK_DEF(p2b_bake_unit_vmap_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2b_async_root); - P2B_BakeUnitVMapIn *in = (P2B_BakeUnitVMapIn *)input; - RDIM_UnitVMapBakeResult *out = push_array(arena, RDIM_UnitVMapBakeResult, 1); - *out = rdim_bake_unit_vmap(arena, in->units); - ProfEnd(); - return out; -} - -//- rjf: line table baking - -ASYNC_WORK_DEF(p2b_bake_line_table_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2b_async_root); - P2B_BakeLineTablesIn *in = (P2B_BakeLineTablesIn *)input; - RDIM_LineTableBakeResult *out = push_array(arena, RDIM_LineTableBakeResult, 1); - *out = rdim_bake_line_tables(arena, in->line_tables); - ProfEnd(); - return out; -} - -//- rjf: per-procedure chunk dumping - -ASYNC_WORK_DEF(p2b_dump_proc_chunk_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2b_async_root); - P2B_DumpProcChunkIn *in = (P2B_DumpProcChunkIn *)input; - String8List *out = push_array(arena, String8List, 1); - RDI_LineTable *line_tables = in->line_tables_bake->line_tables; - RDI_U64 line_tables_count = in->line_tables_bake->line_tables_count; - RDI_U64 *line_table_voffs = in->line_tables_bake->line_table_voffs; - RDI_U64 line_table_voffs_count = in->line_tables_bake->line_table_voffs_count; - RDI_Line *line_table_lines = in->line_tables_bake->line_table_lines; - RDI_U64 line_table_lines_count = in->line_tables_bake->line_table_lines_count; - for(U64 idx = 0; idx < in->chunk->count; idx += 1) - { - // NOTE(rjf): breakpad does not support multiple voff ranges per procedure. - RDIM_Symbol *proc = &in->chunk->v[idx]; - RDIM_Scope *root_scope = proc->root_scope; - if(root_scope != 0 && root_scope->voff_ranges.first != 0) - { - // rjf: dump function record - RDIM_Rng1U64 voff_range = root_scope->voff_ranges.first->v; - str8_list_pushf(arena, out, "FUNC %I64x %I64x %I64x %S\n", voff_range.min, voff_range.max-voff_range.min, 0ull, proc->name); - - // rjf: dump function lines - U64 unit_idx = rdi_vmap_idx_from_voff(in->unit_vmap, in->unit_vmap_count, voff_range.min); - if(0 < unit_idx && unit_idx <= in->unit_count) - { - U32 line_table_idx = in->unit_line_table_idxs[unit_idx]; - if(0 < line_table_idx && line_table_idx <= line_tables_count) - { - // rjf: unpack unit line info - RDI_LineTable *line_table = &line_tables[line_table_idx]; - RDI_ParsedLineTable line_info = - { - line_table_voffs + line_table->voffs_base_idx, - line_table_lines + line_table->lines_base_idx, - 0, - line_table->lines_count, - 0 - }; - for(U64 voff = voff_range.min, last_voff = 0; - voff < voff_range.max && voff > last_voff;) - { - RDI_U64 line_info_idx = rdi_line_info_idx_from_voff(&line_info, voff); - if(line_info_idx < line_info.count) - { - RDI_Line *line = &line_info.lines[line_info_idx]; - U64 line_voff_min = line_info.voffs[line_info_idx]; - U64 line_voff_opl = line_info.voffs[line_info_idx+1]; - if(line->file_idx != 0) - { - str8_list_pushf(arena, out, "%I64x %I64x %I64u %I64u\n", - line_voff_min, - line_voff_opl-line_voff_min, - (U64)line->line_num, - (U64)line->file_idx); - } - last_voff = voff; - voff = line_voff_opl; - } - else - { - break; - } - } - } - } - } - } - ProfEnd(); - return out; -} diff --git a/src/rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.h b/src/rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.h deleted file mode 100644 index 1cc29d20..00000000 --- a/src/rdi_breakpad_from_pdb/rdi_breakpad_from_pdb.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_BREAKPAD_FROM_PDB_H -#define RDI_BREAKPAD_FROM_PDB_H - -//////////////////////////////// -//~ rjf: Baking Tasks - -//- rjf: unit vmap baking - -typedef struct P2B_BakeUnitVMapIn P2B_BakeUnitVMapIn; -struct P2B_BakeUnitVMapIn -{ - RDIM_UnitChunkList *units; -}; -ASYNC_WORK_DEF(p2b_bake_unit_vmap_work); - -//- rjf: line table baking - -typedef struct P2B_BakeLineTablesIn P2B_BakeLineTablesIn; -struct P2B_BakeLineTablesIn -{ - RDIM_LineTableChunkList *line_tables; -}; -ASYNC_WORK_DEF(p2b_bake_line_table_work); - -//- rjf: per-procedure chunk dumping - -typedef struct P2B_DumpProcChunkIn P2B_DumpProcChunkIn; -struct P2B_DumpProcChunkIn -{ - RDI_VMapEntry *unit_vmap; - U32 unit_vmap_count; - U32 *unit_line_table_idxs; - U64 unit_count; - RDIM_LineTableBakeResult *line_tables_bake; - RDIM_SymbolChunkNode *chunk; -}; -ASYNC_WORK_DEF(p2b_dump_proc_chunk_work); - -//////////////////////////////// -//~ rjf: Globals - -global ASYNC_Root *p2b_async_root = 0; - -#endif // RDI_BREAKPAD_FROM_PDB_H diff --git a/src/rdi_from_dwarf/rdi_from_dwarf.c b/src/rdi_from_dwarf/rdi_from_dwarf.c index 963a7164..25645a49 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf.c +++ b/src/rdi_from_dwarf/rdi_from_dwarf.c @@ -7,6 +7,58 @@ // however it is optional and in case it is missing converter has to generate the ranges from scopes. // [ ] Error handling +//////////////////////////////// + +static const U64 UNIT_CHUNK_CAP = 256; +static const U64 UDT_CHUNK_CAP = 256; +static const U64 TYPE_CHUNK_CAP = 256; +static const U64 SRC_FILE_CAP = 256; +static const U64 LINE_TABLE_CAP = 256; +static const U64 LOCATIONS_CAP = 256; +static const U64 GVAR_CHUNK_CAP = 256; +static const U64 TVAR_CHUNK_CAP = 256; +static const U64 PROC_CHUNK_CAP = 256; +static const U64 SCOPE_CHUNK_CAP = 256; +static const U64 INLINE_SITE_CHUNK_CAP = 256; + +RDIM_TopLevelInfo top_level_info = {0}; +RDIM_BinarySectionList binary_sections = {0}; +RDIM_UnitChunkList units = {0}; +RDIM_UDTChunkList udts = {0}; +RDIM_TypeChunkList types = {0}; +RDIM_SrcFileChunkList src_files = {0}; +RDIM_LineTableChunkList line_tables = {0}; +RDIM_LocationChunkList locations = {0}; +RDIM_SymbolChunkList gvars = {0}; +RDIM_SymbolChunkList tvars = {0}; +RDIM_SymbolChunkList procs = {0}; +RDIM_ScopeChunkList scopes = {0}; +RDIM_InlineSiteChunkList inline_sites = {0}; + +//////////////////////////////// + +internal B32 +rdim_is_eval_bytecode_static(RDIM_EvalBytecode bc) +{ + B32 is_static = 1; + RDI_EvalOp dynamic_ops[] = { RDI_EvalOp_MemRead, RDI_EvalOp_RegRead, RDI_EvalOp_RegReadDyn, RDI_EvalOp_CFA }; + for EachNode (n, RDIM_EvalBytecodeOp, bc.first_op) { + for EachIndex(i, ArrayCount(dynamic_ops)) { + is_static = 0; + goto exit; + } + } + exit:; + return is_static; +} + +internal U64 +rdim_do_static_bytecode_eval(RDIM_EvalBytecode bc, U64 image_base) +{ + NotImplemented; + return 0; +} + //////////////////////////////// //~ rjf: Enum Conversion Helpers @@ -90,23 +142,28 @@ d2r_create_type(Arena *arena, D2R_TypeTable *type_table) } internal RDIM_Type * -d2r_find_or_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off) +d2r_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off) { - RDIM_Type *type = 0; - KeyValuePair *is_type_present = hash_table_search_u64(type_table->ht, info_off); - if (is_type_present) { - type = is_type_present->value_raw; - } else { - type = d2r_create_type(arena, type_table); - hash_table_push_u64_raw(arena, type_table->ht, info_off, type); + RDIM_Type *type = d2r_create_type(arena, type_table); + Assert(hash_table_search_u64_raw(type_table->ht, info_off) == 0); + hash_table_push_u64_raw(arena, type_table->ht, info_off, type); + return type; +} + +internal RDIM_Type * +d2r_type_from_offset(D2R_TypeTable *type_table, U64 info_off) +{ + RDIM_Type *type = hash_table_search_u64_raw(type_table->ht, info_off); + if (type == 0) { + type = type_table->builtin_types[RDI_TypeKind_NULL]; } return type; } internal RDIM_Type * -d2r_type_from_attrib(Arena *arena, D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind) +d2r_type_from_attrib(D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind) { - RDIM_Type *type = 0; + RDIM_Type *type = type_table->builtin_types[RDI_TypeKind_Void]; // find attrib DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); @@ -122,14 +179,11 @@ d2r_type_from_attrib(Arena *arena, D2R_TypeTable *type_table, DW_Input *input, D // TODO: support for external compile unit references AssertAlways(ref.cu == cu); - // find or create type - type = d2r_find_or_create_type_from_offset(arena, type_table, ref.info_off); + // find type + type = d2r_type_from_offset(type_table, ref.info_off); } else { Assert(!"unexpected attrib class"); } - } else if (attrib->attrib_kind == DW_AttribKind_Null) { - // TODO(rjf): - // type = rdim_builtin_type_from_kind(*type_table->types, RDI_TypeKind_NULL); } return type; @@ -142,39 +196,45 @@ d2r_range_list_from_tag(Arena *arena, DW_Input *input, DW_CompUnit *cu, U64 imag Rng1U64List ranges = dw_rnglist_from_tag_attrib_kind(arena, input, cu, tag, DW_AttribKind_Ranges); // debase ranges - for (Rng1U64Node *range_n = ranges.first; range_n != 0; range_n = range_n->next) { + for EachNode(r, Rng1U64Node, ranges.first) { // TODO: error handling - AssertAlways(range_n->v.min >= image_base); - AssertAlways(range_n->v.max >= image_base); - range_n->v.min -= image_base; - range_n->v.max -= image_base; + AssertAlways(r->v.min >= image_base); + AssertAlways(r->v.max >= image_base); + r->v.min -= image_base; + r->v.max -= image_base; } // collect contiguous range - DW_Attrib *lo_pc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_LowPc); - DW_Attrib *hi_pc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_HighPc); - if (lo_pc_attrib->attrib_kind != DW_AttribKind_Null && hi_pc_attrib->attrib_kind != DW_AttribKind_Null) { - U64 lo_pc = dw_address_from_attrib(input, cu, lo_pc_attrib); - - U64 hi_pc; - DW_AttribClass hi_pc_class = dw_value_class_from_attrib(cu, hi_pc_attrib); - if (hi_pc_class == DW_AttribClass_Address) { - hi_pc = dw_address_from_attrib(input, cu, hi_pc_attrib); - } else if (hi_pc_class == DW_AttribClass_Const) { - hi_pc = dw_const_u64_from_attrib(input, cu, hi_pc_attrib); - hi_pc += lo_pc; - } else { - AssertAlways(!"undefined attrib encoding"); + { + DW_Attrib *lo_pc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_LowPc); + DW_Attrib *hi_pc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_HighPc); + if (lo_pc_attrib->attrib_kind != DW_AttribKind_Null && hi_pc_attrib->attrib_kind != DW_AttribKind_Null) { + U64 lo_pc = dw_address_from_attrib(input, cu, lo_pc_attrib); + + U64 hi_pc = 0; + DW_AttribClass hi_pc_class = dw_value_class_from_attrib(cu, hi_pc_attrib); + if (hi_pc_class == DW_AttribClass_Address) { + hi_pc = dw_address_from_attrib(input, cu, hi_pc_attrib); + } else if (hi_pc_class == DW_AttribClass_Const) { + hi_pc = dw_const_u64_from_attrib(input, cu, hi_pc_attrib); + hi_pc += lo_pc; + } else { + AssertAlways(!"unexpected attribute encoding"); + } + + if (lo_pc >= image_base && hi_pc >= image_base) { + if (lo_pc < hi_pc) { + rng1u64_list_push(arena, &ranges, rng_1u64(lo_pc - image_base, hi_pc - image_base)); + } else { + // TODO: error handling + } + } else { + // invalid low and hi PC are likely are caused by an optimization pass during linking + } + } else if (lo_pc_attrib->attrib_kind == DW_AttribKind_Null && hi_pc_attrib->attrib_kind != DW_AttribKind_Null || + lo_pc_attrib->attrib_kind != DW_AttribKind_Null && hi_pc_attrib->attrib_kind == DW_AttribKind_Null) { + // TODO: error handling } - - // TODO: error handling - AssertAlways(lo_pc >= image_base); - AssertAlways(hi_pc >= image_base); - AssertAlways(lo_pc <= hi_pc); - - U64 lo_voff = lo_pc - image_base; - U64 hi_voff = hi_pc - image_base; - rng1u64_list_push(arena, &ranges, rng_1u64(lo_voff, hi_voff)); } return ranges; @@ -190,7 +250,7 @@ d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Input *input for (DW_TagNode *i = cur_node->first_child; i != 0; i = i->sibling) { if (i->tag.kind == DW_TagKind_FormalParameter) { RDIM_TypeNode *n = push_array(scratch.arena, RDIM_TypeNode, 1); - n->v = d2r_type_from_attrib(arena, type_table, input, cu, i->tag, DW_AttribKind_Type); + n->v = d2r_type_from_attrib(type_table, input, cu, i->tag, DW_AttribKind_Type); SLLQueuePush(list.first, list.last, n); ++list.count; } else if (i->tag.kind == DW_TagKind_UnspecifiedParameters) { @@ -200,7 +260,7 @@ d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Input *input if (has_vargs) { RDIM_TypeNode *n = push_array(scratch.arena, RDIM_TypeNode, 1); - n->v = type_table->varg_type; + n->v = type_table->builtin_types[RDI_TypeKind_Variadic]; SLLQueuePush(list.first, list.last, n); ++list.count; } @@ -304,9 +364,9 @@ d2r_bytecode_from_expression(Arena *arena, }; struct Frame *stack = 0; #define push_of_type(type) do { \ -struct Frame *f = push_array(scratch.arena, struct Frame, 1); \ -f->value_type = d2r_type_group_from_type_kind(type); \ -SLLStackPush(stack, f); \ + struct Frame *f = push_array(scratch.arena, struct Frame, 1); \ + f->value_type = d2r_type_group_from_type_kind(type); \ + SLLStackPush(stack, f); \ } while (0) #define pop_type() stack->value_type; SLLStackPop(stack) #define peek_type() stack->value_type @@ -852,6 +912,13 @@ SLLStackPush(stack, f); \ rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Stop, 0); } break; + case DW_ExprOp_GNU_PushTlsAddress: { + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_ModuleOff, 0); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Sub, peek_type()); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_TLSOff, 0); + rdim_bytecode_push_op(arena, &bc, RDI_EvalOp_Add, peek_type()); + } break; + default: InvalidPath; break; } } @@ -864,40 +931,43 @@ SLLStackPush(stack, f); \ } internal RDIM_Location * -d2r_transpile_expression(Arena *arena, DW_Input *input, U64 image_base, U64 address_size, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr) +d2r_transpile_expression(Arena *arena, RDIM_LocationChunkList *locations, DW_Input *input, U64 image_base, U64 address_size, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr) { RDIM_Location *loc = 0; if (expr.size) { B32 is_addr = 0; RDIM_EvalBytecode bytecode = d2r_bytecode_from_expression(arena, input, image_base, address_size, arch, addr_lu, expr, cu, &is_addr); - loc = push_array(arena, RDIM_Location, 1); - loc->kind = is_addr ? RDI_LocationKind_AddrBytecodeStream : RDI_LocationKind_ValBytecodeStream; - loc->bytecode = bytecode; + RDIM_LocationInfo *loc_info = push_array(arena, RDIM_LocationInfo, 1); + loc_info->kind = is_addr ? RDI_LocationKind_AddrBytecodeStream : RDI_LocationKind_ValBytecodeStream; + loc_info->bytecode = bytecode; + + loc = rdim_location_chunk_list_push_new(arena, locations, LOCATIONS_CAP, loc_info); } return loc; } internal RDIM_Location * -d2r_location_from_attrib(Arena *arena, DW_Input *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind) +d2r_location_from_attrib(Arena *arena, RDIM_LocationChunkList *locations, DW_Input *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind) { String8 expr = dw_exprloc_from_tag_attrib_kind(input, cu, tag, kind); - RDIM_Location *location = d2r_transpile_expression(arena, input, image_base, cu->address_size, arch, cu->addr_lu, cu, expr); + RDIM_Location *location = d2r_transpile_expression(arena, locations, input, image_base, cu->address_size, arch, cu->addr_lu, cu, expr); return location; } -internal RDIM_LocationSet -d2r_locset_from_attrib(Arena *arena, - DW_Input *input, - DW_CompUnit *cu, - RDIM_ScopeChunkList *scopes, - RDIM_Scope *curr_scope, - U64 image_base, - Arch arch, - DW_Tag tag, - DW_AttribKind kind) +internal RDIM_LocationCaseList +d2r_locset_from_attrib(Arena *arena, + RDIM_ScopeChunkList *scopes, + RDIM_Scope *curr_scope, + RDIM_LocationChunkList *locations, + DW_Input *input, + DW_CompUnit *cu, + U64 image_base, + Arch arch, + DW_Tag tag, + DW_AttribKind kind) { - RDIM_LocationSet locset = {0}; + RDIM_LocationCaseList locset = {0}; // extract attrib from tag DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); @@ -910,10 +980,10 @@ d2r_locset_from_attrib(Arena *arena, DW_LocList loclist = dw_loclist_from_attrib(scratch.arena, input, cu, attrib); // convert location list to RDIM location set - for (DW_LocNode *loc_n = loclist.first; loc_n != 0; loc_n = loc_n->next) { - RDIM_Location *location = d2r_transpile_expression(arena, input, image_base, cu->address_size, arch, cu->addr_lu, cu, loc_n->v.expr); + for EachNode(loc_n, DW_LocNode, loclist.first) { + RDIM_Location *location = d2r_transpile_expression(arena, locations, input, image_base, cu->address_size, arch, cu->addr_lu, cu, loc_n->v.expr); RDIM_Rng1U64 voff_range = { .min = loc_n->v.range.min - image_base, .max = loc_n->v.range.max - image_base }; - rdim_location_set_push_case(arena, scopes, &locset, voff_range, location); + rdim_push_location_case(arena, scopes, &locset, location, voff_range); } scratch_end(scratch); @@ -922,9 +992,9 @@ d2r_locset_from_attrib(Arena *arena, String8 expr = dw_exprloc_from_attrib(input, cu, attrib); // convert expression and inherit life-time ranges from enclosed scope - RDIM_Location *location = d2r_transpile_expression(arena, input, image_base, cu->address_size, arch, cu->addr_lu, cu, expr); - for (RDIM_Rng1U64Node *range_n = curr_scope->voff_ranges.first; range_n != 0; range_n = range_n->next) { - rdim_location_set_push_case(arena, scopes, &locset, range_n->v, location); + RDIM_Location *location = d2r_transpile_expression(arena, locations, input, image_base, cu->address_size, arch, cu->addr_lu, cu, expr); + for EachNode(range_n, RDIM_Rng1U64Node, curr_scope->voff_ranges.first) { + rdim_push_location_case(arena, scopes, &locset, location, range_n->v); } } else if (attrib_class != DW_AttribClass_Null) { AssertAlways(!"unexpected attrib class"); @@ -933,17 +1003,18 @@ d2r_locset_from_attrib(Arena *arena, return locset; } -internal RDIM_LocationSet -d2r_var_locset_from_tag(Arena *arena, - DW_Input *input, - DW_CompUnit *cu, - RDIM_ScopeChunkList *scopes, - RDIM_Scope *curr_scope, - U64 image_base, - Arch arch, - DW_Tag tag) +internal RDIM_LocationCaseList +d2r_var_locset_from_tag(Arena *arena, + RDIM_ScopeChunkList *scopes, + RDIM_Scope *curr_scope, + RDIM_LocationChunkList *locations, + DW_Input *input, + DW_CompUnit *cu, + U64 image_base, + Arch arch, + DW_Tag tag) { - RDIM_LocationSet locset = {0}; + RDIM_LocationCaseList locset = {0}; B32 has_const_value = dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ConstValue); B32 has_location = dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Location); @@ -962,16 +1033,17 @@ d2r_var_locset_from_tag(Arena *arena, rdim_bytecode_push_uconst(arena, &bc, const_value); // fill out location - RDIM_Location *loc = push_array(arena, RDIM_Location, 1); - loc->kind = RDI_LocationKind_ValBytecodeStream; - loc->bytecode = bc; - + RDIM_LocationInfo *loc_info = push_array(arena, RDIM_LocationInfo, 1); + loc_info->kind = RDI_LocationKind_ValBytecodeStream; + loc_info->bytecode = bc; + RDIM_Location *loc = rdim_location_chunk_list_push_new(arena, locations, LOCATIONS_CAP, loc_info); + // push location cases - for (RDIM_Rng1U64Node *range_n = curr_scope->voff_ranges.first; range_n != 0; range_n = range_n->next) { - rdim_location_set_push_case(arena, scopes, &locset, range_n->v, loc); + for EachNode(range_n, RDIM_Rng1U64Node, curr_scope->voff_ranges.first) { + rdim_push_location_case(arena, scopes, &locset, loc, range_n->v); } } else if (has_location) { - locset = d2r_locset_from_attrib(arena, input, cu, scopes, curr_scope, image_base, arch, tag, DW_AttribKind_Location); + locset = d2r_locset_from_attrib(arena, scopes, curr_scope, locations, input, cu, image_base, arch, tag, DW_AttribKind_Location); } return locset; @@ -990,22 +1062,18 @@ d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Input *input, U64 image_base) cm.info_off_arr = push_array(arena, U64, unit_range_list.count); cm.voff_range_arr = push_array(arena, RDIM_Rng1U64ChunkList, unit_range_list.count); - for (Rng1U64Node *range_n = unit_range_list.first; range_n != 0; range_n = range_n->next) { + for EachNode(range_n, Rng1U64Node, unit_range_list.first) { String8 unit_data = str8_substr(aranges_data, range_n->v); U64 unit_cursor = 0; U64 unit_length = 0; U64 unit_length_size = str8_deserial_read_dwarf_packed_size(unit_data, unit_cursor, &unit_length); - if (unit_length_size == 0) { - continue; - } + if (unit_length_size == 0) { continue; } unit_cursor += unit_length_size; DW_Version version = 0; U64 version_size = str8_deserial_read_struct(unit_data, unit_cursor, &version); - if (version_size == 0) { - continue; - } + if (version_size == 0) { continue; } unit_cursor += version; if (version != DW_Version_2) { @@ -1016,23 +1084,17 @@ d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Input *input, U64 image_base) DW_Format unit_format = DW_FormatFromSize(unit_length); U64 cu_info_off = 0; U64 cu_info_off_size = str8_deserial_read_dwarf_uint(unit_data, unit_cursor, unit_format, &cu_info_off); - if (cu_info_off_size == 0) { - continue; - } + if (cu_info_off_size == 0) { continue; } unit_cursor += cu_info_off_size; U8 address_size = 0; U64 address_size_size = str8_deserial_read_struct(unit_data, unit_cursor, &address_size); - if (address_size_size == 0) { - continue; - } + if (address_size_size == 0) { continue; } unit_cursor += address_size_size; U8 segment_selector_size = 0; U64 segment_selector_size_size = str8_deserial_read_struct(unit_data, unit_cursor, &segment_selector_size); - if (segment_selector_size_size == 0) { - continue; - } + if (segment_selector_size_size == 0) { continue; } unit_cursor += segment_selector_size_size; U64 tuple_size = address_size * 2 + segment_selector_size; @@ -1049,9 +1111,8 @@ d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Input *input, U64 image_base) unit_cursor += str8_deserial_read(unit_data, unit_cursor, &address, address_size, address_size); unit_cursor += str8_deserial_read(unit_data, unit_cursor, &length, address_size, address_size); - if (address == 0 && length == 0) { - break; - } + if (address == 0 && length == 0) { break; } + if (address == 0) { continue; } // TODO: error handling AssertAlways(address >= image_base); @@ -1081,7 +1142,7 @@ internal RDIM_Rng1U64ChunkList d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off) { RDIM_Rng1U64ChunkList voff_ranges = {0}; - U64 voff_list_idx = u64_array_bsearch(map.info_off_arr, map.count, info_off); + U64 voff_list_idx = u64_array_bsearch(map.info_off_arr, map.count, info_off); if (voff_list_idx < map.count) { voff_ranges = map.voff_range_arr[voff_list_idx]; } @@ -1089,13 +1150,13 @@ d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off) } internal RDIM_Scope * -d2r_push_scope(Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D2R_TagNode *tag_stack, Rng1U64List ranges) +d2r_push_scope(Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D2R_TagFrame *tag_stack, Rng1U64List ranges) { // fill out scope RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, scopes, scope_chunk_cap); // push ranges - for (Rng1U64Node *i = ranges.first; i != 0; i = i->next) { + for EachNode(i, Rng1U64Node, ranges.first) { rdim_scope_push_voff_range(arena, scopes, scope, (RDIM_Rng1U64){.min = i->v.min, i->v.max}); } @@ -1103,7 +1164,7 @@ d2r_push_scope(Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D tag_stack->scope = scope; // update scope hierarchy - DW_TagKind parent_tag_kind = tag_stack->next->cur_node->tag.kind; + DW_TagKind parent_tag_kind = tag_stack->next->node->tag.kind; if (parent_tag_kind == DW_TagKind_SubProgram || parent_tag_kind == DW_TagKind_InlinedSubroutine || parent_tag_kind == DW_TagKind_LexicalBlock) { RDIM_Scope *parent = tag_stack->next->scope; @@ -1122,990 +1183,1146 @@ d2r_push_scope(Arena *arena, RDIM_ScopeChunkList *scopes, U64 scope_chunk_cap, D //////////////////////////////// //~ rjf: Main Conversion Entry Point +internal D2R_TagIterator * +d2r_tag_iterator_init(Arena *arena, DW_TagNode *root) +{ + D2R_TagIterator *iter = push_array(arena, D2R_TagIterator, 1); + iter->free_list = 0; + iter->stack = push_array(arena, D2R_TagFrame, 1); + iter->stack->node = push_array(arena, DW_TagNode, 1); + *iter->stack->node = *root; + iter->stack->node->sibling = 0; + iter->visit_children = 1; + iter->tag_node = root; + return iter; +} + +internal void +d2r_tag_iterator_next(Arena *arena, D2R_TagIterator *iter) +{ + // descend to first child + if (iter->visit_children) { + if (iter->stack->node->first_child) { + D2R_TagFrame *f = iter->free_list; + if (f) { SLLStackPop(iter->free_list); MemoryZeroStruct(f); } + else { f = push_array(arena, D2R_TagFrame, 1); } + f->node = iter->stack->node->first_child; + SLLStackPush(iter->stack, f); + goto exit; + } + } + + while (iter->stack) { + // go to sibling + iter->stack->node = iter->stack->node->sibling; + if (iter->stack->node) { break; } + + // no more siblings, go up + D2R_TagFrame *f = iter->stack; + SLLStackPop(iter->stack); + SLLStackPush(iter->free_list, f); + } + +exit:; + // update iterator + iter->visit_children = 1; + iter->tag_node = iter->stack ? iter->stack->node : 0; +} + +internal void +d2r_tag_iterator_skip_children(D2R_TagIterator *iter) +{ + iter->visit_children = 0; +} + +internal DW_TagNode * +d2r_tag_iterator_parent_tag_node(D2R_TagIterator *iter) +{ + return iter->stack->next->node; +} + +internal DW_Tag +d2r_tag_iterator_parent_tag(D2R_TagIterator *iter) +{ + DW_TagNode *tag_node = d2r_tag_iterator_parent_tag_node(iter); + return tag_node->tag; +} + +internal void +d2r_flag_converted_tag(DW_TagNode *tag_node) +{ + tag_node->tag.v[0] = 1; +} + +internal B8 +d2r_is_tag_converted(DW_TagNode *tag_node) +{ + return tag_node->tag.v[0]; +} + +internal RDIM_Type * +d2r_find_or_convert_type(Arena *arena, D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_Language cu_lang, U64 arch_addr_size, DW_Tag tag, DW_AttribKind kind) +{ + RDIM_Type *type = type_table->builtin_types[RDI_TypeKind_Void]; + + // find attrib + DW_Attrib *attrib = dw_attrib_from_tag(input, cu, tag, kind); + + // does tag have this attribute? + if (attrib->attrib_kind == kind) { + DW_AttribClass value_class = dw_value_class_from_attrib(cu, attrib); + + if (value_class == DW_AttribClass_Reference) { + // resolve reference + DW_Reference ref = dw_ref_from_attrib(input, cu, attrib); + + // TODO: support for external compile unit references + AssertAlways(ref.cu == cu); + + // find type + type = d2r_type_from_offset(type_table, ref.info_off); + + // was type converted? + if (type == 0) { + // issue type conversion + DW_TagNode *ref_node = dw_tag_node_from_info_off(cu, ref.info_off); + d2r_convert_types(arena, type_table, input, cu, cu_lang, arch_addr_size, ref_node); + + // if we do not have a converted type at this point then debug info is malformed + type = d2r_type_from_offset(type_table, ref.info_off); + Assert(type); + } + } else { + Assert(!"unexpected attrib class"); + } + } + + return type; +} + +internal void +d2r_convert_types(Arena *arena, + D2R_TypeTable *type_table, + DW_Input *input, + DW_CompUnit *cu, + DW_Language cu_lang, + U64 arch_addr_size, + DW_TagNode *root) +{ + Temp scratch = scratch_begin(&arena, 1); + for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { + DW_TagNode *tag_node = it->tag_node; + DW_Tag tag = tag_node->tag; + + // skip converted tags + if (d2r_is_tag_converted(tag_node)) { + d2r_tag_iterator_skip_children(it); + continue; + } + // mark the tag as converted here, because during conversion we may recurse on the same tag + d2r_flag_converted_tag(tag_node); + + switch (tag.kind) { + case DW_TagKind_ClassType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_IncompleteClass; + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + Assert(!tag_node->first_child); + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_Class; + type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); + type->direct_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + } + } break; + case DW_TagKind_StructureType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_IncompleteStruct; + + // TODO: error handling + Assert(!tag_node->first_child); + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_Struct; + type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); + } + } break; + case DW_TagKind_UnionType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_IncompleteUnion; + + // TODO: error handling + Assert(!tag_node->first_child); + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_Union; + type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); + } + } break; + case DW_TagKind_EnumerationType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_IncompleteEnum; + // TODO: error handling + Assert(!tag_node->first_child); + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *enum_base_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->kind = RDI_TypeKind_Enum; + type->byte_size = dw_byte_size_32_from_tag(input, cu, tag); + type->direct_type = enum_base_type; + } + } break; + case DW_TagKind_SubroutineType: { + RDIM_Type *ret_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + + // collect parameters + RDIM_TypeList param_list = {0}; + for (DW_TagNode *n = tag_node->first_child; n != 0; n = n->sibling) { + if (n->tag.kind == DW_TagKind_FormalParameter) { + RDIM_Type *param_type = d2r_type_from_attrib(type_table, input, cu, n->tag, DW_AttribKind_Type); + rdim_type_list_push(scratch.arena, ¶m_list, param_type); + } else if (n->tag.kind == DW_TagKind_UnspecifiedParameters) { + rdim_type_list_push(scratch.arena, ¶m_list, type_table->builtin_types[RDI_TypeKind_Variadic]); + } else { + // TODO: error handling + AssertAlways(!"unexpected tag"); + } + } + + // init proceudre type + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Function; + type->byte_size = arch_addr_size; + type->direct_type = ret_type; + type->count = param_list.count; + type->param_types = rdim_array_from_type_list(arena, param_list); + + d2r_tag_iterator_skip_children(it); + } break; + case DW_TagKind_Typedef: { + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Alias; + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->direct_type = direct_type; + for (RDIM_Type *n = direct_type; n != 0; n = n->direct_type) { + if (n->byte_size) { + type->byte_size = n->byte_size; + break; + } + } + } break; + case DW_TagKind_BaseType: { + DW_ATE encoding = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Encoding); + U64 byte_size = dw_byte_size_from_tag(input, cu, tag); + + // convert base type encoding to RDI version + RDI_TypeKind kind = RDI_TypeKind_NULL; + switch (encoding) { + case DW_ATE_Null: kind = RDI_TypeKind_NULL; break; + case DW_ATE_Address: kind = RDI_TypeKind_Void; break; + case DW_ATE_Boolean: kind = RDI_TypeKind_Bool; break; + case DW_ATE_ComplexFloat: { + switch (byte_size) { + case 4: kind = RDI_TypeKind_ComplexF32; break; + case 8: kind = RDI_TypeKind_ComplexF64; break; + case 10: kind = RDI_TypeKind_ComplexF80; break; + case 16: kind = RDI_TypeKind_ComplexF128; break; + default: AssertAlways(!"unexpected size"); break; // TODO: error handling + } + } break; + case DW_ATE_Float: { + switch (byte_size) { + case 2: kind = RDI_TypeKind_F16; break; + case 4: kind = RDI_TypeKind_F32; break; + case 6: kind = RDI_TypeKind_F48; break; + case 8: kind = RDI_TypeKind_F64; break; + case 16: kind = RDI_TypeKind_F128; break; + default: AssertAlways(!"unexpected size"); break; // TODO: error handling + } + } break; + case DW_ATE_Signed: { + switch (byte_size) { + case 1: kind = RDI_TypeKind_S8; break; + case 2: kind = RDI_TypeKind_S16; break; + case 4: kind = RDI_TypeKind_S32; break; + case 8: kind = RDI_TypeKind_S64; break; + case 16: kind = RDI_TypeKind_S128; break; + case 32: kind = RDI_TypeKind_S256; break; + case 64: kind = RDI_TypeKind_S512; break; + default: AssertAlways(!"unexpected size"); break; // TODO: error handling + } + } break; + case DW_ATE_SignedChar: { + switch (byte_size) { + case 1: kind = RDI_TypeKind_Char8; break; + case 2: kind = RDI_TypeKind_Char16; break; + case 4: kind = RDI_TypeKind_Char32; break; + default: AssertAlways(!"unexpected size"); break; // TODO: error handling + } + } break; + case DW_ATE_Unsigned: { + switch (byte_size) { + case 1: kind = RDI_TypeKind_U8; break; + case 2: kind = RDI_TypeKind_U16; break; + case 4: kind = RDI_TypeKind_U32; break; + case 8: kind = RDI_TypeKind_U64; break; + case 16: kind = RDI_TypeKind_U128; break; + case 32: kind = RDI_TypeKind_U256; break; + case 64: kind = RDI_TypeKind_U512; break; + default: AssertAlways(!"unexpected size"); break; // TODO: error handling + } + } break; + case DW_ATE_UnsignedChar: { + switch (byte_size) { + case 1: kind = RDI_TypeKind_UChar8; break; + case 2: kind = RDI_TypeKind_UChar16; break; + case 4: kind = RDI_TypeKind_UChar32; break; + default: AssertAlways(!"unexpected size"); break; // TODO: error handling + } + } break; + case DW_ATE_ImaginaryFloat: { + NotImplemented; + } break; + case DW_ATE_PackedDecimal: { + NotImplemented; + } break; + case DW_ATE_NumericString: { + NotImplemented; + } break; + case DW_ATE_Edited: { + NotImplemented; + } break; + case DW_ATE_SignedFixed: { + NotImplemented; + } break; + case DW_ATE_UnsignedFixed: { + NotImplemented; + } break; + case DW_ATE_DecimalFloat: { + NotImplemented; + } break; + case DW_ATE_Utf: { + NotImplemented; + } break; + case DW_ATE_Ucs: { + NotImplemented; + } break; + case DW_ATE_Ascii: { + NotImplemented; + } break; + default: AssertAlways(!"unexpected base type encoding"); break; // TODO: error handling + } + + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Alias; + type->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + type->direct_type = type_table->builtin_types[kind]; + type->byte_size = byte_size; + } break; + case DW_TagKind_PointerType: { + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + + // TODO: + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Allocated)); + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Associated)); + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)); + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)); + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_AddressClass)); + + U64 byte_size = arch_addr_size; + if (cu->version == DW_Version_5 || cu->relaxed) { + dw_try_byte_size_from_tag(input, cu, tag, &byte_size); + } + + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Ptr; + type->byte_size = byte_size; + type->direct_type = direct_type; + } break; + case DW_TagKind_RestrictType: { + // TODO: + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)); + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)); + + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Modifier; + type->byte_size = arch_addr_size; + type->flags = RDI_TypeModifierFlag_Restrict; + type->direct_type = direct_type; + } break; + case DW_TagKind_VolatileType: { + // TODO: + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)); + + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Modifier; + type->byte_size = arch_addr_size; + type->flags = RDI_TypeModifierFlag_Volatile; + type->direct_type = direct_type; + } break; + case DW_TagKind_ConstType: { + // TODO: + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Name)); + Assert(!dw_tag_has_attrib(input, cu, tag, DW_AttribKind_Alignment)); + + RDIM_Type *direct_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + RDIM_Type *type = d2r_create_type_from_offset(arena, type_table, tag.info_off); + type->kind = RDI_TypeKind_Modifier; + type->byte_size = arch_addr_size; + type->flags = RDI_TypeModifierFlag_Const; + type->direct_type = direct_type; + } break; + case DW_TagKind_ArrayType: { + // * DWARF vs RDI Array Type Graph * + // + // For example lets take following decl: + // + // int (*foo[2])[3]; + // + // This compiles to in DWARF: + // + // foo -> DW_TAG_ArrayType -> (A0) DW_TAG_Subrange [2] + // \ + // -> (B0) DW_TAG_PointerType -> (A1) DW_TAG_ArrayType -> DW_TAG_Subrange [3] + // \ + // -> (B1) DW_TAG_BaseType (int) + // + // RDI expects: + // + // foo -> Array[2] -> Pointer -> Array[3] -> int + // + // Note that DWARF forks the graph on DW_TAG_ArrayType to describe array ranges in branch A and + // in branch B describes array type which might be a struct, pointer, base type, or any other type tag. + // However, in RDI we have a simple list of type nodes and to convert we need to append type nodes from + // B to A. + struct SubrangeNode { struct SubrangeNode *next; U64 count; }; + struct SubrangeNode *subrange_stack = 0; + for (DW_TagNode *n = tag_node->first_child; n != 0; n = n->sibling) { + if (n->tag.kind != DW_TagKind_SubrangeType) { + // TODO: error handling + AssertAlways(!"unexpected tag"); + continue; + } + + // resolve lower bound + U64 lower_bound = 0; + if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_LowerBound)) { + lower_bound = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_LowerBound); + } else { + lower_bound = dw_pick_default_lower_bound(cu_lang); + } + + // resolve upper bound + U64 upper_bound = 0; + if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_Count)) { + U64 count = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_Count); + upper_bound = lower_bound + count; + } else if (dw_tag_has_attrib(input, cu, n->tag, DW_AttribKind_UpperBound)) { + upper_bound = dw_u64_from_attrib(input, cu, n->tag, DW_AttribKind_UpperBound); + // turn upper bound into exclusive range + upper_bound += 1; + } else { + // zero sized array + } + + struct SubrangeNode *s = push_array(scratch.arena, struct SubrangeNode, 1); + s->count = upper_bound - lower_bound; + SLLStackPush(subrange_stack, s); + } + + RDIM_Type *array_base_type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + RDIM_Type *direct_type = array_base_type; + U64 size_cursor = array_base_type->byte_size; + for EachNode(s, struct SubrangeNode, subrange_stack) { + size_cursor *= s->count; + + RDIM_Type *t; + if (s->next) { t = d2r_create_type(arena, type_table); } + else { t = d2r_create_type_from_offset(arena, type_table, tag.info_off); } + + t->kind = RDI_TypeKind_Array; + t->direct_type = direct_type; + t->byte_size = size_cursor; + t->count = s->count; + + direct_type = t; + } + + d2r_tag_iterator_skip_children(it); + } break; + case DW_TagKind_SubrangeType: { + // TODO: error handling + AssertAlways(!"unexpected tag"); + } break; + case DW_TagKind_Inheritance: { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind != DW_TagKind_StructureType && parent_tag.kind != DW_TagKind_ClassType) { + // TODO: error handling + AssertAlways(!"unexpected parent tag"); + } + + RDIM_Type *parent = d2r_type_from_offset(type_table, parent_tag.info_off); + RDIM_Type *type = d2r_find_or_convert_type(arena, type_table, input, cu, cu_lang, arch_addr_size, tag, DW_AttribKind_Type); + RDIM_UDTMember *member = rdim_udt_push_member(arena, &udts, parent->udt); + member->kind = RDI_MemberKind_Base; + member->type = type; + member->off = safe_cast_u32(dw_const_u32_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_DataMemberLocation)); + } break; + } + } + scratch_end(scratch); +} + +internal void +d2r_convert_udts(Arena *arena, + D2R_TypeTable *type_table, + DW_Input *input, + DW_CompUnit *cu, + DW_Language cu_lang, + U64 arch_addr_size, + DW_TagNode *root) +{ + Temp scratch = scratch_begin(&arena, 1); + for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { + DW_TagNode *tag_node = it->tag_node; + DW_Tag tag = tag_node->tag; + switch (tag.kind) { + case DW_TagKind_ClassType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *type = d2r_type_from_offset(type_table, tag.info_off); + RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &udts, UDT_CHUNK_CAP); + udt->self_type = type; + type->udt = udt; + } + } break; + case DW_TagKind_StructureType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *type = d2r_type_from_offset(type_table, tag.info_off); + RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &udts, UDT_CHUNK_CAP); + udt->self_type = type; + type->udt = udt; + } + } break; + case DW_TagKind_UnionType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *type = d2r_type_from_offset(type_table, tag.info_off); + RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &udts, UDT_CHUNK_CAP); + udt->self_type = type; + type->udt = udt; + } + } break; + case DW_TagKind_EnumerationType: { + B32 is_decl = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Declaration); + if (is_decl) { + d2r_tag_iterator_skip_children(it); + } else { + RDIM_Type *type = d2r_type_from_offset(type_table, tag.info_off); + RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &udts, UDT_CHUNK_CAP); + udt->self_type = type; + type->udt = udt; + } + } break; + case DW_TagKind_Member: { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + B32 is_parent_udt = parent_tag.kind == DW_TagKind_StructureType || + parent_tag.kind == DW_TagKind_ClassType || + parent_tag.kind == DW_TagKind_UnionType; + if (is_parent_udt) { + DW_Attrib *data_member_location = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_DataMemberLocation); + DW_AttribClass data_member_location_class = dw_value_class_from_attrib(cu, data_member_location); + if (data_member_location_class == DW_AttribClass_LocList) { + AssertAlways(!"UDT member with multiple locations are not supported"); + } + + RDIM_Type *parent_type = d2r_type_from_offset(type_table, parent_tag.info_off); + RDIM_UDTMember *udt_member = rdim_udt_push_member(arena, &udts, parent_type->udt); + udt_member->kind = RDI_MemberKind_DataField; + udt_member->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + udt_member->type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + udt_member->off = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_DataMemberLocation); + } else { + // TODO: error handling + AssertAlways(!"unexpected parent tag"); + } + } break; + case DW_TagKind_Enumerator: { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_EnumerationType) { + RDIM_Type *parent_type = d2r_type_from_offset(type_table, parent_tag.info_off); + RDIM_UDTEnumVal *udt_member = rdim_udt_push_enum_val(arena, &udts, parent_type->udt); + udt_member->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + udt_member->val = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_ConstValue); + } else { + // TODO: error handling + AssertAlways(!"unexpected parent tag"); + } + } break; + } + } + scratch_end(scratch); +} + +internal void +d2r_convert_symbols(Arena *arena, + D2R_TypeTable *type_table, + RDIM_Scope *global_scope, + DW_Input *input, + DW_CompUnit *cu, + DW_Language cu_lang, + U64 arch_addr_size, + U64 image_base, + Arch arch, + DW_TagNode *root) +{ + Temp scratch = scratch_begin(&arena, 1); + for (D2R_TagIterator *it = d2r_tag_iterator_init(scratch.arena, root); it->tag_node != 0; d2r_tag_iterator_next(scratch.arena, it)) { + DW_TagNode *tag_node = it->tag_node; + DW_Tag tag = tag_node->tag; + switch (tag.kind) { + case DW_TagKind_Null: { InvalidPath; } break; + case DW_TagKind_ClassType: + case DW_TagKind_StructureType: + case DW_TagKind_UnionType: { + // visit children to collect methods and variables + } break; + case DW_TagKind_EnumerationType: + case DW_TagKind_SubroutineType: + case DW_TagKind_Typedef: + case DW_TagKind_BaseType: + case DW_TagKind_PointerType: + case DW_TagKind_RestrictType: + case DW_TagKind_VolatileType: + case DW_TagKind_ConstType: + case DW_TagKind_ArrayType: + case DW_TagKind_SubrangeType: + case DW_TagKind_Inheritance: + case DW_TagKind_Enumerator: + case DW_TagKind_Member: { + d2r_tag_iterator_skip_children(it); + } break; + case DW_TagKind_SubProgram: { + DW_InlKind inl = dw_u64_from_attrib(input, cu, tag, DW_AttribKind_Inline); + switch (inl) { + case DW_Inl_NotInlined: { + U64 param_count = 0; + RDIM_Type **params = d2r_collect_proc_params(arena, type_table, input, cu, tag_node, ¶m_count); + + // get return type + RDIM_Type *ret_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + + // fill out proc type + RDIM_Type *proc_type = d2r_create_type(arena, type_table); + proc_type->kind = RDI_TypeKind_Function; + proc_type->byte_size = arch_addr_size; + proc_type->direct_type = ret_type; + proc_type->count = param_count; + proc_type->param_types = params; + + // get container type + RDIM_Type *container_type = 0; + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ContainingType)) { + container_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_ContainingType); + } + + // get frame base expression + String8 frame_base_expr = dw_exprloc_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_FrameBase); + + // get proc container symbol + RDIM_Symbol *proc = rdim_symbol_chunk_list_push(arena, &procs, PROC_CHUNK_CAP); + + // make scope + Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); + RDIM_Scope *root_scope = d2r_push_scope(arena, &scopes, SCOPE_CHUNK_CAP, it->stack, ranges); + root_scope->symbol = proc; + + // fill out proc + proc->is_extern = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_External); + proc->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + proc->link_name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_LinkageName); + proc->type = proc_type; + proc->container_symbol = 0; + proc->container_type = container_type; + proc->root_scope = root_scope; + proc->location_cases = d2r_locset_from_attrib(arena, &scopes, root_scope, &locations, input, cu, image_base, arch, tag, DW_AttribKind_FrameBase); + + // sub program with user-defined parent tag is a method + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_ClassType || parent_tag.kind == DW_TagKind_StructureType) { + RDI_MemberKind member_kind = RDI_MemberKind_NULL; + DW_VirtualityKind virtuality = dw_const_u64_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Virtuality); + switch (virtuality) { + case DW_VirtualityKind_None: member_kind = RDI_MemberKind_Method; break; + case DW_VirtualityKind_Virtual: member_kind = RDI_MemberKind_VirtualMethod; break; + case DW_VirtualityKind_PureVirtual: member_kind = RDI_MemberKind_VirtualMethod; break; // TODO: create kind for pure virutal + //default: InvalidPath; break; + } + + RDIM_Type *type = d2r_type_from_offset(type_table, parent_tag.info_off); + RDIM_UDTMember *member = rdim_udt_push_member(arena, &udts, type->udt); + member->kind = member_kind; + member->type = type; + member->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + } else if (parent_tag.kind != DW_TagKind_CompileUnit) { + //AssertAlways(!"unexpected tag"); + } + + it->stack->scope = root_scope; + } break; + case DW_Inl_DeclaredNotInlined: + case DW_Inl_DeclaredInlined: + case DW_Inl_Inlined: { + d2r_tag_iterator_skip_children(it); + } break; + default: InvalidPath; break; + } + } break; + case DW_TagKind_InlinedSubroutine: { + U64 param_count = 0; + RDIM_Type **params = d2r_collect_proc_params(arena, type_table, input, cu, tag_node, ¶m_count); + + // get return type + RDIM_Type *ret_type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + + // fill out proc type + RDIM_Type *proc_type = d2r_create_type(arena, type_table); + proc_type->kind = RDI_TypeKind_Function; + proc_type->byte_size = arch_addr_size; + proc_type->direct_type = ret_type; + proc_type->count = param_count; + proc_type->param_types = params; + + // get container type + RDIM_Type *owner = 0; + if (dw_tag_has_attrib(input, cu, tag, DW_AttribKind_ContainingType)) { + owner = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_ContainingType); + } + + // fill out inline site + RDIM_InlineSite *inline_site = rdim_inline_site_chunk_list_push(arena, &inline_sites, INLINE_SITE_CHUNK_CAP); + inline_site->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + inline_site->type = proc_type; + inline_site->owner = owner; + inline_site->line_table = 0; + + // make scope + Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); + RDIM_Scope *root_scope = d2r_push_scope(arena, &scopes, SCOPE_CHUNK_CAP, it->stack, ranges); + root_scope->inline_site = inline_site; + } break; + case DW_TagKind_Variable: { + String8 name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + RDIM_Type *type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_SubProgram || + parent_tag.kind == DW_TagKind_InlinedSubroutine || + parent_tag.kind == DW_TagKind_LexicalBlock) { + RDIM_Scope *scope = it->stack->next->scope; + RDIM_Local *local = rdim_scope_push_local(arena, &scopes, scope); + local->kind = RDI_LocalKind_Variable; + local->name = name; + local->type = type; + local->location_cases = d2r_var_locset_from_tag(arena, &scopes, scope, &locations, input, cu, image_base, arch, tag); + } else { + + // NOTE: due to a bug in clang in stb_sprint.h local variables + // are declared in global scope without a name + if (name.size == 0) { break; } + + B32 is_thread_var = 0; + U64 voff = 0; + { + DW_Attrib *loc_attrib = dw_attrib_from_tag(input, cu, tag, DW_AttribKind_Location); + DW_AttribClass loc_class = dw_value_class_from_attrib(cu, loc_attrib); + if (loc_class == DW_AttribClass_ExprLoc) { + String8 expr = dw_exprloc_from_attrib(input, cu, loc_attrib); + B32 is_addr = 0; + RDIM_EvalBytecode bc = d2r_bytecode_from_expression(arena, input, image_base, arch_addr_size, arch, cu->addr_lu, expr, cu, &is_addr); + + for EachNode(n, RDIM_EvalBytecodeOp, bc.first_op) { + if (n->op == RDI_EvalOp_TLSOff) { + is_thread_var = 1; + break; + } + } + + if (is_addr) { + if (rdim_is_eval_bytecode_static(bc)) { + voff = rdim_do_static_bytecode_eval(bc, image_base); + } + } + } + } + + RDIM_SymbolChunkList *var_chunks; U64 var_chunks_cap; + if (is_thread_var) { var_chunks = &tvars; var_chunks_cap = TVAR_CHUNK_CAP; } + else { var_chunks = &gvars; var_chunks_cap = GVAR_CHUNK_CAP; } + + RDIM_Symbol *var = rdim_symbol_chunk_list_push(arena, var_chunks, var_chunks_cap); + var->is_extern = dw_flag_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_External); + var->name = name; + var->link_name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_LinkageName); + var->type = type; + var->offset = voff; + var->container_symbol = 0; + var->container_type = 0; // TODO: NotImplemented; + } + } break; + case DW_TagKind_FormalParameter: { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_SubProgram || parent_tag.kind == DW_TagKind_InlinedSubroutine) { + RDIM_Scope *scope = it->stack->next->scope; + RDIM_Local *param = rdim_scope_push_local(arena, &scopes, scope); + param->kind = RDI_LocalKind_Parameter; + param->name = dw_string_from_tag_attrib_kind(input, cu, tag, DW_AttribKind_Name); + param->type = d2r_type_from_attrib(type_table, input, cu, tag, DW_AttribKind_Type); + param->location_cases = d2r_var_locset_from_tag(arena, &scopes, scope, &locations, input, cu, image_base, arch, tag); + } else { + // TODO: error handling + AssertAlways(!"this is a local variable"); + } + } break; + case DW_TagKind_LexicalBlock: { + DW_Tag parent_tag = d2r_tag_iterator_parent_tag(it); + if (parent_tag.kind == DW_TagKind_SubProgram || + parent_tag.kind == DW_TagKind_InlinedSubroutine || + parent_tag.kind == DW_TagKind_LexicalBlock) { + Rng1U64List ranges = d2r_range_list_from_tag(scratch.arena, input, cu, image_base, tag); + d2r_push_scope(arena, &scopes, SCOPE_CHUNK_CAP, it->stack, ranges); + } + } break; + case DW_TagKind_CallSite: { + // TODO + } break; + case DW_TagKind_CallSiteParameter: { + // TODO + } break; + case DW_TagKind_Label: + case DW_TagKind_CompileUnit: + case DW_TagKind_UnspecifiedParameters: + case DW_TagKind_Namespace: + case DW_TagKind_ImportedDeclaration: + case DW_TagKind_PtrToMemberType: + case DW_TagKind_TemplateTypeParameter: + case DW_TagKind_ReferenceType: { + // TODO: + } break; + default: NotImplemented; break; + } + } + scratch_end(scratch); +} + internal RDIM_BakeParams -d2r_convert(Arena *arena, ASYNC_Root *async_root, D2R_ConvertParams *params) +d2r_convert(Arena *arena, D2R_ConvertParams *params) { Temp scratch = scratch_begin(&arena, 1); - //////////////////////////////// - - ProfBegin("compute exe hash"); - U64 exe_hash = rdi_hash(params->exe_data.str, params->exe_data.size); - ProfEnd(); - - //////////////////////////////// - - Arch arch = Arch_Null; - U64 image_base = 0; - RDIM_BinarySectionList binary_sections = {0}; - DW_Input input = {0}; - - switch(params->exe_kind) - { + if (lane_idx() == 0) { + //////////////////////////////// + + ProfBegin("compute exe hash"); + U64 exe_hash = rdi_hash(params->exe_data.str, params->exe_data.size); + ProfEnd(); + + //////////////////////////////// + + Arch arch = Arch_Null; + U64 image_base = 0; + DW_Input input = {0}; + + switch(params->exe_kind) { default:{}break; - case ExecutableImageKind_CoffPe: - { - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, params->exe_data); - arch = pe.arch; - image_base = pe.image_base; - String8 raw_sections = str8_substr(params->exe_data, pe.section_table_range); - U64 section_count = raw_sections.size / sizeof(COFF_SectionHeader); - COFF_SectionHeader *section_table = (COFF_SectionHeader *)raw_sections.str; - String8 string_table = str8_substr(params->exe_data, pe.string_table_range); - binary_sections = c2r_rdi_binary_sections_from_coff_sections(arena, params->exe_data, string_table, section_count, section_table); - input = dw_input_from_coff_section_table(scratch.arena, params->exe_data, string_table, section_count, section_table); - }break; + case ExecutableImageKind_CoffPe: { + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, params->exe_data); + String8 raw_sections = str8_substr(params->exe_data, pe.section_table_range); + COFF_SectionHeader *section_table = str8_deserial_get_raw_ptr(raw_sections, 0, sizeof(COFF_SectionHeader) * pe.section_count); + String8 string_table = str8_substr(params->exe_data, pe.string_table_range); + arch = pe.arch; + image_base = pe.image_base; + binary_sections = c2r_rdi_binary_sections_from_coff_sections(arena, params->exe_data, string_table, pe.section_count, section_table); + input = dw_input_from_coff_section_table(scratch.arena, params->exe_data, string_table, pe.section_count, section_table); + } break; case ExecutableImageKind_Elf32: - case ExecutableImageKind_Elf64: - { + case ExecutableImageKind_Elf64: { ELF_Bin bin = elf_bin_from_data(scratch.arena, params->dbg_data); - arch = arch_from_elf_machine(bin.hdr.e_machine); - image_base = elf_base_addr_from_bin(&bin); + arch = arch_from_elf_machine(bin.hdr.e_machine); + image_base = elf_base_addr_from_bin(&bin); binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, bin.shdrs); - input = dw_input_from_elf_bin(scratch.arena, params->dbg_data, &bin); - }break; - } - - //////////////////////////////// - - RDIM_TopLevelInfo top_level_info = rdim_make_top_level_info(params->exe_name, arch, exe_hash, binary_sections); - - //////////////////////////////// - - U64 arch_addr_size = rdi_addr_size_from_arch(arch); - - //////////////////////////////// - - static const U64 UNIT_CHUNK_CAP = 256; - static const U64 UDT_CHUNK_CAP = 256; - static const U64 TYPE_CHUNK_CAP = 256; - static const U64 GVAR_CHUNK_CAP = 256; - static const U64 TVAR_CHUNK_CAP = 256; - static const U64 PROC_CHUNK_CAP = 256; - static const U64 SCOPE_CHUNK_CAP = 256; - static const U64 INLINE_SITE_CHUNK_CAP = 256; - static const U64 SRC_FILE_CAP = 256; - static const U64 LINE_TABLE_CAP = 256; - - RDIM_UnitChunkList units = {0}; - RDIM_UDTChunkList udts = {0}; - RDIM_TypeChunkList types = {0}; - RDIM_SymbolChunkList gvars = {0}; - RDIM_SymbolChunkList tvars = {0}; - RDIM_SymbolChunkList procs = {0}; - RDIM_ScopeChunkList scopes = {0}; - RDIM_InlineSiteChunkList inline_sites = {0}; - RDIM_SrcFileChunkList src_files = {0}; - RDIM_LineTableChunkList line_tables = {0}; - - //////////////////////////////// - - RDIM_Scope *global_scope = rdim_scope_chunk_list_push(arena, &scopes, SCOPE_CHUNK_CAP); - - //////////////////////////////// - - ProfBegin("Make Unit Contrib Map"); - D2R_CompUnitContribMap cu_contrib_map = {0}; - if (input.sec[DW_Section_ARanges].data.size > 0) { - cu_contrib_map = d2r_cu_contrib_map_from_aranges(arena, &input, image_base); - } else { - // TODO: synthesize cu ranges from scopes - NotImplemented; - } - ProfEnd(); - - ProfBegin("Parse Comop Unit Ranges"); - DW_ListUnitInput lu_input = dw_list_unit_input_from_input(scratch.arena, &input); - Rng1U64List cu_range_list = dw_unit_ranges_from_data(scratch.arena, input.sec[DW_Section_Info].data); - Rng1U64Array cu_ranges = rng1u64_array_from_list(scratch.arena, &cu_range_list); - ProfEnd(); - - //////////////////////////////// - - ProfBegin("Parse Compile Unit Headers"); - // TODO(rjf): parse should always be relaxed. any verification checks we do - // should just be logged via log_info(...), and then the caller of this - // converter can collect those & display as necessary. - B32 is_parse_relaxed = 1; - DW_CompUnit *cu_arr = push_array(scratch.arena, DW_CompUnit, cu_ranges.count); - for (U64 cu_idx = 0; cu_idx < cu_ranges.count; ++cu_idx) { - cu_arr[cu_idx] = dw_cu_from_info_off(scratch.arena, &input, lu_input, cu_ranges.v[cu_idx].min, is_parse_relaxed); - } - ProfEnd(); - - //////////////////////////////// - - ProfBegin("Parse Line Tables"); - DW_LineTableParseResult *cu_line_tables = push_array(scratch.arena, DW_LineTableParseResult, cu_ranges.count); - for (U64 cu_idx = 0; cu_idx < cu_ranges.count; ++cu_idx) { - DW_CompUnit *cu = &cu_arr[cu_idx]; - String8 cu_stmt_list = dw_line_ptr_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_StmtList); - String8 cu_dir = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_CompDir); - String8 cu_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Name); - cu_line_tables[cu_idx] = dw_parsed_line_table_from_data(scratch.arena, cu_stmt_list, &input, cu_dir, cu_name, cu->address_size, cu->str_offsets_lu); - } - ProfEnd(); - - //////////////////////////////// - - ProfBegin("Convert Line Tables"); - - HashTable *source_file_ht = hash_table_init(scratch.arena, 0x4000); - RDIM_LineTable **cu_line_tables_rdi = push_array(scratch.arena, RDIM_LineTable *, cu_ranges.count); - - for (U64 cu_idx = 0; cu_idx < cu_ranges.count; ++cu_idx) { - cu_line_tables_rdi[cu_idx] = rdim_line_table_chunk_list_push(arena, &line_tables, LINE_TABLE_CAP); - - DW_LineTableParseResult *line_table = &cu_line_tables[cu_idx]; - DW_LineVMFileArray *dir_table = &line_table->vm_header.dir_table; - DW_LineVMFileArray *file_table = &line_table->vm_header.file_table; - RDIM_SrcFile **src_file_map = push_array(scratch.arena, RDIM_SrcFile *, file_table->count); - for (U64 file_idx = 0; file_idx < file_table->count; ++file_idx) { - DW_LineFile *file = &file_table->v[file_idx]; - String8 file_path = dw_path_from_file_idx(scratch.arena, &line_table->vm_header, file_idx); - String8List file_path_split = str8_split_path(scratch.arena, file_path); - str8_path_list_resolve_dots_in_place(&file_path_split, PathStyle_WindowsAbsolute); - String8 file_path_resolved = str8_path_list_join_by_style(scratch.arena, &file_path_split, PathStyle_WindowsAbsolute); - RDIM_SrcFile *src_file = hash_table_search_path_raw(source_file_ht, file_path_resolved); - if (src_file == 0) { - src_file = rdim_src_file_chunk_list_push(arena, &src_files, SRC_FILE_CAP); - src_file->path = push_str8_copy(arena, file_path_resolved); - hash_table_push_path_raw(scratch.arena, source_file_ht, src_file->path, src_file); - } - src_file_map[file_idx] = src_file; + input = dw_input_from_elf_bin(scratch.arena, params->dbg_data, &bin); + } break; } - - for (DW_LineSeqNode *line_seq = line_table->first_seq; line_seq != 0; line_seq = line_seq->next) { - if (line_seq->count == 0) { - continue; + + //////////////////////////////// + + top_level_info = rdim_make_top_level_info(params->exe_name, arch, exe_hash, binary_sections); + + //////////////////////////////// + + U64 arch_addr_size = rdi_addr_size_from_arch(top_level_info.arch); + + //////////////////////////////// + + RDIM_Scope *global_scope = rdim_scope_chunk_list_push(arena, &scopes, SCOPE_CHUNK_CAP); + + //////////////////////////////// + + ProfBegin("Parse Unit Contrib Map"); + D2R_CompUnitContribMap cu_contrib_map = {0}; + if (input.sec[DW_Section_ARanges].data.size) { + cu_contrib_map = d2r_cu_contrib_map_from_aranges(arena, &input, image_base); + } + ProfEnd(); + + ProfBegin("Parse Comop Unit Ranges"); + DW_ListUnitInput lu_input = dw_list_unit_input_from_input(scratch.arena, &input); + Rng1U64List cu_range_list = dw_unit_ranges_from_data(scratch.arena, input.sec[DW_Section_Info].data); + Rng1U64Array cu_ranges = rng1u64_array_from_list(scratch.arena, &cu_range_list); + ProfEnd(); + + //////////////////////////////// + + ProfBegin("Parse Compile Unit Headers"); + // TODO(rjf): parse should always be relaxed. any verification checks we do + // should just be logged via log_info(...), and then the caller of this + // converter can collect those & display as necessary. + B32 is_parse_relaxed = 1; + DW_CompUnit *cu_arr = push_array(scratch.arena, DW_CompUnit, cu_ranges.count); + for EachIndex(cu_idx, cu_ranges.count) { + cu_arr[cu_idx] = dw_cu_from_info_off(scratch.arena, &input, lu_input, cu_ranges.v[cu_idx].min, is_parse_relaxed); + } + ProfEnd(); + + //////////////////////////////// + + ProfBegin("Parse Line Tables"); + DW_LineTableParseResult *cu_line_tables = push_array(scratch.arena, DW_LineTableParseResult, cu_ranges.count); + for EachIndex(cu_idx, cu_ranges.count) { + DW_CompUnit *cu = &cu_arr[cu_idx]; + String8 cu_stmt_list = dw_line_ptr_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_StmtList); + String8 cu_dir = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_CompDir); + String8 cu_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Name); + cu_line_tables[cu_idx] = dw_parsed_line_table_from_data(scratch.arena, cu_stmt_list, &input, cu_dir, cu_name, cu->address_size, cu->str_offsets_lu); + } + ProfEnd(); + + //////////////////////////////// + + ProfBegin("Convert Line Tables"); + HashTable *source_file_ht = hash_table_init(scratch.arena, 0x4000); + RDIM_LineTable **cu_line_tables_rdi = push_array(scratch.arena, RDIM_LineTable *, cu_ranges.count); + for EachIndex(cu_idx, cu_ranges.count) { + cu_line_tables_rdi[cu_idx] = rdim_line_table_chunk_list_push(arena, &line_tables, LINE_TABLE_CAP); + + DW_LineTableParseResult *line_table = &cu_line_tables[cu_idx]; + DW_LineVMFileArray *dir_table = &line_table->vm_header.dir_table; + DW_LineVMFileArray *file_table = &line_table->vm_header.file_table; + RDIM_SrcFile **src_file_map = push_array(scratch.arena, RDIM_SrcFile *, file_table->count); + for EachIndex(file_idx, file_table->count) { + DW_LineFile *file = &file_table->v[file_idx]; + String8 file_path = dw_path_from_file_idx(scratch.arena, &line_table->vm_header, file_idx); + String8List file_path_split = str8_split_path(scratch.arena, file_path); + str8_path_list_resolve_dots_in_place(&file_path_split, PathStyle_WindowsAbsolute); + String8 file_path_resolved = str8_path_list_join_by_style(scratch.arena, &file_path_split, PathStyle_WindowsAbsolute); + RDIM_SrcFile *src_file = hash_table_search_path_raw(source_file_ht, file_path_resolved); + if (src_file == 0) { + src_file = rdim_src_file_chunk_list_push(arena, &src_files, SRC_FILE_CAP); + src_file->path = push_str8_copy(arena, file_path_resolved); + hash_table_push_path_raw(scratch.arena, source_file_ht, src_file->path, src_file); + } + src_file_map[file_idx] = src_file; } - - U64 *voffs = push_array(arena, U64, line_seq->count); - U32 *line_nums = push_array(arena, U32, line_seq->count); - U16 *col_nums = 0; - U64 line_idx = 0; - - DW_LineNode *file_line_n = line_seq->first; - U64 file_line_count = 0; - - for (DW_LineNode *line_n = file_line_n; line_n != 0; line_n = line_n->next) { - if (file_line_n->v.file_index != line_n->v.file_index || line_n->next == 0) { + + for EachNode(line_seq, DW_LineSeqNode, line_table->first_seq) { + if (line_seq->count == 0) { continue; } + + U64 *voffs = push_array(arena, U64, line_seq->count); + U32 *line_nums = push_array(arena, U32, line_seq->count); + U16 *col_nums = 0; + U64 line_idx = 0; + + DW_LineNode *file_line_n = line_seq->first; + U64 file_line_count = 0; + + for EachNode(line_n, DW_LineNode, file_line_n) { + if (file_line_n->v.file_index != line_n->v.file_index || line_n->next == 0) { + U64 file_index = file_line_n->v.file_index; + U64 *file_voffs = &voffs[line_idx]; + U32 *file_line_nums = &line_nums[line_idx]; + U16 *file_col_nums = 0; + + U64 lines_written = 0; + U64 prev_ln = max_U64; + DW_LineNode *sentinel = line_n->v.file_index != file_line_n->v.file_index ? line_n : 0; + for (; file_line_n != sentinel; file_line_n = file_line_n->next) { + if (file_line_n->v.line != prev_ln) { + if (file_line_n->v.address == 0) { continue; } + + voffs[line_idx] = file_line_n->v.address - image_base; + line_nums[line_idx] = file_line_n->v.line; + + ++lines_written; + ++line_idx; + + prev_ln = file_line_n->v.line; + } + } + + RDIM_SrcFile *src_file = src_file_map[file_index]; + RDIM_LineSequence *line_seq = rdim_line_table_push_sequence(arena, &line_tables, cu_line_tables_rdi[cu_idx], src_file, file_voffs, file_line_nums, file_col_nums, lines_written); + rdim_src_file_push_line_sequence(arena, &src_files, src_file, line_seq); + + file_line_count = 1; + } else { + file_line_count += 1; + } + } + + // handle last line + if (file_line_n) { U64 file_index = file_line_n->v.file_index; U64 *file_voffs = &voffs[line_idx]; U32 *file_line_nums = &line_nums[line_idx]; U16 *file_col_nums = 0; - - U64 lines_written = 0; - U64 prev_ln = max_U64; - DW_LineNode *sentinel = line_n->v.file_index != file_line_n->v.file_index ? line_n : 0; - for (; file_line_n != sentinel; file_line_n = file_line_n->next) { - if (file_line_n->v.line != prev_ln) { - // TODO: error handling - AssertAlways(file_line_n->v.address >= image_base); - - voffs[line_idx] = file_line_n->v.address - image_base; - line_nums[line_idx] = file_line_n->v.line; - - ++lines_written; - ++line_idx; - - prev_ln = file_line_n->v.line; - } - } - - RDIM_SrcFile *src_file = src_file_map[file_index]; - RDIM_LineSequence *line_seq = rdim_line_table_push_sequence(arena, &line_tables, cu_line_tables_rdi[cu_idx], src_file, file_voffs, file_line_nums, file_col_nums, lines_written); - rdim_src_file_push_line_sequence(arena, &src_files, src_file, line_seq); - - file_line_count = 1; - } else { - ++file_line_count; - } - } - - // handle last line - if (file_line_n) { - U64 file_index = file_line_n->v.file_index; - U64 *file_voffs = &voffs[line_idx]; - U32 *file_line_nums = &line_nums[line_idx]; - U16 *file_col_nums = 0; - - for (; file_line_n != 0; file_line_n = file_line_n->next, ++line_idx) { - // TODO: error handling - AssertAlways(file_line_n->v.address >= image_base); - voffs[line_idx] = file_line_n->v.address - image_base; - line_nums[line_idx] = file_line_n->v.line; - } - - RDIM_SrcFile *src_file = src_file_map[file_index]; - RDIM_LineSequence *line_seq = rdim_line_table_push_sequence(arena, &line_tables, cu_line_tables_rdi[cu_idx], src_file, file_voffs, file_line_nums, file_col_nums, file_line_count); - rdim_src_file_push_line_sequence(arena, &src_files, src_file, line_seq); - } - - //Assert(line_idx == line_seq->count); - } - } - - ProfEnd(); - - //////////////////////////////// - - ProfBegin("Convert Units"); - - for (U64 cu_idx = 0; cu_idx < cu_ranges.count; ++cu_idx) { - Temp comp_temp = temp_begin(scratch.arena); - - DW_CompUnit *cu = &cu_arr[cu_idx]; - - // parse and build tag tree - DW_TagTree tag_tree = dw_tag_tree_from_cu(comp_temp.arena, &input, cu); - - // build tag hash table for abstract origin resolution - cu->tag_ht = dw_make_tag_hash_table(comp_temp.arena, tag_tree); - - String8 dwo_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_DwoName); - String8 gnu_dwo_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_GNU_DwoName); - if (dwo_name.size || gnu_dwo_name.size || cu->dwo_id) { - // TODO: report that we dont support DWO - continue; - } - - // get unit's contribution ranges - RDIM_Rng1U64ChunkList cu_voff_ranges = d2r_voff_ranges_from_cu_info_off(cu_contrib_map, cu_ranges.v[cu_idx].min); - - String8 cu_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Name); - String8 cu_dir = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_CompDir); - String8 cu_prod = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Producer); - DW_Language cu_lang = dw_const_u64_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Language); - - RDIM_Unit *unit = rdim_unit_chunk_list_push(arena, &units, UNIT_CHUNK_CAP); - unit->unit_name = cu_name; - unit->compiler_name = cu_prod; - unit->source_file = str8_zero(); - unit->object_file = str8_zero(); - unit->archive_file = str8_zero(); - unit->build_path = cu_dir; - unit->language = d2r_rdi_language_from_dw_language(cu_lang); - unit->line_table = cu_line_tables_rdi[cu_idx]; - unit->voff_ranges = cu_voff_ranges; - - D2R_TypeTable *type_table = push_array(comp_temp.arena, D2R_TypeTable, 1); - type_table->ht = hash_table_init(comp_temp.arena, 0x4000); - type_table->types = &types; - type_table->type_chunk_cap = TYPE_CHUNK_CAP; - type_table->varg_type = d2r_create_type(arena, type_table); - type_table->varg_type->kind = RDI_TypeKind_Variadic; - - D2R_TagNode *free_tags = push_array(comp_temp.arena, D2R_TagNode, 1); - D2R_TagNode *tag_stack = push_array(comp_temp.arena, D2R_TagNode, 1); - tag_stack->cur_node = tag_tree.root; - - while (tag_stack) { - while (tag_stack->cur_node) { - DW_TagNode *cur_node = tag_stack->cur_node; - DW_Tag tag = cur_node->tag; - B32 visit_children = 1; - - switch (tag.kind) { - case DW_TagKind_Null: { - InvalidPath; - } break; - case DW_TagKind_ClassType: { - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - - B32 is_decl = dw_flag_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - type->kind = RDI_TypeKind_IncompleteClass; - - Assert(!cur_node->first_child); - visit_children = 0; - } else { - RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &udts, UDT_CHUNK_CAP); - udt->self_type = type; - - type->kind = RDI_TypeKind_Class; - type->byte_size = dw_byte_size_32_from_tag(&input, cu, tag); - type->udt = udt; - type->direct_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - - tag_stack->type = type; - } - } break; - case DW_TagKind_StructureType: { - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - - B32 is_decl = dw_flag_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - type->kind = RDI_TypeKind_IncompleteStruct; - - // TODO: error handling - Assert(!cur_node->first_child); - visit_children = 0; - } else { - RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &udts, UDT_CHUNK_CAP); - udt->self_type = type; - - type->kind = RDI_TypeKind_Struct; - type->udt = udt; - type->byte_size = dw_byte_size_32_from_tag(&input, cu, tag); - - tag_stack->type = type; - } - } break; - case DW_TagKind_UnionType: { - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - - B32 is_decl = dw_flag_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - type->kind = RDI_TypeKind_IncompleteUnion; - - // TODO: error handling - Assert(!cur_node->first_child); - visit_children = 0; - } else { - RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &udts, UDT_CHUNK_CAP); - udt->self_type = type; - - type->kind = RDI_TypeKind_Union; - type->byte_size = dw_byte_size_32_from_tag(&input, cu, tag); - type->udt = udt; - - tag_stack->type = type; - } - } break; - case DW_TagKind_EnumerationType: { - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - - B32 is_decl = dw_flag_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Declaration); - if (is_decl) { - type->kind = RDI_TypeKind_IncompleteEnum; - - // TODO: error handling - Assert(!cur_node->first_child); - visit_children = 0; - } else { - RDIM_UDT *udt = rdim_udt_chunk_list_push(arena, &udts, UDT_CHUNK_CAP); - udt->self_type = type; - - type->kind = RDI_TypeKind_Enum; - type->byte_size = dw_byte_size_32_from_tag(&input, cu, tag); - type->udt = udt; - - tag_stack->type = type; - } - } break; - case DW_TagKind_SubroutineType: { - // collect parameters - RDIM_TypeList param_list = {0}; - for (DW_TagNode *n = cur_node->first_child; n != 0; n = n->sibling) { - if (n->tag.kind == DW_TagKind_FormalParameter) { - RDIM_Type *param_type = d2r_type_from_attrib(arena, type_table, &input, cu, n->tag, DW_AttribKind_Type); - rdim_type_list_push(comp_temp.arena, ¶m_list, param_type); - } else if (n->tag.kind == DW_TagKind_UnspecifiedParameters) { - rdim_type_list_push(comp_temp.arena, ¶m_list, type_table->varg_type); - } else { - // TODO: error handling - AssertAlways(!"unexpected tag"); - } - } - - // init proceudre type - RDIM_Type *ret_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Function; - type->byte_size = arch_addr_size; - type->direct_type = ret_type; - type->count = param_list.count; - type->param_types = rdim_array_from_type_list(arena, param_list); - - visit_children = 0; - } break; - case DW_TagKind_Typedef: { - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Alias; - type->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - type->direct_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - } break; - case DW_TagKind_BaseType: { - DW_ATE encoding = dw_const_u64_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Encoding); - U64 byte_size = dw_byte_size_from_tag(&input, cu, tag); - - // convert base type encoding to RDI version - RDI_TypeKind kind = RDI_TypeKind_NULL; - switch (encoding) { - case DW_ATE_Null: kind = RDI_TypeKind_NULL; break; - case DW_ATE_Address: kind = RDI_TypeKind_Void; break; - case DW_ATE_Boolean: kind = RDI_TypeKind_Bool; break; - case DW_ATE_ComplexFloat: { - switch (byte_size) { - case 4: kind = RDI_TypeKind_ComplexF32; break; - case 8: kind = RDI_TypeKind_ComplexF64; break; - case 10: kind = RDI_TypeKind_ComplexF80; break; - case 16: kind = RDI_TypeKind_ComplexF128; break; - default: AssertAlways(!"unexpected size"); break; // TODO: error handling - } - } break; - case DW_ATE_Float: { - switch (byte_size) { - case 2: kind = RDI_TypeKind_F16; break; - case 4: kind = RDI_TypeKind_F32; break; - case 6: kind = RDI_TypeKind_F48; break; - case 8: kind = RDI_TypeKind_F64; break; - case 16: kind = RDI_TypeKind_F128; break; - default: AssertAlways(!"unexpected size"); break; // TODO: error handling - } - } break; - case DW_ATE_Signed: { - switch (byte_size) { - case 1: kind = RDI_TypeKind_S8; break; - case 2: kind = RDI_TypeKind_S16; break; - case 4: kind = RDI_TypeKind_S32; break; - case 8: kind = RDI_TypeKind_S64; break; - case 16: kind = RDI_TypeKind_S128; break; - case 32: kind = RDI_TypeKind_S256; break; - case 64: kind = RDI_TypeKind_S512; break; - default: AssertAlways(!"unexpected size"); break; // TODO: error handling - } - } break; - case DW_ATE_SignedChar: { - switch (byte_size) { - case 1: kind = RDI_TypeKind_Char8; break; - case 2: kind = RDI_TypeKind_Char16; break; - case 4: kind = RDI_TypeKind_Char32; break; - default: AssertAlways(!"unexpected size"); break; // TODO: error handling - } - } break; - case DW_ATE_Unsigned: { - switch (byte_size) { - case 1: kind = RDI_TypeKind_U8; break; - case 2: kind = RDI_TypeKind_U16; break; - case 4: kind = RDI_TypeKind_U32; break; - case 8: kind = RDI_TypeKind_U64; break; - case 16: kind = RDI_TypeKind_U128; break; - case 32: kind = RDI_TypeKind_U256; break; - case 64: kind = RDI_TypeKind_U512; break; - default: AssertAlways(!"unexpected size"); break; // TODO: error handling - } - } break; - case DW_ATE_UnsignedChar: { - switch (byte_size) { - case 1: kind = RDI_TypeKind_UChar8; break; - case 2: kind = RDI_TypeKind_UChar16; break; - case 4: kind = RDI_TypeKind_UChar32; break; - default: AssertAlways(!"unexpected size"); break; // TODO: error handling - } - } break; - case DW_ATE_ImaginaryFloat: { - NotImplemented; - } break; - case DW_ATE_PackedDecimal: { - NotImplemented; - } break; - case DW_ATE_NumericString: { - NotImplemented; - } break; - case DW_ATE_Edited: { - NotImplemented; - } break; - case DW_ATE_SignedFixed: { - NotImplemented; - } break; - case DW_ATE_UnsignedFixed: { - NotImplemented; - } break; - case DW_ATE_DecimalFloat: { - NotImplemented; - } break; - case DW_ATE_Utf: { - NotImplemented; - } break; - case DW_ATE_Ucs: { - NotImplemented; - } break; - case DW_ATE_Ascii: { - NotImplemented; - } break; - default: AssertAlways(!"unexpected base type encoding"); break; // TODO: error handling - } - - // TODO(rjf): this is not good. we can't grab existing type nodes & mutate them here. - // to parallelize this properly, we need to *produce* new data only, otherwise threads - // will stomp over each other everywhere. - // - RDIM_Type *base_type = 0; // rdim_builtin_type_from_kind(types, kind); - base_type->kind = kind; - base_type->byte_size = byte_size; - - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Alias; - type->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - type->direct_type = base_type; - } break; - case DW_TagKind_PointerType: { - RDIM_Type *direct_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - - // TODO: - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Allocated)); - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Associated)); - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Alignment)); - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Name)); - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_AddressClass)); - - U64 byte_size = arch_addr_size; - if (cu->version == DW_Version_5 || cu->relaxed) { - dw_try_byte_size_from_tag(&input, cu, tag, &byte_size); - } - - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Ptr; - type->byte_size = byte_size; - type->direct_type = direct_type; - } break; - case DW_TagKind_RestrictType: { - // TODO: - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Alignment)); - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Name)); - - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Modifier; - type->byte_size = arch_addr_size; - type->flags = RDI_TypeModifierFlag_Restrict; - type->direct_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - } break; - case DW_TagKind_VolatileType: { - // TODO: - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Name)); - - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Modifier; - type->byte_size = arch_addr_size; - type->flags = RDI_TypeModifierFlag_Volatile; - type->direct_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - } break; - case DW_TagKind_ConstType: { - // TODO: - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Name)); - Assert(!dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_Alignment)); - - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Modifier; - type->byte_size = arch_addr_size; - type->flags = RDI_TypeModifierFlag_Const; - type->direct_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - } break; - case DW_TagKind_ArrayType: { - // * DWARF vs RDI Array Type Graph * - // - // For example lets take following decl: - // - // int (*foo[2])[3][4]; - // - // This compiles to in DWARF: - // - // foo -> DW_TAG_ArrayType -> (A0) DW_TAG_Subrange [2] - // \ - // -> (B0) DW_TAG_PointerType -> (A1) DW_TAG_ArrayType -> DW_TAG_Subrange [3] -> DW_TagKind_Subrange [4] - // \ - // -> (B1) DW_TAG_BaseType (int) - // - // RDI expects: - // - // foo -> Array (2) -> Pointer -> Array (3) -> Array (4) -> int - // - // Note that DWARF forks the graph on DW_TAG_ArrayType to describe array ranges in branch A and - // in branch B describes array type which might be a struct, pointer, base type, or any other type tag. - // However, in RDI we have a simple list of type nodes and to convert we need to append type nodes from - // B to A. - - RDIM_Type *type = d2r_find_or_create_type_from_offset(arena, type_table, tag.info_off); - type->kind = RDI_TypeKind_Array; - type->direct_type = 0; - - U64 subrange_count = 0; - RDIM_Type *t = type; - for (DW_TagNode *n = cur_node->first_child; n != 0; n = n->sibling) { - if (n->tag.kind != DW_TagKind_SubrangeType) { - // TODO: error handling - AssertAlways(!"unexpected tag"); - continue; - } - - if (subrange_count > 0) { - // init array type node - RDIM_Type *s = d2r_create_type(arena, type_table); - s->kind = RDI_TypeKind_Array; - s->direct_type = 0; - - // append new array type node - t->direct_type = s; - t = s; - } - - // resolve array lower bound - U64 lower_bound = 0; - if (dw_tag_has_attrib(&input, cu, n->tag, DW_AttribKind_LowerBound)) { - lower_bound = dw_u64_from_attrib(&input, cu, n->tag, DW_AttribKind_LowerBound); - } else { - lower_bound = dw_pick_default_lower_bound(cu_lang); - } - - // resolve array upper bound - U64 upper_bound = 0; - if (dw_tag_has_attrib(&input, cu, n->tag, DW_AttribKind_Count)) { - U64 count = dw_u64_from_attrib(&input, cu, n->tag, DW_AttribKind_Count); - upper_bound = lower_bound + count; - } else if (dw_tag_has_attrib(&input, cu, n->tag, DW_AttribKind_UpperBound)) { - upper_bound = dw_u64_from_attrib(&input, cu, n->tag, DW_AttribKind_UpperBound); - // turn upper bound into exclusive range - upper_bound += 1; - } else { - // zero size array - } - - t->count = upper_bound - lower_bound; - ++subrange_count; - } - - Assert(t->direct_type == 0); - t->direct_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - - visit_children = 0; - } break; - case DW_TagKind_SubrangeType: { + + for (; file_line_n != 0; file_line_n = file_line_n->next, line_idx += 1) { // TODO: error handling - AssertAlways(!"unexpected tag"); - } break; - case DW_TagKind_Inheritance: { - DW_TagNode *parent_node = tag_stack->next->cur_node; - if (parent_node->tag.kind != DW_TagKind_StructureType && - parent_node->tag.kind != DW_TagKind_ClassType) { - // TODO: error handling - AssertAlways(!"unexpected parent tag"); - } - - RDIM_Type *parent = tag_stack->next->type; - RDIM_UDTMember *member = rdim_udt_push_member(arena, &udts, parent->udt); - member->kind = RDI_MemberKind_Base; - member->type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - member->off = safe_cast_u32(dw_const_u32_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_DataMemberLocation)); - } break; - case DW_TagKind_Enumerator: { - DW_TagNode *parent_node = tag_stack->next->cur_node; - if (parent_node->tag.kind != DW_TagKind_EnumerationType) { - // TODO: error handling - AssertAlways(!"unexpected parent tag"); - } - - RDIM_Type *type = tag_stack->next->type; - RDIM_UDTEnumVal *member = rdim_udt_push_enum_val(arena, &udts, type->udt); - member->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - member->val = dw_const_u64_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_ConstValue); - } break; - case DW_TagKind_Member: { - DW_TagNode *parent_node = tag_stack->next->cur_node; - if (parent_node->tag.kind != DW_TagKind_StructureType && - parent_node->tag.kind != DW_TagKind_ClassType && - parent_node->tag.kind != DW_TagKind_UnionType && - parent_node->tag.kind != DW_TagKind_EnumerationType) { - // TODO: error handling - AssertAlways(!"unexpected parent tag"); - } - - DW_Attrib *data_member_location = dw_attrib_from_tag(&input, cu, tag, DW_AttribKind_DataMemberLocation); - DW_AttribClass data_member_location_class = dw_value_class_from_attrib(cu, data_member_location); - if (data_member_location_class == DW_AttribClass_LocList) { - AssertAlways(!"UDT member with multiple locations are not supported"); - } - - RDIM_Type *type = tag_stack->next->type; - RDIM_UDTMember *member = rdim_udt_push_member(arena, &udts, type->udt); - member->kind = RDI_MemberKind_DataField; - member->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - member->type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - member->off = dw_const_u64_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_DataMemberLocation); - } break; - case DW_TagKind_SubProgram: { - DW_InlKind inl = dw_u64_from_attrib(&input, cu, tag, DW_AttribKind_Inline); - switch (inl) { - case DW_Inl_NotInlined: { - U64 param_count = 0; - RDIM_Type **params = d2r_collect_proc_params(arena, type_table, &input, cu, cur_node, ¶m_count); - - // get return type - RDIM_Type *ret_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - - // fill out proc type - RDIM_Type *proc_type = d2r_create_type(arena, type_table); - proc_type->kind = RDI_TypeKind_Function; - proc_type->byte_size = arch_addr_size; - proc_type->direct_type = ret_type; - proc_type->count = param_count; - proc_type->param_types = params; - - // get container type - RDIM_Type *container_type = 0; - if (dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_ContainingType)) { - container_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_ContainingType); - } - - // get frame base expression - String8 frame_base_expr = dw_exprloc_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_FrameBase); - - // get proc container symbol - RDIM_Symbol *proc = rdim_symbol_chunk_list_push(arena, &procs, PROC_CHUNK_CAP ); - - // make scope - Rng1U64List ranges = d2r_range_list_from_tag(comp_temp.arena, &input, cu, image_base, tag); - RDIM_Scope *root_scope = d2r_push_scope(arena, &scopes, SCOPE_CHUNK_CAP, tag_stack, ranges); - root_scope->symbol = proc; - - // fill out proc - proc->is_extern = dw_flag_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_External); - proc->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - proc->link_name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_LinkageName); - proc->type = proc_type; - proc->container_symbol = 0; - proc->container_type = container_type; - proc->root_scope = root_scope; - proc->frame_base = d2r_locset_from_attrib(arena, &input, cu, &scopes, root_scope, image_base, arch, tag, DW_AttribKind_FrameBase); - - // sub program with user-defined parent tag is a method - DW_TagKind parent_tag_kind = tag_stack->next->cur_node->tag.kind; - if (parent_tag_kind == DW_TagKind_ClassType || parent_tag_kind == DW_TagKind_StructureType) { - RDI_MemberKind member_kind = RDI_MemberKind_NULL; - DW_VirtualityKind virtuality = dw_const_u64_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Virtuality); - switch (virtuality) { - case DW_VirtualityKind_None: member_kind = RDI_MemberKind_Method; break; - case DW_VirtualityKind_Virtual: member_kind = RDI_MemberKind_VirtualMethod; break; - case DW_VirtualityKind_PureVirtual: member_kind = RDI_MemberKind_VirtualMethod; break; // TODO: create kind for pure virutal - //default: InvalidPath; break; - } - - RDIM_Type *type = tag_stack->next->type; - RDIM_UDTMember *member = rdim_udt_push_member(arena, &udts, type->udt); - member->kind = member_kind; - member->type = type; - member->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - } else if (parent_tag_kind != DW_TagKind_CompileUnit) { - //AssertAlways(!"unexpected tag"); - } - - tag_stack->scope = root_scope; - } break; - case DW_Inl_DeclaredNotInlined: - case DW_Inl_DeclaredInlined: - case DW_Inl_Inlined: { - visit_children = 0; - } break; - default: InvalidPath; break; - } - } break; - case DW_TagKind_InlinedSubroutine: { - U64 param_count = 0; - RDIM_Type **params = d2r_collect_proc_params(arena, type_table, &input, cu, tag_stack->cur_node, ¶m_count); - - // get return type - RDIM_Type *ret_type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - - // fill out proc type - RDIM_Type *proc_type = d2r_create_type(arena, type_table); - proc_type->kind = RDI_TypeKind_Function; - proc_type->byte_size = arch_addr_size; - proc_type->direct_type = ret_type; - proc_type->count = param_count; - proc_type->param_types = params; - - // get container type - RDIM_Type *owner = 0; - if (dw_tag_has_attrib(&input, cu, tag, DW_AttribKind_ContainingType)) { - owner = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_ContainingType); - } - - // fill out inline site - RDIM_InlineSite *inline_site = rdim_inline_site_chunk_list_push(arena, &inline_sites, INLINE_SITE_CHUNK_CAP); - inline_site->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - inline_site->type = proc_type; - inline_site->owner = owner; - inline_site->line_table = 0; - - // make scope - Rng1U64List ranges = d2r_range_list_from_tag(comp_temp.arena, &input, cu, image_base, tag); - RDIM_Scope *root_scope = d2r_push_scope(arena, &scopes, SCOPE_CHUNK_CAP, tag_stack, ranges); - root_scope->inline_site = inline_site; - } break; - case DW_TagKind_Variable: { - String8 name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - RDIM_Type *type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - - DW_TagKind parent_tag_kind = tag_stack->next->cur_node->tag.kind; - if (parent_tag_kind == DW_TagKind_SubProgram || - parent_tag_kind == DW_TagKind_InlinedSubroutine || - parent_tag_kind == DW_TagKind_LexicalBlock) { - RDIM_Scope *scope = tag_stack->next->scope; - RDIM_Local *local = rdim_scope_push_local(arena, &scopes, tag_stack->next->scope); - local->kind = RDI_LocalKind_Variable; - local->name = name; - local->type = type; - local->locset = d2r_var_locset_from_tag(arena, &input, cu, &scopes, scope, image_base, arch, tag); - } else { - - // NOTE: due to a bug in clang in stb_sprint.h local variables - // are declared in global scope without a name - if (name.size == 0) { - break; - } - - RDIM_Symbol *gvar = rdim_symbol_chunk_list_push(arena, &gvars, GVAR_CHUNK_CAP); - gvar->is_extern = dw_flag_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_External); - gvar->name = name; - gvar->link_name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_LinkageName); - gvar->type = type; - //gvar->locset = d2r_locset_from_attrib(arena, &input, cu, &scopes, global_scope, image_base, arch, tag, DW_AttribKind_Location); - gvar->container_symbol = 0; - gvar->container_type = 0; // TODO: NotImplemented; - } - } break; - case DW_TagKind_FormalParameter: { - DW_TagKind parent_tag_kind = tag_stack->next->cur_node->tag.kind; - if (parent_tag_kind == DW_TagKind_SubProgram || parent_tag_kind == DW_TagKind_InlinedSubroutine) { - RDIM_Scope *scope = tag_stack->next->scope; - RDIM_Local *param = rdim_scope_push_local(arena, &scopes, scope); - param->kind = RDI_LocalKind_Parameter; - param->name = dw_string_from_tag_attrib_kind(&input, cu, tag, DW_AttribKind_Name); - param->type = d2r_type_from_attrib(arena, type_table, &input, cu, tag, DW_AttribKind_Type); - param->locset = d2r_var_locset_from_tag(arena, &input, cu, &scopes, scope, image_base, arch, tag); - } else { - // TODO: error handling - AssertAlways(!"this is a local variable"); - } - } break; - case DW_TagKind_LexicalBlock: { - if (tag_stack->next->cur_node->tag.kind == DW_TagKind_SubProgram || - tag_stack->next->cur_node->tag.kind == DW_TagKind_InlinedSubroutine || - tag_stack->next->cur_node->tag.kind == DW_TagKind_LexicalBlock) { - Rng1U64List ranges = d2r_range_list_from_tag(comp_temp.arena, &input, cu, image_base, tag); - d2r_push_scope(arena, &scopes, SCOPE_CHUNK_CAP, tag_stack, ranges); - } - } break; - case DW_TagKind_CallSite: { - // TODO - } break; - case DW_TagKind_CallSiteParameter: { - // TODO - } break; - case DW_TagKind_Label: - case DW_TagKind_CompileUnit: - case DW_TagKind_UnspecifiedParameters: - break; - case DW_TagKind_Namespace: break; - case DW_TagKind_ImportedDeclaration: break; - case DW_TagKind_PtrToMemberType: break; - case DW_TagKind_TemplateTypeParameter: break; - case DW_TagKind_ReferenceType: break; - default: NotImplemented; break; - } - - if (tag_stack->cur_node->first_child && visit_children) { - D2R_TagNode *frame = free_tags; - if (frame) { - SLLStackPop(free_tags); - MemoryZeroStruct(frame); - } else { - frame = push_array(scratch.arena, D2R_TagNode, 1); + AssertAlways(file_line_n->v.address >= image_base); + voffs[line_idx] = file_line_n->v.address - image_base; + line_nums[line_idx] = file_line_n->v.line; } - frame->cur_node = tag_stack->cur_node->first_child; - SLLStackPush(tag_stack, frame); - } else { - tag_stack->cur_node = tag_stack->cur_node->sibling; + + RDIM_SrcFile *src_file = src_file_map[file_index]; + RDIM_LineSequence *line_seq = rdim_line_table_push_sequence(arena, &line_tables, cu_line_tables_rdi[cu_idx], src_file, file_voffs, file_line_nums, file_col_nums, file_line_count); + rdim_src_file_push_line_sequence(arena, &src_files, src_file, line_seq); } - } - - // recycle free frame - D2R_TagNode *frame = tag_stack; - SLLStackPop(tag_stack); - SLLStackPush(free_tags, frame); - - if (tag_stack) { - tag_stack->cur_node = tag_stack->cur_node->sibling; + + //Assert(line_idx == line_seq->count); } } - - temp_end(comp_temp); - } - - ProfEnd(); - - { - for (RDIM_TypeChunkNode *chunk_n = types.first; chunk_n != 0; chunk_n = chunk_n->next) { - for (U64 i = 0; i < chunk_n->count; ++i) { - RDIM_Type *type = &chunk_n->v[i]; - if (type->kind == RDI_TypeKind_Alias) { - for (RDIM_Type *t = type->direct_type; t != 0; t = t->direct_type) { - if (t->byte_size != 0) { - type->byte_size = t->byte_size; - break; - } - } - } - } + ProfEnd(); + + //////////////////////////////// + + RDIM_Type *builtin_types[RDI_TypeKind_Count] = {0}; + for (RDI_TypeKind type_kind = RDI_TypeKind_FirstBuiltIn; type_kind <= RDI_TypeKind_LastBuiltIn; type_kind += 1) { + RDIM_Type *type = rdim_type_chunk_list_push(arena, &types, TYPE_CHUNK_CAP); + type->kind = type_kind; + type->name.str = rdi_string_from_type_kind(type_kind, &type->name.size); + type->byte_size = rdi_size_from_basic_type_kind(type_kind); + builtin_types[type_kind] = type; } - } - - { - RDIM_TypeNode *type_stack = 0; - RDIM_TypeNode *free_types = 0; - - for (RDIM_TypeChunkNode *chunk_n = types.first; chunk_n != 0; chunk_n = chunk_n->next) { - for (U64 i = 0; i < chunk_n->count; ++i) { - RDIM_Type *type = &chunk_n->v[i]; - if (type->kind == RDI_TypeKind_Array) { - if (type->byte_size != 0) - continue; - - RDIM_Type *t; - for (t = type; t != 0 && t->kind == RDI_TypeKind_Array; t = t->direct_type) { - RDIM_TypeNode *f = free_types; - if (f == 0) { - f = push_array(scratch.arena, RDIM_TypeNode, 1); - } else { - SLLStackPop(free_types); - } - f->v = t; - SLLStackPush(type_stack, f); - } - - U64 base_type_size = 0; - if (t) { - base_type_size = t->byte_size; - } - - U64 array_size = base_type_size; - while (type_stack) { - if (type_stack->v->count) { - array_size *= type_stack->v->count; - } else { - array_size += type_stack->v->byte_size; - } - SLLStackPop(type_stack); - } - - type->count = 0; - type->byte_size = array_size; - - // recycle frames - free_types = type_stack; - type_stack = 0; - } + builtin_types[RDI_TypeKind_Void]->byte_size = arch_addr_size; + builtin_types[RDI_TypeKind_Handle]->byte_size = arch_addr_size; + + builtin_types[RDI_TypeKind_Variadic] = rdim_type_chunk_list_push(arena, &types, TYPE_CHUNK_CAP); + builtin_types[RDI_TypeKind_Variadic]->kind = RDI_TypeKind_Variadic; + + //////////////////////////////// + + ProfBegin("Convert Units"); + for EachIndex(cu_idx, cu_ranges.count) { + Temp comp_temp = temp_begin(scratch.arena); + + DW_CompUnit *cu = &cu_arr[cu_idx]; + + // parse and build tag tree + DW_TagTree tag_tree = dw_tag_tree_from_cu(comp_temp.arena, &input, cu); + + // skip DWO + { + if (cu->dwo_id) { goto next_cu; } + + String8 dwo_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_DwoName); + if (dwo_name.size) { goto next_cu; } + + String8 gnu_dwo_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_GNU_DwoName); + if (gnu_dwo_name.size) { goto next_cu; } } + + // build (info offset -> tag) hash table to resolve tags with abstract origin + cu->tag_ht = dw_make_tag_hash_table(comp_temp.arena, tag_tree); + + // extract compile unit info + String8 cu_name = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Name); + String8 cu_dir = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_CompDir); + String8 cu_prod = dw_string_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Producer); + DW_Language cu_lang = dw_const_u64_from_tag_attrib_kind(&input, cu, cu->tag, DW_AttribKind_Language); + + // init type table + D2R_TypeTable *type_table = push_array(comp_temp.arena, D2R_TypeTable, 1); + type_table->ht = hash_table_init(comp_temp.arena, 0x4000); + type_table->types = &types; + type_table->type_chunk_cap = TYPE_CHUNK_CAP; + type_table->builtin_types = builtin_types; + + // convert debug info + d2r_convert_types(arena, type_table, &input, cu, cu_lang, arch_addr_size, tag_tree.root); + d2r_convert_udts(arena, type_table, &input, cu, cu_lang, arch_addr_size, tag_tree.root); + d2r_convert_symbols(arena, type_table, global_scope, &input, cu, cu_lang, arch_addr_size, image_base, arch, tag_tree.root); + + RDIM_Rng1U64ChunkList cu_voff_ranges = {0}; + if (cu_idx < cu_contrib_map.count) { + cu_voff_ranges = d2r_voff_ranges_from_cu_info_off(cu_contrib_map, cu_ranges.v[cu_idx].min); + } else { + // TODO: synthesize cu ranges from scopes + NotImplemented; + } + + // convert compile unit + { + RDIM_Unit *unit = rdim_unit_chunk_list_push(arena, &units, UNIT_CHUNK_CAP); + unit->unit_name = cu_name; + unit->compiler_name = cu_prod; + unit->source_file = str8_zero(); // TODO + unit->object_file = str8_zero(); // TODO + unit->archive_file = str8_zero(); // TODO + unit->build_path = cu_dir; + unit->language = d2r_rdi_language_from_dw_language(cu_lang); + unit->line_table = cu_line_tables_rdi[cu_idx]; + unit->voff_ranges = cu_voff_ranges; + } + + next_cu:; + temp_end(comp_temp); } + ProfEnd(); } - - //////////////////////////////// - + + lane_sync(); + RDIM_BakeParams bake_params = {0}; bake_params.top_level_info = top_level_info; bake_params.binary_sections = binary_sections; @@ -2114,12 +2331,14 @@ d2r_convert(Arena *arena, ASYNC_Root *async_root, D2R_ConvertParams *params) bake_params.udts = udts; bake_params.src_files = src_files; bake_params.line_tables = line_tables; + bake_params.locations = locations; bake_params.global_variables = gvars; bake_params.thread_variables = tvars; bake_params.procedures = procs; bake_params.scopes = scopes; bake_params.inline_sites = inline_sites; - + scratch_end(scratch); return bake_params; } + diff --git a/src/rdi_from_dwarf/rdi_from_dwarf.h b/src/rdi_from_dwarf/rdi_from_dwarf.h index 50ee8d8e..ae9155b0 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf.h +++ b/src/rdi_from_dwarf/rdi_from_dwarf.h @@ -6,30 +6,37 @@ typedef struct D2R_ConvertParams D2R_ConvertParams; struct D2R_ConvertParams { - String8 dbg_name; - String8 dbg_data; - String8 exe_name; - String8 exe_data; + String8 dbg_name; + String8 dbg_data; + String8 exe_name; + String8 exe_data; ExecutableImageKind exe_kind; - RDIM_SubsetFlags subset_flags; - B32 deterministic; + RDIM_SubsetFlags subset_flags; + B32 deterministic; }; typedef struct D2R_TypeTable { - HashTable *ht; - RDIM_TypeChunkList *types; - U64 type_chunk_cap; - RDIM_Type *varg_type; + HashTable *ht; + RDIM_TypeChunkList *types; + U64 type_chunk_cap; + RDIM_Type **builtin_types; } D2R_TypeTable; -typedef struct D2R_TagNode +typedef struct D2R_TagFrame { - struct D2R_TagNode *next; - DW_TagNode *cur_node; - RDIM_Type *type; - RDIM_Scope *scope; -} D2R_TagNode; + DW_TagNode *node; + RDIM_Scope *scope; + struct D2R_TagFrame *next; +} D2R_TagFrame; + +typedef struct D2R_TagIterator +{ + D2R_TagFrame *free_list; + D2R_TagFrame *stack; + DW_TagNode *tag_node; + B32 visit_children; +} D2R_TagIterator; typedef struct D2R_CompUnitContribMap { @@ -44,60 +51,56 @@ typedef struct D2R_CompUnitContribMap internal RDI_Language d2r_rdi_language_from_dw_language(DW_Language v); internal RDI_RegCodeX86 d2r_rdi_reg_code_from_dw_reg_x86(DW_RegX86 v); internal RDI_RegCodeX64 d2r_rdi_reg_code_from_dw_reg_x64(DW_RegX64 v); -internal RDI_RegCode d2r_rdi_reg_code_from_dw_reg(Arch arch, DW_Reg v); +internal RDI_RegCode d2r_rdi_reg_code_from_dw_reg(Arch arch, DW_Reg v); //////////////////////////////// //~ rjf: Type Conversion Helpers -internal RDIM_Type *d2r_create_type(Arena *arena, D2R_TypeTable *type_table); -internal RDIM_Type *d2r_find_or_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off); -internal RDIM_Type *d2r_type_from_attrib(Arena *arena, D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind); -internal Rng1U64List d2r_range_list_from_tag(Arena *arena, DW_Input *input, DW_CompUnit *cu, U64 image_base, DW_Tag tag); -internal RDIM_Type **d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_TagNode *cur_node, U64 *param_count_out); -internal RDI_TypeKind d2r_unsigned_type_kind_from_size(U64 byte_size); -internal RDI_TypeKind d2r_signed_type_kind_from_size(U64 byte_size); +internal RDIM_Type * d2r_create_type(Arena *arena, D2R_TypeTable *type_table); +internal RDIM_Type * d2r_create_type_from_offset(Arena *arena, D2R_TypeTable *type_table, U64 info_off); +internal RDIM_Type * d2r_type_from_offset(D2R_TypeTable *type_table, U64 info_off); +internal RDIM_Type * d2r_type_from_attrib(D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_Tag tag, DW_AttribKind kind); +internal Rng1U64List d2r_range_list_from_tag(Arena *arena, DW_Input *input, DW_CompUnit *cu, U64 image_base, DW_Tag tag); +internal RDIM_Type ** d2r_collect_proc_params(Arena *arena, D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_TagNode *cur_node, U64 *param_count_out); +internal RDI_TypeKind d2r_unsigned_type_kind_from_size(U64 byte_size); +internal RDI_TypeKind d2r_signed_type_kind_from_size(U64 byte_size); internal RDI_EvalTypeGroup d2r_type_group_from_type_kind(RDI_TypeKind x); //////////////////////////////// //~ rjf: Bytecode Conversion Helpers -internal RDIM_EvalBytecode -d2r_bytecode_from_expression(Arena *arena, - DW_Input *input, - U64 image_base, - U64 address_size, - Arch arch, - DW_ListUnit *addr_lu, - String8 expr, - DW_CompUnit *cu, - B32 *is_addr_out); -internal RDIM_Location *d2r_transpile_expression(Arena *arena, DW_Input *input, U64 image_base, U64 address_size, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr); -internal RDIM_Location *d2r_location_from_attrib(Arena *arena, DW_Input *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind); -internal RDIM_LocationSet d2r_locset_from_attrib(Arena *arena, - DW_Input *input, - DW_CompUnit *cu, - RDIM_ScopeChunkList *scopes, - RDIM_Scope *curr_scope, - U64 image_base, - Arch arch, - DW_Tag tag, - DW_AttribKind kind); -internal RDIM_LocationSet d2r_var_locset_from_tag(Arena *arena, - DW_Input *input, - DW_CompUnit *cu, - RDIM_ScopeChunkList *scopes, - RDIM_Scope *curr_scope, - U64 image_base, - Arch arch, - DW_Tag tag); +internal RDIM_EvalBytecode d2r_bytecode_from_expression(Arena *arena, DW_Input *input, U64 image_base, U64 address_size, Arch arch, DW_ListUnit *addr_lu, String8 expr, DW_CompUnit *cu, B32 *is_addr_out); +internal RDIM_Location * d2r_transpile_expression(Arena *arena, RDIM_LocationChunkList *locations, DW_Input *input, U64 image_base, U64 address_size, Arch arch, DW_ListUnit *addr_lu, DW_CompUnit *cu, String8 expr); +internal RDIM_Location * d2r_location_from_attrib(Arena *arena, RDIM_LocationChunkList *locations, DW_Input *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind); +internal RDIM_LocationCaseList d2r_locset_from_attrib(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *curr_scope, RDIM_LocationChunkList *locations, DW_Input *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag, DW_AttribKind kind); +internal RDIM_LocationCaseList d2r_var_locset_from_tag(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Scope *curr_scope, RDIM_LocationChunkList *locations, DW_Input *input, DW_CompUnit *cu, U64 image_base, Arch arch, DW_Tag tag); //////////////////////////////// //~ rjf: Compilation Unit / Scope Conversion Helpers internal D2R_CompUnitContribMap d2r_cu_contrib_map_from_aranges(Arena *arena, DW_Input *input, U64 image_base); -internal RDIM_Rng1U64ChunkList d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off); +internal RDIM_Rng1U64ChunkList d2r_voff_ranges_from_cu_info_off(D2R_CompUnitContribMap map, U64 info_off); + +//////////////////////////////// +//~ Tag Iterator + +internal D2R_TagIterator * d2r_tag_iterator_init(Arena *arena, DW_TagNode *root); +internal void d2r_tag_iterator_next(Arena *arena, D2R_TagIterator *iter); +internal void d2r_tag_iterator_skip_children(D2R_TagIterator *iter); +internal DW_TagNode * d2r_tag_iterator_parent_tag_node(D2R_TagIterator *iter); +internal DW_Tag d2r_tag_iterator_parent_tag(D2R_TagIterator *iter); + +//////////////////////////////// +//~ Type/UDT/Symbol Conversion + +internal void d2r_flag_converted_tag(DW_TagNode *tag_node); +internal B8 d2r_is_tag_converted(DW_TagNode *tag_node); + +internal void d2r_convert_types(Arena *arena, D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_Language cu_lang, U64 arch_addr_size, DW_TagNode *root); +internal void d2r_convert_udts(Arena *arena, D2R_TypeTable *type_table, DW_Input *input, DW_CompUnit *cu, DW_Language cu_lang, U64 arch_addr_size, DW_TagNode *root); +internal void d2r_convert_symbols(Arena *arena, D2R_TypeTable *type_table, RDIM_Scope *global_scope, DW_Input *input, DW_CompUnit *cu, DW_Language cu_lang, U64 arch_addr_size, U64 image_base, Arch arch, DW_TagNode *root); //////////////////////////////// //~ rjf: Main Conversion Entry Point -internal RDIM_BakeParams d2r_convert(Arena *arena, ASYNC_Root *async_root, D2R_ConvertParams *params); +internal RDIM_BakeParams d2r_convert(Arena *arena, D2R_ConvertParams *params); diff --git a/src/rdi_from_pdb/rdi_from_pdb.c b/src/rdi_from_pdb/rdi_from_pdb.c index 09ab8fb2..37b84abf 100644 --- a/src/rdi_from_pdb/rdi_from_pdb.c +++ b/src/rdi_from_pdb/rdi_from_pdb.c @@ -267,38 +267,6 @@ p2r_rdi_type_kind_from_cv_basic_type(CV_BasicType basic_type) //////////////////////////////// //~ rjf: Location Info Building Helpers -internal RDIM_Location * -p2r_location_from_addr_reg_off(Arena *arena, RDI_Arch arch, RDI_RegCode reg_code, U32 reg_byte_size, U32 reg_byte_pos, S64 offset, B32 extra_indirection) -{ - RDIM_Location *result = 0; - if(0 <= offset && offset <= (S64)max_U16) - { - if(extra_indirection) - { - result = rdim_push_location_addr_addr_reg_plus_u16(arena, reg_code, (U16)offset); - } - else - { - result = rdim_push_location_addr_reg_plus_u16(arena, reg_code, (U16)offset); - } - } - else - { - RDIM_EvalBytecode bytecode = {0}; - U32 regread_param = RDI_EncodeRegReadParam(reg_code, reg_byte_size, reg_byte_pos); - rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_RegRead, regread_param); - rdim_bytecode_push_sconst(arena, &bytecode, offset); - rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_Add, 0); - if(extra_indirection) - { - U64 addr_size = rdi_addr_size_from_arch(arch); - rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_MemRead, addr_size); - } - result = rdim_push_location_addr_bytecode_stream(arena, &bytecode); - } - return result; -} - internal RDI_RegCode p2r_reg_code_from_arch_encoded_fp_reg(RDI_Arch arch, CV_EncodedFramePtrReg encoded_reg) { @@ -346,8 +314,45 @@ p2r_reg_code_from_arch_encoded_fp_reg(RDI_Arch arch, CV_EncodedFramePtrReg encod return(result); } +internal RDIM_LocationInfo +p2r_location_info_from_addr_reg_off(Arena *arena, RDI_Arch arch, RDI_RegCode reg_code, U32 reg_byte_size, U32 reg_byte_pos, S64 offset, B32 extra_indirection) +{ + RDIM_LocationInfo result = {0}; + if(0 <= offset && offset <= (S64)max_U16) + { + if(extra_indirection) + { + result.kind = RDI_LocationKind_AddrAddrRegPlusU16; + result.reg_code = reg_code; + result.offset = offset; + } + else + { + result.kind = RDI_LocationKind_AddrRegPlusU16; + result.reg_code = reg_code; + result.offset = offset; + } + } + else + { + RDIM_EvalBytecode bytecode = {0}; + U32 regread_param = RDI_EncodeRegReadParam(reg_code, reg_byte_size, reg_byte_pos); + rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_RegRead, regread_param); + rdim_bytecode_push_sconst(arena, &bytecode, offset); + rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_Add, 0); + if(extra_indirection) + { + U64 addr_size = rdi_addr_size_from_arch(arch); + rdim_bytecode_push_op(arena, &bytecode, RDI_EvalOp_MemRead, addr_size); + } + result.kind = RDI_LocationKind_AddrBytecodeStream; + result.bytecode = bytecode; + } + return result; +} + internal void -p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_LocationSet *locset, RDIM_Location *location, CV_LvarAddrRange *range, COFF_SectionHeader *section, CV_LvarAddrGap *gaps, U64 gap_count) +p2r_local_push_location_cases_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Local *local, RDIM_Location *loc, CV_LvarAddrRange *range, COFF_SectionHeader *section, CV_LvarAddrGap *gaps, U64 gap_count) { //- rjf: extract range info U64 voff_first = 0; @@ -358,7 +363,7 @@ p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDI voff_opl = voff_first + range->len; } - //- rjf: emit ranges + //- rjf: emit location for ranges not coverd by gaps CV_LvarAddrGap *gap_ptr = gaps; U64 voff_cursor = voff_first; for(U64 i = 0; i < gap_count; i += 1, gap_ptr += 1) @@ -368,7 +373,7 @@ p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDI if(voff_cursor < voff_gap_first) { RDIM_Rng1U64 voff_range = {voff_cursor, voff_gap_first}; - rdim_location_set_push_case(arena, scopes, locset, voff_range, location); + rdim_local_push_location_case(arena, scopes, local, loc, voff_range); } voff_cursor = voff_gap_opl; } @@ -377,616 +382,819 @@ p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDI if(voff_cursor < voff_opl) { RDIM_Rng1U64 voff_range = {voff_cursor, voff_opl}; - rdim_location_set_push_case(arena, scopes, locset, voff_range, location); + rdim_local_push_location_case(arena, scopes, local, loc, voff_range); } } //////////////////////////////// -//~ rjf: Initial Parsing & Preparation Pass Tasks +//~ rjf: Top-Level Conversion Entry Point -ASYNC_WORK_DEF(p2r_exe_hash_work) +internal RDIM_BakeParams +p2r_convert(Arena *arena, P2R_ConvertParams *params) { - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_EXEHashIn *in = (P2R_EXEHashIn *)input; - U64 *out = push_array(arena, U64, 1); - ProfScope("hash exe") *out = rdi_hash(in->exe_data.str, in->exe_data.size); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(p2r_tpi_hash_parse_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_TPIHashParseIn *in = (P2R_TPIHashParseIn *)input; - void *out = 0; - ProfScope("parse tpi hash") out = pdb_tpi_hash_from_data(arena, in->strtbl, in->tpi, in->hash_data, in->aux_data); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(p2r_tpi_leaf_parse_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_TPILeafParseIn *in = (P2R_TPILeafParseIn *)input; - void *out = 0; - ProfScope("parse tpi leaf") out = cv_leaf_from_data(arena, in->leaf_data, in->itype_first); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(p2r_symbol_stream_parse_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_SymbolStreamParseIn *in = (P2R_SymbolStreamParseIn *)input; - void *out = 0; - ProfScope("parse symbol stream") out = cv_sym_from_data(arena, in->data, 4); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(p2r_c13_stream_parse_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_C13StreamParseIn *in = (P2R_C13StreamParseIn *)input; - void *out = 0; - ProfScope("parse c13 stream") out = cv_c13_parsed_from_data(arena, in->data, in->strtbl, in->coff_sections); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(p2r_comp_unit_parse_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_CompUnitParseIn *in = (P2R_CompUnitParseIn *)input; - void *out = 0; - ProfScope("parse comp units") out = pdb_comp_unit_array_from_data(arena, in->data); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(p2r_comp_unit_contributions_parse_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_CompUnitContributionsParseIn *in = (P2R_CompUnitContributionsParseIn *)input; - void *out = 0; - ProfScope("parse comp unit contributions") out = pdb_comp_unit_contribution_array_from_data(arena, in->data, in->coff_sections); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(p2r_comp_unit_contributions_bucket_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_CompUnitContributionsBucketIn *in = (P2R_CompUnitContributionsBucketIn *)input; - P2R_CompUnitContributionsBucketOut *out = push_array(arena, P2R_CompUnitContributionsBucketOut, 1); + ////////////////////////////////////////////////////////////// + //- rjf: do base MSF parse + // { - out->unit_ranges = push_array(arena, RDIM_Rng1U64ChunkList, in->comp_unit_count); - for(U64 idx = 0; idx < in->contributions.count; idx += 1) + // rjf: setup output buckets + if(lane_idx() == 0) { - PDB_CompUnitContribution *contribution = &in->contributions.contributions[idx]; - if(contribution->mod < in->comp_unit_count) + p2r_shared = push_array(arena, P2R_Shared, 1); + p2r_shared->msf_raw_stream_table = msf_raw_stream_table_from_data(arena, params->input_pdb_data); + p2r_shared->msf = push_array(arena, MSF_Parsed, 1); + p2r_shared->msf->page_size = p2r_shared->msf_raw_stream_table->page_size; + p2r_shared->msf->page_count = p2r_shared->msf_raw_stream_table->total_page_count; + p2r_shared->msf->stream_count = p2r_shared->msf_raw_stream_table->stream_count; + p2r_shared->msf->streams = push_array(arena, String8, p2r_shared->msf->stream_count); + p2r_shared->msf_stream_lane_counter = 0; + } + lane_sync(); + + // rjf: do wide fill + { + for(;;) { - RDIM_Rng1U64 r = {contribution->voff_first, contribution->voff_opl}; - rdim_rng1u64_chunk_list_push(arena, &out->unit_ranges[contribution->mod], 256, r); + U64 stream_num = ins_atomic_u64_inc_eval(&p2r_shared->msf_stream_lane_counter); + if(stream_num < 1 || p2r_shared->msf->stream_count < stream_num) + { + break; + } + U64 stream_idx = stream_num-1; + p2r_shared->msf->streams[stream_idx] = msf_data_from_stream_number(arena, params->input_pdb_data, p2r_shared->msf_raw_stream_table, stream_idx); } } } - ProfEnd(); - return out; -} - -//////////////////////////////// -//~ rjf: Unit Source File Gathering Tasks - -ASYNC_WORK_DEF(p2r_gather_unit_src_file_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - Temp scratch = scratch_begin(&arena, 1); - P2R_GatherUnitSrcFilesIn *in = (P2R_GatherUnitSrcFilesIn *)input; - P2R_GatherUnitSrcFilesOut *out = push_array(arena, P2R_GatherUnitSrcFilesOut, 1); - PDB_CompUnit *pdb_unit = in->comp_unit; - CV_SymParsed *pdb_unit_sym = in->comp_unit_syms; - CV_C13Parsed *pdb_unit_c13 = in->comp_unit_c13s; - CV_RecRange *rec_ranges_first = pdb_unit_sym->sym_ranges.ranges; - CV_RecRange *rec_ranges_opl = rec_ranges_first+pdb_unit_sym->sym_ranges.count; - String8List src_file_paths = {0}; + lane_sync(); + MSF_Parsed *msf = p2r_shared->msf; + + ////////////////////////////////////////////////////////////// + //- rjf: do top-level MSF/PDB extraction + // + ProfScope("do top-level MSF/PDB extraction") if(lane_idx() == 0) { - //- rjf: build local hash table to dedup files within this unit - U64 hit_path_slots_count = 4096; - String8Node **hit_path_slots = push_array(scratch.arena, String8Node *, hit_path_slots_count); - - //- rjf: produce obj name/path - String8 obj_name = pdb_unit->obj_name; - if(str8_match(obj_name, str8_lit("* Linker *"), 0) || - str8_match(obj_name, str8_lit("Import:"), StringMatchFlag_RightSideSloppy)) + ProfScope("parse PDB info") { - MemoryZeroStruct(&obj_name); - } - String8 obj_folder_path = lower_from_str8(scratch.arena, str8_chop_last_slash(obj_name)); - - //- rjf: find all files in this unit's (non-inline) line info - ProfScope("find all files in this unit's (non-inline) line info") - for(CV_C13SubSectionNode *node = pdb_unit_c13->first_sub_section; - node != 0; - node = node->next) - { - if(node->kind == CV_C13SubSectionKind_Lines) + String8 info_data = msf_data_from_stream(msf, PDB_FixedStream_Info); + p2r_shared->pdb_info = pdb_info_from_data(arena, info_data); + if(p2r_shared->pdb_info->features & PDB_FeatureFlag_MINIMAL_DBG_INFO) { - for(CV_C13LinesParsedNode *lines_n = node->lines_first; - lines_n != 0; - lines_n = lines_n->next) + log_user_error(str8_lit("PDB was linked with /DEBUG:FASTLINK; partial debug info is not supported. Please relink using /DEBUG:FULL.")); + } + } + ProfScope("parse named streams table") + { + p2r_shared->named_streams = pdb_named_stream_table_from_info(arena, p2r_shared->pdb_info); + } + } + lane_sync(); + PDB_Info *pdb_info = p2r_shared->pdb_info; + PDB_NamedStreamTable *named_streams = p2r_shared->named_streams; + + ////////////////////////////////////////////////////////////// + //- rjf: parse PDB strtbl & top-level streams + // + ProfScope("parse PDB strtbl & top-level streams") + { + if(lane_idx() == lane_from_task_idx(0)) ProfScope("parse PDB strtbl") + { + MSF_StreamNumber strtbl_sn = named_streams->sn[PDB_NamedStream_StringTable]; + String8 strtbl_data = msf_data_from_stream(msf, strtbl_sn); + p2r_shared->strtbl = pdb_strtbl_from_data(arena, strtbl_data); + p2r_shared->raw_strtbl = str8_substr(strtbl_data, rng_1u64(p2r_shared->strtbl->strblock_min, p2r_shared->strtbl->strblock_max)); + } + if(lane_idx() == lane_from_task_idx(1)) ProfScope("parse DBI") + { + String8 dbi_data = msf_data_from_stream(msf, PDB_FixedStream_Dbi); + p2r_shared->dbi = pdb_dbi_from_data(arena, dbi_data); + } + if(lane_idx() == lane_from_task_idx(2)) ProfScope("parse TPI") + { + String8 tpi_data = msf_data_from_stream(msf, PDB_FixedStream_Tpi); + p2r_shared->tpi = pdb_tpi_from_data(arena, tpi_data); + } + if(lane_idx() == lane_from_task_idx(3)) ProfScope("parse IPI") + { + String8 ipi_data = msf_data_from_stream(msf, PDB_FixedStream_Ipi); + p2r_shared->ipi = pdb_tpi_from_data(arena, ipi_data); + } + } + lane_sync(); + PDB_Strtbl *strtbl = p2r_shared->strtbl; + String8 raw_strtbl = p2r_shared->raw_strtbl; + PDB_DbiParsed *dbi = p2r_shared->dbi; + PDB_TpiParsed *tpi = p2r_shared->tpi; + PDB_TpiParsed *ipi = p2r_shared->ipi; + + ////////////////////////////////////////////////////////////// + //- rjf: unpack DBI + // + ProfScope("unpack DBI") + { + if(lane_idx() == lane_from_task_idx(0)) ProfScope("parse COFF sections") + { + MSF_StreamNumber section_stream = dbi->dbg_streams[PDB_DbiStream_SECTION_HEADER]; + String8 section_data = msf_data_from_stream(msf, section_stream); + p2r_shared->coff_sections = pdb_coff_section_array_from_data(arena, section_data); + } + if(lane_idx() == lane_from_task_idx(1)) ProfScope("parse GSI") + { + String8 gsi_data = msf_data_from_stream(msf, dbi->gsi_sn); + p2r_shared->gsi = pdb_gsi_from_data(arena, gsi_data); + } + if(lane_idx() == lane_from_task_idx(2)) ProfScope("parse GSI part of PSI") + { + String8 psi_data = msf_data_from_stream(msf, dbi->psi_sn); + String8 psi_data_gsi_part = str8_range(psi_data.str + sizeof(PDB_PsiHeader), psi_data.str + psi_data.size); + p2r_shared->psi_gsi_part = pdb_gsi_from_data(arena, psi_data_gsi_part); + } + } + lane_sync(); + COFF_SectionHeaderArray coff_sections = p2r_shared->coff_sections; + PDB_GsiParsed *gsi = p2r_shared->gsi; + PDB_GsiParsed *psi_gsi_part = p2r_shared->psi_gsi_part; + + ////////////////////////////////////////////////////////////// + //- rjf: hash EXE, parse TPI/IPI hash/leaf & global symbol stream & comp units + // + ProfScope("hash EXE, parse TPI/IPI hash/leaf & global symbol stream & comp units") + { + if(lane_idx() == lane_from_task_idx(0)) ProfScope("hash EXE") + { + p2r_shared->exe_hash = rdi_hash(params->input_exe_data.str, params->input_exe_data.size); + } + if(lane_idx() == lane_from_task_idx(1)) ProfScope("parse TPI hash") + { + String8 hash_data = msf_data_from_stream(msf, tpi->hash_sn); + String8 aux_data = msf_data_from_stream(msf, tpi->hash_sn_aux); + if(!(params->subset_flags & (RDIM_SubsetFlag_Types|RDIM_SubsetFlag_UDTs))) + { + hash_data = aux_data = str8_zero(); + } + p2r_shared->tpi_hash = pdb_tpi_hash_from_data(arena, strtbl, tpi, hash_data, aux_data); + } + if(lane_idx() == lane_from_task_idx(2)) ProfScope("parse TPI leaf") + { + String8 leaf_data = pdb_leaf_data_from_tpi(tpi); + p2r_shared->tpi_leaf = cv_leaf_from_data(arena, leaf_data, tpi->itype_first); + } + if(lane_idx() == lane_from_task_idx(3)) ProfScope("parse IPI hash") + { + String8 hash_data = msf_data_from_stream(msf, ipi->hash_sn); + String8 aux_data = msf_data_from_stream(msf, ipi->hash_sn_aux); + if(!(params->subset_flags & (RDIM_SubsetFlag_Types|RDIM_SubsetFlag_UDTs))) + { + hash_data = aux_data = str8_zero(); + } + p2r_shared->ipi_hash = pdb_tpi_hash_from_data(arena, strtbl, ipi, hash_data, aux_data); + } + if(lane_idx() == lane_from_task_idx(4)) ProfScope("parse IPI leaf") + { + String8 leaf_data = pdb_leaf_data_from_tpi(ipi); + p2r_shared->ipi_leaf = cv_leaf_from_data(arena, leaf_data, ipi->itype_first); + } + if(lane_idx() == lane_from_task_idx(5)) ProfScope("parse compilation units") + { + String8 comp_units_data = pdb_data_from_dbi_range(dbi, PDB_DbiRange_ModuleInfo); + p2r_shared->comp_units = pdb_comp_unit_array_from_data(arena, comp_units_data); + } + if(lane_idx() == lane_from_task_idx(6)) ProfScope("parse compilation unit contributions") + { + String8 contribs_data = pdb_data_from_dbi_range(dbi, PDB_DbiRange_SecCon); + p2r_shared->comp_unit_contributions = pdb_comp_unit_contribution_array_from_data(arena, contribs_data, coff_sections); + } + } + lane_sync(); + U64 exe_hash = p2r_shared->exe_hash; + PDB_TpiHashParsed *tpi_hash = p2r_shared->tpi_hash; + CV_LeafParsed *tpi_leaf = p2r_shared->tpi_leaf; + PDB_TpiHashParsed *ipi_hash = p2r_shared->ipi_hash; + CV_LeafParsed *ipi_leaf = p2r_shared->ipi_leaf; + PDB_CompUnitArray *comp_units = p2r_shared->comp_units; + PDB_CompUnitContributionArray *comp_unit_contributions = &p2r_shared->comp_unit_contributions; + + ////////////////////////////////////////////////////////////// + //- rjf: bucket compilation unit contributions + // + ProfScope("bucket compilation unit contributions") if(lane_idx() == 0) + { + p2r_shared->unit_ranges = push_array(arena, RDIM_Rng1U64ChunkList, comp_units->count); + for(U64 idx = 0; idx < comp_unit_contributions->count; idx += 1) + { + PDB_CompUnitContribution *contribution = &comp_unit_contributions->contributions[idx]; + if(contribution->mod < comp_units->count) + { + RDIM_Rng1U64 r = {contribution->voff_first, contribution->voff_opl}; + rdim_rng1u64_chunk_list_push(arena, &p2r_shared->unit_ranges[contribution->mod], 256, r); + } + } + } + lane_sync(); + RDIM_Rng1U64ChunkList *unit_ranges = p2r_shared->unit_ranges; + + ////////////////////////////////////////////////////////////// + //- rjf: parse all syms & c13 line info streams + // + ProfScope("parse all syms & c13 line info streams") + { + //- rjf: setup outputs + if(lane_idx() == 0) + { + p2r_shared->all_syms_count = comp_units->count+1; // +1 for global symbol stream from DBI + p2r_shared->all_syms = push_array(arena, CV_SymParsed *, p2r_shared->all_syms_count); + p2r_shared->all_c13s = push_array(arena, CV_C13Parsed *, p2r_shared->all_syms_count); + p2r_shared->sym_c13_unit_lane_counter = 0; + } + lane_sync(); + + //- rjf: wide fill + { + U64 task_count = p2r_shared->all_syms_count; + for(;;) + { + U64 task_num = ins_atomic_u64_inc_eval(&p2r_shared->sym_c13_unit_lane_counter); + if(task_num == 0 || task_count < task_num) { - // rjf: file name -> sanitized file path - String8 file_path = lines_n->v.file_name; - String8 file_path_sanitized = str8_copy(scratch.arena, str8_skip_chop_whitespace(file_path)); - { - PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); - String8List file_path_sanitized_parts = str8_split_path(scratch.arena, file_path_sanitized); - if(file_path_sanitized_style == PathStyle_Relative) - { - String8List obj_folder_path_parts = str8_split_path(scratch.arena, obj_folder_path); - str8_list_concat_in_place(&obj_folder_path_parts, &file_path_sanitized_parts); - file_path_sanitized_parts = obj_folder_path_parts; - file_path_sanitized_style = path_style_from_str8(obj_folder_path); - } - str8_path_list_resolve_dots_in_place(&file_path_sanitized_parts, file_path_sanitized_style); - file_path_sanitized = str8_path_list_join_by_style(scratch.arena, &file_path_sanitized_parts, file_path_sanitized_style); - } - - // rjf: sanitized file path -> source file node - U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); - U64 hit_path_slot = file_path_sanitized_hash%hit_path_slots_count; - String8Node *hit_path_node = 0; - for(String8Node *n = hit_path_slots[hit_path_slot]; n != 0; n = n->next) - { - if(str8_match(n->string, file_path_sanitized, 0)) - { - hit_path_node = n; - break; - } - } - if(hit_path_node == 0) - { - hit_path_node = push_array(scratch.arena, String8Node, 1); - SLLStackPush(hit_path_slots[hit_path_slot], hit_path_node); - hit_path_node->string = file_path_sanitized; - str8_list_push(scratch.arena, &src_file_paths, push_str8_copy(arena, file_path_sanitized)); - } + break; + } + U64 task_idx = task_num-1; + if(task_idx > 0) + { + PDB_CompUnit *unit = comp_units->units[task_idx-1]; + String8 unit_sym_data = pdb_data_from_unit_range(msf, unit, PDB_DbiCompUnitRange_Symbols); + String8 unit_c13_data = pdb_data_from_unit_range(msf, unit, PDB_DbiCompUnitRange_C13); + p2r_shared->all_syms[task_idx] = cv_sym_from_data(arena, unit_sym_data, 4); + p2r_shared->all_c13s[task_idx] = cv_c13_parsed_from_data(arena, unit_c13_data, raw_strtbl, coff_sections); + } + else + { + String8 global_sym_data = msf_data_from_stream(msf, dbi->sym_sn); + p2r_shared->all_syms[task_idx] = cv_sym_from_data(arena, global_sym_data, 4); } } } - - //- rjf: find all files in unit's inline line info - ProfScope("find all files in unit's inline line info") + } + lane_sync(); + U64 all_syms_count = p2r_shared->all_syms_count; + CV_SymParsed **all_syms = p2r_shared->all_syms; + CV_C13Parsed **all_c13s = p2r_shared->all_c13s; + + ////////////////////////////////////////////////////////////// + //- rjf: calculate EXE's max voff + // + if(lane_idx() == 0) + { + COFF_SectionHeader *coff_sec_ptr = coff_sections.v; + COFF_SectionHeader *coff_ptr_opl = coff_sec_ptr + coff_sections.count; + for(;coff_sec_ptr < coff_ptr_opl; coff_sec_ptr += 1) { - U64 base_voff = 0; + U64 sec_voff_max = coff_sec_ptr->voff + coff_sec_ptr->vsize; + p2r_shared->exe_voff_max = Max(p2r_shared->exe_voff_max, sec_voff_max); + } + } + lane_sync(); + U64 exe_voff_max = p2r_shared->exe_voff_max; + + ////////////////////////////////////////////////////////////// + //- rjf: determine architecture + // + if(lane_idx() == 0) + { + // + // TODO(rjf): in some cases, the first compilation unit has a zero + // architecture, as it's sometimes used as a "nil" unit. this causes bugs + // in later stages of conversion - particularly, this was detected via + // busted location info. so i've converted this to a scan-until-we-find-an- + // architecture. however, this may still be fundamentally insufficient, + // because Nick has informed me that x86 units can be linked with x64 + // units, meaning the appropriate architecture at any point in time is not + // a top-level concept, and is rather dependent on to which compilation + // unit particular symbols belong. so in the future, to support that (odd) + // case, we'll need to not only have this be a top-level "contextual" piece + // of info, but to use the appropriate compilation unit's architecture when + // possible. assuming, of course, that we care about supporting that case. + // + for EachIndex(idx, all_syms_count) + { + p2r_shared->arch = p2r_rdi_arch_from_cv_arch(all_syms[idx]->info.arch); + if(p2r_shared->arch != RDI_Arch_NULL) + { + break; + } + } + } + lane_sync(); + RDI_Arch arch = p2r_shared->arch; + U64 arch_addr_size = rdi_addr_size_from_arch(arch); + + ////////////////////////////////////////////////////////////// + //- rjf: predict total symbol count + // + if(lane_idx() == 0) + { + U64 rec_range_count = 0; + for EachIndex(idx, all_syms_count) + { + rec_range_count += all_syms[idx]->sym_ranges.count; + } + p2r_shared->symbol_count_prediction = rec_range_count/8; + p2r_shared->symbol_count_prediction = Max(p2r_shared->symbol_count_prediction, 256); + } + lane_sync(); + U64 symbol_count_prediction = p2r_shared->symbol_count_prediction; + + ////////////////////////////////////////////////////////////// + //- rjf: build link name map + // + ProfScope("build link name map") if(lane_idx() == 0 && all_syms_count != 0) + { + // rjf: set up + { + p2r_shared->link_name_map.buckets_count = symbol_count_prediction; + p2r_shared->link_name_map.buckets = push_array(arena, P2R_LinkNameNode *, p2r_shared->link_name_map.buckets_count); + } + + // rjf: fill + if(params->subset_flags & RDIM_SubsetFlag_Procedures) + { + CV_SymParsed *sym = all_syms[0]; + CV_RecRange *rec_ranges_first = sym->sym_ranges.ranges; + CV_RecRange *rec_ranges_opl = rec_ranges_first + sym->sym_ranges.count; for(CV_RecRange *rec_range = rec_ranges_first; rec_range < rec_ranges_opl; rec_range += 1) { - //- rjf: rec range -> symbol info range - U64 sym_off_first = rec_range->off + 2; - U64 sym_off_opl = rec_range->off + rec_range->hdr.size; - - //- rjf: skip invalid ranges - if(sym_off_opl > pdb_unit_sym->data.size || sym_off_first > pdb_unit_sym->data.size || sym_off_first > sym_off_opl) - { - continue; - } - - //- rjf: unpack symbol info + //- rjf: unpack symbol range info CV_SymKind kind = rec_range->hdr.kind; - U64 sym_header_struct_size = cv_header_struct_size_from_sym_kind(kind); - void *sym_header_struct_base = pdb_unit_sym->data.str + sym_off_first; - void *sym_data_opl = pdb_unit_sym->data.str + sym_off_opl; + U64 header_struct_size = cv_header_struct_size_from_sym_kind(kind); + U8 *sym_first = sym->data.str + rec_range->off + 2; + U8 *sym_opl = sym_first + rec_range->hdr.size; - //- rjf: skip bad sizes - if(sym_off_first + sym_header_struct_size > sym_off_opl) + //- rjf: skip bad ranges + if(sym_opl > sym->data.str + sym->data.size || sym_first + header_struct_size > sym->data.str + sym->data.size) { continue; } - //- rjf: process symbol + //- rjf: consume symbol switch(kind) { default:{}break; - - //- rjf: LPROC32/GPROC32 (gather base address) - case CV_SymKind_LPROC32: - case CV_SymKind_GPROC32: - { - CV_SymProc32 *proc32 = (CV_SymProc32 *)sym_header_struct_base; - COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= in->coff_sections.count) ? &in->coff_sections.v[proc32->sec-1] : 0; - if(section != 0) - { - base_voff = section->voff + proc32->off; - } - }break; - - //- rjf: INLINESITE - case CV_SymKind_INLINESITE: + case CV_SymKind_PUB32: { // rjf: unpack sym - CV_SymInlineSite *sym = (CV_SymInlineSite *)sym_header_struct_base; - String8 binary_annots = str8((U8 *)(sym+1), rec_range->hdr.size - sizeof(rec_range->hdr.kind) - sizeof(*sym)); - - // rjf: map inlinee -> parsed cv c13 inlinee line info - CV_C13InlineeLinesParsed *inlinee_lines_parsed = 0; + CV_SymPub32 *pub32 = (CV_SymPub32 *)sym_first; + String8 name = str8_cstring_capped(pub32+1, sym_opl); + COFF_SectionHeader *section = (0 < pub32->sec && pub32->sec <= coff_sections.count) ? &coff_sections.v[pub32->sec-1] : 0; + U64 voff = 0; + if(section != 0) { - U64 hash = cv_hash_from_item_id(sym->inlinee); - U64 slot_idx = hash%pdb_unit_c13->inlinee_lines_parsed_slots_count; - for(CV_C13InlineeLinesParsedNode *n = pdb_unit_c13->inlinee_lines_parsed_slots[slot_idx]; n != 0; n = n->hash_next) - { - if(n->v.inlinee == sym->inlinee) - { - inlinee_lines_parsed = &n->v; - break; - } - } + voff = section->voff + pub32->off; } - // rjf: build line table, fill with parsed binary annotations - if(inlinee_lines_parsed != 0) - { - // rjf: grab checksums sub-section - CV_C13SubSectionNode *file_chksms = pdb_unit_c13->file_chksms_sub_section; - - // rjf: gathered lines - U32 last_file_off = max_U32; - U32 curr_file_off = max_U32; - U64 line_count = 0; - CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(inlinee_lines_parsed->file_off, inlinee_lines_parsed->first_source_ln, base_voff); - for(;;) - { - // rjf: step & update - CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); - if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitFile) - { - last_file_off = curr_file_off; - curr_file_off = step.file_off; - } - if(step.flags == 0 && line_count > 0) - { - last_file_off = curr_file_off; - curr_file_off = max_U32; - } - - // rjf: file updated -> gather new file name - if(last_file_off != max_U32 && last_file_off != curr_file_off) - { - String8 seq_file_name = {0}; - if(last_file_off + sizeof(CV_C13Checksum) <= file_chksms->size) - { - CV_C13Checksum *checksum = (CV_C13Checksum*)(pdb_unit_c13->data.str + file_chksms->off + last_file_off); - U32 name_off = checksum->name_off; - seq_file_name = pdb_strtbl_string_from_off(in->pdb_strtbl, name_off); - } - - // rjf: file name -> normalized file path - String8 file_path = seq_file_name; - String8 file_path_normalized = lower_from_str8(scratch.arena, str8_skip_chop_whitespace(file_path)); - { - PathStyle file_path_normalized_style = path_style_from_str8(file_path_normalized); - String8List file_path_normalized_parts = str8_split_path(scratch.arena, file_path_normalized); - if(file_path_normalized_style == PathStyle_Relative) - { - String8List obj_folder_path_parts = str8_split_path(scratch.arena, obj_folder_path); - str8_list_concat_in_place(&obj_folder_path_parts, &file_path_normalized_parts); - file_path_normalized_parts = obj_folder_path_parts; - file_path_normalized_style = path_style_from_str8(obj_folder_path); - } - str8_path_list_resolve_dots_in_place(&file_path_normalized_parts, file_path_normalized_style); - file_path_normalized = str8_path_list_join_by_style(scratch.arena, &file_path_normalized_parts, file_path_normalized_style); - } - - // rjf: normalized file path -> source file node - U64 file_path_normalized_hash = rdi_hash(file_path_normalized.str, file_path_normalized.size); - U64 hit_path_slot = file_path_normalized_hash%hit_path_slots_count; - String8Node *hit_path_node = 0; - for(String8Node *n = hit_path_slots[hit_path_slot]; n != 0; n = n->next) - { - if(str8_match(n->string, file_path_normalized, 0)) - { - hit_path_node = n; - break; - } - } - if(hit_path_node == 0) - { - hit_path_node = push_array(scratch.arena, String8Node, 1); - SLLStackPush(hit_path_slots[hit_path_slot], hit_path_node); - hit_path_node->string = file_path_normalized; - str8_list_push(scratch.arena, &src_file_paths, push_str8_copy(arena, file_path_normalized)); - } - line_count = 0; - } - - // rjf: count lines - if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitLine) - { - line_count += 1; - } - - // rjf: no more flags -> done - if(step.flags == 0) - { - break; - } - } - } + // rjf: commit to link name map + U64 hash = p2r_hash_from_voff(voff); + U64 bucket_idx = hash%p2r_shared->link_name_map.buckets_count; + P2R_LinkNameNode *node = push_array(arena, P2R_LinkNameNode, 1); + SLLStackPush(p2r_shared->link_name_map.buckets[bucket_idx], node); + node->voff = voff; + node->name = name; }break; } } } } - out->src_file_paths = str8_array_from_list(arena, &src_file_paths); - scratch_end(scratch); - ProfEnd(); - return out; -} - -//////////////////////////////// -//~ rjf: Unit Conversion Tasks - -ASYNC_WORK_DEF(p2r_unit_convert_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - Temp scratch = scratch_begin(&arena, 1); - P2R_UnitConvertIn *in = (P2R_UnitConvertIn *)input; - P2R_UnitConvertOut *out = push_array(arena, P2R_UnitConvertOut, 1); + lane_sync(); + P2R_LinkNameMap link_name_map = p2r_shared->link_name_map; - //////////////////////////// - //- rjf: pass 1: build per-unit info & per-unit line table + ////////////////////////////////////////////////////////////// + //- rjf: gather all file paths // - ProfScope("pass 1: build per-unit info & per-unit line table") + ProfScope("gather all file paths") { - PDB_CompUnit *pdb_unit = in->comp_unit; - CV_SymParsed *pdb_unit_sym = in->comp_unit_syms; - CV_C13Parsed *pdb_unit_c13 = in->comp_unit_c13s; - - //- rjf: produce unit name - String8 unit_name = pdb_unit->obj_name; - if(unit_name.size != 0) + //- rjf: prep outputs + ProfScope("prep outputs") if(lane_idx() == 0) { - String8 unit_name_past_last_slash = str8_skip_last_slash(unit_name); - if(unit_name_past_last_slash.size != 0) - { - unit_name = unit_name_past_last_slash; - } + p2r_shared->unit_file_paths = push_array(arena, String8Array, comp_units->count); + p2r_shared->unit_file_paths_hashes = push_array(arena, U64Array, comp_units->count); + p2r_shared->sym_lane_take_counter = 0; } + lane_sync(); - //- rjf: produce obj name/path - String8 obj_name = pdb_unit->obj_name; - if(str8_match(obj_name, str8_lit("* Linker *"), 0) || - str8_match(obj_name, str8_lit("Import:"), StringMatchFlag_RightSideSloppy)) + //- rjf: do wide gather + ProfScope("do wide gather") { - MemoryZeroStruct(&obj_name); - } - String8 obj_folder_path = lower_from_str8(scratch.arena, str8_chop_last_slash(obj_name)); - - //- rjf: build this unit's line table, fill out primary line info (inline info added after) - RDIM_LineTable *line_table = 0; - for(CV_C13SubSectionNode *node = pdb_unit_c13->first_sub_section; - node != 0; - node = node->next) - { - if(node->kind == CV_C13SubSectionKind_Lines) + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: build local hash table to dedup files within this lane + U64 hit_path_slots_count = 4096; + String8Node **hit_path_slots = push_array(scratch.arena, String8Node *, hit_path_slots_count); + + //- rjf: take units across lanes, find all file paths + ProfScope("take units across lanes, find all file paths") + for(;;) { - for(CV_C13LinesParsedNode *lines_n = node->lines_first; - lines_n != 0; - lines_n = lines_n->next) + //- rjf: take next unit + U64 unit_num = ins_atomic_u64_inc_eval(&p2r_shared->sym_lane_take_counter); + if(unit_num > comp_units->count) { - CV_C13LinesParsed *lines = &lines_n->v; - - // rjf: file name -> sanitized file path - String8 file_path = lines->file_name; - String8 file_path_sanitized = str8_copy(scratch.arena, str8_skip_chop_whitespace(file_path)); + break; + } + U64 unit_idx = unit_num-1; + + //- rjf: unpack unit + PDB_CompUnit *unit = comp_units->units[unit_idx]; + CV_SymParsed *sym = all_syms[unit_idx+1]; + CV_C13Parsed *c13 = all_c13s[unit_idx+1]; + CV_RecRange *rec_ranges_first = sym->sym_ranges.ranges; + CV_RecRange *rec_ranges_opl = sym->sym_ranges.ranges + sym->sym_ranges.count; + + //- rjf: produce obj name/path + String8 obj_name = unit->obj_name; + { + if(str8_match(obj_name, str8_lit("* Linker *"), 0) || + str8_match(obj_name, str8_lit("Import:"), StringMatchFlag_RightSideSloppy)) { - PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); - String8List file_path_sanitized_parts = str8_split_path(scratch.arena, file_path_sanitized); - if(file_path_sanitized_style == PathStyle_Relative) - { - String8List obj_folder_path_parts = str8_split_path(scratch.arena, obj_folder_path); - str8_list_concat_in_place(&obj_folder_path_parts, &file_path_sanitized_parts); - file_path_sanitized_parts = obj_folder_path_parts; - file_path_sanitized_style = path_style_from_str8(obj_folder_path); - } - str8_path_list_resolve_dots_in_place(&file_path_sanitized_parts, file_path_sanitized_style); - file_path_sanitized = str8_path_list_join_by_style(scratch.arena, &file_path_sanitized_parts, file_path_sanitized_style); + MemoryZeroStruct(&obj_name); + } + } + String8 obj_folder_path = backslashed_from_str8(scratch.arena, str8_chop_last_slash(obj_name)); + + //- rjf: find all inline site symbols & gather filenames + String8List src_file_paths = {0}; + U64 base_voff = 0; + for(CV_RecRange *rec_range = rec_ranges_first; + rec_range < rec_ranges_opl; + rec_range += 1) + { + //- rjf: rec range -> symbol info range + U64 sym_off_first = rec_range->off + 2; + U64 sym_off_opl = rec_range->off + rec_range->hdr.size; + + //- rjf: skip invalid ranges + if(sym_off_opl > sym->data.size || sym_off_first > sym->data.size || sym_off_first > sym_off_opl) + { + continue; } - // rjf: sanitized file path -> source file node - U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); - U64 src_file_slot = file_path_sanitized_hash%in->src_file_map->slots_count; - P2R_SrcFileNode *src_file_node = 0; - if(lines->line_count != 0) + //- rjf: unpack symbol info + CV_SymKind kind = rec_range->hdr.kind; + U64 sym_header_struct_size = cv_header_struct_size_from_sym_kind(kind); + void *sym_header_struct_base = sym->data.str + sym_off_first; + void *sym_data_opl = sym->data.str + sym_off_opl; + + //- rjf: skip bad sizes + if(sym_off_first + sym_header_struct_size > sym_off_opl) { - for(P2R_SrcFileNode *n = in->src_file_map->slots[src_file_slot]; n != 0; n = n->next) + continue; + } + + //- rjf: process symbol + switch(kind) + { + default:{}break; + + //- rjf: LPROC32/GPROC32 (gather base address) + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: { - if(str8_match(n->src_file->path, file_path_sanitized, 0)) + CV_SymProc32 *proc32 = (CV_SymProc32 *)sym_header_struct_base; + COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= coff_sections.count) ? &coff_sections.v[proc32->sec-1] : 0; + if(section != 0) { - src_file_node = n; - break; + base_voff = section->voff + proc32->off; + } + }break; + + //- rjf: INLINESITE + case CV_SymKind_INLINESITE: + { + // rjf: unpack sym + CV_SymInlineSite *sym = (CV_SymInlineSite *)sym_header_struct_base; + String8 binary_annots = str8((U8 *)(sym+1), rec_range->hdr.size - sizeof(rec_range->hdr.kind) - sizeof(*sym)); + + // rjf: map inlinee -> parsed cv c13 inlinee line info + CV_C13InlineeLinesParsed *inlinee_lines_parsed = 0; + { + U64 hash = cv_hash_from_item_id(sym->inlinee); + U64 slot_idx = hash%c13->inlinee_lines_parsed_slots_count; + for(CV_C13InlineeLinesParsedNode *n = c13->inlinee_lines_parsed_slots[slot_idx]; n != 0; n = n->hash_next) + { + if(n->v.inlinee == sym->inlinee) + { + inlinee_lines_parsed = &n->v; + break; + } + } + } + + // rjf: build line table, fill with parsed binary annotations + if(inlinee_lines_parsed != 0) + { + // rjf: grab checksums sub-section + CV_C13SubSectionNode *file_chksms = c13->file_chksms_sub_section; + + // rjf: gathered lines + U32 last_file_off = max_U32; + U32 curr_file_off = max_U32; + U64 line_count = 0; + CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(inlinee_lines_parsed->file_off, inlinee_lines_parsed->first_source_ln, base_voff); + for(;;) + { + // rjf: step & update + CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitFile) + { + last_file_off = curr_file_off; + curr_file_off = step.file_off; + } + if(step.flags == 0 && line_count > 0) + { + last_file_off = curr_file_off; + curr_file_off = max_U32; + } + + // rjf: file updated -> gather new file name + if(last_file_off != max_U32 && last_file_off != curr_file_off) + { + String8 seq_file_name = {0}; + if(last_file_off + sizeof(CV_C13Checksum) <= file_chksms->size) + { + CV_C13Checksum *checksum = (CV_C13Checksum *)(c13->data.str + file_chksms->off + last_file_off); + U32 name_off = checksum->name_off; + seq_file_name = pdb_strtbl_string_from_off(strtbl, name_off); + } + + // rjf: file name -> normalized file path + String8 file_path = seq_file_name; + String8 file_path_normalized = lower_from_str8(scratch.arena, str8_skip_chop_whitespace(file_path)); + { + PathStyle file_path_normalized_style = path_style_from_str8(file_path_normalized); + String8List file_path_normalized_parts = str8_split_path(scratch.arena, file_path_normalized); + if(file_path_normalized_style == PathStyle_Relative) + { + String8List obj_folder_path_parts = str8_split_path(scratch.arena, obj_folder_path); + str8_list_concat_in_place(&obj_folder_path_parts, &file_path_normalized_parts); + file_path_normalized_parts = obj_folder_path_parts; + file_path_normalized_style = path_style_from_str8(obj_folder_path); + } + str8_path_list_resolve_dots_in_place(&file_path_normalized_parts, file_path_normalized_style); + file_path_normalized = str8_path_list_join_by_style(scratch.arena, &file_path_normalized_parts, file_path_normalized_style); + } + + // rjf: normalized file path -> source file node + U64 file_path_normalized_hash = rdi_hash(file_path_normalized.str, file_path_normalized.size); + U64 hit_path_slot = file_path_normalized_hash%hit_path_slots_count; + String8Node *hit_path_node = 0; + for(String8Node *n = hit_path_slots[hit_path_slot]; n != 0; n = n->next) + { + if(str8_match(n->string, file_path_normalized, 0)) + { + hit_path_node = n; + break; + } + } + if(hit_path_node == 0) + { + hit_path_node = push_array(scratch.arena, String8Node, 1); + SLLStackPush(hit_path_slots[hit_path_slot], hit_path_node); + hit_path_node->string = file_path_normalized; + str8_list_push(arena, &src_file_paths, push_str8_copy(arena, file_path_normalized)); + } + line_count = 0; + } + + // rjf: count lines + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitLine) + { + line_count += 1; + } + + // rjf: no more flags -> done + if(step.flags == 0) + { + break; + } + } + } + }break; + } + } + + // rjf: find all files in this unit's (non-inline) line info + ProfScope("find all files in this unit's (non-inline) line info") + for(CV_C13SubSectionNode *node = c13->first_sub_section; + node != 0; + node = node->next) + { + if(node->kind == CV_C13SubSectionKind_Lines) + { + for(CV_C13LinesParsedNode *lines_n = node->lines_first; + lines_n != 0; + lines_n = lines_n->next) + { + // rjf: file name -> sanitized file path + String8 file_path = lines_n->v.file_name; + String8 file_path_sanitized = str8_copy(scratch.arena, str8_skip_chop_whitespace(file_path)); + { + PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); + String8List file_path_sanitized_parts = str8_split_path(scratch.arena, file_path_sanitized); + if(file_path_sanitized_style == PathStyle_Relative) + { + String8List obj_folder_path_parts = str8_split_path(scratch.arena, obj_folder_path); + str8_list_concat_in_place(&obj_folder_path_parts, &file_path_sanitized_parts); + file_path_sanitized_parts = obj_folder_path_parts; + file_path_sanitized_style = path_style_from_str8(obj_folder_path); + } + str8_path_list_resolve_dots_in_place(&file_path_sanitized_parts, file_path_sanitized_style); + file_path_sanitized = str8_path_list_join_by_style(scratch.arena, &file_path_sanitized_parts, file_path_sanitized_style); + } + + // rjf: sanitized file path -> source file node + U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); + U64 hit_path_slot = file_path_sanitized_hash%hit_path_slots_count; + String8Node *hit_path_node = 0; + for(String8Node *n = hit_path_slots[hit_path_slot]; n != 0; n = n->next) + { + if(str8_match(n->string, file_path_sanitized, 0)) + { + hit_path_node = n; + break; + } + } + if(hit_path_node == 0) + { + hit_path_node = push_array(scratch.arena, String8Node, 1); + SLLStackPush(hit_path_slots[hit_path_slot], hit_path_node); + hit_path_node->string = file_path_sanitized; + str8_list_push(scratch.arena, &src_file_paths, push_str8_copy(arena, file_path_sanitized)); } } } - - // rjf: push sequence into both line table & source file's line map - if(src_file_node != 0) + } + + //- rjf: merge into array for this unit + p2r_shared->unit_file_paths[unit_idx] = str8_array_from_list(arena, &src_file_paths); + + //- rjf: hash this unit's file paths + U64Array hashes = {0}; + hashes.count = p2r_shared->unit_file_paths[unit_idx].count; + hashes.v = push_array(arena, U64, hashes.count); + for EachIndex(idx, p2r_shared->unit_file_paths[unit_idx].count) + { + hashes.v[idx] = rdi_hash(p2r_shared->unit_file_paths[unit_idx].v[idx].str, p2r_shared->unit_file_paths[unit_idx].v[idx].size); + } + p2r_shared->unit_file_paths_hashes[unit_idx] = hashes; + } + scratch_end(scratch); + } + } + lane_sync(); + String8Array *unit_file_paths = p2r_shared->unit_file_paths; + U64Array *unit_file_paths_hashes = p2r_shared->unit_file_paths_hashes; + + ////////////////////////////////////////////////////////////// + //- rjf: build unified collection & map for source files + // + if(params->subset_flags & (RDIM_SubsetFlag_NormalSourcePathNameMap| + RDIM_SubsetFlag_LineInfo| + RDIM_SubsetFlag_InlineLineInfo)) + { + //- rjf: set up table + ProfScope("set up table") if(lane_idx() == 0) + { + p2r_shared->total_path_count = 0; + for EachIndex(idx, comp_units->count) + { + p2r_shared->total_path_count += unit_file_paths[idx].count; + } + p2r_shared->src_file_map.slots_count = p2r_shared->total_path_count + p2r_shared->total_path_count/2 + 1; + p2r_shared->src_file_map.slots = push_array(arena, P2R_SrcFileNode *, p2r_shared->src_file_map.slots_count); + } + lane_sync(); + + //- rjf: fill table + ProfScope("fill table") if(lane_idx() == 0) + { + for EachIndex(idx, comp_units->count) + { + String8Array paths = unit_file_paths[idx]; + U64Array hashes = unit_file_paths_hashes[idx]; + for EachIndex(path_idx, paths.count) + { + String8 file_path_sanitized = paths.v[path_idx]; + U64 file_path_sanitized_hash = hashes.v[path_idx]; + U64 src_file_slot = file_path_sanitized_hash%p2r_shared->src_file_map.slots_count; + P2R_SrcFileNode *src_file_node = 0; + for(P2R_SrcFileNode *n = p2r_shared->src_file_map.slots[src_file_slot]; n != 0; n = n->next) { - if(line_table == 0) + if(str8_match(n->src_file->path, file_path_sanitized, 0)) { - line_table = rdim_line_table_chunk_list_push(arena, &out->line_tables, 256); + src_file_node = n; + break; } - RDIM_LineSequence *seq = rdim_line_table_push_sequence(arena, &out->line_tables, line_table, src_file_node->src_file, lines->voffs, lines->line_nums, lines->col_nums, lines->line_count); + } + if(src_file_node == 0) + { + src_file_node = push_array(arena, P2R_SrcFileNode, 1); + SLLStackPush(p2r_shared->src_file_map.slots[src_file_slot], src_file_node); + src_file_node->src_file = rdim_src_file_chunk_list_push(arena, &p2r_shared->all_src_files__sequenceless, p2r_shared->total_path_count); + src_file_node->src_file->path = push_str8_copy(arena, file_path_sanitized); } } } } - - //- rjf: build unit - RDIM_Unit *dst_unit = rdim_unit_chunk_list_push(arena, &out->units, 1); - dst_unit->unit_name = unit_name; - dst_unit->compiler_name = pdb_unit_sym->info.compiler_name; - dst_unit->object_file = obj_name; - dst_unit->archive_file = pdb_unit->group_name; - dst_unit->language = p2r_rdi_language_from_cv_language(pdb_unit_sym->info.language); - dst_unit->line_table = line_table; - dst_unit->voff_ranges = in->comp_unit_ranges; } + lane_sync(); + RDIM_SrcFileChunkList all_src_files__sequenceless = p2r_shared->all_src_files__sequenceless; + P2R_SrcFileMap src_file_map = p2r_shared->src_file_map; - //////////////////////////// - //- rjf: pass 2: parse all inlinee line tables + ////////////////////////////////////////////////////////////// + //- rjf: convert unit info // - ProfScope("pass 2: parse all inlinee line tables") + ProfScope("convert unit info") { - //- rjf: unpack unit - PDB_CompUnit *pdb_unit = in->comp_unit; - CV_SymParsed *pdb_unit_sym = in->comp_unit_syms; - CV_C13Parsed *pdb_unit_c13 = in->comp_unit_c13s; - CV_RecRange *rec_ranges_first = pdb_unit_sym->sym_ranges.ranges; - CV_RecRange *rec_ranges_opl = rec_ranges_first+pdb_unit_sym->sym_ranges.count; - - //- rjf: produce obj name/path - String8 obj_name = pdb_unit->obj_name; - if(str8_match(obj_name, str8_lit("* Linker *"), 0) || - str8_match(obj_name, str8_lit("Import:"), StringMatchFlag_RightSideSloppy)) + //- rjf: set up outputs + ProfScope("set up outputs") if(lane_idx() == 0) { - MemoryZeroStruct(&obj_name); + if(params->subset_flags & RDIM_SubsetFlag_Units) + { + for EachIndex(idx, comp_units->count) + { + rdim_unit_chunk_list_push(arena, &p2r_shared->all_units, comp_units->count); + } + } + p2r_shared->units_line_tables = push_array(arena, RDIM_LineTableChunkList, comp_units->count); + p2r_shared->units_first_inline_site_line_tables = push_array(arena, RDIM_LineTable *, comp_units->count); + p2r_shared->sym_lane_take_counter = 0; } - String8 obj_folder_path = lower_from_str8(scratch.arena, str8_chop_last_slash(obj_name)); + lane_sync(); + RDIM_Unit *units = p2r_shared->all_units.first ? p2r_shared->all_units.first->v : 0; + U64 units_count = p2r_shared->all_units.first ? p2r_shared->all_units.first->count : 0; + RDIM_LineTableChunkList *units_line_tables = p2r_shared->units_line_tables; + Assert(units_count == comp_units->count); - //- rjf: parse inlinee line tables - U64 base_voff = 0; - for(CV_RecRange *rec_range = rec_ranges_first; - rec_range < rec_ranges_opl; - rec_range += 1) + //- rjf: do per-lane work + if(params->subset_flags & (RDIM_SubsetFlag_Units| + RDIM_SubsetFlag_NormalSourcePathNameMap| + RDIM_SubsetFlag_LineInfo| + RDIM_SubsetFlag_InlineLineInfo)) { - //- rjf: rec range -> symbol info range - U64 sym_off_first = rec_range->off + 2; - U64 sym_off_opl = rec_range->off + rec_range->hdr.size; - - //- rjf: skip invalid ranges - if(sym_off_opl > pdb_unit_sym->data.size || sym_off_first > pdb_unit_sym->data.size || sym_off_first > sym_off_opl) + ProfScope("wide fill") for(;;) { - continue; - } - - //- rjf: unpack symbol info - CV_SymKind kind = rec_range->hdr.kind; - U64 sym_header_struct_size = cv_header_struct_size_from_sym_kind(kind); - void *sym_header_struct_base = pdb_unit_sym->data.str + sym_off_first; - void *sym_data_opl = pdb_unit_sym->data.str + sym_off_opl; - - //- rjf: skip bad sizes - if(sym_off_first + sym_header_struct_size > sym_off_opl) - { - continue; - } - - //- rjf: process symbol - switch(kind) - { - default:{}break; - - //- rjf: LPROC32/GPROC32 (gather base address) - case CV_SymKind_LPROC32: - case CV_SymKind_GPROC32: + //- rjf: take next unit + U64 unit_num = ins_atomic_u64_inc_eval(&p2r_shared->sym_lane_take_counter); + if(unit_num > comp_units->count) { - CV_SymProc32 *proc32 = (CV_SymProc32 *)sym_header_struct_base; - COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= in->coff_sections.count) ? &in->coff_sections.v[proc32->sec-1] : 0; - if(section != 0) - { - base_voff = section->voff + proc32->off; - } - }break; + break; + } + Temp scratch = scratch_begin(&arena, 1); + U64 unit_idx = unit_num-1; + RDIM_LineTableChunkList *dst_line_tables = &units_line_tables[unit_idx]; + PDB_CompUnit *src_unit = comp_units->units[unit_idx]; + CV_SymParsed *src_unit_sym = all_syms[unit_idx+1]; + CV_C13Parsed *src_unit_c13 = all_c13s[unit_idx+1]; + RDIM_Unit *dst_unit = 0; + if(params->subset_flags & RDIM_SubsetFlag_Units) { dst_unit = &units[unit_idx]; } - //- rjf: INLINESITE - case CV_SymKind_INLINESITE: + // rjf: extract unit name + String8 unit_name = src_unit->obj_name; + if(unit_name.size != 0) { - // rjf: unpack sym - CV_SymInlineSite *sym = (CV_SymInlineSite *)sym_header_struct_base; - String8 binary_annots = str8((U8 *)(sym+1), rec_range->hdr.size - sizeof(rec_range->hdr.kind) - sizeof(*sym)); - - // rjf: map inlinee -> parsed cv c13 inlinee line info - CV_C13InlineeLinesParsed *inlinee_lines_parsed = 0; + String8 unit_name_past_last_slash = str8_skip_last_slash(unit_name); + if(unit_name_past_last_slash.size != 0) { - U64 hash = cv_hash_from_item_id(sym->inlinee); - U64 slot_idx = hash%pdb_unit_c13->inlinee_lines_parsed_slots_count; - for(CV_C13InlineeLinesParsedNode *n = pdb_unit_c13->inlinee_lines_parsed_slots[slot_idx]; n != 0; n = n->hash_next) - { - if(n->v.inlinee == sym->inlinee) - { - inlinee_lines_parsed = &n->v; - break; - } - } + unit_name = unit_name_past_last_slash; } - - // rjf: build line table, fill with parsed binary annotations - if(inlinee_lines_parsed != 0) + } + + // rjf: produce obj name/path + String8 obj_name = src_unit->obj_name; + if(str8_match(obj_name, str8_lit("* Linker *"), 0) || + str8_match(obj_name, str8_lit("Import:"), StringMatchFlag_RightSideSloppy)) + { + MemoryZeroStruct(&obj_name); + } + String8 obj_folder_path = backslashed_from_str8(scratch.arena, str8_chop_last_slash(obj_name)); + + //- rjf: main unit line table conversion + if(params->subset_flags & RDIM_SubsetFlag_LineInfo) ProfScope("main unit line table conversion") + { + RDIM_LineTable *line_table = 0; + for(CV_C13SubSectionNode *node = src_unit_c13->first_sub_section; + node != 0; + node = node->next) { - // rjf: grab checksums sub-section - CV_C13SubSectionNode *file_chksms = pdb_unit_c13->file_chksms_sub_section; - - // rjf: gathered lines - typedef struct LineChunk LineChunk; - struct LineChunk + if(node->kind == CV_C13SubSectionKind_Lines) { - LineChunk *next; - U64 cap; - U64 count; - U64 *voffs; // [line_count + 1] (sorted) - U32 *line_nums; // [line_count] - U16 *col_nums; // [2*line_count] - }; - LineChunk *first_line_chunk = 0; - LineChunk *last_line_chunk = 0; - U64 total_line_chunk_line_count = 0; - U32 last_file_off = max_U32; - U32 curr_file_off = max_U32; - RDIM_LineTable* line_table = 0; - - CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(inlinee_lines_parsed->file_off, inlinee_lines_parsed->first_source_ln, base_voff); - for(;;) - { - // rjf: step & update - CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); - if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitFile) + for(CV_C13LinesParsedNode *lines_n = node->lines_first; + lines_n != 0; + lines_n = lines_n->next) { - last_file_off = curr_file_off; - curr_file_off = step.file_off; - } - if(step.flags == 0 && total_line_chunk_line_count > 0) - { - last_file_off = curr_file_off; - curr_file_off = max_U32; - } - - // rjf: file updated -> push line chunks gathered for this file - if(last_file_off != max_U32 && last_file_off != curr_file_off) - { - String8 seq_file_name = {0}; - if(last_file_off + sizeof(CV_C13Checksum) <= file_chksms->size) - { - CV_C13Checksum *checksum = (CV_C13Checksum*)(pdb_unit_c13->data.str + file_chksms->off + last_file_off); - U32 name_off = checksum->name_off; - seq_file_name = pdb_strtbl_string_from_off(in->pdb_strtbl, name_off); - } + CV_C13LinesParsed *lines = &lines_n->v; // rjf: file name -> sanitized file path - String8 file_path = seq_file_name; - String8 file_path_sanitized = str8_copy(scratch.arena, str8_skip_chop_whitespace(file_path)); + String8 file_path = lines->file_name; + String8 file_path_sanitized = str8_copy(scratch.arena, str8_skip_chop_whitespace(file_path)); { PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); String8List file_path_sanitized_parts = str8_split_path(scratch.arena, file_path_sanitized); @@ -1002,2622 +1210,310 @@ ASYNC_WORK_DEF(p2r_unit_convert_work) } // rjf: sanitized file path -> source file node - U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); - U64 src_file_slot = file_path_sanitized_hash%in->src_file_map->slots_count; - P2R_SrcFileNode *src_file_node = 0; - for(P2R_SrcFileNode *n = in->src_file_map->slots[src_file_slot]; n != 0; n = n->next) + U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); + U64 src_file_slot = file_path_sanitized_hash%src_file_map.slots_count; + P2R_SrcFileNode *src_file_node = 0; + if(lines->line_count != 0) { - if(str8_match(n->src_file->path, file_path_sanitized, 0)) + for(P2R_SrcFileNode *n = src_file_map.slots[src_file_slot]; n != 0; n = n->next) { - src_file_node = n; - break; + if(str8_match(n->src_file->path, file_path_sanitized, 0)) + { + src_file_node = n; + break; + } } } - // rjf: gather all lines - RDI_U64 *voffs = 0; - RDI_U32 *line_nums = 0; - RDI_U64 line_count = 0; + // rjf: push sequence into both line table & source file's line map if(src_file_node != 0) - { - voffs = push_array_no_zero(arena, RDI_U64, total_line_chunk_line_count+1); - line_nums = push_array_no_zero(arena, RDI_U32, total_line_chunk_line_count); - line_count = total_line_chunk_line_count; - U64 dst_idx = 0; - for(LineChunk *chunk = first_line_chunk; chunk != 0; chunk = chunk->next) - { - MemoryCopy(voffs+dst_idx, chunk->voffs, sizeof(U64)*(chunk->count+1)); - MemoryCopy(line_nums+dst_idx, chunk->line_nums, sizeof(U32)*chunk->count); - dst_idx += chunk->count; - } - } - - // rjf: push - if(line_count != 0) { if(line_table == 0) { - line_table = rdim_line_table_chunk_list_push(arena, &out->line_tables, 256); - if(out->unit_first_inline_site_line_table == 0) + line_table = rdim_line_table_chunk_list_push(arena, dst_line_tables, 256); + } + RDIM_LineSequence *seq = rdim_line_table_push_sequence(arena, dst_line_tables, line_table, src_file_node->src_file, lines->voffs, lines->line_nums, lines->col_nums, lines->line_count); + } + } + } + } + + // rjf: fill unit + if(dst_unit != 0) + { + dst_unit->unit_name = unit_name; + dst_unit->compiler_name = src_unit_sym->info.compiler_name; + dst_unit->object_file = obj_name; + dst_unit->archive_file = src_unit->group_name; + dst_unit->language = p2r_rdi_language_from_cv_language(src_unit_sym->info.language); + dst_unit->line_table = line_table; + dst_unit->voff_ranges = unit_ranges[unit_idx]; + } + } + + //- rjf: build per-inline-site line tables + if(params->subset_flags & RDIM_SubsetFlag_InlineLineInfo) ProfScope("build per-inline-site line tables") + { + CV_RecRange *rec_ranges_first = src_unit_sym->sym_ranges.ranges; + CV_RecRange *rec_ranges_opl = src_unit_sym->sym_ranges.ranges + src_unit_sym->sym_ranges.count; + U64 base_voff = 0; + for(CV_RecRange *rec_range = rec_ranges_first; + rec_range < rec_ranges_opl; + rec_range += 1) + { + //- rjf: rec range -> symbol info range + U64 sym_off_first = rec_range->off + 2; + U64 sym_off_opl = rec_range->off + rec_range->hdr.size; + + //- rjf: skip invalid ranges + if(sym_off_opl > src_unit_sym->data.size || sym_off_first > src_unit_sym->data.size || sym_off_first > sym_off_opl) + { + continue; + } + + //- rjf: unpack symbol info + CV_SymKind kind = rec_range->hdr.kind; + U64 sym_header_struct_size = cv_header_struct_size_from_sym_kind(kind); + void *sym_header_struct_base = src_unit_sym->data.str + sym_off_first; + void *sym_data_opl = src_unit_sym->data.str + sym_off_opl; + + //- rjf: skip bad sizes + if(sym_off_first + sym_header_struct_size > sym_off_opl) + { + continue; + } + + //- rjf: process symbol + switch(kind) + { + default:{}break; + + //- rjf: LPROC32/GPROC32 (gather base address) + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + CV_SymProc32 *proc32 = (CV_SymProc32 *)sym_header_struct_base; + COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= coff_sections.count) ? &coff_sections.v[proc32->sec-1] : 0; + if(section != 0) + { + base_voff = section->voff + proc32->off; + } + }break; + + //- rjf: INLINESITE + case CV_SymKind_INLINESITE: + { + // rjf: unpack sym + CV_SymInlineSite *sym = (CV_SymInlineSite *)sym_header_struct_base; + String8 binary_annots = str8((U8 *)(sym+1), rec_range->hdr.size - sizeof(rec_range->hdr.kind) - sizeof(*sym)); + + // rjf: map inlinee -> parsed cv c13 inlinee line info + CV_C13InlineeLinesParsed *inlinee_lines_parsed = 0; + { + U64 hash = cv_hash_from_item_id(sym->inlinee); + U64 slot_idx = hash%src_unit_c13->inlinee_lines_parsed_slots_count; + for(CV_C13InlineeLinesParsedNode *n = src_unit_c13->inlinee_lines_parsed_slots[slot_idx]; n != 0; n = n->hash_next) + { + if(n->v.inlinee == sym->inlinee) { - out->unit_first_inline_site_line_table = line_table; + inlinee_lines_parsed = &n->v; + break; } } - rdim_line_table_push_sequence(arena, &out->line_tables, line_table, src_file_node->src_file, voffs, line_nums, 0, line_count); } - // rjf: clear line chunks for subsequent sequences - first_line_chunk = last_line_chunk = 0; - total_line_chunk_line_count = 0; - } - - // rjf: new line -> emit to chunk - if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitLine) - { - LineChunk *chunk = last_line_chunk; - if(chunk == 0 || chunk->count+1 >= chunk->cap) + // rjf: build line table, fill with parsed binary annotations + if(inlinee_lines_parsed != 0) { - chunk = push_array(scratch.arena, LineChunk, 1); - SLLQueuePush(first_line_chunk, last_line_chunk, chunk); - chunk->cap = 8; - chunk->voffs = push_array_no_zero(scratch.arena, U64, chunk->cap); - chunk->line_nums = push_array_no_zero(scratch.arena, U32, chunk->cap); - } - chunk->voffs[chunk->count] = step.line_voff; - chunk->voffs[chunk->count+1] = step.line_voff_end; - chunk->line_nums[chunk->count] = step.ln; - chunk->count += 1; - total_line_chunk_line_count += 1; - } - - // rjf: no more flags -> done - if(step.flags == 0) - { - break; - } - } - } - }break; - } - } - } - scratch_end(scratch); - ProfEnd(); - return out; -} - -//////////////////////////////// -//~ rjf: Source File Sequence Equipping Task - -ASYNC_WORK_DEF(p2r_src_file_seq_equip_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_SrcFileSeqEquipIn *in = (P2R_SrcFileSeqEquipIn *)input; - for(RDIM_LineTableChunkNode *line_table_chunk_n = in->line_tables.first; line_table_chunk_n != 0; line_table_chunk_n = line_table_chunk_n->next) - { - for EachIndex(chunk_line_table_idx, line_table_chunk_n->count) - { - RDIM_LineTable *line_table = &line_table_chunk_n->v[chunk_line_table_idx]; - for(RDIM_LineSequenceNode *s = line_table->first_seq; s != 0; s = s->next) - { - rdim_src_file_push_line_sequence(arena, &in->src_files, s->v.src_file, &s->v); - } - } - } - ProfEnd(); - return 0; -} - -//////////////////////////////// -//~ rjf: Link Name Map Building Tasks - -ASYNC_WORK_DEF(p2r_link_name_map_build_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_LinkNameMapBuildIn *in = (P2R_LinkNameMapBuildIn *)input; - CV_RecRange *rec_ranges_first = in->sym->sym_ranges.ranges; - CV_RecRange *rec_ranges_opl = rec_ranges_first + in->sym->sym_ranges.count; - for(CV_RecRange *rec_range = rec_ranges_first; - rec_range < rec_ranges_opl; - rec_range += 1) - { - //- rjf: unpack symbol range info - CV_SymKind kind = rec_range->hdr.kind; - U64 header_struct_size = cv_header_struct_size_from_sym_kind(kind); - U8 *sym_first = in->sym->data.str + rec_range->off + 2; - U8 *sym_opl = sym_first + rec_range->hdr.size; - - //- rjf: skip bad ranges - if(sym_opl > in->sym->data.str + in->sym->data.size || sym_first + header_struct_size > in->sym->data.str + in->sym->data.size) - { - continue; - } - - //- rjf: consume symbol - switch(kind) - { - default:{}break; - case CV_SymKind_PUB32: - { - // rjf: unpack sym - CV_SymPub32 *pub32 = (CV_SymPub32 *)sym_first; - String8 name = str8_cstring_capped(pub32+1, sym_opl); - COFF_SectionHeader *section = (0 < pub32->sec && pub32->sec <= in->coff_sections.count) ? &in->coff_sections.v[pub32->sec-1] : 0; - U64 voff = 0; - if(section != 0) - { - voff = section->voff + pub32->off; - } - - // rjf: commit to link name map - U64 hash = p2r_hash_from_voff(voff); - U64 bucket_idx = hash%in->link_name_map->buckets_count; - P2R_LinkNameNode *node = push_array(arena, P2R_LinkNameNode, 1); - SLLStackPush(in->link_name_map->buckets[bucket_idx], node); - node->voff = voff; - node->name = name; - in->link_name_map->link_name_count += 1; - in->link_name_map->bucket_collision_count += (node->next != 0); - }break; - } - } - ProfEnd(); - return 0; -} - -//////////////////////////////// -//~ rjf: Type Parsing/Conversion Tasks - -ASYNC_WORK_DEF(p2r_itype_fwd_map_fill_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_ITypeFwdMapFillIn *in = (P2R_ITypeFwdMapFillIn *)input; - ProfScope("fill itype fwd map") for(CV_TypeId itype = in->itype_first; itype < in->itype_opl; itype += 1) - { - //- rjf: skip if not in the actually stored itype range - if(itype < in->tpi_leaf->itype_first) - { - continue; - } - - //- rjf: determine if this itype resolves to another - CV_TypeId itype_fwd = 0; - CV_RecRange *range = &in->tpi_leaf->leaf_ranges.ranges[itype-in->tpi_leaf->itype_first]; - CV_LeafKind kind = range->hdr.kind; - U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); - if(range->off+range->hdr.size <= in->tpi_leaf->data.size && - range->off+2+header_struct_size <= in->tpi_leaf->data.size && - range->hdr.size >= 2) - { - U8 *itype_leaf_first = in->tpi_leaf->data.str + range->off+2; - U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; - switch(kind) - { - default:{}break; - - //- rjf: CLASS/STRUCTURE - case CV_LeafKind_CLASS: - case CV_LeafKind_STRUCTURE: - { - // rjf: unpack leaf header - CV_LeafStruct *lf_struct = (CV_LeafStruct *)itype_leaf_first; - - // rjf: has fwd ref flag -> lookup itype that this itype resolves to - if(lf_struct->props & CV_TypeProp_FwdRef) - { - // rjf: unpack rest of leaf - U8 *numeric_ptr = (U8 *)(lf_struct + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); - U8 *name_ptr = numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); - U8 *unique_name_ptr = name_ptr + name.size + 1; - String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); - - // rjf: lookup - B32 do_unique_name_lookup = (((lf_struct->props & CV_TypeProp_Scoped) != 0) && - ((lf_struct->props & CV_TypeProp_HasUniqueName) != 0)); - itype_fwd = pdb_tpi_first_itype_from_name(in->tpi_hash, in->tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); - } - }break; - - //- rjf: CLASS2/STRUCT2 - case CV_LeafKind_CLASS2: - case CV_LeafKind_STRUCT2: - { - // rjf: unpack leaf header - CV_LeafStruct2 *lf_struct = (CV_LeafStruct2 *)itype_leaf_first; - - // rjf: has fwd ref flag -> lookup itype that this itype resolves to - if(lf_struct->props & CV_TypeProp_FwdRef) - { - // rjf: unpack rest of leaf - U8 *numeric_ptr = (U8 *)(lf_struct + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); - U8 *name_ptr = (U8 *)numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); - U8 *unique_name_ptr = name_ptr + name.size + 1; - String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); - - // rjf: lookup - B32 do_unique_name_lookup = (((lf_struct->props & CV_TypeProp_Scoped) != 0) && - ((lf_struct->props & CV_TypeProp_HasUniqueName) != 0)); - itype_fwd = pdb_tpi_first_itype_from_name(in->tpi_hash, in->tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); - } - }break; - - //- rjf: UNION - case CV_LeafKind_UNION: - { - // rjf: unpack leaf - CV_LeafUnion *lf_union = (CV_LeafUnion *)itype_leaf_first; - U8 *numeric_ptr = (U8 *)(lf_union + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); - U8 *name_ptr = numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); - U8 *unique_name_ptr = name_ptr + name.size + 1; - String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); - - // rjf: has fwd ref flag -> lookup itype that this itype resolves tos - if(lf_union->props & CV_TypeProp_FwdRef) - { - B32 do_unique_name_lookup = (((lf_union->props & CV_TypeProp_Scoped) != 0) && - ((lf_union->props & CV_TypeProp_HasUniqueName) != 0)); - itype_fwd = pdb_tpi_first_itype_from_name(in->tpi_hash, in->tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); - } - }break; - - //- rjf: ENUM - case CV_LeafKind_ENUM: - { - // rjf: unpack leaf - CV_LeafEnum *lf_enum = (CV_LeafEnum*)itype_leaf_first; - U8 *name_ptr = (U8 *)(lf_enum + 1); - String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); - U8 *unique_name_ptr = name_ptr + name.size + 1; - String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); - - // rjf: has fwd ref flag -> lookup itype that this itype resolves to - if(lf_enum->props & CV_TypeProp_FwdRef) - { - B32 do_unique_name_lookup = (((lf_enum->props & CV_TypeProp_Scoped) != 0) && - ((lf_enum->props & CV_TypeProp_HasUniqueName) != 0)); - itype_fwd = pdb_tpi_first_itype_from_name(in->tpi_hash, in->tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); - } - }break; - } - } - - //- rjf: if the forwarded itype is nonzero & in TPI range -> save to map - if(itype_fwd != 0 && itype_fwd < in->tpi_leaf->itype_opl) - { - in->itype_fwd_map[itype] = itype_fwd; - } - } - ProfEnd(); - return 0; -} - -ASYNC_WORK_DEF(p2r_itype_chain_build_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - Temp scratch = scratch_begin(&arena, 1); - P2R_ITypeChainBuildIn *in = (P2R_ITypeChainBuildIn *)input; - ProfScope("dependency itype chain build") - { - for(CV_TypeId itype = in->itype_first; itype < in->itype_opl; itype += 1) - { - //- rjf: push initial itype - should be final-visited-itype for this itype - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = itype; - SLLStackPush(in->itype_chains[itype], c); - } - - //- rjf: skip basic types for dependency walk - if(itype < in->tpi_leaf->itype_first) - { - continue; - } - - //- rjf: walk dependent types, push to chain - P2R_TypeIdChain start_walk_task = {0, itype}; - P2R_TypeIdChain *first_walk_task = &start_walk_task; - P2R_TypeIdChain *last_walk_task = &start_walk_task; - for(P2R_TypeIdChain *walk_task = first_walk_task; - walk_task != 0; - walk_task = walk_task->next) - { - CV_TypeId walk_itype = in->itype_fwd_map[walk_task->itype] ? in->itype_fwd_map[walk_task->itype] : walk_task->itype; - if(walk_itype < in->tpi_leaf->itype_first) - { - continue; - } - CV_RecRange *range = &in->tpi_leaf->leaf_ranges.ranges[walk_itype-in->tpi_leaf->itype_first]; - CV_LeafKind kind = range->hdr.kind; - U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); - if(range->off+range->hdr.size <= in->tpi_leaf->data.size && - range->off+2+header_struct_size <= in->tpi_leaf->data.size && - range->hdr.size >= 2) - { - U8 *itype_leaf_first = in->tpi_leaf->data.str + range->off+2; - U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; - switch(kind) - { - default:{}break; - - //- rjf: MODIFIER - case CV_LeafKind_MODIFIER: - { - CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; - - // rjf: push dependent itype to chain - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->itype; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk dependency itype - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - }break; - - //- rjf: POINTER - case CV_LeafKind_POINTER: - { - CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; - - // rjf: push dependent itype to chain - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->itype; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk dependency itype - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - }break; - - //- rjf: PROCEDURE - case CV_LeafKind_PROCEDURE: - { - CV_LeafProcedure *lf = (CV_LeafProcedure *)itype_leaf_first; - - // rjf: push return itypes to chain - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->ret_itype; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk return itype - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->ret_itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - - // rjf: unpack arglist range - CV_RecRange *arglist_range = &in->tpi_leaf->leaf_ranges.ranges[lf->arg_itype-in->tpi_leaf->itype_first]; - if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || - arglist_range->hdr.size<2 || - arglist_range->off + arglist_range->hdr.size > in->tpi_leaf->data.size) - { - break; - } - U8 *arglist_first = in->tpi_leaf->data.str + arglist_range->off + 2; - U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; - if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) - { - break; - } - - // rjf: unpack arglist info - CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; - CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); - U32 arglist_itypes_count = arglist->count; - - // rjf: push arg types to chain - for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = arglist_itypes_base[idx]; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk arg types - for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = arglist_itypes_base[idx]; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - }break; - - //- rjf: MFUNCTION - case CV_LeafKind_MFUNCTION: - { - CV_LeafMFunction *lf = (CV_LeafMFunction *)itype_leaf_first; - - // rjf: push dependent itypes to chain - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->ret_itype; - SLLStackPush(in->itype_chains[itype], c); - } - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->arg_itype; - SLLStackPush(in->itype_chains[itype], c); - } - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->this_itype; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk dependency itypes - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->ret_itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->arg_itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->this_itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - - // rjf: unpack arglist range - CV_RecRange *arglist_range = &in->tpi_leaf->leaf_ranges.ranges[lf->arg_itype-in->tpi_leaf->itype_first]; - if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || - arglist_range->hdr.size<2 || - arglist_range->off + arglist_range->hdr.size > in->tpi_leaf->data.size) - { - break; - } - U8 *arglist_first = in->tpi_leaf->data.str + arglist_range->off + 2; - U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; - if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) - { - break; - } - - // rjf: unpack arglist info - CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; - CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); - U32 arglist_itypes_count = arglist->count; - - // rjf: push arg types to chain - for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = arglist_itypes_base[idx]; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk arg types - for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = arglist_itypes_base[idx]; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - }break; - - //- rjf: BITFIELD - case CV_LeafKind_BITFIELD: - { - CV_LeafBitField *lf = (CV_LeafBitField *)itype_leaf_first; - - // rjf: push dependent itype to chain - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->itype; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk dependency itype - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - }break; - - //- rjf: ARRAY - case CV_LeafKind_ARRAY: - { - CV_LeafArray *lf = (CV_LeafArray *)itype_leaf_first; - - // rjf: push dependent itypes to chain - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->entry_itype; - SLLStackPush(in->itype_chains[itype], c); - } - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->index_itype; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk dependency itypes - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->entry_itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->index_itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - }break; - - //- rjf: ENUM - case CV_LeafKind_ENUM: - { - CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; - - // rjf: push dependent itypes to chain - { - P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); - c->itype = lf->base_itype; - SLLStackPush(in->itype_chains[itype], c); - } - - // rjf: push task to walk dependency itypes - { - P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); - c->itype = lf->base_itype; - SLLQueuePush(first_walk_task, last_walk_task, c); - } - }break; - } - } - } - } - } - scratch_end(scratch); - ProfEnd(); - return 0; -} - -//////////////////////////////// -//~ rjf: UDT Conversion Tasks - -ASYNC_WORK_DEF(p2r_udt_convert_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - P2R_UDTConvertIn *in = (P2R_UDTConvertIn *)input; -#define p2r_type_ptr_from_itype(itype) ((in->itype_type_ptrs && (itype) < in->tpi_leaf->itype_opl) ? (in->itype_type_ptrs[(in->itype_fwd_map[(itype)] ? in->itype_fwd_map[(itype)] : (itype))]) : 0) - RDIM_UDTChunkList *udts = push_array(arena, RDIM_UDTChunkList, 1); - RDI_U64 udts_chunk_cap = 1024; - ProfScope("convert UDT info") - { - for(CV_TypeId itype = in->itype_first; itype < in->itype_opl; itype += 1) - { - //- rjf: skip basics - if(itype < in->tpi_leaf->itype_first) { continue; } - - //- rjf: grab type for this itype - skip if empty - RDIM_Type *dst_type = in->itype_type_ptrs[itype]; - if(dst_type == 0) { continue; } - - //- rjf: unpack itype leaf range - skip if out-of-range - CV_RecRange *range = &in->tpi_leaf->leaf_ranges.ranges[itype-in->tpi_leaf->itype_first]; - CV_LeafKind kind = range->hdr.kind; - U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); - U8 *itype_leaf_first = in->tpi_leaf->data.str + range->off+2; - U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; - if(range->off+range->hdr.size > in->tpi_leaf->data.size || - range->off+2+header_struct_size > in->tpi_leaf->data.size || - range->hdr.size < 2) - { - continue; - } - - //- rjf: build UDT - CV_TypeId field_itype = 0; - switch(kind) - { - default:{}break; - - //////////////////////// - //- rjf: structs/unions/classes -> equip members - // - case CV_LeafKind_CLASS: - case CV_LeafKind_STRUCTURE: - { - CV_LeafStruct *lf = (CV_LeafStruct *)itype_leaf_first; - if(lf->props & CV_TypeProp_FwdRef) - { - break; - } - field_itype = lf->field_itype; - }goto equip_members; - case CV_LeafKind_UNION: - { - CV_LeafUnion *lf = (CV_LeafUnion *)itype_leaf_first; - if(lf->props & CV_TypeProp_FwdRef) - { - break; - } - field_itype = lf->field_itype; - }goto equip_members; - case CV_LeafKind_CLASS2: - case CV_LeafKind_STRUCT2: - { - CV_LeafStruct2 *lf = (CV_LeafStruct2 *)itype_leaf_first; - if(lf->props & CV_TypeProp_FwdRef) - { - break; - } - field_itype = lf->field_itype; - }goto equip_members; - equip_members: - { - Temp scratch = scratch_begin(&arena, 1); - - //- rjf: grab UDT info - RDIM_UDT *dst_udt = dst_type->udt; - if(dst_udt == 0) - { - dst_udt = dst_type->udt = rdim_udt_chunk_list_push(arena, udts, udts_chunk_cap); - dst_udt->self_type = dst_type; - } - - //- rjf: gather all fields - typedef struct FieldListTask FieldListTask; - struct FieldListTask - { - FieldListTask *next; - CV_TypeId itype; - }; - FieldListTask start_fl_task = {0, field_itype}; - FieldListTask *fl_todo_stack = &start_fl_task; - FieldListTask *fl_done_stack = 0; - for(;fl_todo_stack != 0;) - { - //- rjf: take & unpack task - FieldListTask *fl_task = fl_todo_stack; - SLLStackPop(fl_todo_stack); - SLLStackPush(fl_done_stack, fl_task); - CV_TypeId field_list_itype = fl_task->itype; - - //- rjf: skip bad itypes - if(field_list_itype < in->tpi_leaf->itype_first || in->tpi_leaf->itype_opl <= field_list_itype) - { - continue; - } - - //- rjf: field list itype -> range - CV_RecRange *range = &in->tpi_leaf->leaf_ranges.ranges[field_list_itype-in->tpi_leaf->itype_first]; - - //- rjf: skip bad headers - if(range->off+range->hdr.size > in->tpi_leaf->data.size || - range->hdr.size < 2 || - range->hdr.kind != CV_LeafKind_FIELDLIST) - { - continue; - } - - //- rjf: loop over all fields - { - U8 *field_list_first = in->tpi_leaf->data.str+range->off+2; - U8 *field_list_opl = field_list_first+range->hdr.size-2; - for(U8 *read_ptr = field_list_first, *next_read_ptr = field_list_opl; - read_ptr < field_list_opl; - read_ptr = next_read_ptr) - { - // rjf: unpack field - CV_LeafKind field_kind = *(CV_LeafKind *)read_ptr; - U64 field_leaf_header_size = cv_header_struct_size_from_leaf_kind(field_kind); - U8 *field_leaf_first = read_ptr+2; - U8 *field_leaf_opl = field_list_opl; - next_read_ptr = field_leaf_opl; - - // rjf: skip out-of-bounds fields - if(field_leaf_first+field_leaf_header_size > field_list_opl) - { - continue; - } - - // rjf: process field - switch(field_kind) - { - //- rjf: unhandled/invalid cases - default: - { - // TODO(rjf): log - }break; + // rjf: grab checksums sub-section + CV_C13SubSectionNode *file_chksms = src_unit_c13->file_chksms_sub_section; - //- rjf: INDEX - case CV_LeafKind_INDEX: + // rjf: gathered lines + typedef struct LineChunk LineChunk; + struct LineChunk { - // rjf: unpack leaf - CV_LeafIndex *lf = (CV_LeafIndex *)field_leaf_first; - CV_TypeId new_itype = lf->itype; - - // rjf: bump next read pointer past header - next_read_ptr = (U8 *)(lf+1); - - // rjf: determine if index itype is new - B32 is_new = 1; - for(FieldListTask *t = fl_done_stack; t != 0; t = t->next) + LineChunk *next; + U64 cap; + U64 count; + U64 *voffs; // [line_count + 1] (sorted) + U32 *line_nums; // [line_count] + U16 *col_nums; // [2*line_count] + }; + LineChunk *first_line_chunk = 0; + LineChunk *last_line_chunk = 0; + U64 total_line_chunk_line_count = 0; + U32 last_file_off = max_U32; + U32 curr_file_off = max_U32; + RDIM_LineTable* line_table = 0; + + CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(inlinee_lines_parsed->file_off, inlinee_lines_parsed->first_source_ln, base_voff); + for(;;) + { + // rjf: step & update + CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitFile) { - if(t->itype == new_itype) + last_file_off = curr_file_off; + curr_file_off = step.file_off; + } + if(step.flags == 0 && total_line_chunk_line_count > 0) + { + last_file_off = curr_file_off; + curr_file_off = max_U32; + } + + // rjf: file updated -> push line chunks gathered for this file + if(last_file_off != max_U32 && last_file_off != curr_file_off) + { + String8 seq_file_name = {0}; + if(last_file_off + sizeof(CV_C13Checksum) <= file_chksms->size) { - is_new = 0; - break; + CV_C13Checksum *checksum = (CV_C13Checksum*)(src_unit_c13->data.str + file_chksms->off + last_file_off); + U32 name_off = checksum->name_off; + seq_file_name = pdb_strtbl_string_from_off(strtbl, name_off); } + + // rjf: file name -> sanitized file path + String8 file_path = seq_file_name; + String8 file_path_sanitized = str8_copy(scratch.arena, str8_skip_chop_whitespace(file_path)); + { + PathStyle file_path_sanitized_style = path_style_from_str8(file_path_sanitized); + String8List file_path_sanitized_parts = str8_split_path(scratch.arena, file_path_sanitized); + if(file_path_sanitized_style == PathStyle_Relative) + { + String8List obj_folder_path_parts = str8_split_path(scratch.arena, obj_folder_path); + str8_list_concat_in_place(&obj_folder_path_parts, &file_path_sanitized_parts); + file_path_sanitized_parts = obj_folder_path_parts; + file_path_sanitized_style = path_style_from_str8(obj_folder_path); + } + str8_path_list_resolve_dots_in_place(&file_path_sanitized_parts, file_path_sanitized_style); + file_path_sanitized = str8_path_list_join_by_style(scratch.arena, &file_path_sanitized_parts, file_path_sanitized_style); + } + + // rjf: sanitized file path -> source file node + U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); + U64 src_file_slot = file_path_sanitized_hash%src_file_map.slots_count; + P2R_SrcFileNode *src_file_node = 0; + for(P2R_SrcFileNode *n = src_file_map.slots[src_file_slot]; n != 0; n = n->next) + { + if(str8_match(n->src_file->path, file_path_sanitized, 0)) + { + src_file_node = n; + break; + } + } + + // rjf: gather all lines + RDI_U64 *voffs = 0; + RDI_U32 *line_nums = 0; + RDI_U64 line_count = 0; + if(src_file_node != 0) + { + voffs = push_array_no_zero(arena, RDI_U64, total_line_chunk_line_count+1); + line_nums = push_array_no_zero(arena, RDI_U32, total_line_chunk_line_count); + line_count = total_line_chunk_line_count; + U64 dst_idx = 0; + for(LineChunk *chunk = first_line_chunk; chunk != 0; chunk = chunk->next) + { + MemoryCopy(voffs+dst_idx, chunk->voffs, sizeof(U64)*(chunk->count+1)); + MemoryCopy(line_nums+dst_idx, chunk->line_nums, sizeof(U32)*chunk->count); + dst_idx += chunk->count; + } + } + + // rjf: push + if(line_count != 0) + { + if(line_table == 0) + { + line_table = rdim_line_table_chunk_list_push(arena, dst_line_tables, 256); + if(p2r_shared->units_first_inline_site_line_tables[unit_idx] == 0) + { + p2r_shared->units_first_inline_site_line_tables[unit_idx] = line_table; + } + } + rdim_line_table_push_sequence(arena, dst_line_tables, line_table, src_file_node->src_file, voffs, line_nums, 0, line_count); + } + + // rjf: clear line chunks for subsequent sequences + first_line_chunk = last_line_chunk = 0; + total_line_chunk_line_count = 0; } - // rjf: if new -> push task to follow new itype - if(is_new) + // rjf: new line -> emit to chunk + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitLine) { - FieldListTask *new_task = push_array(scratch.arena, FieldListTask, 1); - SLLStackPush(fl_todo_stack, new_task); - new_task->itype = new_itype; + LineChunk *chunk = last_line_chunk; + if(chunk == 0 || chunk->count+1 >= chunk->cap) + { + chunk = push_array(scratch.arena, LineChunk, 1); + SLLQueuePush(first_line_chunk, last_line_chunk, chunk); + chunk->cap = 8; + chunk->voffs = push_array_no_zero(scratch.arena, U64, chunk->cap); + chunk->line_nums = push_array_no_zero(scratch.arena, U32, chunk->cap); + } + chunk->voffs[chunk->count] = step.line_voff; + chunk->voffs[chunk->count+1] = step.line_voff_end; + chunk->line_nums[chunk->count] = step.ln; + chunk->count += 1; + total_line_chunk_line_count += 1; } - }break; - - //- rjf: MEMBER - case CV_LeafKind_MEMBER: - { - // TODO(rjf): log on bad offset - // rjf: unpack leaf - CV_LeafMember *lf = (CV_LeafMember *)field_leaf_first; - U8 *offset_ptr = (U8 *)(lf+1); - CV_NumericParsed offset = cv_numeric_from_data_range(offset_ptr, field_leaf_opl); - U64 offset64 = cv_u64_from_numeric(&offset); - U8 *name_ptr = offset_ptr + offset.encoded_size; - String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); - - // rjf: bump next read pointer past variable length parts - next_read_ptr = name.str+name.size+1; - - // rjf: emit member - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_DataField; - mem->name = name; - mem->type = p2r_type_ptr_from_itype(lf->itype); - mem->off = (U32)offset64; - }break; - - //- rjf: STMEMBER - case CV_LeafKind_STMEMBER: - { - // TODO(rjf): handle attribs - - // rjf: unpack leaf - CV_LeafStMember *lf = (CV_LeafStMember *)field_leaf_first; - U8 *name_ptr = (U8 *)(lf+1); - String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); - - // rjf: bump next read pointer past variable length parts - next_read_ptr = name.str+name.size+1; - - // rjf: emit member - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_StaticData; - mem->name = name; - mem->type = p2r_type_ptr_from_itype(lf->itype); - }break; - - //- rjf: METHOD - case CV_LeafKind_METHOD: - { - // rjf: unpack leaf - CV_LeafMethod *lf = (CV_LeafMethod *)field_leaf_first; - U8 *name_ptr = (U8 *)(lf+1); - String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); - - // rjf: bump next read pointer past variable length parts - next_read_ptr = name.str+name.size+1; - - //- rjf: method list itype -> range - CV_RecRange *method_list_range = &in->tpi_leaf->leaf_ranges.ranges[lf->list_itype-in->tpi_leaf->itype_first]; - - //- rjf: skip bad method lists - if(method_list_range->off+method_list_range->hdr.size > in->tpi_leaf->data.size || - method_list_range->hdr.size < 2 || - method_list_range->hdr.kind != CV_LeafKind_METHODLIST) + // rjf: no more flags -> done + if(step.flags == 0) { break; } - - //- rjf: loop through all methods & emit members - U8 *method_list_first = in->tpi_leaf->data.str + method_list_range->off + 2; - U8 *method_list_opl = method_list_first + method_list_range->hdr.size-2; - for(U8 *method_read_ptr = method_list_first, *next_method_read_ptr = method_list_opl; - method_read_ptr < method_list_opl; - method_read_ptr = next_method_read_ptr) - { - CV_LeafMethodListMember *method = (CV_LeafMethodListMember*)method_read_ptr; - CV_MethodProp prop = CV_FieldAttribs_Extract_MethodProp(method->attribs); - RDIM_Type *method_type = p2r_type_ptr_from_itype(method->itype); - next_method_read_ptr = (U8 *)(method+1); - - // TODO(allen): PROBLEM - // We only get offsets for virtual functions (the "vbaseoff") from - // "Intro" and "PureIntro". In C++ inheritance, when we have a chain - // of inheritance (let's just talk single inheritance for now) the - // first class in the chain that introduces a new virtual function - // has this "Intro" method. If a later class in the chain redefines - // the virtual function it only has a "Virtual" method which does - // not update the offset. There is a "Virtual" and "PureVirtual" - // variant of "Virtual". The "Pure" in either case means there - // is no concrete procedure. When there is no "Pure" the method - // should have a corresponding procedure symbol id. - // - // The issue is we will want to mark all of our virtual methods as - // virtual and give them an offset, but that means we have to do - // some extra figuring to propogate offsets from "Intro" methods - // to "Virtual" methods in inheritance trees. That is - IF we want - // to start preserving the offsets of virtuals. There is room in - // the method struct to make this work, but for now I've just - // decided to drop this information. It is not urgently useful to - // us and greatly complicates matters. - - // rjf: read vbaseoff - U32 vbaseoff = 0; - if(prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro) - { - if(next_method_read_ptr+4 <= method_list_opl) - { - vbaseoff = *(U32 *)next_method_read_ptr; - } - next_method_read_ptr += 4; - } - - // rjf: emit method - switch(prop) - { - default: - { - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_Method; - mem->name = name; - mem->type = method_type; - }break; - case CV_MethodProp_Static: - { - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_StaticMethod; - mem->name = name; - mem->type = method_type; - }break; - case CV_MethodProp_Virtual: - case CV_MethodProp_PureVirtual: - case CV_MethodProp_Intro: - case CV_MethodProp_PureIntro: - { - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_VirtualMethod; - mem->name = name; - mem->type = method_type; - }break; - } - } - - }break; - - //- rjf: ONEMETHOD - case CV_LeafKind_ONEMETHOD: - { - // TODO(rjf): handle attribs - - // rjf: unpack leaf - CV_LeafOneMethod *lf = (CV_LeafOneMethod *)field_leaf_first; - CV_MethodProp prop = CV_FieldAttribs_Extract_MethodProp(lf->attribs); - U8 *vbaseoff_ptr = (U8 *)(lf+1); - U8 *vbaseoff_opl_ptr = vbaseoff_ptr; - U32 vbaseoff = 0; - if(prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro) - { - vbaseoff = *(U32 *)(vbaseoff_ptr); - vbaseoff_opl_ptr += sizeof(U32); - } - U8 *name_ptr = vbaseoff_opl_ptr; - String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); - RDIM_Type *method_type = p2r_type_ptr_from_itype(lf->itype); - - // rjf: bump next read pointer past variable length parts - next_read_ptr = name.str+name.size+1; - - // rjf: emit method - switch(prop) - { - default: - { - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_Method; - mem->name = name; - mem->type = method_type; - }break; - - case CV_MethodProp_Static: - { - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_StaticMethod; - mem->name = name; - mem->type = method_type; - }break; - - case CV_MethodProp_Virtual: - case CV_MethodProp_PureVirtual: - case CV_MethodProp_Intro: - case CV_MethodProp_PureIntro: - { - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_VirtualMethod; - mem->name = name; - mem->type = method_type; - }break; - } - }break; - - //- rjf: NESTTYPE - case CV_LeafKind_NESTTYPE: - { - // rjf: unpack leaf - CV_LeafNestType *lf = (CV_LeafNestType *)field_leaf_first; - U8 *name_ptr = (U8 *)(lf+1); - String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); - - // rjf: bump next read pointer past variable length parts - next_read_ptr = name.str+name.size+1; - - // rjf: emit member - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_NestedType; - mem->name = name; - mem->type = p2r_type_ptr_from_itype(lf->itype); - }break; - - //- rjf: NESTTYPEEX - case CV_LeafKind_NESTTYPEEX: - { - // TODO(rjf): handle attribs - - // rjf: unpack leaf - CV_LeafNestTypeEx *lf = (CV_LeafNestTypeEx *)field_leaf_first; - U8 *name_ptr = (U8 *)(lf+1); - String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); - - // rjf: bump next read pointer past variable length parts - next_read_ptr = name.str+name.size+1; - - // rjf: emit member - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_NestedType; - mem->name = name; - mem->type = p2r_type_ptr_from_itype(lf->itype); - }break; - - //- rjf: BCLASS - case CV_LeafKind_BCLASS: - { - // TODO(rjf): log on bad offset - - // rjf: unpack leaf - CV_LeafBClass *lf = (CV_LeafBClass *)field_leaf_first; - U8 *offset_ptr = (U8 *)(lf+1); - CV_NumericParsed offset = cv_numeric_from_data_range(offset_ptr, field_leaf_opl); - U64 offset64 = cv_u64_from_numeric(&offset); - - // rjf: bump next read pointer past variable length parts - next_read_ptr = offset_ptr+offset.encoded_size; - - // rjf: emit member - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_Base; - mem->type = p2r_type_ptr_from_itype(lf->itype); - mem->off = (U32)offset64; - }break; - - //- rjf: VBCLASS/IVBCLASS - case CV_LeafKind_VBCLASS: - case CV_LeafKind_IVBCLASS: - { - // TODO(rjf): log on bad offsets - // TODO(rjf): handle attribs - // TODO(rjf): offsets? - - // rjf: unpack leaf - CV_LeafVBClass *lf = (CV_LeafVBClass *)field_leaf_first; - U8 *num1_ptr = (U8 *)(lf+1); - CV_NumericParsed num1 = cv_numeric_from_data_range(num1_ptr, field_leaf_opl); - U8 *num2_ptr = num1_ptr + num1.encoded_size; - CV_NumericParsed num2 = cv_numeric_from_data_range(num2_ptr, field_leaf_opl); - - // rjf: bump next read pointer past header - next_read_ptr = (U8 *)(lf+1); - - // rjf: emit member - RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); - mem->kind = RDI_MemberKind_VirtualBase; - mem->type = p2r_type_ptr_from_itype(lf->itype); - }break; - - //- rjf: VFUNCTAB - case CV_LeafKind_VFUNCTAB: - { - CV_LeafVFuncTab *lf = (CV_LeafVFuncTab *)field_leaf_first; - - // rjf: bump next read pointer past header - next_read_ptr = (U8 *)(lf+1); - - // NOTE(rjf): currently no-op this case - (void)lf; - }break; + } } - - // rjf: align-up next field - next_read_ptr = (U8 *)AlignPow2((U64)next_read_ptr, 4); - } - } - } - - scratch_end(scratch); - }break; - - //////////////////////// - //- rjf: enums -> equip enumerates - // - case CV_LeafKind_ENUM: - { - CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; - if(lf->props & CV_TypeProp_FwdRef) - { - break; - } - field_itype = lf->field_itype; - }goto equip_enum_vals; - equip_enum_vals:; - { - Temp scratch = scratch_begin(&arena, 1); - - //- rjf: grab UDT info - RDIM_UDT *dst_udt = dst_type->udt; - if(dst_udt == 0) - { - dst_udt = dst_type->udt = rdim_udt_chunk_list_push(arena, udts, udts_chunk_cap); - dst_udt->self_type = dst_type; - } - - //- rjf: gather all fields - typedef struct FieldListTask FieldListTask; - struct FieldListTask - { - FieldListTask *next; - CV_TypeId itype; - }; - FieldListTask start_fl_task = {0, field_itype}; - FieldListTask *fl_todo_stack = &start_fl_task; - FieldListTask *fl_done_stack = 0; - for(;fl_todo_stack != 0;) - { - //- rjf: take & unpack task - FieldListTask *fl_task = fl_todo_stack; - SLLStackPop(fl_todo_stack); - SLLStackPush(fl_done_stack, fl_task); - CV_TypeId field_list_itype = fl_task->itype; - - //- rjf: skip bad itypes - if(field_list_itype < in->tpi_leaf->itype_first || in->tpi_leaf->itype_opl <= field_list_itype) - { - continue; - } - - //- rjf: field list itype -> range - CV_RecRange *range = &in->tpi_leaf->leaf_ranges.ranges[field_list_itype-in->tpi_leaf->itype_first]; - - //- rjf: skip bad headers - if(range->off+range->hdr.size > in->tpi_leaf->data.size || - range->hdr.size < 2 || - range->hdr.kind != CV_LeafKind_FIELDLIST) - { - continue; - } - - //- rjf: loop over all fields - { - U8 *field_list_first = in->tpi_leaf->data.str+range->off+2; - U8 *field_list_opl = field_list_first+range->hdr.size-2; - for(U8 *read_ptr = field_list_first, *next_read_ptr = field_list_opl; - read_ptr < field_list_opl; - read_ptr = next_read_ptr) - { - // rjf: unpack field - CV_LeafKind field_kind = *(CV_LeafKind *)read_ptr; - U64 field_leaf_header_size = cv_header_struct_size_from_leaf_kind(field_kind); - U8 *field_leaf_first = read_ptr+2; - U8 *field_leaf_opl = field_leaf_first+range->hdr.size-2; - next_read_ptr = field_leaf_opl; - - // rjf: skip out-of-bounds fields - if(field_leaf_first+field_leaf_header_size > field_list_opl) - { - continue; - } - - // rjf: process field - switch(field_kind) - { - //- rjf: unhandled/invalid cases - default: - { - // TODO(rjf): log - }break; - - //- rjf: INDEX - case CV_LeafKind_INDEX: - { - // rjf: unpack leaf - CV_LeafIndex *lf = (CV_LeafIndex *)field_leaf_first; - CV_TypeId new_itype = lf->itype; - - // rjf: determine if index itype is new - B32 is_new = 1; - for(FieldListTask *t = fl_done_stack; t != 0; t = t->next) - { - if(t->itype == new_itype) - { - is_new = 0; - break; - } - } - - // rjf: if new -> push task to follow new itype - if(is_new) - { - FieldListTask *new_task = push_array(scratch.arena, FieldListTask, 1); - SLLStackPush(fl_todo_stack, new_task); - new_task->itype = new_itype; - } - }break; - - //- rjf: ENUMERATE - case CV_LeafKind_ENUMERATE: - { - // TODO(rjf): attribs - - // rjf: unpack leaf - CV_LeafEnumerate *lf = (CV_LeafEnumerate *)field_leaf_first; - U8 *val_ptr = (U8 *)(lf+1); - CV_NumericParsed val = cv_numeric_from_data_range(val_ptr, field_leaf_opl); - U64 val64 = cv_u64_from_numeric(&val); - U8 *name_ptr = val_ptr + val.encoded_size; - String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); - - // rjf: bump next read pointer past variable length parts - next_read_ptr = name.str+name.size+1; - - // rjf: emit member - RDIM_UDTEnumVal *enum_val = rdim_udt_push_enum_val(arena, udts, dst_udt); - enum_val->name = name; - enum_val->val = val64; - }break; - } - - // rjf: align-up next field - next_read_ptr = (U8 *)AlignPow2((U64)next_read_ptr, 4); - } - } - } - - scratch_end(scratch); - }break; - } - } - } -#undef p2r_type_ptr_from_itype - ProfEnd(); - return udts; -} - -//////////////////////////////// -//~ rjf: Symbol Stream Conversion Path & Thread - -ASYNC_WORK_DEF(p2r_symbol_stream_convert_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(p2r_async_root); - Temp scratch = scratch_begin(&arena, 1); - P2R_SymbolStreamConvertIn *in = (P2R_SymbolStreamConvertIn *)input; -#define p2r_type_ptr_from_itype(itype) ((in->itype_type_ptrs && (itype) < in->tpi_leaf->itype_opl) ? (in->itype_type_ptrs[(in->itype_fwd_map[(itype)] ? in->itype_fwd_map[(itype)] : (itype))]) : 0) - - ////////////////////////// - //- rjf: set up outputs for this sym stream - // - U64 sym_procedures_chunk_cap = 1024; - U64 sym_global_variables_chunk_cap = 1024; - U64 sym_thread_variables_chunk_cap = 1024; - U64 sym_constants_chunk_cap = 1024; - U64 sym_scopes_chunk_cap = 1024; - U64 sym_inline_sites_chunk_cap = 1024; - RDIM_SymbolChunkList sym_procedures = {0}; - RDIM_SymbolChunkList sym_global_variables = {0}; - RDIM_SymbolChunkList sym_thread_variables = {0}; - RDIM_SymbolChunkList sym_constants = {0}; - RDIM_ScopeChunkList sym_scopes = {0}; - RDIM_InlineSiteChunkList sym_inline_sites = {0}; - RDIM_TypeChunkList typedefs = {0}; - - ////////////////////////// - //- rjf: symbols pass 1: produce procedure frame info map (procedure -> frame info) - // - U64 procedure_frameprocs_count = 0; - U64 procedure_frameprocs_cap = (in->sym_ranges_opl - in->sym_ranges_first); - CV_SymFrameproc **procedure_frameprocs = push_array_no_zero(scratch.arena, CV_SymFrameproc *, procedure_frameprocs_cap); - ProfScope("symbols pass 1: produce procedure frame info map (procedure -> frame info)") - { - U64 procedure_num = 0; - CV_RecRange *rec_ranges_first = in->sym->sym_ranges.ranges + in->sym_ranges_first; - CV_RecRange *rec_ranges_opl = in->sym->sym_ranges.ranges + in->sym_ranges_opl; - for(CV_RecRange *rec_range = rec_ranges_first; - rec_range < rec_ranges_opl; - rec_range += 1) - { - //- rjf: rec range -> symbol info range - U64 sym_off_first = rec_range->off + 2; - U64 sym_off_opl = rec_range->off + rec_range->hdr.size; - - //- rjf: skip invalid ranges - if(sym_off_opl > in->sym->data.size || sym_off_first > in->sym->data.size || sym_off_first > sym_off_opl) - { - continue; - } - - //- rjf: unpack symbol info - CV_SymKind kind = rec_range->hdr.kind; - U64 sym_header_struct_size = cv_header_struct_size_from_sym_kind(kind); - void *sym_header_struct_base = in->sym->data.str + sym_off_first; - - //- rjf: skip bad sizes - if(sym_off_first + sym_header_struct_size > sym_off_opl) - { - continue; - } - - //- rjf: consume symbol based on kind - switch(kind) - { - default:{}break; - - //- rjf: FRAMEPROC - case CV_SymKind_FRAMEPROC: - { - if(procedure_num == 0) { break; } - if(procedure_num > procedure_frameprocs_cap) { break; } - CV_SymFrameproc *frameproc = (CV_SymFrameproc*)sym_header_struct_base; - procedure_frameprocs[procedure_num-1] = frameproc; - procedure_frameprocs_count = Max(procedure_frameprocs_count, procedure_num); - }break; - - //- rjf: LPROC32/GPROC32 - case CV_SymKind_LPROC32: - case CV_SymKind_GPROC32: - { - procedure_num += 1; - }break; - } - } - U64 scratch_overkill = sizeof(procedure_frameprocs[0])*(procedure_frameprocs_cap-procedure_frameprocs_count); - arena_pop(scratch.arena, scratch_overkill); - } - - ////////////////////////// - //- rjf: symbols pass 2: construct all symbols, given procedure frame info map - // - ProfScope("symbols pass 2: construct all symbols, given procedure frame info map") - { - RDIM_LocationSet *defrange_target = 0; - B32 defrange_target_is_param = 0; - U64 procedure_num = 0; - U64 procedure_base_voff = 0; - CV_RecRange *rec_ranges_first = in->sym->sym_ranges.ranges + in->sym_ranges_first; - CV_RecRange *rec_ranges_opl = in->sym->sym_ranges.ranges + in->sym_ranges_opl; - typedef struct P2R_ScopeNode P2R_ScopeNode; - struct P2R_ScopeNode - { - P2R_ScopeNode *next; - RDIM_Scope *scope; - }; - P2R_ScopeNode *top_scope_node = 0; - P2R_ScopeNode *free_scope_node = 0; - RDIM_LineTable *inline_site_line_table = in->first_inline_site_line_table; - for(CV_RecRange *rec_range = rec_ranges_first; - rec_range < rec_ranges_opl; - rec_range += 1) - { - //- rjf: rec range -> symbol info range - U64 sym_off_first = rec_range->off + 2; - U64 sym_off_opl = rec_range->off + rec_range->hdr.size; - - //- rjf: skip invalid ranges - if(sym_off_opl > in->sym->data.size || sym_off_first > in->sym->data.size || sym_off_first > sym_off_opl) - { - continue; - } - - //- rjf: unpack symbol info - CV_SymKind kind = rec_range->hdr.kind; - U64 sym_header_struct_size = cv_header_struct_size_from_sym_kind(kind); - void *sym_header_struct_base = in->sym->data.str + sym_off_first; - void *sym_data_opl = in->sym->data.str + sym_off_opl; - - //- rjf: skip bad sizes - if(sym_off_first + sym_header_struct_size > sym_off_opl) - { - continue; - } - - //- rjf: consume symbol based on kind - switch(kind) - { - default:{}break; - - //- rjf: END - case CV_SymKind_END: - { - P2R_ScopeNode *n = top_scope_node; - if(n != 0) - { - SLLStackPop(top_scope_node); - SLLStackPush(free_scope_node, n); - } - defrange_target = 0; - defrange_target_is_param = 0; - }break; - - //- rjf: BLOCK32 - case CV_SymKind_BLOCK32: - { - // rjf: unpack sym - CV_SymBlock32 *block32 = (CV_SymBlock32 *)sym_header_struct_base; - - // rjf: build scope, insert into current parent scope - RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, &sym_scopes, sym_scopes_chunk_cap); - { - if(top_scope_node == 0) - { - // TODO(rjf): log - } - if(top_scope_node != 0) - { - RDIM_Scope *top_scope = top_scope_node->scope; - SLLQueuePush_N(top_scope->first_child, top_scope->last_child, scope, next_sibling); - scope->parent_scope = top_scope; - scope->symbol = top_scope->symbol; - } - COFF_SectionHeader *section = (0 < block32->sec && block32->sec <= in->coff_sections.count) ? &in->coff_sections.v[block32->sec-1] : 0; - if(section != 0) - { - U64 voff_first = section->voff + block32->off; - U64 voff_last = voff_first + block32->len; - RDIM_Rng1U64 voff_range = {voff_first, voff_last}; - rdim_scope_push_voff_range(arena, &sym_scopes, scope, voff_range); - } - } - - // rjf: push this scope to scope stack - { - P2R_ScopeNode *node = free_scope_node; - if(node != 0) { SLLStackPop(free_scope_node); } - else { node = push_array_no_zero(scratch.arena, P2R_ScopeNode, 1); } - node->scope = scope; - SLLStackPush(top_scope_node, node); - } - }break; - - //- rjf: LDATA32/GDATA32 - case CV_SymKind_LDATA32: - case CV_SymKind_GDATA32: - { - // rjf: unpack sym - CV_SymData32 *data32 = (CV_SymData32 *)sym_header_struct_base; - String8 name = str8_cstring_capped(data32+1, sym_data_opl); - COFF_SectionHeader *section = (0 < data32->sec && data32->sec <= in->coff_sections.count) ? &in->coff_sections.v[data32->sec-1] : 0; - U64 voff = (section ? section->voff : 0) + data32->off; - - // rjf: determine if this is an exact duplicate global - // - // PDB likes to have duplicates of these spread across different - // symbol streams so we deduplicate across the entire translation - // context. - // - B32 is_duplicate = 0; - { - // TODO(rjf): @important global symbol dedup - } - - // rjf: is not duplicate -> push new global - if(!is_duplicate) - { - // rjf: unpack global variable's type - RDIM_Type *type = p2r_type_ptr_from_itype(data32->itype); - - // rjf: unpack global's container type - RDIM_Type *container_type = 0; - U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); - if(container_name_opl > 2) - { - String8 container_name = str8(name.str, container_name_opl - 2); - CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(in->tpi_hash, in->tpi_leaf, container_name, 0); - container_type = p2r_type_ptr_from_itype(cv_type_id); - } - - // rjf: unpack global's container symbol - RDIM_Symbol *container_symbol = 0; - if(container_type == 0 && top_scope_node != 0) - { - container_symbol = top_scope_node->scope->symbol; - } - - // rjf: build symbol - RDIM_Symbol *symbol = rdim_symbol_chunk_list_push(arena, &sym_global_variables, sym_global_variables_chunk_cap); - symbol->is_extern = (kind == CV_SymKind_GDATA32); - symbol->name = name; - symbol->type = type; - symbol->offset = voff; - symbol->container_symbol = container_symbol; - symbol->container_type = container_type; - } - }break; - - //- rjf: UDT (typedefs) - case CV_SymKind_UDT: - if(in->parsing_global_stream && top_scope_node == 0) - { - CV_SymUDT *udt = (CV_SymUDT *)sym_header_struct_base; - String8 name = str8_cstring_capped(udt+1, sym_data_opl); - RDIM_Type *type = rdim_type_chunk_list_push(arena, &typedefs, 4096); - type->kind = RDI_TypeKind_Alias; - type->name = name; - type->direct_type = p2r_type_ptr_from_itype(udt->itype); - if(type->direct_type != 0) - { - type->byte_size = type->direct_type->byte_size; - } - }break; - - //- rjf: LPROC32/GPROC32 - case CV_SymKind_LPROC32: - case CV_SymKind_GPROC32: - { - // rjf: unpack sym - CV_SymProc32 *proc32 = (CV_SymProc32 *)sym_header_struct_base; - String8 name = str8_cstring_capped(proc32+1, sym_data_opl); - RDIM_Type *type = p2r_type_ptr_from_itype(proc32->itype); - - // rjf: unpack proc's container type - RDIM_Type *container_type = 0; - U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); - if(container_name_opl > 2 && in->tpi_hash != 0 && in->tpi_leaf != 0) - { - String8 container_name = str8(name.str, container_name_opl - 2); - CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(in->tpi_hash, in->tpi_leaf, container_name, 0); - container_type = p2r_type_ptr_from_itype(cv_type_id); - } - - // rjf: unpack proc's container symbol - RDIM_Symbol *container_symbol = 0; - if(container_type == 0 && top_scope_node != 0) - { - container_symbol = top_scope_node->scope->symbol; - } - - // rjf: build procedure's root scope - // - // NOTE: even if there could be a containing scope at this point (which should be - // illegal in C/C++ but not necessarily in another language) we would not use - // it here because these scopes refer to the ranges of code that make up a - // procedure *not* the namespaces, so a procedure's root scope always has - // no parent. - RDIM_Scope *procedure_root_scope = rdim_scope_chunk_list_push(arena, &sym_scopes, sym_scopes_chunk_cap); - { - COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= in->coff_sections.count) ? &in->coff_sections.v[proc32->sec-1] : 0; - if(section != 0) - { - U64 voff_first = section->voff + proc32->off; - U64 voff_last = voff_first + proc32->len; - RDIM_Rng1U64 voff_range = {voff_first, voff_last}; - rdim_scope_push_voff_range(arena, &sym_scopes, procedure_root_scope, voff_range); - procedure_base_voff = voff_first; - } - } - - // rjf: root scope voff minimum range -> link name - String8 link_name = {0}; - if(procedure_root_scope->voff_ranges.min != 0) - { - U64 voff = procedure_root_scope->voff_ranges.min; - U64 hash = p2r_hash_from_voff(voff); - U64 bucket_idx = hash%in->link_name_map->buckets_count; - P2R_LinkNameNode *node = 0; - for(P2R_LinkNameNode *n = in->link_name_map->buckets[bucket_idx]; n != 0; n = n->next) - { - if(n->voff == voff) - { - link_name = n->name; - break; - } - } - } - - // rjf: build procedure symbol - RDIM_Symbol *procedure_symbol = rdim_symbol_chunk_list_push(arena, &sym_procedures, sym_procedures_chunk_cap); - procedure_symbol->is_extern = (kind == CV_SymKind_GPROC32); - procedure_symbol->name = name; - procedure_symbol->link_name = link_name; - procedure_symbol->type = type; - procedure_symbol->container_symbol = container_symbol; - procedure_symbol->container_type = container_type; - procedure_symbol->root_scope = procedure_root_scope; - - // rjf: fill root scope's symbol - procedure_root_scope->symbol = procedure_symbol; - - // rjf: push scope to scope stack - { - P2R_ScopeNode *node = free_scope_node; - if(node != 0) { SLLStackPop(free_scope_node); } - else { node = push_array_no_zero(scratch.arena, P2R_ScopeNode, 1); } - node->scope = procedure_root_scope; - SLLStackPush(top_scope_node, node); - } - - // rjf: increment procedure counter - procedure_num += 1; - }break; - - //- rjf: REGREL32 - case CV_SymKind_REGREL32: - { - // TODO(rjf): apparently some of the information here may end up being - // redundant with "better" information from CV_SymKind_LOCAL record. - // we don't currently handle this, but if those cases arise then it - // will obviously be better to prefer the better information from both - // records. - - // rjf: no containing scope? -> malformed data; locals cannot be produced - // outside of a containing scope - if(top_scope_node == 0) - { - break; - } - - // rjf: unpack sym - CV_SymRegrel32 *regrel32 = (CV_SymRegrel32 *)sym_header_struct_base; - String8 name = str8_cstring_capped(regrel32+1, sym_data_opl); - RDIM_Type *type = p2r_type_ptr_from_itype(regrel32->itype); - CV_Reg cv_reg = regrel32->reg; - U32 var_off = regrel32->reg_off; - - // rjf: determine if this is a parameter - RDI_LocalKind local_kind = RDI_LocalKind_Variable; - { - B32 is_stack_reg = 0; - switch(in->arch) - { - default:{}break; - case RDI_Arch_X86:{is_stack_reg = (cv_reg == CV_Regx86_ESP);}break; - case RDI_Arch_X64:{is_stack_reg = (cv_reg == CV_Regx64_RSP);}break; - } - if(is_stack_reg) - { - U32 frame_size = 0xFFFFFFFF; - if(procedure_num != 0 && procedure_frameprocs[procedure_num-1] != 0 && procedure_num <= procedure_frameprocs_count) - { - CV_SymFrameproc *frameproc = procedure_frameprocs[procedure_num-1]; - frame_size = frameproc->frame_size; - } - if(var_off > frame_size) - { - local_kind = RDI_LocalKind_Parameter; - } - } - } - - // TODO(rjf): is this correct? - // rjf: redirect type, if 0, and if outside frame, to the return type of the - // containing procedure - if(local_kind == RDI_LocalKind_Parameter && regrel32->itype == 0 && - top_scope_node->scope->symbol != 0 && - top_scope_node->scope->symbol->type != 0) - { - type = top_scope_node->scope->symbol->type->direct_type; - } - - // rjf: build local - RDIM_Scope *scope = top_scope_node->scope; - RDIM_Local *local = rdim_scope_push_local(arena, &sym_scopes, scope); - local->kind = local_kind; - local->name = name; - local->type = type; - - // rjf: add location info to local - if(type != 0) - { - // rjf: determine if we need an extra indirection to the value - B32 extra_indirection_to_value = 0; - switch(in->arch) - { - case RDI_Arch_X86: - { - extra_indirection_to_value = (local_kind == RDI_LocalKind_Parameter && (type->byte_size > 4 || !IsPow2OrZero(type->byte_size))); - }break; - case RDI_Arch_X64: - { - extra_indirection_to_value = (local_kind == RDI_LocalKind_Parameter && (type->byte_size > 8 || !IsPow2OrZero(type->byte_size))); }break; } - - // rjf: get raddbg register code - RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(in->arch, cv_reg); - // TODO(rjf): real byte_size & byte_pos from cv_reg goes here - U32 byte_size = 8; - U32 byte_pos = 0; - - // rjf: set location case - RDIM_Location *loc = p2r_location_from_addr_reg_off(arena, in->arch, reg_code, byte_size, byte_pos, (S64)(S32)var_off, extra_indirection_to_value); - RDIM_Rng1U64 voff_range = {0, max_U64}; - rdim_location_set_push_case(arena, &sym_scopes, &local->locset, voff_range, loc); } - }break; + } - //- rjf: LTHREAD32/GTHREAD32 - case CV_SymKind_LTHREAD32: - case CV_SymKind_GTHREAD32: - { - // rjf: unpack sym - CV_SymThread32 *thread32 = (CV_SymThread32 *)sym_header_struct_base; - String8 name = str8_cstring_capped(thread32+1, sym_data_opl); - U32 tls_off = thread32->tls_off; - RDIM_Type *type = p2r_type_ptr_from_itype(thread32->itype); - - // rjf: unpack thread variable's container type - RDIM_Type *container_type = 0; - U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); - if(container_name_opl > 2) - { - String8 container_name = str8(name.str, container_name_opl - 2); - CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(in->tpi_hash, in->tpi_leaf, container_name, 0); - container_type = p2r_type_ptr_from_itype(cv_type_id); - } - - // rjf: unpack thread variable's container symbol - RDIM_Symbol *container_symbol = 0; - if(container_type == 0 && top_scope_node != 0) - { - container_symbol = top_scope_node->scope->symbol; - } - - // rjf: build symbol - RDIM_Symbol *tvar = rdim_symbol_chunk_list_push(arena, &sym_thread_variables, sym_thread_variables_chunk_cap); - tvar->name = name; - tvar->type = type; - tvar->is_extern = (kind == CV_SymKind_GTHREAD32); - tvar->offset = tls_off; - tvar->container_type = container_type; - tvar->container_symbol = container_symbol; - }break; - - //- rjf: LOCAL - case CV_SymKind_LOCAL: - { - // rjf: no containing scope? -> malformed data; locals cannot be produced - // outside of a containing scope - if(top_scope_node == 0) - { - break; - } - - // rjf: unpack sym - CV_SymLocal *slocal = (CV_SymLocal *)sym_header_struct_base; - String8 name = str8_cstring_capped(slocal+1, sym_data_opl); - RDIM_Type *type = p2r_type_ptr_from_itype(slocal->itype); - - // rjf: determine if this symbol encodes the beginning of a global modification - B32 is_global_modification = 0; - if((slocal->flags & CV_LocalFlag_Global) || - (slocal->flags & CV_LocalFlag_Static)) - { - is_global_modification = 1; - } - - // rjf: is global modification -> emit global modification symbol - if(is_global_modification) - { - // TODO(rjf): add global modification symbols - defrange_target = 0; - defrange_target_is_param = 0; - } - - // rjf: is not a global modification -> emit a local variable - if(!is_global_modification) - { - // rjf: determine local kind - RDI_LocalKind local_kind = RDI_LocalKind_Variable; - if(slocal->flags & CV_LocalFlag_Param) - { - local_kind = RDI_LocalKind_Parameter; - } - - // rjf: build local - RDIM_Scope *scope = top_scope_node->scope; - RDIM_Local *local = rdim_scope_push_local(arena, &sym_scopes, scope); - local->kind = local_kind; - local->name = name; - local->type = type; - - // rjf: save defrange target, for subsequent defrange symbols - defrange_target = &local->locset; - defrange_target_is_param = (local_kind == RDI_LocalKind_Parameter); - } - }break; - - //- rjf: DEFRANGE_REGISTESR - case CV_SymKind_DEFRANGE_REGISTER: - { - // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing - // a local - break immediately - if(defrange_target == 0) - { - break; - } - - // rjf: unpack sym - CV_SymDefrangeRegister *defrange_register = (CV_SymDefrangeRegister*)sym_header_struct_base; - CV_Reg cv_reg = defrange_register->reg; - CV_LvarAddrRange *range = &defrange_register->range; - COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= in->coff_sections.count) ? &in->coff_sections.v[range->sec-1] : 0; - CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_register+1); - U64 gap_count = ((U8*)sym_data_opl - (U8*)gaps) / sizeof(*gaps); - RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(in->arch, cv_reg); - - // rjf: build location - RDIM_Location *location = rdim_push_location_val_reg(arena, reg_code); - - // rjf: emit locations over ranges - p2r_location_over_lvar_addr_range(arena, &sym_scopes, defrange_target, location, range, range_section, gaps, gap_count); - }break; - - //- rjf: DEFRANGE_FRAMEPOINTER_REL - case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL: - { - // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing - // a local - break immediately - if(defrange_target == 0) - { - break; - } - - // rjf: find current procedure's frameproc - CV_SymFrameproc *frameproc = 0; - if(procedure_num != 0 && procedure_num <= procedure_frameprocs_count && procedure_frameprocs[procedure_num-1] != 0) - { - frameproc = procedure_frameprocs[procedure_num-1]; - } - - // rjf: no current valid frameproc? -> somehow we got a to a framepointer-relative defrange - // without having an actually active procedure - break - if(frameproc == 0) - { - break; - } - - // rjf: unpack sym - CV_SymDefrangeFramepointerRel *defrange_fprel = (CV_SymDefrangeFramepointerRel*)sym_header_struct_base; - CV_LvarAddrRange *range = &defrange_fprel->range; - COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= in->coff_sections.count) ? &in->coff_sections.v[range->sec-1] : 0; - CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_fprel + 1); - U64 gap_count = ((U8*)sym_data_opl - (U8*)gaps) / sizeof(*gaps); - - // rjf: select frame pointer register - CV_EncodedFramePtrReg encoded_fp_reg = cv_pick_fp_encoding(frameproc, defrange_target_is_param); - RDI_RegCode fp_register_code = p2r_reg_code_from_arch_encoded_fp_reg(in->arch, encoded_fp_reg); - - // rjf: build location - B32 extra_indirection = 0; - U32 byte_size = rdi_addr_size_from_arch(in->arch); - U32 byte_pos = 0; - S64 var_off = (S64)defrange_fprel->off; - RDIM_Location *location = p2r_location_from_addr_reg_off(arena, in->arch, fp_register_code, byte_size, byte_pos, var_off, extra_indirection); - - // rjf: emit locations over ranges - p2r_location_over_lvar_addr_range(arena, &sym_scopes, defrange_target, location, range, range_section, gaps, gap_count); - }break; - - //- rjf: DEFRANGE_SUBFIELD_REGISTER - case CV_SymKind_DEFRANGE_SUBFIELD_REGISTER: - { - // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing - // a local - break immediately - if(defrange_target == 0) - { - break; - } - - // rjf: unpack sym - CV_SymDefrangeSubfieldRegister *defrange_subfield_register = (CV_SymDefrangeSubfieldRegister*)sym_header_struct_base; - CV_Reg cv_reg = defrange_subfield_register->reg; - CV_LvarAddrRange *range = &defrange_subfield_register->range; - COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= in->coff_sections.count) ? &in->coff_sections.v[range->sec-1] : 0; - CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_subfield_register + 1); - U64 gap_count = ((U8*)sym_data_opl - (U8*)gaps) / sizeof(*gaps); - RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(in->arch, cv_reg); - - // rjf: skip "subfield" location info - currently not supported - if(defrange_subfield_register->field_offset != 0) - { - break; - } - - // rjf: build location - RDIM_Location *location = rdim_push_location_val_reg(arena, reg_code); - - // rjf: emit locations over ranges - p2r_location_over_lvar_addr_range(arena, &sym_scopes, defrange_target, location, range, range_section, gaps, gap_count); - }break; - - //- rjf: DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE - case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: - { - // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing - // a local - break immediately - if(defrange_target == 0) - { - break; - } - - // rjf: find current procedure's frameproc - CV_SymFrameproc *frameproc = 0; - if(procedure_num != 0 && procedure_num <= procedure_frameprocs_count && procedure_frameprocs[procedure_num-1] != 0) - { - frameproc = procedure_frameprocs[procedure_num-1]; - } - - // rjf: no current valid frameproc? -> somehow we got a to a framepointer-relative defrange - // without having an actually active procedure - break - if(frameproc == 0) - { - break; - } - - // rjf: unpack sym - CV_SymDefrangeFramepointerRelFullScope *defrange_fprel_full_scope = (CV_SymDefrangeFramepointerRelFullScope*)sym_header_struct_base; - CV_EncodedFramePtrReg encoded_fp_reg = cv_pick_fp_encoding(frameproc, defrange_target_is_param); - RDI_RegCode fp_register_code = p2r_reg_code_from_arch_encoded_fp_reg(in->arch, encoded_fp_reg); - - // rjf: build location - B32 extra_indirection = 0; - U32 byte_size = rdi_addr_size_from_arch(in->arch); - U32 byte_pos = 0; - S64 var_off = (S64)defrange_fprel_full_scope->off; - RDIM_Location *location = p2r_location_from_addr_reg_off(arena, in->arch, fp_register_code, byte_size, byte_pos, var_off, extra_indirection); - - // rjf: emit location over ranges - RDIM_Rng1U64 voff_range = {0, max_U64}; - rdim_location_set_push_case(arena, &sym_scopes, defrange_target, voff_range, location); - }break; - - //- rjf: DEFRANGE_REGISTER_REL - case CV_SymKind_DEFRANGE_REGISTER_REL: - { - // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing - // a local - break immediately - if(defrange_target == 0) - { - break; - } - - // rjf: unpack sym - CV_SymDefrangeRegisterRel *defrange_register_rel = (CV_SymDefrangeRegisterRel*)sym_header_struct_base; - CV_Reg cv_reg = defrange_register_rel->reg; - RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(in->arch, cv_reg); - CV_LvarAddrRange *range = &defrange_register_rel->range; - COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= in->coff_sections.count) ? &in->coff_sections.v[range->sec-1] : 0; - CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_register_rel + 1); - U64 gap_count = ((U8*)sym_data_opl - (U8*)gaps) / sizeof(*gaps); - - // rjf: build location - // TODO(rjf): offset & size from cv_reg code - U32 byte_size = rdi_addr_size_from_arch(in->arch); - U32 byte_pos = 0; - B32 extra_indirection_to_value = 0; - S64 var_off = defrange_register_rel->reg_off; - RDIM_Location *location = p2r_location_from_addr_reg_off(arena, in->arch, reg_code, byte_size, byte_pos, var_off, extra_indirection_to_value); - - // rjf: emit locations over ranges - p2r_location_over_lvar_addr_range(arena, &sym_scopes, defrange_target, location, range, range_section, gaps, gap_count); - }break; - - //- rjf: FILESTATIC - case CV_SymKind_FILESTATIC: - { - CV_SymFileStatic *file_static = (CV_SymFileStatic*)sym_header_struct_base; - String8 name = str8_cstring_capped(file_static+1, sym_data_opl); - RDIM_Type *type = p2r_type_ptr_from_itype(file_static->itype); - // TODO(rjf): emit a global modifier symbol - defrange_target = 0; - defrange_target_is_param = 0; - }break; - - //- rjf: INLINESITE - case CV_SymKind_INLINESITE: - { - // rjf: unpack sym - CV_SymInlineSite *sym = (CV_SymInlineSite *)sym_header_struct_base; - String8 binary_annots = str8((U8 *)(sym+1), rec_range->hdr.size - sizeof(rec_range->hdr.kind) - sizeof(*sym)); - - // rjf: extract external info about inline site - String8 name = str8_zero(); - RDIM_Type *type = 0; - RDIM_Type *owner = 0; - if(in->ipi_leaf != 0 && in->ipi_leaf->itype_first <= sym->inlinee && sym->inlinee < in->ipi_leaf->itype_opl) - { - CV_RecRange rec_range = in->ipi_leaf->leaf_ranges.ranges[sym->inlinee - in->ipi_leaf->itype_first]; - String8 rec_data = str8_substr(in->ipi_leaf->data, rng_1u64(rec_range.off, rec_range.off + rec_range.hdr.size)); - void *raw_leaf = rec_data.str + sizeof(U16); - - // rjf: extract method inline info - if(rec_range.hdr.kind == CV_LeafKind_MFUNC_ID && - rec_range.hdr.size >= sizeof(CV_LeafMFuncId)) - { - CV_LeafMFuncId *mfunc_id = (CV_LeafMFuncId*)raw_leaf; - name = str8_cstring_capped(mfunc_id + 1, rec_data.str + rec_data.size); - type = p2r_type_ptr_from_itype(mfunc_id->itype); - owner = mfunc_id->owner_itype != 0 ? p2r_type_ptr_from_itype(mfunc_id->owner_itype) : 0; - } - - // rjf: extract non-method function inline info - else if(rec_range.hdr.kind == CV_LeafKind_FUNC_ID && - rec_range.hdr.size >= sizeof(CV_LeafFuncId)) - { - CV_LeafFuncId *func_id = (CV_LeafFuncId*)raw_leaf; - name = str8_cstring_capped(func_id + 1, rec_data.str + rec_data.size); - type = p2r_type_ptr_from_itype(func_id->itype); - owner = func_id->scope_string_id != 0 ? p2r_type_ptr_from_itype(func_id->scope_string_id) : 0; - } - } - - // rjf: build inline site - RDIM_InlineSite *inline_site = rdim_inline_site_chunk_list_push(arena, &sym_inline_sites, sym_inline_sites_chunk_cap); - inline_site->name = name; - inline_site->type = type; - inline_site->owner = owner; - inline_site->line_table = inline_site_line_table; - - // rjf: increment to next inline site line table in this unit - if(inline_site_line_table != 0 && inline_site_line_table->chunk != 0) - { - RDIM_LineTableChunkNode *chunk = inline_site_line_table->chunk; - U64 current_idx = (U64)(inline_site_line_table - chunk->v); - if(current_idx+1 < chunk->count) - { - inline_site_line_table += 1; - } - else - { - chunk = chunk->next; - inline_site_line_table = 0; - if(chunk != 0) - { - inline_site_line_table = chunk->v; - } - } - } - - // rjf: build scope - RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, &sym_scopes, sym_scopes_chunk_cap); - scope->inline_site = inline_site; - if(top_scope_node == 0) - { - // TODO(rjf): log - } - if(top_scope_node != 0) - { - RDIM_Scope *top_scope = top_scope_node->scope; - SLLQueuePush_N(top_scope->first_child, top_scope->last_child, scope, next_sibling); - scope->parent_scope = top_scope; - scope->symbol = top_scope->symbol; - } - - // rjf: push this scope to scope stack - { - P2R_ScopeNode *node = free_scope_node; - if(node != 0) { SLLStackPop(free_scope_node); } - else { node = push_array_no_zero(scratch.arena, P2R_ScopeNode, 1); } - node->scope = scope; - SLLStackPush(top_scope_node, node); - } - - // rjf: parse offset ranges of this inline site - attach to scope - { - CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(0, 0, procedure_base_voff); - for(;;) - { - CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); - - if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitRange) - { - // rjf: build new range & add to scope - RDIM_Rng1U64 voff_range = { step.range.min, step.range.max }; - rdim_scope_push_voff_range(arena, &sym_scopes, scope, voff_range); - } - - if(step.flags & CV_C13InlineSiteDecoderStepFlag_ExtendLastRange) - { - if(scope->voff_ranges.last != 0) - { - scope->voff_ranges.last->v.max = step.range.max; - } - } - - if(step.flags == 0) - { - break; - } - } - } - }break; - - //- rjf: INLINESITE_END - case CV_SymKind_INLINESITE_END: - { - P2R_ScopeNode *n = top_scope_node; - if(n != 0) - { - SLLStackPop(top_scope_node); - SLLStackPush(free_scope_node, n); - } - defrange_target = 0; - defrange_target_is_param = 0; - }break; - - //- rjf: CONSTANT - case CV_SymKind_CONSTANT: - { - // rjf: unpack - CV_SymConstant *sym = (CV_SymConstant *)sym_header_struct_base; - RDIM_Type *type = p2r_type_ptr_from_itype(sym->itype); - U8 *val_ptr = (U8 *)(sym+1); - CV_NumericParsed val = cv_numeric_from_data_range(val_ptr, sym_data_opl); - U64 val64 = cv_u64_from_numeric(&val); - U8 *name_ptr = val_ptr + val.encoded_size; - String8 name = str8_cstring_capped(name_ptr, sym_data_opl); - String8 val_data = str8_struct(&val64); - U64 container_name_opl = 0; - if(type != 0) - { - container_name_opl = p2r_end_of_cplusplus_container_name(type->name); - } - String8 name_qualified = name; - if(container_name_opl != 0) - { - name_qualified = push_str8f(arena, "%S%S", str8_prefix(type->name, container_name_opl), name); - } - - // rjf: build constant symbol - if(name_qualified.size != 0) - { - RDIM_Symbol *cnst = rdim_symbol_chunk_list_push(arena, &sym_constants, sym_constants_chunk_cap); - cnst->name = name_qualified; - cnst->type = type; - rdim_symbol_push_value_data(arena, &sym_constants, cnst, val_data); - } - }break; + scratch_end(scratch); } } } - - ////////////////////////// - //- rjf: allocate & fill output - // - P2R_SymbolStreamConvertOut *out = push_array(arena, P2R_SymbolStreamConvertOut, 1); - { - out->procedures = sym_procedures; - out->global_variables = sym_global_variables; - out->thread_variables = sym_thread_variables; - out->constants = sym_constants; - out->scopes = sym_scopes; - out->inline_sites = sym_inline_sites; - out->typedefs = typedefs; - } - -#undef p2r_type_ptr_from_itype - scratch_end(scratch); - ProfEnd(); - return out; -} - -//////////////////////////////// -//~ rjf: Top-Level Conversion Entry Point - -internal RDIM_BakeParams -p2r_convert(Arena *arena, ASYNC_Root *async_root, P2R_ConvertParams *in) -{ - Temp scratch = scratch_begin(&arena, 1); - p2r_async_root = async_root; + lane_sync(); + RDIM_UnitChunkList all_units = p2r_shared->all_units; + RDIM_LineTableChunkList *units_line_tables = p2r_shared->units_line_tables; + RDIM_LineTable **units_first_inline_site_line_tables = p2r_shared->units_first_inline_site_line_tables; ////////////////////////////////////////////////////////////// - //- rjf: parse MSF structure + //- rjf: join all line tables // - MSF_Parsed *msf = 0; - if(in->input_pdb_data.size != 0) ProfScope("parse MSF structure") + ProfScope("join all line tables") if(lane_idx() == 0) { - msf = msf_parsed_from_data(arena, in->input_pdb_data); - } - - ////////////////////////////////////////////////////////////// - //- rjf: parse PDB auth_guid & named streams table - // - PDB_NamedStreamTable *named_streams = 0; - Guid auth_guid = {0}; - if(msf != 0) ProfScope("parse PDB auth_guid & named streams table") - { - Temp scratch = scratch_begin(&arena, 1); - String8 info_data = msf_data_from_stream(msf, PDB_FixedStream_Info); - PDB_Info *info = pdb_info_from_data(scratch.arena, info_data); - named_streams = pdb_named_stream_table_from_info(arena, info); - MemoryCopyStruct(&auth_guid, &info->auth_guid); - scratch_end(scratch); - - if (info->features & PDB_FeatureFlag_MINIMAL_DBG_INFO) { - fprintf(stderr, "ERROR: PDB was linked with /DEBUG:FASTLINK (partial debug info is not supported). Please relink using /DEBUG:FULL."); - os_abort(1); + for EachIndex(idx, comp_units->count) + { + rdim_line_table_chunk_list_concat_in_place(&p2r_shared->all_line_tables, &units_line_tables[idx]); } } + lane_sync(); + RDIM_LineTableChunkList all_line_tables = p2r_shared->all_line_tables; ////////////////////////////////////////////////////////////// - //- rjf: parse PDB strtbl + //- rjf: equip source files with line sequences // - PDB_Strtbl *strtbl = 0; - String8 raw_strtbl = str8_zero(); - if(named_streams != 0) ProfScope("parse PDB strtbl") + ProfScope("equip source files with line sequences") if(lane_idx() == 0) { - MSF_StreamNumber strtbl_sn = named_streams->sn[PDB_NamedStream_StringTable]; - String8 strtbl_data = msf_data_from_stream(msf, strtbl_sn); - strtbl = pdb_strtbl_from_data(arena, strtbl_data); - raw_strtbl = str8_substr(strtbl_data, rng_1u64(strtbl->strblock_min, strtbl->strblock_max)); - } - - ////////////////////////////////////////////////////////////// - //- rjf: parse dbi - // - PDB_DbiParsed *dbi = 0; - if(msf != 0) ProfScope("parse dbi") - { - String8 dbi_data = msf_data_from_stream(msf, PDB_FixedStream_Dbi); - dbi = pdb_dbi_from_data(arena, dbi_data); - } - - ////////////////////////////////////////////////////////////// - //- rjf: parse tpi - // - PDB_TpiParsed *tpi = 0; - if(msf != 0) ProfScope("parse tpi") - { - String8 tpi_data = msf_data_from_stream(msf, PDB_FixedStream_Tpi); - tpi = pdb_tpi_from_data(arena, tpi_data); - } - - ////////////////////////////////////////////////////////////// - //- rjf: parse ipi - // - PDB_TpiParsed *ipi = 0; - if(msf != 0) ProfScope("parse ipi") - { - String8 ipi_data = msf_data_from_stream(msf, PDB_FixedStream_Ipi); - ipi = pdb_tpi_from_data(arena, ipi_data); - } - - ////////////////////////////////////////////////////////////// - //- rjf: parse coff sections - // - COFF_SectionHeaderArray coff_sections = {0}; - if(dbi != 0) ProfScope("parse coff sections") - { - MSF_StreamNumber section_stream = dbi->dbg_streams[PDB_DbiStream_SECTION_HEADER]; - String8 section_data = msf_data_from_stream(msf, section_stream); - coff_sections = pdb_coff_section_array_from_data(arena, section_data); - } - - ////////////////////////////////////////////////////////////// - //- rjf: parse gsi - // - PDB_GsiParsed *gsi = 0; - if(dbi != 0) ProfScope("parse gsi") - { - String8 gsi_data = msf_data_from_stream(msf, dbi->gsi_sn); - gsi = pdb_gsi_from_data(arena, gsi_data); - } - - ////////////////////////////////////////////////////////////// - //- rjf: parse psi - // - PDB_GsiParsed *psi_gsi_part = 0; - if(dbi != 0) ProfScope("parse psi") - { - String8 psi_data = msf_data_from_stream(msf, dbi->psi_sn); - String8 psi_data_gsi_part = str8_range(psi_data.str + sizeof(PDB_PsiHeader), psi_data.str + psi_data.size); - psi_gsi_part = pdb_gsi_from_data(arena, psi_data_gsi_part); - } - - ////////////////////////////////////////////////////////////// - //- rjf: kickoff EXE hash - // - P2R_EXEHashIn exe_hash_in = {in->input_exe_data}; - ASYNC_Task *exe_hash_task = async_task_launch(scratch.arena, p2r_exe_hash_work, .input = &exe_hash_in); - - ////////////////////////////////////////////////////////////// - //- rjf: kickoff TPI hash parse - // - P2R_TPIHashParseIn tpi_hash_in = {0}; - ASYNC_Task *tpi_hash_task = 0; - if(tpi != 0) - { - tpi_hash_in.strtbl = strtbl; - tpi_hash_in.tpi = tpi; - tpi_hash_in.hash_data = msf_data_from_stream(msf, tpi->hash_sn); - tpi_hash_in.aux_data = msf_data_from_stream(msf, tpi->hash_sn_aux); - tpi_hash_task = async_task_launch(scratch.arena, p2r_tpi_hash_parse_work, .input = &tpi_hash_in); - } - - ////////////////////////////////////////////////////////////// - //- rjf: kickoff TPI leaf parse - // - P2R_TPILeafParseIn tpi_leaf_in = {0}; - ASYNC_Task *tpi_leaf_task = 0; - if(tpi != 0) - { - tpi_leaf_in.leaf_data = pdb_leaf_data_from_tpi(tpi); - tpi_leaf_in.itype_first = tpi->itype_first; - tpi_leaf_task = async_task_launch(scratch.arena, p2r_tpi_leaf_parse_work, .input = &tpi_leaf_in); - } - - ////////////////////////////////////////////////////////////// - //- rjf: kickoff IPI hash parse - // - P2R_TPIHashParseIn ipi_hash_in = {0}; - ASYNC_Task *ipi_hash_task = 0; - if(ipi != 0) - { - ipi_hash_in.strtbl = strtbl; - ipi_hash_in.tpi = ipi; - ipi_hash_in.hash_data = msf_data_from_stream(msf, ipi->hash_sn); - ipi_hash_in.aux_data = msf_data_from_stream(msf, ipi->hash_sn_aux); - ipi_hash_task = async_task_launch(scratch.arena, p2r_tpi_hash_parse_work, .input = &ipi_hash_in); - } - - ////////////////////////////////////////////////////////////// - //- rjf: kickoff IPI leaf parse - // - P2R_TPILeafParseIn ipi_leaf_in = {0}; - ASYNC_Task *ipi_leaf_task = 0; - if(ipi != 0) - { - ipi_leaf_in.leaf_data = pdb_leaf_data_from_tpi(ipi); - ipi_leaf_in.itype_first = ipi->itype_first; - ipi_leaf_task = async_task_launch(scratch.arena, p2r_tpi_leaf_parse_work, .input = &ipi_leaf_in); - } - - ////////////////////////////////////////////////////////////// - //- rjf: kickoff top-level global symbol stream parse - // - P2R_SymbolStreamParseIn sym_parse_in = {dbi ? msf_data_from_stream(msf, dbi->sym_sn) : str8_zero()}; - ASYNC_Task *sym_parse_task = !dbi ? 0 : async_task_launch(scratch.arena, p2r_symbol_stream_parse_work, .input = &sym_parse_in); - - ////////////////////////////////////////////////////////////// - //- rjf: do compilation unit parse - // - PDB_CompUnitArray *comp_units = 0; - U64 comp_unit_count = 0; - { - P2R_CompUnitParseIn comp_unit_parse_in = {dbi ? pdb_data_from_dbi_range(dbi, PDB_DbiRange_ModuleInfo) : str8_zero()}; - ASYNC_Task *comp_unit_parse_task = !dbi ? 0 : async_task_launch(scratch.arena, p2r_comp_unit_parse_work, .input = &comp_unit_parse_in); - comp_units = async_task_join_struct(comp_unit_parse_task, PDB_CompUnitArray); - comp_unit_count = comp_units ? comp_units->count : 0; - } - - ////////////////////////////////////////////////////////////// - //- rjf: do compilation unit contributions parse - // - PDB_CompUnitContributionArray *comp_unit_contributions = 0; - U64 comp_unit_contribution_count = 0; - { - P2R_CompUnitContributionsParseIn comp_unit_contributions_parse_in = {dbi ? pdb_data_from_dbi_range(dbi, PDB_DbiRange_SecCon) : str8_zero(), coff_sections}; - ASYNC_Task *comp_unit_contributions_parse_task = !dbi ? 0 : async_task_launch(scratch.arena, p2r_comp_unit_contributions_parse_work, .input = &comp_unit_contributions_parse_in); - comp_unit_contributions = async_task_join_struct(comp_unit_contributions_parse_task, PDB_CompUnitContributionArray); - comp_unit_contribution_count = comp_unit_contributions ? comp_unit_contributions->count : 0; - } - - ////////////////////////////////////////////////////////////// - //- rjf: do compilation unit contributions bucket - // - RDIM_Rng1U64ChunkList *unit_ranges = 0; - if(comp_unit_contributions) - { - P2R_CompUnitContributionsBucketIn in = {comp_unit_count, *comp_unit_contributions}; - ASYNC_Task *task = async_task_launch(scratch.arena, p2r_comp_unit_contributions_bucket_work, .input = &in); - P2R_CompUnitContributionsBucketOut *out = async_task_join_struct(task, P2R_CompUnitContributionsBucketOut); - unit_ranges = out->unit_ranges; - } - - ////////////////////////////////////////////////////////////// - //- rjf: parse syms & line info for each compilation unit - // - CV_SymParsed **sym_for_unit = push_array(arena, CV_SymParsed *, comp_unit_count); - CV_C13Parsed **c13_for_unit = push_array(arena, CV_C13Parsed *, comp_unit_count); - if(comp_units != 0) ProfScope("parse syms & line info for each compilation unit") - { - //- rjf: kick off tasks - P2R_SymbolStreamParseIn *sym_tasks_inputs = push_array(scratch.arena, P2R_SymbolStreamParseIn, comp_unit_count); - ASYNC_Task **sym_tasks = push_array(scratch.arena, ASYNC_Task *, comp_unit_count); - P2R_C13StreamParseIn *c13_tasks_inputs = push_array(scratch.arena, P2R_C13StreamParseIn, comp_unit_count); - ASYNC_Task **c13_tasks = push_array(scratch.arena, ASYNC_Task *, comp_unit_count); - for(U64 idx = 0; idx < comp_unit_count; idx += 1) + for(RDIM_LineTableChunkNode *line_table_chunk_n = all_line_tables.first; + line_table_chunk_n != 0; + line_table_chunk_n = line_table_chunk_n->next) { - PDB_CompUnit *unit = comp_units->units[idx]; - sym_tasks_inputs[idx].data = pdb_data_from_unit_range(msf, unit, PDB_DbiCompUnitRange_Symbols); - sym_tasks[idx] = async_task_launch(scratch.arena, p2r_symbol_stream_parse_work, .input = &sym_tasks_inputs[idx]); - c13_tasks_inputs[idx].data = pdb_data_from_unit_range(msf, unit, PDB_DbiCompUnitRange_C13); - c13_tasks_inputs[idx].strtbl = raw_strtbl; - c13_tasks_inputs[idx].coff_sections = coff_sections; - c13_tasks[idx] = async_task_launch(scratch.arena, p2r_c13_stream_parse_work, .input = &c13_tasks_inputs[idx]); - } - - //- rjf: join tasks - for(U64 idx = 0; idx < comp_unit_count; idx += 1) - { - sym_for_unit[idx] = async_task_join_struct(sym_tasks[idx], CV_SymParsed); - c13_for_unit[idx] = async_task_join_struct(c13_tasks[idx], CV_C13Parsed); - } - } - - ////////////////////////////////////////////////////////////// - //- rjf: calculate EXE's max voff - // - U64 exe_voff_max = 0; - { - COFF_SectionHeader *coff_sec_ptr = coff_sections.v; - COFF_SectionHeader *coff_ptr_opl = coff_sec_ptr + coff_sections.count; - for(;coff_sec_ptr < coff_ptr_opl; coff_sec_ptr += 1) - { - U64 sec_voff_max = coff_sec_ptr->voff + coff_sec_ptr->vsize; - exe_voff_max = Max(exe_voff_max, sec_voff_max); - } - } - - ////////////////////////////////////////////////////////////// - //- rjf: determine architecture - // - RDI_Arch arch = RDI_Arch_NULL; - U64 arch_addr_size = 0; - { - // TODO(rjf): in some cases, the first compilation unit has a zero - // architecture, as it's sometimes used as a "nil" unit. this causes bugs - // in later stages of conversion - particularly, this was detected via - // busted location info. so i've converted this to a scan-until-we-find-an- - // architecture. however, this may still be fundamentally insufficient, - // because Nick has informed me that x86 units can be linked with x64 - // units, meaning the appropriate architecture at any point in time is not - // a top-level concept, and is rather dependent on to which compilation - // unit particular symbols belong. so in the future, to support that (odd) - // case, we'll need to not only have this be a top-level "contextual" piece - // of info, but to use the appropriate compilation unit's architecture when - // possible. assuming, of course, that we care about supporting that case. - for(U64 comp_unit_idx = 0; comp_unit_idx < comp_unit_count; comp_unit_idx += 1) - { - if(sym_for_unit[comp_unit_idx] != 0) + for EachIndex(chunk_line_table_idx, line_table_chunk_n->count) { - arch = p2r_rdi_arch_from_cv_arch(sym_for_unit[comp_unit_idx]->info.arch); - if(arch != RDI_Arch_NULL) + RDIM_LineTable *line_table = &line_table_chunk_n->v[chunk_line_table_idx]; + for(RDIM_LineSequenceNode *s = line_table->first_seq; s != 0; s = s->next) { - break; - } - } - } - arch_addr_size = rdi_addr_size_from_arch(arch); - } - - ////////////////////////////////////////////////////////////// - //- rjf: join EXE hash - // - U64 exe_hash = *async_task_join_struct(exe_hash_task, U64); - - ////////////////////////////////////////////////////////////// - //- rjf: produce top-level-info - // - RDIM_TopLevelInfo top_level_info = {0}; - { - top_level_info.arch = arch; - top_level_info.exe_name = str8_skip_last_slash(in->input_exe_name); - top_level_info.exe_hash = exe_hash; - top_level_info.voff_max = exe_voff_max; - if(in->deterministic) - { - top_level_info.producer_name = str8_lit(BUILD_TITLE_STRING_LITERAL); - } - } - - ////////////////////////////////////////////////////////////// - //- rjf: build binary sections list - // - RDIM_BinarySectionList binary_sections = {0}; - ProfScope("build binary section list") - { - COFF_SectionHeader *coff_ptr = coff_sections.v; - COFF_SectionHeader *coff_opl = coff_ptr + coff_sections.count; - for(;coff_ptr < coff_opl; coff_ptr += 1) - { - char *name_first = (char*)coff_ptr->name; - char *name_opl = name_first + sizeof(coff_ptr->name); - RDIM_BinarySection *sec = rdim_binary_section_list_push(arena, &binary_sections); - sec->name = str8_cstring_capped(name_first, name_opl); - sec->flags = p2r_rdi_binary_section_flags_from_coff_section_flags(coff_ptr->flags); - sec->voff_first = coff_ptr->voff; - sec->voff_opl = coff_ptr->voff+coff_ptr->vsize; - sec->foff_first = coff_ptr->foff; - sec->foff_opl = coff_ptr->foff+coff_ptr->fsize; - } - } - - ////////////////////////////////////////////////////////////// - //- rjf: gather all source file paths; build nodes - // - RDIM_SrcFileChunkList all_src_files__sequenceless = {0}; - P2R_SrcFileMap src_file_map = {0}; - ProfScope("gather all source file paths; build nodes") - { - U64 tasks_count = comp_unit_count; - P2R_GatherUnitSrcFilesIn *tasks_inputs = push_array(scratch.arena, P2R_GatherUnitSrcFilesIn, tasks_count); - P2R_GatherUnitSrcFilesOut *tasks_outputs = push_array(scratch.arena, P2R_GatherUnitSrcFilesOut, tasks_count); - ASYNC_Task **tasks = push_array(scratch.arena, ASYNC_Task *, tasks_count); - for EachIndex(idx, tasks_count) - { - tasks_inputs[idx].pdb_strtbl = strtbl; - tasks_inputs[idx].coff_sections = coff_sections; - tasks_inputs[idx].comp_unit = comp_units->units[idx]; - tasks_inputs[idx].comp_unit_syms = sym_for_unit[idx]; - tasks_inputs[idx].comp_unit_c13s = c13_for_unit[idx]; - tasks[idx] = async_task_launch(scratch.arena, p2r_gather_unit_src_file_work, .input = &tasks_inputs[idx]); - } - U64 total_path_count = 0; - for EachIndex(idx, tasks_count) - { - tasks_outputs[idx] = *async_task_join_struct(tasks[idx], P2R_GatherUnitSrcFilesOut); - total_path_count += tasks_outputs[idx].src_file_paths.count; - } - src_file_map.slots_count = total_path_count + total_path_count/2 + 1; - src_file_map.slots = push_array(scratch.arena, P2R_SrcFileNode *, src_file_map.slots_count); - for EachIndex(idx, tasks_count) - { - for EachIndex(path_idx, tasks_outputs[idx].src_file_paths.count) - { - String8 file_path_sanitized = tasks_outputs[idx].src_file_paths.v[path_idx]; - U64 file_path_sanitized_hash = rdi_hash(file_path_sanitized.str, file_path_sanitized.size); - U64 src_file_slot = file_path_sanitized_hash%src_file_map.slots_count; - P2R_SrcFileNode *src_file_node = 0; - for(P2R_SrcFileNode *n = src_file_map.slots[src_file_slot]; n != 0; n = n->next) - { - if(str8_match(n->src_file->path, file_path_sanitized, 0)) - { - src_file_node = n; - break; - } - } - if(src_file_node == 0) - { - src_file_node = push_array(scratch.arena, P2R_SrcFileNode, 1); - SLLStackPush(src_file_map.slots[src_file_slot], src_file_node); - src_file_node->src_file = rdim_src_file_chunk_list_push(arena, &all_src_files__sequenceless, total_path_count); - src_file_node->src_file->path = push_str8_copy(arena, file_path_sanitized); + rdim_src_file_push_line_sequence(arena, &p2r_shared->all_src_files__sequenceless, s->v.src_file, &s->v); } } } } - - ////////////////////////////////////////////////////////////// - //- rjf: kick off unit conversion tasks - // - U64 unit_convert_tasks_count = comp_unit_count; - P2R_UnitConvertIn *unit_convert_tasks_ins = push_array(scratch.arena, P2R_UnitConvertIn, unit_convert_tasks_count); - ASYNC_Task **unit_convert_tasks = push_array(scratch.arena, ASYNC_Task *, unit_convert_tasks_count); - for EachIndex(idx, unit_convert_tasks_count) - { - P2R_UnitConvertIn *in = &unit_convert_tasks_ins[idx]; - in->comp_unit_idx = idx; - in->pdb_strtbl = strtbl; - in->coff_sections = coff_sections; - in->comp_unit = comp_units->units[idx]; - in->comp_unit_ranges = unit_ranges[idx]; - in->comp_unit_syms = sym_for_unit[idx]; - in->comp_unit_c13s = c13_for_unit[idx]; - in->src_file_map = &src_file_map; - unit_convert_tasks[idx] = async_task_launch(scratch.arena, p2r_unit_convert_work, .input = in); - } - - ////////////////////////////////////////////////////////////// - //- rjf: join global sym stream parse - // - CV_SymParsed *sym = async_task_join_struct(sym_parse_task, CV_SymParsed); - - ////////////////////////////// - //- rjf: predict symbol count - // - U64 symbol_count_prediction = 0; - ProfScope("predict symbol count") - { - U64 rec_range_count = 0; - if(sym != 0) - { - rec_range_count += sym->sym_ranges.count; - } - for(U64 comp_unit_idx = 0; comp_unit_idx < comp_unit_count; comp_unit_idx += 1) - { - CV_SymParsed *unit_sym = sym_for_unit[comp_unit_idx]; - rec_range_count += unit_sym->sym_ranges.count; - } - symbol_count_prediction = rec_range_count/8; - if(symbol_count_prediction < 256) - { - symbol_count_prediction = 256; - } - } - - ////////////////////////////////////////////////////////////// - //- rjf: kick off link name map production - // - P2R_LinkNameMap link_name_map__in_progress = {0}; - P2R_LinkNameMapBuildIn link_name_map_build_in = {0}; - ASYNC_Task *link_name_map_task = 0; - if(sym != 0) ProfScope("kick off link name map build task") - { - link_name_map__in_progress.buckets_count = symbol_count_prediction; - link_name_map__in_progress.buckets = push_array(arena, P2R_LinkNameNode *, link_name_map__in_progress.buckets_count); - link_name_map_build_in.sym = sym; - link_name_map_build_in.coff_sections = coff_sections; - link_name_map_build_in.link_name_map = &link_name_map__in_progress; - link_name_map_task = async_task_launch(scratch.arena, p2r_link_name_map_build_work, .input = &link_name_map_build_in); - } - - ////////////////////////////////////////////////////////////// - //- rjf: join ipi/tpi hash/leaf parses - // - PDB_TpiHashParsed *tpi_hash = 0; - CV_LeafParsed *tpi_leaf = 0; - PDB_TpiHashParsed *ipi_hash = 0; - CV_LeafParsed *ipi_leaf = 0; - { - tpi_hash = async_task_join_struct(tpi_hash_task, PDB_TpiHashParsed); - tpi_leaf = async_task_join_struct(tpi_leaf_task, CV_LeafParsed); - ipi_hash = async_task_join_struct(ipi_hash_task, PDB_TpiHashParsed); - ipi_leaf = async_task_join_struct(ipi_leaf_task, CV_LeafParsed); - } + lane_sync(); + RDIM_SrcFileChunkList all_src_files = p2r_shared->all_src_files__sequenceless; ////////////////////////////////////////////////////////////// //- rjf: types pass 1: produce type forward resolution map @@ -3628,38 +1524,145 @@ p2r_convert(Arena *arena, ASYNC_Root *async_root, P2R_ConvertParams *in) // to hook types up to their actual destination complete types wherever // possible, and so this map can be used to do that in subsequent stages. // - CV_TypeId *itype_fwd_map = 0; - CV_TypeId itype_first = 0; - CV_TypeId itype_opl = 0; - if(tpi_leaf != 0 && in->subset_flags & RDIM_SubsetFlag_Types) ProfScope("types pass 1: produce type forward resolution map") + ProfScope("types pass 1: produce type forward resolution map") { //- rjf: allocate forward resolution map - itype_first = tpi_leaf->itype_first; - itype_opl = tpi_leaf->itype_opl; - itype_fwd_map = push_array(arena, CV_TypeId, (U64)itype_opl); - - //- rjf: kick off tasks to fill forward resolution map - U64 task_size_itypes = 1024; - U64 tasks_count = ((U64)itype_opl+(task_size_itypes-1))/task_size_itypes; - P2R_ITypeFwdMapFillIn *tasks_inputs = push_array(scratch.arena, P2R_ITypeFwdMapFillIn, tasks_count); - ASYNC_Task **tasks = push_array(scratch.arena, ASYNC_Task *, tasks_count); - for(U64 idx = 0; idx < tasks_count; idx += 1) + if(lane_idx() == 0) { - tasks_inputs[idx].tpi_hash = tpi_hash; - tasks_inputs[idx].tpi_leaf = tpi_leaf; - tasks_inputs[idx].itype_first = idx*task_size_itypes; - tasks_inputs[idx].itype_opl = tasks_inputs[idx].itype_first + task_size_itypes; - tasks_inputs[idx].itype_opl = ClampTop(tasks_inputs[idx].itype_opl, itype_opl); - tasks_inputs[idx].itype_fwd_map = itype_fwd_map; - tasks[idx] = async_task_launch(scratch.arena, p2r_itype_fwd_map_fill_work, .input = &tasks_inputs[idx]); + p2r_shared->itype_first = tpi_leaf->itype_first; + p2r_shared->itype_opl = tpi_leaf->itype_opl; + p2r_shared->itype_fwd_map = push_array(arena, CV_TypeId, (U64)p2r_shared->itype_opl); } + lane_sync(); - //- rjf: join all tasks - for(U64 idx = 0; idx < tasks_count; idx += 1) + //- rjf: do wide fill + if(params->subset_flags & RDIM_SubsetFlag_Types) { - async_task_join(tasks[idx]); + Rng1U64 range = lane_range(p2r_shared->itype_opl); + for EachInRange(idx, range) + { + CV_TypeId itype = (CV_TypeId)idx; + if(itype < p2r_shared->itype_first) { continue; } + + //- rjf: determine if this itype resolves to another + CV_TypeId itype_fwd = 0; + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[itype-tpi_leaf->itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + if(range->off+range->hdr.size <= tpi_leaf->data.size && + range->off+2+header_struct_size <= tpi_leaf->data.size && + range->hdr.size >= 2) + { + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + switch(kind) + { + default:{}break; + + //- rjf: CLASS/STRUCTURE + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + // rjf: unpack leaf header + CV_LeafStruct *lf_struct = (CV_LeafStruct *)itype_leaf_first; + + // rjf: has fwd ref flag -> lookup itype that this itype resolves to + if(lf_struct->props & CV_TypeProp_FwdRef) + { + // rjf: unpack rest of leaf + U8 *numeric_ptr = (U8 *)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); + + // rjf: lookup + B32 do_unique_name_lookup = (((lf_struct->props & CV_TypeProp_Scoped) != 0) && + ((lf_struct->props & CV_TypeProp_HasUniqueName) != 0)); + itype_fwd = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); + } + }break; + + //- rjf: CLASS2/STRUCT2 + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + // rjf: unpack leaf header + CV_LeafStruct2 *lf_struct = (CV_LeafStruct2 *)itype_leaf_first; + + // rjf: has fwd ref flag -> lookup itype that this itype resolves to + if(lf_struct->props & CV_TypeProp_FwdRef) + { + // rjf: unpack rest of leaf + U8 *numeric_ptr = (U8 *)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = (U8 *)numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); + + // rjf: lookup + B32 do_unique_name_lookup = (((lf_struct->props & CV_TypeProp_Scoped) != 0) && + ((lf_struct->props & CV_TypeProp_HasUniqueName) != 0)); + itype_fwd = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); + } + }break; + + //- rjf: UNION + case CV_LeafKind_UNION: + { + // rjf: unpack leaf + CV_LeafUnion *lf_union = (CV_LeafUnion *)itype_leaf_first; + U8 *numeric_ptr = (U8 *)(lf_union + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); + + // rjf: has fwd ref flag -> lookup itype that this itype resolves tos + if(lf_union->props & CV_TypeProp_FwdRef) + { + B32 do_unique_name_lookup = (((lf_union->props & CV_TypeProp_Scoped) != 0) && + ((lf_union->props & CV_TypeProp_HasUniqueName) != 0)); + itype_fwd = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); + } + }break; + + //- rjf: ENUM + case CV_LeafKind_ENUM: + { + // rjf: unpack leaf + CV_LeafEnum *lf_enum = (CV_LeafEnum*)itype_leaf_first; + U8 *name_ptr = (U8 *)(lf_enum + 1); + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + U8 *unique_name_ptr = name_ptr + name.size + 1; + String8 unique_name = str8_cstring_capped(unique_name_ptr, itype_leaf_opl); + + // rjf: has fwd ref flag -> lookup itype that this itype resolves to + if(lf_enum->props & CV_TypeProp_FwdRef) + { + B32 do_unique_name_lookup = (((lf_enum->props & CV_TypeProp_Scoped) != 0) && + ((lf_enum->props & CV_TypeProp_HasUniqueName) != 0)); + itype_fwd = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, do_unique_name_lookup?unique_name:name, do_unique_name_lookup); + } + }break; + } + } + + //- rjf: if the forwarded itype is nonzero & in TPI range -> save to map + if(itype_fwd != 0 && itype_fwd < tpi_leaf->itype_opl) + { + p2r_shared->itype_fwd_map[itype] = itype_fwd; + } + } } } + lane_sync(); + CV_TypeId *itype_fwd_map = p2r_shared->itype_fwd_map; + CV_TypeId itype_first = p2r_shared->itype_first; + CV_TypeId itype_opl = p2r_shared->itype_opl; ////////////////////////////////////////////////////////////// //- rjf: types pass 2: produce per-itype itype chain @@ -3671,34 +1674,314 @@ p2r_convert(Arena *arena, ASYNC_Root *async_root, P2R_ConvertParams *in) // this chain is walked, so that deeper dependencies are built first, and // as such, always show up *earlier* in the actually built types. // - P2R_TypeIdChain **itype_chains = 0; - if(tpi_leaf != 0 && in->subset_flags & RDIM_SubsetFlag_Types) ProfScope("types pass 2: produce per-itype itype chain (for producing dependent types first)") + ProfScope("types pass 2: produce per-itype itype chain (for producing dependent types first)") { //- rjf: allocate itype chain table - itype_chains = push_array(arena, P2R_TypeIdChain *, (U64)itype_opl); - - //- rjf: kick off tasks to fill itype chain table - U64 task_size_itypes = 1024; - U64 tasks_count = ((U64)itype_opl+(task_size_itypes-1))/task_size_itypes; - P2R_ITypeChainBuildIn *tasks_inputs = push_array(scratch.arena, P2R_ITypeChainBuildIn, tasks_count); - ASYNC_Task **tasks = push_array(scratch.arena, ASYNC_Task *, tasks_count); - for(U64 idx = 0; idx < tasks_count; idx += 1) + if(lane_idx() == 0) { - tasks_inputs[idx].tpi_leaf = tpi_leaf; - tasks_inputs[idx].itype_first = idx*task_size_itypes; - tasks_inputs[idx].itype_opl = tasks_inputs[idx].itype_first + task_size_itypes; - tasks_inputs[idx].itype_opl = ClampTop(tasks_inputs[idx].itype_opl, itype_opl); - tasks_inputs[idx].itype_chains = itype_chains; - tasks_inputs[idx].itype_fwd_map = itype_fwd_map; - tasks[idx] = async_task_launch(scratch.arena, p2r_itype_chain_build_work, .input = &tasks_inputs[idx]); + p2r_shared->itype_chains = push_array(arena, P2R_TypeIdChain *, (U64)p2r_shared->itype_opl); } + lane_sync(); - //- rjf: join all tasks - for(U64 idx = 0; idx < tasks_count; idx += 1) + //- rjf: do wide fill + if(params->subset_flags & RDIM_SubsetFlag_Types) { - async_task_join(tasks[idx]); + Rng1U64 range = lane_range(p2r_shared->itype_opl); + for EachInRange(idx, range) + { + CV_TypeId itype = (CV_TypeId)idx; + if(itype < p2r_shared->itype_first) { continue; } + + //- rjf: push initial itype - should be final-visited-itype for this itype + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + //- rjf: skip basic types for dependency walk + if(itype < tpi_leaf->itype_first) + { + continue; + } + + //- rjf: walk dependent types, push to chain + Temp scratch = scratch_begin(&arena, 1); + P2R_TypeIdChain start_walk_task = {0, itype}; + P2R_TypeIdChain *first_walk_task = &start_walk_task; + P2R_TypeIdChain *last_walk_task = &start_walk_task; + for(P2R_TypeIdChain *walk_task = first_walk_task; + walk_task != 0; + walk_task = walk_task->next) + { + CV_TypeId walk_itype = itype_fwd_map[walk_task->itype] ? itype_fwd_map[walk_task->itype] : walk_task->itype; + if(walk_itype < tpi_leaf->itype_first) + { + continue; + } + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[walk_itype-tpi_leaf->itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + if(range->off+range->hdr.size <= tpi_leaf->data.size && + range->off+2+header_struct_size <= tpi_leaf->data.size && + range->hdr.size >= 2) + { + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + switch(kind) + { + default:{}break; + + //- rjf: MODIFIER + case CV_LeafKind_MODIFIER: + { + CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; + + // rjf: push dependent itype to chain + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk dependency itype + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: POINTER + case CV_LeafKind_POINTER: + { + CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; + + // rjf: push dependent itype to chain + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk dependency itype + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: PROCEDURE + case CV_LeafKind_PROCEDURE: + { + CV_LeafProcedure *lf = (CV_LeafProcedure *)itype_leaf_first; + + // rjf: push return itypes to chain + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->ret_itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk return itype + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->ret_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + + // rjf: unpack arglist range + CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-tpi_leaf->itype_first]; + if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || + arglist_range->hdr.size<2 || + arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + { + break; + } + U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; + U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; + if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) + { + break; + } + + // rjf: unpack arglist info + CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; + CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); + U32 arglist_itypes_count = arglist->count; + + // rjf: push arg types to chain + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = arglist_itypes_base[idx]; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk arg types + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = arglist_itypes_base[idx]; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: MFUNCTION + case CV_LeafKind_MFUNCTION: + { + CV_LeafMFunction *lf = (CV_LeafMFunction *)itype_leaf_first; + + // rjf: push dependent itypes to chain + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->ret_itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->arg_itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->this_itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk dependency itypes + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->ret_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->arg_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->this_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + + // rjf: unpack arglist range + CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-tpi_leaf->itype_first]; + if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || + arglist_range->hdr.size<2 || + arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + { + break; + } + U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; + U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; + if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) + { + break; + } + + // rjf: unpack arglist info + CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; + CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); + U32 arglist_itypes_count = arglist->count; + + // rjf: push arg types to chain + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = arglist_itypes_base[idx]; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk arg types + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = arglist_itypes_base[idx]; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: BITFIELD + case CV_LeafKind_BITFIELD: + { + CV_LeafBitField *lf = (CV_LeafBitField *)itype_leaf_first; + + // rjf: push dependent itype to chain + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk dependency itype + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: ARRAY + case CV_LeafKind_ARRAY: + { + CV_LeafArray *lf = (CV_LeafArray *)itype_leaf_first; + + // rjf: push dependent itypes to chain + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->entry_itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->index_itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk dependency itypes + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->entry_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->index_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + + //- rjf: ENUM + case CV_LeafKind_ENUM: + { + CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; + + // rjf: push dependent itypes to chain + { + P2R_TypeIdChain *c = push_array(arena, P2R_TypeIdChain, 1); + c->itype = lf->base_itype; + SLLStackPush(p2r_shared->itype_chains[itype], c); + } + + // rjf: push task to walk dependency itypes + { + P2R_TypeIdChain *c = push_array(scratch.arena, P2R_TypeIdChain, 1); + c->itype = lf->base_itype; + SLLQueuePush(first_walk_task, last_walk_task, c); + } + }break; + } + } + } + scratch_end(scratch); + } } } + lane_sync(); + P2R_TypeIdChain **itype_chains = p2r_shared->itype_chains; ////////////////////////////////////////////////////////////// //- rjf: types pass 3: construct all types from TPI @@ -3707,19 +1990,18 @@ p2r_convert(Arena *arena, ASYNC_Root *async_root, P2R_ConvertParams *in) // subsequent passes, to build RDI "UDT" information, which is distinct // from regular type info. // - RDIM_Type **itype_type_ptrs = 0; - RDIM_Type **basic_type_ptrs = 0; - RDIM_TypeChunkList all_types = {0}; + if(lane_idx() == 0) ProfScope("types pass 3: construct all root/stub types from TPI") + { #define p2r_builtin_type_ptr_from_kind(kind) ((basic_type_ptrs && RDI_TypeKind_FirstBuiltIn <= (kind) && (kind) <= RDI_TypeKind_LastBuiltIn) ? (basic_type_ptrs[(kind) - RDI_TypeKind_FirstBuiltIn]) : 0) #define p2r_type_ptr_from_itype(itype) ((itype_type_ptrs && (itype) < itype_opl) ? (itype_type_ptrs[(itype_fwd_map[(itype)] ? itype_fwd_map[(itype)] : (itype))]) : 0) - if(in->subset_flags & RDIM_SubsetFlag_Types) ProfScope("types pass 3: construct all root/stub types from TPI") - { - itype_type_ptrs = push_array(arena, RDIM_Type *, (U64)(itype_opl)); - basic_type_ptrs = push_array(arena, RDIM_Type *, (RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1)); + RDIM_Type **itype_type_ptrs = push_array(arena, RDIM_Type *, (U64)(itype_opl)); + RDIM_Type **basic_type_ptrs = push_array(arena, RDIM_Type *, (RDI_TypeKind_LastBuiltIn - RDI_TypeKind_FirstBuiltIn + 1)); + RDIM_TypeChunkList all_types = {0}; //////////////////////////// //- rjf: build basic types // + if(params->subset_flags & RDIM_SubsetFlag_Types) { for(RDI_TypeKind type_kind = RDI_TypeKind_FirstBuiltIn; type_kind <= RDI_TypeKind_LastBuiltIn; @@ -3736,8 +2018,9 @@ p2r_convert(Arena *arena, ASYNC_Root *async_root, P2R_ConvertParams *in) //////////////////////////// //- rjf: build basic type aliases // + if(params->subset_flags & RDIM_SubsetFlag_Types) { - RDIM_DataModel data_model = rdim_data_model_from_os_arch(OperatingSystem_Windows, top_level_info.arch); + RDIM_DataModel data_model = rdim_data_model_from_os_arch(OperatingSystem_Windows, arch); RDI_TypeKind short_type = rdim_short_type_kind_from_data_model(data_model); RDI_TypeKind ushort_type = rdim_unsigned_short_type_kind_from_data_model(data_model); RDI_TypeKind long_type = rdim_long_type_kind_from_data_model(data_model); @@ -3781,6 +2064,7 @@ p2r_convert(Arena *arena, ASYNC_Root *async_root, P2R_ConvertParams *in) { "__uint8" , RDI_TypeKind_U8 , CV_BasicType_UINT8 }, { "__int16" , RDI_TypeKind_S16 , CV_BasicType_INT16 }, { "__uint16" , RDI_TypeKind_U16 , CV_BasicType_UINT16 }, + { "int" , RDI_TypeKind_S32 , CV_BasicType_INT32 }, { "int32" , RDI_TypeKind_S32 , CV_BasicType_INT32 }, { "uint32" , RDI_TypeKind_U32 , CV_BasicType_UINT32 }, { "__int64" , RDI_TypeKind_S64 , CV_BasicType_INT64 }, @@ -3810,600 +2094,2198 @@ p2r_convert(Arena *arena, ASYNC_Root *async_root, P2R_ConvertParams *in) //////////////////////////// //- rjf: build types from TPI // - for(CV_TypeId root_itype = 0; root_itype < itype_opl; root_itype += 1) + if(params->subset_flags & RDIM_SubsetFlag_Types) { - for(P2R_TypeIdChain *itype_chain = itype_chains[root_itype]; - itype_chain != 0; - itype_chain = itype_chain->next) + for(CV_TypeId root_itype = 0; root_itype < itype_opl; root_itype += 1) { - CV_TypeId itype = (root_itype != itype_chain->itype && itype_chain->itype < itype_opl && itype_fwd_map[itype_chain->itype]) ? itype_fwd_map[itype_chain->itype] : itype_chain->itype; - B32 itype_is_basic = (itype < tpi->itype_first); - - ////////////////////////// - //- rjf: skip forward-reference itypes - all future resolutions will - // reference whatever this itype resolves to, and so there is no point - // in filling out this slot - // - if(itype_fwd_map[root_itype] != 0) + for(P2R_TypeIdChain *itype_chain = itype_chains[root_itype]; + itype_chain != 0; + itype_chain = itype_chain->next) { - continue; - } - - ////////////////////////// - //- rjf: skip already produced dependencies - // - if(itype_type_ptrs[itype] != 0) - { - continue; - } - - ////////////////////////// - //- rjf: build basic type - // - if(itype_is_basic) - { - RDIM_Type *dst_type = 0; + CV_TypeId itype = (root_itype != itype_chain->itype && itype_chain->itype < itype_opl && itype_fwd_map[itype_chain->itype]) ? itype_fwd_map[itype_chain->itype] : itype_chain->itype; + B32 itype_is_basic = (itype < tpi->itype_first); - // rjf: unpack itype - CV_BasicPointerKind cv_basic_ptr_kind = CV_BasicPointerKindFromTypeId(itype); - CV_BasicType cv_basic_type_code = CV_BasicTypeFromTypeId(itype); - - // rjf: get basic type slot, fill if unfilled - RDIM_Type *basic_type = itype_type_ptrs[cv_basic_type_code]; - if(basic_type == 0) + ////////////////////////// + //- rjf: skip forward-reference itypes - all future resolutions will + // reference whatever this itype resolves to, and so there is no point + // in filling out this slot + // + if(itype_fwd_map[root_itype] != 0) { - RDI_TypeKind type_kind = p2r_rdi_type_kind_from_cv_basic_type(cv_basic_type_code); - U32 byte_size = rdi_size_from_basic_type_kind(type_kind); - basic_type = dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - if(byte_size == 0xffffffff) + continue; + } + + ////////////////////////// + //- rjf: skip already produced dependencies + // + if(itype_type_ptrs[itype] != 0) + { + continue; + } + + ////////////////////////// + //- rjf: build basic type + // + if(itype_is_basic) + { + RDIM_Type *dst_type = 0; + + // rjf: unpack itype + CV_BasicPointerKind cv_basic_ptr_kind = CV_BasicPointerKindFromTypeId(itype); + CV_BasicType cv_basic_type_code = CV_BasicTypeFromTypeId(itype); + + // rjf: get basic type slot, fill if unfilled + RDIM_Type *basic_type = itype_type_ptrs[cv_basic_type_code]; + if(basic_type == 0) { - byte_size = arch_addr_size; + RDI_TypeKind type_kind = p2r_rdi_type_kind_from_cv_basic_type(cv_basic_type_code); + U32 byte_size = rdi_size_from_basic_type_kind(type_kind); + basic_type = dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + if(byte_size == 0xffffffff) + { + byte_size = arch_addr_size; + } + basic_type->kind = type_kind; + basic_type->name = cv_type_name_from_basic_type(cv_basic_type_code); + basic_type->byte_size = byte_size; } - basic_type->kind = type_kind; - basic_type->name = cv_type_name_from_basic_type(cv_basic_type_code); - basic_type->byte_size = byte_size; - } - - // rjf: nonzero ptr kind -> form ptr type to basic tpye - if(cv_basic_ptr_kind != 0) - { - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type->kind = RDI_TypeKind_Ptr; - dst_type->byte_size = arch_addr_size; - dst_type->direct_type = basic_type; - } - - // rjf: fill this itype's slot with the finished type - itype_type_ptrs[itype] = dst_type; - } - - ////////////////////////// - //- rjf: build non-basic type - // - if(!itype_is_basic && itype >= itype_first) - { - RDIM_Type *dst_type = 0; - CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[itype-itype_first]; - CV_LeafKind kind = range->hdr.kind; - U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); - if(range->off+range->hdr.size <= tpi_leaf->data.size && - range->off+2+header_struct_size <= tpi_leaf->data.size && - range->hdr.size >= 2) - { - U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; - U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; - switch(kind) + + // rjf: nonzero ptr kind -> form ptr type to basic tpye + if(cv_basic_ptr_kind != 0) { - //- rjf: MODIFIER - case CV_LeafKind_MODIFIER: + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Ptr; + dst_type->byte_size = arch_addr_size; + dst_type->direct_type = basic_type; + } + + // rjf: fill this itype's slot with the finished type + itype_type_ptrs[itype] = dst_type; + } + + ////////////////////////// + //- rjf: build non-basic type + // + if(!itype_is_basic && itype >= itype_first) + { + RDIM_Type *dst_type = 0; + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[itype-itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + if(range->off+range->hdr.size <= tpi_leaf->data.size && + range->off+2+header_struct_size <= tpi_leaf->data.size && + range->hdr.size >= 2) + { + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + switch(kind) { - // rjf: unpack leaf - CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; - - // rjf: cv -> rdi flags - RDI_TypeModifierFlags flags = 0; - if(lf->flags & CV_ModifierFlag_Const) {flags |= RDI_TypeModifierFlag_Const;} - if(lf->flags & CV_ModifierFlag_Volatile) {flags |= RDI_TypeModifierFlag_Volatile;} - - // rjf: fill type - if(flags == 0) + //- rjf: MODIFIER + case CV_LeafKind_MODIFIER: { - dst_type = p2r_type_ptr_from_itype(lf->itype); - } - else + // rjf: unpack leaf + CV_LeafModifier *lf = (CV_LeafModifier *)itype_leaf_first; + + // rjf: cv -> rdi flags + RDI_TypeModifierFlags flags = 0; + if(lf->flags & CV_ModifierFlag_Const) {flags |= RDI_TypeModifierFlag_Const;} + if(lf->flags & CV_ModifierFlag_Volatile) {flags |= RDI_TypeModifierFlag_Volatile;} + + // rjf: fill type + if(flags == 0) + { + dst_type = p2r_type_ptr_from_itype(lf->itype); + } + else + { + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Modifier; + dst_type->flags = flags; + dst_type->direct_type = p2r_type_ptr_from_itype(lf->itype); + dst_type->byte_size = dst_type->direct_type ? dst_type->direct_type->byte_size : 0; + } + }break; + + //- rjf: POINTER + case CV_LeafKind_POINTER: { + // TODO(rjf): if ptr_mode in {PtrMem, PtrMethod} then output a member pointer instead + + // rjf: unpack leaf + CV_LeafPointer *lf = (CV_LeafPointer *)itype_leaf_first; + RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->itype); + CV_PointerKind ptr_kind = CV_PointerAttribs_Extract_Kind(lf->attribs); + CV_PointerMode ptr_mode = CV_PointerAttribs_Extract_Mode(lf->attribs); + U32 ptr_size = CV_PointerAttribs_Extract_Size(lf->attribs); + + // rjf: cv -> rdi modifier flags + RDI_TypeModifierFlags modifier_flags = 0; + if(lf->attribs & CV_PointerAttrib_Const) {modifier_flags |= RDI_TypeModifierFlag_Const;} + if(lf->attribs & CV_PointerAttrib_Volatile) {modifier_flags |= RDI_TypeModifierFlag_Volatile;} + if(lf->attribs & CV_PointerAttrib_Restricted) {modifier_flags |= RDI_TypeModifierFlag_Restrict;} + + // rjf: cv info -> rdi pointer type kind + RDI_TypeKind type_kind = RDI_TypeKind_Ptr; + { + if(lf->attribs & CV_PointerAttrib_LRef) + { + type_kind = RDI_TypeKind_LRef; + } + else if(lf->attribs & CV_PointerAttrib_RRef) + { + type_kind = RDI_TypeKind_RRef; + } + if(ptr_mode == CV_PointerMode_LRef) + { + type_kind = RDI_TypeKind_LRef; + } + else if(ptr_mode == CV_PointerMode_RRef) + { + type_kind = RDI_TypeKind_RRef; + } + } + + // rjf: fill type + if(modifier_flags != 0) + { + RDIM_Type *pointer_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Modifier; + dst_type->flags = modifier_flags; + dst_type->direct_type = pointer_type; + dst_type->byte_size = arch_addr_size; + pointer_type->kind = type_kind; + pointer_type->byte_size = arch_addr_size; + pointer_type->direct_type = direct_type; + } + else + { + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + dst_type->kind = type_kind; + dst_type->byte_size = arch_addr_size; + dst_type->direct_type = direct_type; + } + }break; + + //- rjf: PROCEDURE + case CV_LeafKind_PROCEDURE: + { + // TODO(rjf): handle call_kind & attribs + + // rjf: unpack leaf + CV_LeafProcedure *lf = (CV_LeafProcedure *)itype_leaf_first; + RDIM_Type *ret_type = p2r_type_ptr_from_itype(lf->ret_itype); + + // rjf: fill type's basics dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type->kind = RDI_TypeKind_Modifier; - dst_type->flags = flags; - dst_type->direct_type = p2r_type_ptr_from_itype(lf->itype); - dst_type->byte_size = dst_type->direct_type ? dst_type->direct_type->byte_size : 0; - } - }break; - - //- rjf: POINTER - case CV_LeafKind_POINTER: - { - // TODO(rjf): if ptr_mode in {PtrMem, PtrMethod} then output a member pointer instead - - // rjf: unpack leaf - CV_LeafPointer *lf = (CV_LeafPointer *)itype_leaf_first; - RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->itype); - CV_PointerKind ptr_kind = CV_PointerAttribs_Extract_Kind(lf->attribs); - CV_PointerMode ptr_mode = CV_PointerAttribs_Extract_Mode(lf->attribs); - U32 ptr_size = CV_PointerAttribs_Extract_Size(lf->attribs); - - // rjf: cv -> rdi modifier flags - RDI_TypeModifierFlags modifier_flags = 0; - if(lf->attribs & CV_PointerAttrib_Const) {modifier_flags |= RDI_TypeModifierFlag_Const;} - if(lf->attribs & CV_PointerAttrib_Volatile) {modifier_flags |= RDI_TypeModifierFlag_Volatile;} - if(lf->attribs & CV_PointerAttrib_Restricted) {modifier_flags |= RDI_TypeModifierFlag_Restrict;} - - // rjf: cv info -> rdi pointer type kind - RDI_TypeKind type_kind = RDI_TypeKind_Ptr; - { - if(lf->attribs & CV_PointerAttrib_LRef) - { - type_kind = RDI_TypeKind_LRef; - } - else if(lf->attribs & CV_PointerAttrib_RRef) - { - type_kind = RDI_TypeKind_RRef; - } - if(ptr_mode == CV_PointerMode_LRef) - { - type_kind = RDI_TypeKind_LRef; - } - else if(ptr_mode == CV_PointerMode_RRef) - { - type_kind = RDI_TypeKind_RRef; - } - } - - // rjf: fill type - if(modifier_flags != 0) - { - RDIM_Type *pointer_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type->kind = RDI_TypeKind_Modifier; - dst_type->flags = modifier_flags; - dst_type->direct_type = pointer_type; - dst_type->byte_size = arch_addr_size; - pointer_type->kind = type_kind; - pointer_type->byte_size = arch_addr_size; - pointer_type->direct_type = direct_type; - } - else - { - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type->kind = type_kind; + dst_type->kind = RDI_TypeKind_Function; dst_type->byte_size = arch_addr_size; + dst_type->direct_type = ret_type; + + // rjf: unpack arglist range + CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-itype_first]; + if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || + arglist_range->hdr.size<2 || + arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + { + break; + } + U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; + U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; + if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) + { + break; + } + + // rjf: unpack arglist info + CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; + CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); + U32 arglist_itypes_count = arglist->count; + + // rjf: build param type array + RDIM_Type **params = push_array(arena, RDIM_Type *, arglist_itypes_count); + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + params[idx] = p2r_type_ptr_from_itype(arglist_itypes_base[idx]); + } + + // rjf: fill dst type + dst_type->count = arglist_itypes_count; + dst_type->param_types = params; + }break; + + //- rjf: MFUNCTION + case CV_LeafKind_MFUNCTION: + { + // TODO(rjf): handle call_kind & attribs + // TODO(rjf): preserve "this_adjust" + + // rjf: unpack leaf + CV_LeafMFunction *lf = (CV_LeafMFunction *)itype_leaf_first; + RDIM_Type *ret_type = p2r_type_ptr_from_itype(lf->ret_itype); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + dst_type->kind = (lf->this_itype != 0) ? RDI_TypeKind_Method : RDI_TypeKind_Function; + dst_type->byte_size = arch_addr_size; + dst_type->direct_type = ret_type; + + // rjf: unpack arglist range + CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-itype_first]; + if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || + arglist_range->hdr.size<2 || + arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + { + break; + } + U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; + U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; + if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) + { + break; + } + + // rjf: unpack arglist info + CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; + CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); + U32 arglist_itypes_count = arglist->count; + + // rjf: build param type array + U64 num_this_extras = 1; + if(lf->this_itype == 0) + { + num_this_extras = 0; + } + RDIM_Type **params = push_array(arena, RDIM_Type *, arglist_itypes_count+num_this_extras); + for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) + { + params[idx+num_this_extras] = p2r_type_ptr_from_itype(arglist_itypes_base[idx]); + } + if(lf->this_itype != 0) + { + params[0] = p2r_type_ptr_from_itype(lf->this_itype); + } + + // rjf: fill dst type + dst_type->count = arglist_itypes_count+num_this_extras; + dst_type->param_types = params; + }break; + + //- rjf: BITFIELD + case CV_LeafKind_BITFIELD: + { + // rjf: unpack leaf + CV_LeafBitField *lf = (CV_LeafBitField *)itype_leaf_first; + RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->itype); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Bitfield; + dst_type->off = lf->pos; + dst_type->count = lf->len; + dst_type->byte_size = direct_type?direct_type->byte_size:0; dst_type->direct_type = direct_type; - } - }break; - - //- rjf: PROCEDURE - case CV_LeafKind_PROCEDURE: - { - // TODO(rjf): handle call_kind & attribs + }break; - // rjf: unpack leaf - CV_LeafProcedure *lf = (CV_LeafProcedure *)itype_leaf_first; - RDIM_Type *ret_type = p2r_type_ptr_from_itype(lf->ret_itype); - - // rjf: fill type's basics - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type->kind = RDI_TypeKind_Function; - dst_type->byte_size = arch_addr_size; - dst_type->direct_type = ret_type; - - // rjf: unpack arglist range - CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-itype_first]; - if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || - arglist_range->hdr.size<2 || - arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) + //- rjf: ARRAY + case CV_LeafKind_ARRAY: { - break; - } - U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; - U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; - if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) - { - break; - } - - // rjf: unpack arglist info - CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; - CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); - U32 arglist_itypes_count = arglist->count; - - // rjf: build param type array - RDIM_Type **params = push_array(arena, RDIM_Type *, arglist_itypes_count); - for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) - { - params[idx] = p2r_type_ptr_from_itype(arglist_itypes_base[idx]); - } - - // rjf: fill dst type - dst_type->count = arglist_itypes_count; - dst_type->param_types = params; - }break; - - //- rjf: MFUNCTION - case CV_LeafKind_MFUNCTION: - { - // TODO(rjf): handle call_kind & attribs - // TODO(rjf): preserve "this_adjust" - - // rjf: unpack leaf - CV_LeafMFunction *lf = (CV_LeafMFunction *)itype_leaf_first; - RDIM_Type *ret_type = p2r_type_ptr_from_itype(lf->ret_itype); - - // rjf: fill type - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type->kind = (lf->this_itype != 0) ? RDI_TypeKind_Method : RDI_TypeKind_Function; - dst_type->byte_size = arch_addr_size; - dst_type->direct_type = ret_type; - - // rjf: unpack arglist range - CV_RecRange *arglist_range = &tpi_leaf->leaf_ranges.ranges[lf->arg_itype-itype_first]; - if(arglist_range->hdr.kind != CV_LeafKind_ARGLIST || - arglist_range->hdr.size<2 || - arglist_range->off + arglist_range->hdr.size > tpi_leaf->data.size) - { - break; - } - U8 *arglist_first = tpi_leaf->data.str + arglist_range->off + 2; - U8 *arglist_opl = arglist_first+arglist_range->hdr.size-2; - if(arglist_first + sizeof(CV_LeafArgList) > arglist_opl) - { - break; - } - - // rjf: unpack arglist info - CV_LeafArgList *arglist = (CV_LeafArgList*)arglist_first; - CV_TypeId *arglist_itypes_base = (CV_TypeId *)(arglist+1); - U32 arglist_itypes_count = arglist->count; - - // rjf: build param type array - U64 num_this_extras = 1; - if(lf->this_itype == 0) - { - num_this_extras = 0; - } - RDIM_Type **params = push_array(arena, RDIM_Type *, arglist_itypes_count+num_this_extras); - for(U32 idx = 0; idx < arglist_itypes_count; idx += 1) - { - params[idx+num_this_extras] = p2r_type_ptr_from_itype(arglist_itypes_base[idx]); - } - if(lf->this_itype != 0) - { - params[0] = p2r_type_ptr_from_itype(lf->this_itype); - } - - // rjf: fill dst type - dst_type->count = arglist_itypes_count+num_this_extras; - dst_type->param_types = params; - }break; - - //- rjf: BITFIELD - case CV_LeafKind_BITFIELD: - { - // rjf: unpack leaf - CV_LeafBitField *lf = (CV_LeafBitField *)itype_leaf_first; - RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->itype); - - // rjf: fill type - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type->kind = RDI_TypeKind_Bitfield; - dst_type->off = lf->pos; - dst_type->count = lf->len; - dst_type->byte_size = direct_type?direct_type->byte_size:0; - dst_type->direct_type = direct_type; - }break; - - //- rjf: ARRAY - case CV_LeafKind_ARRAY: - { - // rjf: unpack leaf - CV_LeafArray *lf = (CV_LeafArray *)itype_leaf_first; - RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->entry_itype); - U8 *numeric_ptr = (U8*)(lf + 1); - CV_NumericParsed array_count = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); - U64 full_size = cv_u64_from_numeric(&array_count); - - // rjf: fill type - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - dst_type->kind = RDI_TypeKind_Array; - dst_type->direct_type = direct_type; - dst_type->byte_size = full_size; - dst_type->count = (direct_type && direct_type->byte_size) ? (dst_type->byte_size/direct_type->byte_size) : 0; - }break; - - //- rjf: CLASS/STRUCTURE - case CV_LeafKind_CLASS: - case CV_LeafKind_STRUCTURE: - { - // TODO(rjf): handle props - - // rjf: unpack leaf - CV_LeafStruct *lf = (CV_LeafStruct *)itype_leaf_first; - U8 *numeric_ptr = (U8*)(lf + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); - U64 size_u64 = cv_u64_from_numeric(&size); - U8 *name_ptr = numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); - - // rjf: fill type - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - if(lf->props & CV_TypeProp_FwdRef) - { - dst_type->kind = (kind == CV_LeafKind_CLASS ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_IncompleteStruct); - dst_type->name = name; - } - else - { - dst_type->kind = (kind == CV_LeafKind_CLASS ? RDI_TypeKind_Class : RDI_TypeKind_Struct); - dst_type->byte_size = (U32)size_u64; - dst_type->name = name; - } - }break; - - //- rjf: CLASS2/STRUCT2 - case CV_LeafKind_CLASS2: - case CV_LeafKind_STRUCT2: - { - // TODO(rjf): handle props - - // rjf: unpack leaf - CV_LeafStruct2 *lf = (CV_LeafStruct2 *)itype_leaf_first; - U8 *numeric_ptr = (U8*)(lf + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); - U64 size_u64 = cv_u64_from_numeric(&size); - U8 *name_ptr = numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); - - // rjf: fill type - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - if(lf->props & CV_TypeProp_FwdRef) - { - dst_type->kind = (kind == CV_LeafKind_CLASS2 ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_IncompleteStruct); - dst_type->name = name; - } - else - { - dst_type->kind = (kind == CV_LeafKind_CLASS2 ? RDI_TypeKind_Class : RDI_TypeKind_Struct); - dst_type->byte_size = (U32)size_u64; - dst_type->name = name; - } - }break; - - //- rjf: UNION - case CV_LeafKind_UNION: - { - // TODO(rjf): handle props - - // rjf: unpack leaf - CV_LeafUnion *lf = (CV_LeafUnion *)itype_leaf_first; - U8 *numeric_ptr = (U8*)(lf + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); - U64 size_u64 = cv_u64_from_numeric(&size); - U8 *name_ptr = numeric_ptr + size.encoded_size; - String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); - - // rjf: fill type - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - if(lf->props & CV_TypeProp_FwdRef) - { - dst_type->kind = RDI_TypeKind_IncompleteUnion; - dst_type->name = name; - } - else - { - dst_type->kind = RDI_TypeKind_Union; - dst_type->byte_size = (U32)size_u64; - dst_type->name = name; - } - }break; - - //- rjf: ENUM - case CV_LeafKind_ENUM: - { - // TODO(rjf): handle props - - // rjf: unpack leaf - CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; - RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->base_itype); - U8 *name_ptr = (U8 *)(lf + 1); - String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); - - // rjf: fill type - dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); - if(lf->props & CV_TypeProp_FwdRef) - { - dst_type->kind = RDI_TypeKind_IncompleteEnum; - dst_type->name = name; - } - else - { - dst_type->kind = RDI_TypeKind_Enum; + // rjf: unpack leaf + CV_LeafArray *lf = (CV_LeafArray *)itype_leaf_first; + RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->entry_itype); + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed array_count = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U64 full_size = cv_u64_from_numeric(&array_count); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + dst_type->kind = RDI_TypeKind_Array; dst_type->direct_type = direct_type; - dst_type->byte_size = direct_type ? direct_type->byte_size : 0; - dst_type->name = name; - } - }break; + dst_type->byte_size = full_size; + dst_type->count = (direct_type && direct_type->byte_size) ? (dst_type->byte_size/direct_type->byte_size) : 0; + }break; + + //- rjf: CLASS/STRUCTURE + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + // TODO(rjf): handle props + + // rjf: unpack leaf + CV_LeafStruct *lf = (CV_LeafStruct *)itype_leaf_first; + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U64 size_u64 = cv_u64_from_numeric(&size); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + if(lf->props & CV_TypeProp_FwdRef) + { + dst_type->kind = (kind == CV_LeafKind_CLASS ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_IncompleteStruct); + dst_type->name = name; + } + else + { + dst_type->kind = (kind == CV_LeafKind_CLASS ? RDI_TypeKind_Class : RDI_TypeKind_Struct); + dst_type->byte_size = (U32)size_u64; + dst_type->name = name; + } + }break; + + //- rjf: CLASS2/STRUCT2 + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + // TODO(rjf): handle props + + // rjf: unpack leaf + CV_LeafStruct2 *lf = (CV_LeafStruct2 *)itype_leaf_first; + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U64 size_u64 = cv_u64_from_numeric(&size); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + if(lf->props & CV_TypeProp_FwdRef) + { + dst_type->kind = (kind == CV_LeafKind_CLASS2 ? RDI_TypeKind_IncompleteClass : RDI_TypeKind_IncompleteStruct); + dst_type->name = name; + } + else + { + dst_type->kind = (kind == CV_LeafKind_CLASS2 ? RDI_TypeKind_Class : RDI_TypeKind_Struct); + dst_type->byte_size = (U32)size_u64; + dst_type->name = name; + } + }break; + + //- rjf: UNION + case CV_LeafKind_UNION: + { + // TODO(rjf): handle props + + // rjf: unpack leaf + CV_LeafUnion *lf = (CV_LeafUnion *)itype_leaf_first; + U8 *numeric_ptr = (U8*)(lf + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, itype_leaf_opl); + U64 size_u64 = cv_u64_from_numeric(&size); + U8 *name_ptr = numeric_ptr + size.encoded_size; + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + if(lf->props & CV_TypeProp_FwdRef) + { + dst_type->kind = RDI_TypeKind_IncompleteUnion; + dst_type->name = name; + } + else + { + dst_type->kind = RDI_TypeKind_Union; + dst_type->byte_size = (U32)size_u64; + dst_type->name = name; + } + }break; + + //- rjf: ENUM + case CV_LeafKind_ENUM: + { + // TODO(rjf): handle props + + // rjf: unpack leaf + CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; + RDIM_Type *direct_type = p2r_type_ptr_from_itype(lf->base_itype); + U8 *name_ptr = (U8 *)(lf + 1); + String8 name = str8_cstring_capped(name_ptr, itype_leaf_opl); + + // rjf: fill type + dst_type = rdim_type_chunk_list_push(arena, &all_types, (U64)itype_opl); + if(lf->props & CV_TypeProp_FwdRef) + { + dst_type->kind = RDI_TypeKind_IncompleteEnum; + dst_type->name = name; + } + else + { + dst_type->kind = RDI_TypeKind_Enum; + dst_type->direct_type = direct_type; + dst_type->byte_size = direct_type ? direct_type->byte_size : 0; + dst_type->name = name; + } + }break; + } } + + //- rjf: store finalized type to this itype's slot + itype_type_ptrs[itype] = dst_type; } - - //- rjf: store finalized type to this itype's slot - itype_type_ptrs[itype] = dst_type; } } } - } + p2r_shared->itype_type_ptrs = itype_type_ptrs; + p2r_shared->basic_type_ptrs = basic_type_ptrs; + p2r_shared->all_types__pre_typedefs = all_types; #undef p2r_type_ptr_from_itype #undef p2r_builtin_type_ptr_from_kind + } + lane_sync(); + RDIM_Type **itype_type_ptrs = p2r_shared->itype_type_ptrs; + RDIM_Type **basic_type_ptrs = p2r_shared->basic_type_ptrs; + RDIM_TypeChunkList all_types__pre_typedefs = p2r_shared->all_types__pre_typedefs; ////////////////////////////////////////////////////////////// - //- rjf: types pass 4: kick off UDT build + //- rjf: types pass 4: build UDTs // - U64 udt_task_size_itypes = 4096; - U64 udt_tasks_count = ((U64)itype_opl+(udt_task_size_itypes-1))/udt_task_size_itypes; - P2R_UDTConvertIn *udt_tasks_inputs = push_array(scratch.arena, P2R_UDTConvertIn, udt_tasks_count); - ASYNC_Task **udt_tasks = push_array(scratch.arena, ASYNC_Task *, udt_tasks_count); - if(in->subset_flags & RDIM_SubsetFlag_UDTs) ProfScope("types pass 4: kick off UDT build") + ProfScope("types pass 4: build UDTs") { - for(U64 idx = 0; idx < udt_tasks_count; idx += 1) +#define p2r_type_ptr_from_itype(itype) ((itype_type_ptrs && (itype) < tpi_leaf->itype_opl) ? (itype_type_ptrs[(itype_fwd_map[(itype)] ? itype_fwd_map[(itype)] : (itype))]) : 0) + + //- rjf: set up + if(lane_idx() == 0) { - udt_tasks_inputs[idx].tpi_leaf = tpi_leaf; - udt_tasks_inputs[idx].itype_first = idx*udt_task_size_itypes; - udt_tasks_inputs[idx].itype_opl = udt_tasks_inputs[idx].itype_first + udt_task_size_itypes; - udt_tasks_inputs[idx].itype_opl = ClampTop(udt_tasks_inputs[idx].itype_opl, itype_opl); - udt_tasks_inputs[idx].itype_fwd_map = itype_fwd_map; - udt_tasks_inputs[idx].itype_type_ptrs = itype_type_ptrs; - udt_tasks[idx] = async_task_launch(scratch.arena, p2r_udt_convert_work, .input = &udt_tasks_inputs[idx]); + p2r_shared->lanes_udts = push_array(arena, RDIM_UDTChunkList, lane_count()); + } + lane_sync(); + + //- rjf: do wide fill + if(params->subset_flags & RDIM_SubsetFlag_Types && + params->subset_flags & RDIM_SubsetFlag_UDTs) + { + U64 udts_chunk_cap = 4096; + RDIM_UDTChunkList *udts = &p2r_shared->lanes_udts[lane_idx()]; + Rng1U64 range = lane_range(itype_opl); + for EachInRange(idx, range) + { + //- rjf: skip basics + CV_TypeId itype = (CV_TypeId)idx; + if(itype < itype_first) { continue; } + + //- rjf: grab type for this itype - skip if empty + RDIM_Type *dst_type = itype_type_ptrs[itype]; + if(dst_type == 0) { continue; } + + //- rjf: unpack itype leaf range - skip if out-of-range + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[itype-tpi_leaf->itype_first]; + CV_LeafKind kind = range->hdr.kind; + U64 header_struct_size = cv_header_struct_size_from_leaf_kind(kind); + U8 *itype_leaf_first = tpi_leaf->data.str + range->off+2; + U8 *itype_leaf_opl = itype_leaf_first + range->hdr.size-2; + if(range->off+range->hdr.size > tpi_leaf->data.size || + range->off+2+header_struct_size > tpi_leaf->data.size || + range->hdr.size < 2) + { + continue; + } + + //- rjf: build UDT + CV_TypeId field_itype = 0; + switch(kind) + { + default:{}break; + + //////////////////////// + //- rjf: structs/unions/classes -> equip members + // + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + CV_LeafStruct *lf = (CV_LeafStruct *)itype_leaf_first; + if(lf->props & CV_TypeProp_FwdRef) + { + break; + } + field_itype = lf->field_itype; + }goto equip_members; + case CV_LeafKind_UNION: + { + CV_LeafUnion *lf = (CV_LeafUnion *)itype_leaf_first; + if(lf->props & CV_TypeProp_FwdRef) + { + break; + } + field_itype = lf->field_itype; + }goto equip_members; + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + CV_LeafStruct2 *lf = (CV_LeafStruct2 *)itype_leaf_first; + if(lf->props & CV_TypeProp_FwdRef) + { + break; + } + field_itype = lf->field_itype; + }goto equip_members; + equip_members: + { + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: grab UDT info + RDIM_UDT *dst_udt = dst_type->udt; + if(dst_udt == 0) + { + dst_udt = dst_type->udt = rdim_udt_chunk_list_push(arena, udts, udts_chunk_cap); + dst_udt->self_type = dst_type; + } + + //- rjf: gather all fields + typedef struct FieldListTask FieldListTask; + struct FieldListTask + { + FieldListTask *next; + CV_TypeId itype; + }; + FieldListTask start_fl_task = {0, field_itype}; + FieldListTask *fl_todo_stack = &start_fl_task; + FieldListTask *fl_done_stack = 0; + for(;fl_todo_stack != 0;) + { + //- rjf: take & unpack task + FieldListTask *fl_task = fl_todo_stack; + SLLStackPop(fl_todo_stack); + SLLStackPush(fl_done_stack, fl_task); + CV_TypeId field_list_itype = fl_task->itype; + + //- rjf: skip bad itypes + if(field_list_itype < tpi_leaf->itype_first || tpi_leaf->itype_opl <= field_list_itype) + { + continue; + } + + //- rjf: field list itype -> range + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[field_list_itype-tpi_leaf->itype_first]; + + //- rjf: skip bad headers + if(range->off+range->hdr.size > tpi_leaf->data.size || + range->hdr.size < 2 || + range->hdr.kind != CV_LeafKind_FIELDLIST) + { + continue; + } + + //- rjf: loop over all fields + { + U8 *field_list_first = tpi_leaf->data.str+range->off+2; + U8 *field_list_opl = field_list_first+range->hdr.size-2; + for(U8 *read_ptr = field_list_first, *next_read_ptr = field_list_opl; + read_ptr < field_list_opl; + read_ptr = next_read_ptr) + { + // rjf: unpack field + CV_LeafKind field_kind = *(CV_LeafKind *)read_ptr; + U64 field_leaf_header_size = cv_header_struct_size_from_leaf_kind(field_kind); + U8 *field_leaf_first = read_ptr+2; + U8 *field_leaf_opl = field_list_opl; + next_read_ptr = field_leaf_opl; + + // rjf: skip out-of-bounds fields + if(field_leaf_first+field_leaf_header_size > field_list_opl) + { + continue; + } + + // rjf: process field + switch(field_kind) + { + //- rjf: unhandled/invalid cases + default: + { + // TODO(rjf): log + }break; + + //- rjf: INDEX + case CV_LeafKind_INDEX: + { + // rjf: unpack leaf + CV_LeafIndex *lf = (CV_LeafIndex *)field_leaf_first; + CV_TypeId new_itype = lf->itype; + + // rjf: bump next read pointer past header + next_read_ptr = (U8 *)(lf+1); + + // rjf: determine if index itype is new + B32 is_new = 1; + for(FieldListTask *t = fl_done_stack; t != 0; t = t->next) + { + if(t->itype == new_itype) + { + is_new = 0; + break; + } + } + + // rjf: if new -> push task to follow new itype + if(is_new) + { + FieldListTask *new_task = push_array(scratch.arena, FieldListTask, 1); + SLLStackPush(fl_todo_stack, new_task); + new_task->itype = new_itype; + } + }break; + + //- rjf: MEMBER + case CV_LeafKind_MEMBER: + { + // TODO(rjf): log on bad offset + + // rjf: unpack leaf + CV_LeafMember *lf = (CV_LeafMember *)field_leaf_first; + U8 *offset_ptr = (U8 *)(lf+1); + CV_NumericParsed offset = cv_numeric_from_data_range(offset_ptr, field_leaf_opl); + U64 offset64 = cv_u64_from_numeric(&offset); + U8 *name_ptr = offset_ptr + offset.encoded_size; + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_DataField; + mem->name = name; + mem->type = p2r_type_ptr_from_itype(lf->itype); + mem->off = (U32)offset64; + }break; + + //- rjf: STMEMBER + case CV_LeafKind_STMEMBER: + { + // TODO(rjf): handle attribs + + // rjf: unpack leaf + CV_LeafStMember *lf = (CV_LeafStMember *)field_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_StaticData; + mem->name = name; + mem->type = p2r_type_ptr_from_itype(lf->itype); + }break; + + //- rjf: METHOD + case CV_LeafKind_METHOD: + { + // rjf: unpack leaf + CV_LeafMethod *lf = (CV_LeafMethod *)field_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + //- rjf: method list itype -> range + CV_RecRange *method_list_range = &tpi_leaf->leaf_ranges.ranges[lf->list_itype-tpi_leaf->itype_first]; + + //- rjf: skip bad method lists + if(method_list_range->off+method_list_range->hdr.size > tpi_leaf->data.size || + method_list_range->hdr.size < 2 || + method_list_range->hdr.kind != CV_LeafKind_METHODLIST) + { + break; + } + + //- rjf: loop through all methods & emit members + U8 *method_list_first = tpi_leaf->data.str + method_list_range->off + 2; + U8 *method_list_opl = method_list_first + method_list_range->hdr.size-2; + for(U8 *method_read_ptr = method_list_first, *next_method_read_ptr = method_list_opl; + method_read_ptr < method_list_opl; + method_read_ptr = next_method_read_ptr) + { + CV_LeafMethodListMember *method = (CV_LeafMethodListMember*)method_read_ptr; + CV_MethodProp prop = CV_FieldAttribs_Extract_MethodProp(method->attribs); + RDIM_Type *method_type = p2r_type_ptr_from_itype(method->itype); + next_method_read_ptr = (U8 *)(method+1); + + // TODO(allen): PROBLEM + // We only get offsets for virtual functions (the "vbaseoff") from + // "Intro" and "PureIntro". In C++ inheritance, when we have a chain + // of inheritance (let's just talk single inheritance for now) the + // first class in the chain that introduces a new virtual function + // has this "Intro" method. If a later class in the chain redefines + // the virtual function it only has a "Virtual" method which does + // not update the offset. There is a "Virtual" and "PureVirtual" + // variant of "Virtual". The "Pure" in either case means there + // is no concrete procedure. When there is no "Pure" the method + // should have a corresponding procedure symbol id. + // + // The issue is we will want to mark all of our virtual methods as + // virtual and give them an offset, but that means we have to do + // some extra figuring to propogate offsets from "Intro" methods + // to "Virtual" methods in inheritance trees. That is - IF we want + // to start preserving the offsets of virtuals. There is room in + // the method struct to make this work, but for now I've just + // decided to drop this information. It is not urgently useful to + // us and greatly complicates matters. + + // rjf: read vbaseoff + U32 vbaseoff = 0; + if(prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro) + { + if(next_method_read_ptr+4 <= method_list_opl) + { + vbaseoff = *(U32 *)next_method_read_ptr; + } + next_method_read_ptr += 4; + } + + // rjf: emit method + switch(prop) + { + default: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_Method; + mem->name = name; + mem->type = method_type; + }break; + case CV_MethodProp_Static: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_StaticMethod; + mem->name = name; + mem->type = method_type; + }break; + case CV_MethodProp_Virtual: + case CV_MethodProp_PureVirtual: + case CV_MethodProp_Intro: + case CV_MethodProp_PureIntro: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_VirtualMethod; + mem->name = name; + mem->type = method_type; + }break; + } + } + + }break; + + //- rjf: ONEMETHOD + case CV_LeafKind_ONEMETHOD: + { + // TODO(rjf): handle attribs + + // rjf: unpack leaf + CV_LeafOneMethod *lf = (CV_LeafOneMethod *)field_leaf_first; + CV_MethodProp prop = CV_FieldAttribs_Extract_MethodProp(lf->attribs); + U8 *vbaseoff_ptr = (U8 *)(lf+1); + U8 *vbaseoff_opl_ptr = vbaseoff_ptr; + U32 vbaseoff = 0; + if(prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro) + { + vbaseoff = *(U32 *)(vbaseoff_ptr); + vbaseoff_opl_ptr += sizeof(U32); + } + U8 *name_ptr = vbaseoff_opl_ptr; + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + RDIM_Type *method_type = p2r_type_ptr_from_itype(lf->itype); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit method + switch(prop) + { + default: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_Method; + mem->name = name; + mem->type = method_type; + }break; + case CV_MethodProp_Static: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_StaticMethod; + mem->name = name; + mem->type = method_type; + }break; + case CV_MethodProp_Virtual: + case CV_MethodProp_PureVirtual: + case CV_MethodProp_Intro: + case CV_MethodProp_PureIntro: + { + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_VirtualMethod; + mem->name = name; + mem->type = method_type; + }break; + } + }break; + + //- rjf: NESTTYPE + case CV_LeafKind_NESTTYPE: + { + // rjf: unpack leaf + CV_LeafNestType *lf = (CV_LeafNestType *)field_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_NestedType; + mem->name = name; + mem->type = p2r_type_ptr_from_itype(lf->itype); + }break; + + //- rjf: NESTTYPEEX + case CV_LeafKind_NESTTYPEEX: + { + // TODO(rjf): handle attribs + + // rjf: unpack leaf + CV_LeafNestTypeEx *lf = (CV_LeafNestTypeEx *)field_leaf_first; + U8 *name_ptr = (U8 *)(lf+1); + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_NestedType; + mem->name = name; + mem->type = p2r_type_ptr_from_itype(lf->itype); + }break; + + //- rjf: BCLASS + case CV_LeafKind_BCLASS: + { + // TODO(rjf): log on bad offset + + // rjf: unpack leaf + CV_LeafBClass *lf = (CV_LeafBClass *)field_leaf_first; + U8 *offset_ptr = (U8 *)(lf+1); + CV_NumericParsed offset = cv_numeric_from_data_range(offset_ptr, field_leaf_opl); + U64 offset64 = cv_u64_from_numeric(&offset); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = offset_ptr+offset.encoded_size; + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_Base; + mem->type = p2r_type_ptr_from_itype(lf->itype); + mem->off = (U32)offset64; + }break; + + //- rjf: VBCLASS/IVBCLASS + case CV_LeafKind_VBCLASS: + case CV_LeafKind_IVBCLASS: + { + // TODO(rjf): log on bad offsets + // TODO(rjf): handle attribs + // TODO(rjf): offsets? + + // rjf: unpack leaf + CV_LeafVBClass *lf = (CV_LeafVBClass *)field_leaf_first; + U8 *num1_ptr = (U8 *)(lf+1); + CV_NumericParsed num1 = cv_numeric_from_data_range(num1_ptr, field_leaf_opl); + U8 *num2_ptr = num1_ptr + num1.encoded_size; + CV_NumericParsed num2 = cv_numeric_from_data_range(num2_ptr, field_leaf_opl); + + // rjf: bump next read pointer past header + next_read_ptr = (U8 *)(lf+1); + + // rjf: emit member + RDIM_UDTMember *mem = rdim_udt_push_member(arena, udts, dst_udt); + mem->kind = RDI_MemberKind_VirtualBase; + mem->type = p2r_type_ptr_from_itype(lf->itype); + }break; + + //- rjf: VFUNCTAB + case CV_LeafKind_VFUNCTAB: + { + CV_LeafVFuncTab *lf = (CV_LeafVFuncTab *)field_leaf_first; + + // rjf: bump next read pointer past header + next_read_ptr = (U8 *)(lf+1); + + // NOTE(rjf): currently no-op this case + (void)lf; + }break; + } + + // rjf: align-up next field + next_read_ptr = (U8 *)AlignPow2((U64)next_read_ptr, 4); + } + } + } + + scratch_end(scratch); + }break; + + //////////////////////// + //- rjf: enums -> equip enumerates + // + case CV_LeafKind_ENUM: + { + CV_LeafEnum *lf = (CV_LeafEnum *)itype_leaf_first; + if(lf->props & CV_TypeProp_FwdRef) + { + break; + } + field_itype = lf->field_itype; + }goto equip_enum_vals; + equip_enum_vals:; + { + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: grab UDT info + RDIM_UDT *dst_udt = dst_type->udt; + if(dst_udt == 0) + { + dst_udt = dst_type->udt = rdim_udt_chunk_list_push(arena, udts, udts_chunk_cap); + dst_udt->self_type = dst_type; + } + + //- rjf: gather all fields + typedef struct FieldListTask FieldListTask; + struct FieldListTask + { + FieldListTask *next; + CV_TypeId itype; + }; + FieldListTask start_fl_task = {0, field_itype}; + FieldListTask *fl_todo_stack = &start_fl_task; + FieldListTask *fl_done_stack = 0; + for(;fl_todo_stack != 0;) + { + //- rjf: take & unpack task + FieldListTask *fl_task = fl_todo_stack; + SLLStackPop(fl_todo_stack); + SLLStackPush(fl_done_stack, fl_task); + CV_TypeId field_list_itype = fl_task->itype; + + //- rjf: skip bad itypes + if(field_list_itype < tpi_leaf->itype_first || tpi_leaf->itype_opl <= field_list_itype) + { + continue; + } + + //- rjf: field list itype -> range + CV_RecRange *range = &tpi_leaf->leaf_ranges.ranges[field_list_itype-tpi_leaf->itype_first]; + + //- rjf: skip bad headers + if(range->off+range->hdr.size > tpi_leaf->data.size || + range->hdr.size < 2 || + range->hdr.kind != CV_LeafKind_FIELDLIST) + { + continue; + } + + //- rjf: loop over all fields + { + U8 *field_list_first = tpi_leaf->data.str+range->off+2; + U8 *field_list_opl = field_list_first+range->hdr.size-2; + for(U8 *read_ptr = field_list_first, *next_read_ptr = field_list_opl; + read_ptr < field_list_opl; + read_ptr = next_read_ptr) + { + // rjf: unpack field + CV_LeafKind field_kind = *(CV_LeafKind *)read_ptr; + U64 field_leaf_header_size = cv_header_struct_size_from_leaf_kind(field_kind); + U8 *field_leaf_first = read_ptr+2; + U8 *field_leaf_opl = field_leaf_first+range->hdr.size-2; + next_read_ptr = field_leaf_opl; + + // rjf: skip out-of-bounds fields + if(field_leaf_first+field_leaf_header_size > field_list_opl) + { + continue; + } + + // rjf: process field + switch(field_kind) + { + //- rjf: unhandled/invalid cases + default: + { + // TODO(rjf): log + }break; + + //- rjf: INDEX + case CV_LeafKind_INDEX: + { + // rjf: unpack leaf + CV_LeafIndex *lf = (CV_LeafIndex *)field_leaf_first; + CV_TypeId new_itype = lf->itype; + + // rjf: determine if index itype is new + B32 is_new = 1; + for(FieldListTask *t = fl_done_stack; t != 0; t = t->next) + { + if(t->itype == new_itype) + { + is_new = 0; + break; + } + } + + // rjf: if new -> push task to follow new itype + if(is_new) + { + FieldListTask *new_task = push_array(scratch.arena, FieldListTask, 1); + SLLStackPush(fl_todo_stack, new_task); + new_task->itype = new_itype; + } + }break; + + //- rjf: ENUMERATE + case CV_LeafKind_ENUMERATE: + { + // TODO(rjf): attribs + + // rjf: unpack leaf + CV_LeafEnumerate *lf = (CV_LeafEnumerate *)field_leaf_first; + U8 *val_ptr = (U8 *)(lf+1); + CV_NumericParsed val = cv_numeric_from_data_range(val_ptr, field_leaf_opl); + U64 val64 = cv_u64_from_numeric(&val); + U8 *name_ptr = val_ptr + val.encoded_size; + String8 name = str8_cstring_capped(name_ptr, field_leaf_opl); + + // rjf: bump next read pointer past variable length parts + next_read_ptr = name.str+name.size+1; + + // rjf: emit member + RDIM_UDTEnumVal *enum_val = rdim_udt_push_enum_val(arena, udts, dst_udt); + enum_val->name = name; + enum_val->val = val64; + }break; + } + + // rjf: align-up next field + next_read_ptr = (U8 *)AlignPow2((U64)next_read_ptr, 4); + } + } + } + + scratch_end(scratch); + }break; + } + } + } +#undef p2r_type_ptr_from_itype + } + lane_sync(); + RDIM_UDTChunkList *lanes_udts = p2r_shared->lanes_udts; + + ////////////////////////////////////////////////////////////// + //- rjf: join all UDTs + // + ProfScope("join all UDTs") if(lane_idx() == 0) + { + for EachIndex(idx, lane_count()) + { + rdim_udt_chunk_list_concat_in_place(&p2r_shared->all_udts, &lanes_udts[idx]); } } - - ////////////////////////////////////////////////////////////// - //- rjf: join link name map building task - // - P2R_LinkNameMap *link_name_map = 0; - ProfScope("join link name map building task") - { - async_task_join(link_name_map_task); - link_name_map = &link_name_map__in_progress; - } - - ////////////////////////////////////////////////////////////// - //- rjf: join unit conversion tasks - // - RDIM_UnitChunkList all_units = {0}; - RDIM_LineTableChunkList all_line_tables = {0}; - RDIM_LineTable **units_first_inline_site_line_tables = push_array(arena, RDIM_LineTable *, unit_convert_tasks_count); - ProfScope("join unit conversion & src file tasks") - { - for EachIndex(idx, unit_convert_tasks_count) - { - P2R_UnitConvertOut *out = async_task_join_struct(unit_convert_tasks[idx], P2R_UnitConvertOut); - rdim_unit_chunk_list_concat_in_place(&all_units, &out->units); - rdim_line_table_chunk_list_concat_in_place(&all_line_tables, &out->line_tables); - units_first_inline_site_line_tables[idx] = out->unit_first_inline_site_line_table; - } - } - - ////////////////////////////////////////////////////////////// - //- rjf: kick off source file line sequence equipping task - // - RDIM_SrcFileChunkList all_src_files = {0}; - { - P2R_SrcFileSeqEquipIn in = {all_src_files__sequenceless, all_line_tables}; - ASYNC_Task *task = async_task_launch(scratch.arena, p2r_src_file_seq_equip_work, .input = &in); - async_task_join(task); - all_src_files = in.src_files; - } + lane_sync(); + RDIM_UDTChunkList all_udts = p2r_shared->all_udts; ////////////////////////////////////////////////////////////// //- rjf: produce symbols from all streams // - RDIM_SymbolChunkList all_procedures = {0}; - RDIM_SymbolChunkList all_global_variables = {0}; - RDIM_SymbolChunkList all_thread_variables = {0}; - RDIM_SymbolChunkList all_constants = {0}; - RDIM_ScopeChunkList all_scopes = {0}; - RDIM_InlineSiteChunkList all_inline_sites = {0}; ProfScope("produce symbols from all streams") { +#define p2r_type_ptr_from_itype(itype) ((itype_type_ptrs && (itype) < itype_opl) ? (itype_type_ptrs[(itype_fwd_map[(itype)] ? itype_fwd_map[(itype)] : (itype))]) : 0) + //////////////////////////// - //- rjf: kick off all symbol conversion tasks + //- rjf: set up // - U64 global_stream_subdivision_tasks_count = sym ? (sym->sym_ranges.count+16383)/16384 : 0; - U64 global_stream_syms_per_task = sym ? sym->sym_ranges.count/global_stream_subdivision_tasks_count : 0; - U64 tasks_count = comp_unit_count + global_stream_subdivision_tasks_count; - P2R_SymbolStreamConvertIn *tasks_inputs = push_array(scratch.arena, P2R_SymbolStreamConvertIn, tasks_count); - ASYNC_Task **tasks = push_array(scratch.arena, ASYNC_Task *, tasks_count); - ProfScope("kick off all symbol conversion tasks") + if(lane_idx() == 0) { - for(U64 idx = 0; idx < tasks_count; idx += 1) + p2r_shared->syms_locations = push_array(arena, RDIM_LocationChunkList, all_syms_count); + p2r_shared->syms_procedures = push_array(arena, RDIM_SymbolChunkList, all_syms_count); + p2r_shared->syms_global_variables = push_array(arena, RDIM_SymbolChunkList, all_syms_count); + p2r_shared->syms_thread_variables = push_array(arena, RDIM_SymbolChunkList, all_syms_count); + p2r_shared->syms_constants = push_array(arena, RDIM_SymbolChunkList, all_syms_count); + p2r_shared->syms_scopes = push_array(arena, RDIM_ScopeChunkList, all_syms_count); + p2r_shared->syms_inline_sites = push_array(arena, RDIM_InlineSiteChunkList, all_syms_count); + p2r_shared->syms_typedefs = push_array(arena, RDIM_TypeChunkList, all_syms_count); + p2r_shared->sym_lane_take_counter = 0; + } + lane_sync(); + + //////////////////////////// + //- rjf: fill outputs for all unit sym blocks in this lane + // + if(params->subset_flags & (RDIM_SubsetFlag_Procedures| + RDIM_SubsetFlag_GlobalVariables| + RDIM_SubsetFlag_ThreadVariables| + RDIM_SubsetFlag_Scopes| + RDIM_SubsetFlag_Locals| + RDIM_SubsetFlag_GlobalVariableNameMap| + RDIM_SubsetFlag_ThreadVariableNameMap| + RDIM_SubsetFlag_ProcedureNameMap| + RDIM_SubsetFlag_ConstantNameMap| + RDIM_SubsetFlag_LinkNameProcedureNameMap| + RDIM_SubsetFlag_Types)) + { + for(;;) { - tasks_inputs[idx].arch = arch; - tasks_inputs[idx].coff_sections = coff_sections; - tasks_inputs[idx].tpi_hash = tpi_hash; - tasks_inputs[idx].tpi_leaf = tpi_leaf; - tasks_inputs[idx].ipi_leaf = ipi_leaf; - tasks_inputs[idx].itype_fwd_map = itype_fwd_map; - tasks_inputs[idx].itype_type_ptrs = itype_type_ptrs; - tasks_inputs[idx].link_name_map = link_name_map; - if(idx < global_stream_subdivision_tasks_count) + //- rjf: take next sym + U64 sym_num = ins_atomic_u64_inc_eval(&p2r_shared->sym_lane_take_counter); + if(sym_num > all_syms_count) { - tasks_inputs[idx].parsing_global_stream = 1; - tasks_inputs[idx].sym = sym; - tasks_inputs[idx].sym_ranges_first = idx*global_stream_syms_per_task; - tasks_inputs[idx].sym_ranges_opl = tasks_inputs[idx].sym_ranges_first + global_stream_syms_per_task; - tasks_inputs[idx].sym_ranges_opl = ClampTop(tasks_inputs[idx].sym_ranges_opl, sym->sym_ranges.count); + break; } - else + U64 sym_idx = sym_num-1; + + //- rjf: unpack sym + Temp scratch = scratch_begin(&arena, 1); + CV_SymParsed *sym = all_syms[sym_idx]; + Rng1U64 sym_rec_range = r1u64(0, sym->sym_ranges.count); + U64 sym_locations_chunk_cap = 16384; + U64 sym_procedures_chunk_cap = 16384; + U64 sym_global_variables_chunk_cap = 16384; + U64 sym_thread_variables_chunk_cap = 16384; + U64 sym_constants_chunk_cap = 16384; + U64 sym_scopes_chunk_cap = 16384; + U64 sym_inline_sites_chunk_cap = 16384; + RDIM_LocationChunkList *sym_locations = &p2r_shared->syms_locations[sym_idx]; + RDIM_SymbolChunkList *sym_procedures = &p2r_shared->syms_procedures[sym_idx]; + RDIM_SymbolChunkList *sym_global_variables = &p2r_shared->syms_global_variables[sym_idx]; + RDIM_SymbolChunkList *sym_thread_variables = &p2r_shared->syms_thread_variables[sym_idx]; + RDIM_SymbolChunkList *sym_constants = &p2r_shared->syms_constants[sym_idx]; + RDIM_ScopeChunkList *sym_scopes = &p2r_shared->syms_scopes[sym_idx]; + RDIM_InlineSiteChunkList *sym_inline_sites = &p2r_shared->syms_inline_sites[sym_idx]; + RDIM_TypeChunkList *typedefs = &p2r_shared->syms_typedefs[sym_idx]; + + ////////////////////////// + //- rjf: symbols pass 1: produce procedure frame info map (procedure -> frame info) + // + U64 procedure_frameprocs_count = 0; + U64 procedure_frameprocs_cap = dim_1u64(sym_rec_range); + CV_SymFrameproc **procedure_frameprocs = push_array_no_zero(scratch.arena, CV_SymFrameproc *, procedure_frameprocs_cap); + ProfScope("symbols pass 1: produce procedure frame info map (procedure -> frame info)") { - tasks_inputs[idx].sym = sym_for_unit[idx-global_stream_subdivision_tasks_count]; - tasks_inputs[idx].sym_ranges_first= 0; - tasks_inputs[idx].sym_ranges_opl = sym_for_unit[idx-global_stream_subdivision_tasks_count]->sym_ranges.count; - tasks_inputs[idx].first_inline_site_line_table = units_first_inline_site_line_tables[idx-global_stream_subdivision_tasks_count]; + U64 procedure_num = 0; + CV_RecRange *rec_ranges_first = sym->sym_ranges.ranges + sym_rec_range.min; + CV_RecRange *rec_ranges_opl = sym->sym_ranges.ranges + sym_rec_range.max; + for(CV_RecRange *rec_range = rec_ranges_first; + rec_range < rec_ranges_opl; + rec_range += 1) + { + //- rjf: rec range -> symbol info range + U64 sym_off_first = rec_range->off + 2; + U64 sym_off_opl = rec_range->off + rec_range->hdr.size; + + //- rjf: skip invalid ranges + if(sym_off_opl > sym->data.size || sym_off_first > sym->data.size || sym_off_first > sym_off_opl) + { + continue; + } + + //- rjf: unpack symbol info + CV_SymKind kind = rec_range->hdr.kind; + U64 sym_header_struct_size = cv_header_struct_size_from_sym_kind(kind); + void *sym_header_struct_base = sym->data.str + sym_off_first; + + //- rjf: skip bad sizes + if(sym_off_first + sym_header_struct_size > sym_off_opl) + { + continue; + } + + //- rjf: consume symbol based on kind + switch(kind) + { + default:{}break; + + //- rjf: FRAMEPROC + case CV_SymKind_FRAMEPROC: + { + if(procedure_num == 0) { break; } + if(procedure_num > procedure_frameprocs_cap) { break; } + CV_SymFrameproc *frameproc = (CV_SymFrameproc*)sym_header_struct_base; + procedure_frameprocs[procedure_num-1] = frameproc; + procedure_frameprocs_count = Max(procedure_frameprocs_count, procedure_num); + }break; + + //- rjf: LPROC32/GPROC32 + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + procedure_num += 1; + }break; + } + } + U64 scratch_overkill = sizeof(procedure_frameprocs[0])*(procedure_frameprocs_cap-procedure_frameprocs_count); + arena_pop(scratch.arena, scratch_overkill); } - tasks[idx] = async_task_launch(scratch.arena, p2r_symbol_stream_convert_work, .input = &tasks_inputs[idx]); + + ////////////////////////// + //- rjf: symbols pass 2: construct all symbols, given procedure frame info map + // + ProfScope("symbols pass 2: construct all symbols, given procedure frame info map") + { + RDIM_Local *defrange_target = 0; + B32 defrange_target_is_param = 0; + U64 procedure_num = 0; + U64 procedure_base_voff = 0; + CV_RecRange *rec_ranges_first = sym->sym_ranges.ranges + sym_rec_range.min; + CV_RecRange *rec_ranges_opl = sym->sym_ranges.ranges + sym_rec_range.max; + typedef struct P2R_ScopeNode P2R_ScopeNode; + struct P2R_ScopeNode + { + P2R_ScopeNode *next; + RDIM_Scope *scope; + }; + P2R_ScopeNode *top_scope_node = 0; + P2R_ScopeNode *free_scope_node = 0; + RDIM_LineTable *inline_site_line_table = sym_idx > 0 ? units_first_inline_site_line_tables[sym_idx-1] : 0; + for(CV_RecRange *rec_range = rec_ranges_first; + rec_range < rec_ranges_opl; + rec_range += 1) + { + //- rjf: rec range -> symbol info range + U64 sym_off_first = rec_range->off + 2; + U64 sym_off_opl = rec_range->off + rec_range->hdr.size; + + //- rjf: skip invalid ranges + if(sym_off_opl > sym->data.size || sym_off_first > sym->data.size || sym_off_first > sym_off_opl) + { + continue; + } + + //- rjf: unpack symbol info + CV_SymKind kind = rec_range->hdr.kind; + U64 sym_header_struct_size = cv_header_struct_size_from_sym_kind(kind); + void *sym_header_struct_base = sym->data.str + sym_off_first; + void *sym_data_opl = sym->data.str + sym_off_opl; + + //- rjf: skip bad sizes + if(sym_off_first + sym_header_struct_size > sym_off_opl) + { + continue; + } + + //- rjf: consume symbol based on kind + switch(kind) + { + default:{}break; + + //- rjf: END + case CV_SymKind_END: + { + P2R_ScopeNode *n = top_scope_node; + if(n != 0) + { + SLLStackPop(top_scope_node); + SLLStackPush(free_scope_node, n); + } + defrange_target = 0; + defrange_target_is_param = 0; + }break; + + //- rjf: BLOCK32 + case CV_SymKind_BLOCK32: + { + // rjf: unpack sym + CV_SymBlock32 *block32 = (CV_SymBlock32 *)sym_header_struct_base; + + // rjf: build scope, insert into current parent scope + RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, sym_scopes, sym_scopes_chunk_cap); + { + if(top_scope_node == 0) + { + // TODO(rjf): log + } + if(top_scope_node != 0) + { + RDIM_Scope *top_scope = top_scope_node->scope; + SLLQueuePush_N(top_scope->first_child, top_scope->last_child, scope, next_sibling); + scope->parent_scope = top_scope; + scope->symbol = top_scope->symbol; + } + COFF_SectionHeader *section = (0 < block32->sec && block32->sec <= coff_sections.count) ? &coff_sections.v[block32->sec-1] : 0; + if(section != 0) + { + U64 voff_first = section->voff + block32->off; + U64 voff_last = voff_first + block32->len; + RDIM_Rng1U64 voff_range = {voff_first, voff_last}; + rdim_scope_push_voff_range(arena, sym_scopes, scope, voff_range); + } + } + + // rjf: push this scope to scope stack + { + P2R_ScopeNode *node = free_scope_node; + if(node != 0) { SLLStackPop(free_scope_node); } + else { node = push_array_no_zero(scratch.arena, P2R_ScopeNode, 1); } + node->scope = scope; + SLLStackPush(top_scope_node, node); + } + }break; + + //- rjf: LDATA32/GDATA32 + case CV_SymKind_LDATA32: + case CV_SymKind_GDATA32: + { + // rjf: unpack sym + CV_SymData32 *data32 = (CV_SymData32 *)sym_header_struct_base; + String8 name = str8_cstring_capped(data32+1, sym_data_opl); + COFF_SectionHeader *section = (0 < data32->sec && data32->sec <= coff_sections.count) ? &coff_sections.v[data32->sec-1] : 0; + U64 voff = (section ? section->voff : 0) + data32->off; + + // rjf: determine if this is an exact duplicate global + // + // PDB likes to have duplicates of these spread across different + // symbol streams so we deduplicate across the entire translation + // context. + // + B32 is_duplicate = 0; + { + // TODO(rjf): @important global symbol dedup + } + + // rjf: is not duplicate -> push new global + if(!is_duplicate) + { + // rjf: unpack global variable's type + RDIM_Type *type = p2r_type_ptr_from_itype(data32->itype); + + // rjf: unpack global's container type + RDIM_Type *container_type = 0; + U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); + if(container_name_opl > 2) + { + String8 container_name = str8(name.str, container_name_opl - 2); + CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0); + container_type = p2r_type_ptr_from_itype(cv_type_id); + } + + // rjf: unpack global's container symbol + RDIM_Symbol *container_symbol = 0; + if(container_type == 0 && top_scope_node != 0) + { + container_symbol = top_scope_node->scope->symbol; + } + + // rjf: build symbol + RDIM_Symbol *symbol = rdim_symbol_chunk_list_push(arena, sym_global_variables, sym_global_variables_chunk_cap); + symbol->is_extern = (kind == CV_SymKind_GDATA32); + symbol->name = name; + symbol->type = type; + symbol->offset = voff; + symbol->container_symbol = container_symbol; + symbol->container_type = container_type; + } + }break; + + //- rjf: UDT (typedefs) + case CV_SymKind_UDT: + if(sym == all_syms[0] && top_scope_node == 0) + { + if(params->subset_flags & (RDIM_SubsetFlag_Types|RDIM_SubsetFlag_UDTs|RDIM_SubsetFlag_TypeNameMap)) + { + CV_SymUDT *udt = (CV_SymUDT *)sym_header_struct_base; + String8 name = str8_cstring_capped(udt+1, sym_data_opl); + RDIM_Type *type = rdim_type_chunk_list_push(arena, typedefs, 4096); + type->kind = RDI_TypeKind_Alias; + type->name = name; + type->direct_type = p2r_type_ptr_from_itype(udt->itype); + if(type->direct_type != 0) + { + type->byte_size = type->direct_type->byte_size; + } + } + }break; + + //- rjf: LPROC32/GPROC32 + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + // rjf: unpack sym + CV_SymProc32 *proc32 = (CV_SymProc32 *)sym_header_struct_base; + String8 name = str8_cstring_capped(proc32+1, sym_data_opl); + RDIM_Type *type = p2r_type_ptr_from_itype(proc32->itype); + + // rjf: unpack proc's container type + RDIM_Type *container_type = 0; + U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); + if(container_name_opl > 2 && tpi_hash != 0 && tpi_leaf != 0) + { + String8 container_name = str8(name.str, container_name_opl - 2); + CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0); + container_type = p2r_type_ptr_from_itype(cv_type_id); + } + + // rjf: unpack proc's container symbol + RDIM_Symbol *container_symbol = 0; + if(container_type == 0 && top_scope_node != 0) + { + container_symbol = top_scope_node->scope->symbol; + } + + // rjf: build procedure's root scope + // + // NOTE: even if there could be a containing scope at this point (which should be + // illegal in C/C++ but not necessarily in another language) we would not use + // it here because these scopes refer to the ranges of code that make up a + // procedure *not* the namespaces, so a procedure's root scope always has + // no parent. + RDIM_Scope *procedure_root_scope = 0; + if(params->subset_flags & RDIM_SubsetFlag_Scopes) + { + procedure_root_scope = rdim_scope_chunk_list_push(arena, sym_scopes, sym_scopes_chunk_cap); + COFF_SectionHeader *section = (0 < proc32->sec && proc32->sec <= coff_sections.count) ? &coff_sections.v[proc32->sec-1] : 0; + if(section != 0) + { + U64 voff_first = section->voff + proc32->off; + U64 voff_last = voff_first + proc32->len; + RDIM_Rng1U64 voff_range = {voff_first, voff_last}; + rdim_scope_push_voff_range(arena, sym_scopes, procedure_root_scope, voff_range); + procedure_base_voff = voff_first; + } + } + + // rjf: root scope voff minimum range -> link name + String8 link_name = {0}; + if(procedure_root_scope && procedure_root_scope->voff_ranges.min != 0) + { + U64 voff = procedure_root_scope->voff_ranges.min; + U64 hash = p2r_hash_from_voff(voff); + U64 bucket_idx = hash%link_name_map.buckets_count; + P2R_LinkNameNode *node = 0; + for(P2R_LinkNameNode *n = link_name_map.buckets[bucket_idx]; n != 0; n = n->next) + { + if(n->voff == voff) + { + link_name = n->name; + break; + } + } + } + + // rjf: build procedure symbol + if(params->subset_flags & (RDIM_SubsetFlag_Procedures|RDIM_SubsetFlag_ProcedureNameMap)) + { + RDIM_Symbol *procedure_symbol = rdim_symbol_chunk_list_push(arena, sym_procedures, sym_procedures_chunk_cap); + procedure_symbol->is_extern = (kind == CV_SymKind_GPROC32); + procedure_symbol->name = name; + procedure_symbol->link_name = link_name; + procedure_symbol->type = type; + procedure_symbol->container_symbol = container_symbol; + procedure_symbol->container_type = container_type; + procedure_symbol->root_scope = procedure_root_scope; + if(procedure_root_scope != 0) + { + procedure_root_scope->symbol = procedure_symbol; + } + } + + // rjf: push scope to scope stack + if(procedure_root_scope) + { + P2R_ScopeNode *node = free_scope_node; + if(node != 0) { SLLStackPop(free_scope_node); } + else { node = push_array_no_zero(scratch.arena, P2R_ScopeNode, 1); } + node->scope = procedure_root_scope; + SLLStackPush(top_scope_node, node); + } + + // rjf: increment procedure counter + procedure_num += 1; + }break; + + //- rjf: REGREL32 + case CV_SymKind_REGREL32: + if(params->subset_flags & RDIM_SubsetFlag_Locals) + { + // TODO(rjf): apparently some of the information here may end up being + // redundant with "better" information from CV_SymKind_LOCAL record. + // we don't currently handle this, but if those cases arise then it + // will obviously be better to prefer the better information from both + // records. + + // rjf: no containing scope? -> malformed data; locals cannot be produced + // outside of a containing scope + if(top_scope_node == 0) + { + break; + } + + // rjf: unpack sym + CV_SymRegrel32 *regrel32 = (CV_SymRegrel32 *)sym_header_struct_base; + String8 name = str8_cstring_capped(regrel32+1, sym_data_opl); + RDIM_Type *type = p2r_type_ptr_from_itype(regrel32->itype); + CV_Reg cv_reg = regrel32->reg; + U32 var_off = regrel32->reg_off; + + // rjf: determine if this is a parameter + RDI_LocalKind local_kind = RDI_LocalKind_Variable; + { + B32 is_stack_reg = 0; + switch(arch) + { + default:{}break; + case RDI_Arch_X86:{is_stack_reg = (cv_reg == CV_Regx86_ESP);}break; + case RDI_Arch_X64:{is_stack_reg = (cv_reg == CV_Regx64_RSP);}break; + } + if(is_stack_reg) + { + U32 frame_size = 0xFFFFFFFF; + if(procedure_num != 0 && procedure_frameprocs[procedure_num-1] != 0 && procedure_num <= procedure_frameprocs_count) + { + CV_SymFrameproc *frameproc = procedure_frameprocs[procedure_num-1]; + frame_size = frameproc->frame_size; + } + if(var_off > frame_size) + { + local_kind = RDI_LocalKind_Parameter; + } + } + } + + // TODO(rjf): is this correct? + // rjf: redirect type, if 0, and if outside frame, to the return type of the + // containing procedure + if(local_kind == RDI_LocalKind_Parameter && regrel32->itype == 0 && + top_scope_node->scope->symbol != 0 && + top_scope_node->scope->symbol->type != 0) + { + type = top_scope_node->scope->symbol->type->direct_type; + } + + // rjf: build local + RDIM_Scope *scope = top_scope_node->scope; + RDIM_Local *local = rdim_scope_push_local(arena, sym_scopes, scope); + local->kind = local_kind; + local->name = name; + local->type = type; + + // rjf: add location info to local + if(type != 0) + { + // rjf: determine if we need an extra indirection to the value + B32 extra_indirection_to_value = 0; + switch(arch) + { + case RDI_Arch_X86: + { + extra_indirection_to_value = (local_kind == RDI_LocalKind_Parameter && (type->byte_size > 4 || !IsPow2OrZero(type->byte_size))); + }break; + case RDI_Arch_X64: + { + extra_indirection_to_value = (local_kind == RDI_LocalKind_Parameter && (type->byte_size > 8 || !IsPow2OrZero(type->byte_size))); + }break; + } + + // rjf: get raddbg register code + RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg); + // TODO(rjf): real byte_size & byte_pos from cv_reg goes here + U32 byte_size = 8; + U32 byte_pos = 0; + + // rjf: build location + RDIM_LocationInfo loc_info = p2r_location_info_from_addr_reg_off(arena, arch, reg_code, byte_size, byte_pos, (S64)(S32)var_off, extra_indirection_to_value); + RDIM_Location *loc2 = rdim_location_chunk_list_push_new(arena, sym_locations, sym_locations_chunk_cap, &loc_info); + RDIM_Rng1U64 voff_range = {0, max_U64}; + rdim_local_push_location_case(arena, sym_scopes, local, loc2, voff_range); + } + }break; + + //- rjf: LTHREAD32/GTHREAD32 + case CV_SymKind_LTHREAD32: + case CV_SymKind_GTHREAD32: + if(params->subset_flags & (RDIM_SubsetFlag_ThreadVariables|RDIM_SubsetFlag_ThreadVariableNameMap)) + { + // rjf: unpack sym + CV_SymThread32 *thread32 = (CV_SymThread32 *)sym_header_struct_base; + String8 name = str8_cstring_capped(thread32+1, sym_data_opl); + U32 tls_off = thread32->tls_off; + RDIM_Type *type = p2r_type_ptr_from_itype(thread32->itype); + + // rjf: unpack thread variable's container type + RDIM_Type *container_type = 0; + U64 container_name_opl = p2r_end_of_cplusplus_container_name(name); + if(container_name_opl > 2) + { + String8 container_name = str8(name.str, container_name_opl - 2); + CV_TypeId cv_type_id = pdb_tpi_first_itype_from_name(tpi_hash, tpi_leaf, container_name, 0); + container_type = p2r_type_ptr_from_itype(cv_type_id); + } + + // rjf: unpack thread variable's container symbol + RDIM_Symbol *container_symbol = 0; + if(container_type == 0 && top_scope_node != 0) + { + container_symbol = top_scope_node->scope->symbol; + } + + // rjf: build symbol + RDIM_Symbol *tvar = rdim_symbol_chunk_list_push(arena, sym_thread_variables, sym_thread_variables_chunk_cap); + tvar->name = name; + tvar->type = type; + tvar->is_extern = (kind == CV_SymKind_GTHREAD32); + tvar->offset = tls_off; + tvar->container_type = container_type; + tvar->container_symbol = container_symbol; + }break; + + //- rjf: LOCAL + case CV_SymKind_LOCAL: + if(params->subset_flags & (RDIM_SubsetFlag_Locals)) + { + // rjf: no containing scope? -> malformed data; locals cannot be produced + // outside of a containing scope + if(top_scope_node == 0) + { + break; + } + + // rjf: unpack sym + CV_SymLocal *slocal = (CV_SymLocal *)sym_header_struct_base; + String8 name = str8_cstring_capped(slocal+1, sym_data_opl); + RDIM_Type *type = p2r_type_ptr_from_itype(slocal->itype); + + // rjf: determine if this symbol encodes the beginning of a global modification + B32 is_global_modification = 0; + if((slocal->flags & CV_LocalFlag_Global) || + (slocal->flags & CV_LocalFlag_Static)) + { + is_global_modification = 1; + } + + // rjf: is global modification -> emit global modification symbol + if(is_global_modification) + { + // TODO(rjf): add global modification symbols + defrange_target = 0; + defrange_target_is_param = 0; + } + + // rjf: is not a global modification -> emit a local variable + if(!is_global_modification) + { + // rjf: determine local kind + RDI_LocalKind local_kind = RDI_LocalKind_Variable; + if(slocal->flags & CV_LocalFlag_Param) + { + local_kind = RDI_LocalKind_Parameter; + } + + // rjf: build local + RDIM_Scope *scope = top_scope_node->scope; + RDIM_Local *local = rdim_scope_push_local(arena, sym_scopes, scope); + local->kind = local_kind; + local->name = name; + local->type = type; + + // rjf: save defrange target, for subsequent defrange symbols + defrange_target = local; + defrange_target_is_param = (local_kind == RDI_LocalKind_Parameter); + } + }break; + + //- rjf: DEFRANGE_REGISTER + case CV_SymKind_DEFRANGE_REGISTER: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeRegister *defrange_register = (CV_SymDefrangeRegister*)sym_header_struct_base; + CV_Reg cv_reg = defrange_register->reg; + CV_LvarAddrRange *range = &defrange_register->range; + COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= coff_sections.count) ? &coff_sections.v[range->sec-1] : 0; + CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_register+1); + U64 gap_count = ((U8*)sym_data_opl - (U8*)gaps) / sizeof(*gaps); + RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg); + + // rjf: build location + RDIM_LocationInfo loc_info = {RDI_LocationKind_ValReg, reg_code}; + RDIM_Location *loc = rdim_location_chunk_list_push_new(arena, sym_locations, sym_locations_chunk_cap, &loc_info); + + // rjf: emit locations over ranges + p2r_local_push_location_cases_over_lvar_addr_range(arena, sym_scopes, defrange_target, loc, range, range_section, gaps, gap_count); + }break; + + //- rjf: DEFRANGE_FRAMEPOINTER_REL + case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: find current procedure's frameproc + CV_SymFrameproc *frameproc = 0; + if(procedure_num != 0 && procedure_num <= procedure_frameprocs_count && procedure_frameprocs[procedure_num-1] != 0) + { + frameproc = procedure_frameprocs[procedure_num-1]; + } + + // rjf: no current valid frameproc? -> somehow we got a to a framepointer-relative defrange + // without having an actually active procedure - break + if(frameproc == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeFramepointerRel *defrange_fprel = (CV_SymDefrangeFramepointerRel*)sym_header_struct_base; + CV_LvarAddrRange *range = &defrange_fprel->range; + COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= coff_sections.count) ? &coff_sections.v[range->sec-1] : 0; + CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_fprel + 1); + U64 gap_count = ((U8*)sym_data_opl - (U8*)gaps) / sizeof(*gaps); + + // rjf: select frame pointer register + CV_EncodedFramePtrReg encoded_fp_reg = cv_pick_fp_encoding(frameproc, defrange_target_is_param); + RDI_RegCode fp_register_code = p2r_reg_code_from_arch_encoded_fp_reg(arch, encoded_fp_reg); + + // rjf: build location + B32 extra_indirection = 0; + U32 byte_size = rdi_addr_size_from_arch(arch); + U32 byte_pos = 0; + S64 var_off = (S64)defrange_fprel->off; + RDIM_LocationInfo location_info = p2r_location_info_from_addr_reg_off(arena, arch, fp_register_code, byte_size, byte_pos, var_off, extra_indirection); + RDIM_Location *location = rdim_location_chunk_list_push_new(arena, sym_locations, sym_locations_chunk_cap, &location_info); + + // rjf: emit locations over ranges + p2r_local_push_location_cases_over_lvar_addr_range(arena, sym_scopes, defrange_target, location, range, range_section, gaps, gap_count); + }break; + + //- rjf: DEFRANGE_SUBFIELD_REGISTER + case CV_SymKind_DEFRANGE_SUBFIELD_REGISTER: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeSubfieldRegister *defrange_subfield_register = (CV_SymDefrangeSubfieldRegister*)sym_header_struct_base; + CV_Reg cv_reg = defrange_subfield_register->reg; + CV_LvarAddrRange *range = &defrange_subfield_register->range; + COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= coff_sections.count) ? &coff_sections.v[range->sec-1] : 0; + CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_subfield_register + 1); + U64 gap_count = ((U8*)sym_data_opl - (U8*)gaps) / sizeof(*gaps); + RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg); + + // rjf: skip "subfield" location info - currently not supported + if(defrange_subfield_register->field_offset != 0) + { + break; + } + + // rjf: build location + RDIM_LocationInfo loc_info = {RDI_LocationKind_ValReg, reg_code}; + RDIM_Location *loc = rdim_location_chunk_list_push_new(arena, sym_locations, sym_locations_chunk_cap, &loc_info); + + // rjf: emit locations over ranges + p2r_local_push_location_cases_over_lvar_addr_range(arena, sym_scopes, defrange_target, loc, range, range_section, gaps, gap_count); + }break; + + //- rjf: DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE + case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: find current procedure's frameproc + CV_SymFrameproc *frameproc = 0; + if(procedure_num != 0 && procedure_num <= procedure_frameprocs_count && procedure_frameprocs[procedure_num-1] != 0) + { + frameproc = procedure_frameprocs[procedure_num-1]; + } + + // rjf: no current valid frameproc? -> somehow we got a to a framepointer-relative defrange + // without having an actually active procedure - break + if(frameproc == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeFramepointerRelFullScope *defrange_fprel_full_scope = (CV_SymDefrangeFramepointerRelFullScope*)sym_header_struct_base; + CV_EncodedFramePtrReg encoded_fp_reg = cv_pick_fp_encoding(frameproc, defrange_target_is_param); + RDI_RegCode fp_register_code = p2r_reg_code_from_arch_encoded_fp_reg(arch, encoded_fp_reg); + + // rjf: build location + B32 extra_indirection = 0; + U32 byte_size = rdi_addr_size_from_arch(arch); + U32 byte_pos = 0; + S64 var_off = (S64)defrange_fprel_full_scope->off; + RDIM_LocationInfo loc_info = p2r_location_info_from_addr_reg_off(arena, arch, fp_register_code, byte_size, byte_pos, var_off, extra_indirection); + RDIM_Location *loc = rdim_location_chunk_list_push_new(arena, sym_locations, sym_locations_chunk_cap, &loc_info); + + // rjf: emit location over ranges + RDIM_Rng1U64 voff_range = {0, max_U64}; + rdim_local_push_location_case(arena, sym_scopes, defrange_target, loc, voff_range); + }break; + + //- rjf: DEFRANGE_REGISTER_REL + case CV_SymKind_DEFRANGE_REGISTER_REL: + { + // rjf: no defrange target? -> somehow we got to a defrange symbol without first seeing + // a local - break immediately + if(defrange_target == 0) + { + break; + } + + // rjf: unpack sym + CV_SymDefrangeRegisterRel *defrange_register_rel = (CV_SymDefrangeRegisterRel*)sym_header_struct_base; + CV_Reg cv_reg = defrange_register_rel->reg; + RDI_RegCode reg_code = p2r_rdi_reg_code_from_cv_reg_code(arch, cv_reg); + CV_LvarAddrRange *range = &defrange_register_rel->range; + COFF_SectionHeader *range_section = (0 < range->sec && range->sec <= coff_sections.count) ? &coff_sections.v[range->sec-1] : 0; + CV_LvarAddrGap *gaps = (CV_LvarAddrGap*)(defrange_register_rel + 1); + U64 gap_count = ((U8*)sym_data_opl - (U8*)gaps) / sizeof(*gaps); + + // rjf: build location + // TODO(rjf): offset & size from cv_reg code + U32 byte_size = rdi_addr_size_from_arch(arch); + U32 byte_pos = 0; + B32 extra_indirection_to_value = 0; + S64 var_off = defrange_register_rel->reg_off; + RDIM_LocationInfo loc_info = p2r_location_info_from_addr_reg_off(arena, arch, reg_code, byte_size, byte_pos, var_off, extra_indirection_to_value); + RDIM_Location *loc = rdim_location_chunk_list_push_new(arena, sym_locations, sym_locations_chunk_cap, &loc_info); + + // rjf: emit locations over ranges + p2r_local_push_location_cases_over_lvar_addr_range(arena, sym_scopes, defrange_target, loc, range, range_section, gaps, gap_count); + }break; + + //- rjf: FILESTATIC + case CV_SymKind_FILESTATIC: + { + CV_SymFileStatic *file_static = (CV_SymFileStatic*)sym_header_struct_base; + String8 name = str8_cstring_capped(file_static+1, sym_data_opl); + RDIM_Type *type = p2r_type_ptr_from_itype(file_static->itype); + // TODO(rjf): emit a global modifier symbol + defrange_target = 0; + defrange_target_is_param = 0; + }break; + + //- rjf: INLINESITE + case CV_SymKind_INLINESITE: + if(params->subset_flags & (RDIM_SubsetFlag_Scopes)) + { + // rjf: unpack sym + CV_SymInlineSite *sym = (CV_SymInlineSite *)sym_header_struct_base; + String8 binary_annots = str8((U8 *)(sym+1), rec_range->hdr.size - sizeof(rec_range->hdr.kind) - sizeof(*sym)); + + // rjf: extract external info about inline site + String8 name = str8_zero(); + RDIM_Type *type = 0; + RDIM_Type *owner = 0; + if(ipi_leaf != 0 && ipi_leaf->itype_first <= sym->inlinee && sym->inlinee < ipi_leaf->itype_opl) + { + CV_RecRange rec_range = ipi_leaf->leaf_ranges.ranges[sym->inlinee - ipi_leaf->itype_first]; + String8 rec_data = str8_substr(ipi_leaf->data, rng_1u64(rec_range.off, rec_range.off + rec_range.hdr.size)); + void *raw_leaf = rec_data.str + sizeof(U16); + + // rjf: extract method inline info + if(rec_range.hdr.kind == CV_LeafKind_MFUNC_ID && + rec_range.hdr.size >= sizeof(CV_LeafMFuncId)) + { + CV_LeafMFuncId *mfunc_id = (CV_LeafMFuncId*)raw_leaf; + name = str8_cstring_capped(mfunc_id + 1, rec_data.str + rec_data.size); + type = p2r_type_ptr_from_itype(mfunc_id->itype); + owner = mfunc_id->owner_itype != 0 ? p2r_type_ptr_from_itype(mfunc_id->owner_itype) : 0; + } + + // rjf: extract non-method function inline info + else if(rec_range.hdr.kind == CV_LeafKind_FUNC_ID && + rec_range.hdr.size >= sizeof(CV_LeafFuncId)) + { + CV_LeafFuncId *func_id = (CV_LeafFuncId*)raw_leaf; + name = str8_cstring_capped(func_id + 1, rec_data.str + rec_data.size); + type = p2r_type_ptr_from_itype(func_id->itype); + owner = func_id->scope_string_id != 0 ? p2r_type_ptr_from_itype(func_id->scope_string_id) : 0; + } + } + + // rjf: build inline site + RDIM_InlineSite *inline_site = rdim_inline_site_chunk_list_push(arena, sym_inline_sites, sym_inline_sites_chunk_cap); + inline_site->name = name; + inline_site->type = type; + inline_site->owner = owner; + inline_site->line_table = inline_site_line_table; + + // rjf: increment to next inline site line table in this unit + if(inline_site_line_table != 0 && inline_site_line_table->chunk != 0) + { + RDIM_LineTableChunkNode *chunk = inline_site_line_table->chunk; + U64 current_idx = (U64)(inline_site_line_table - chunk->v); + if(current_idx+1 < chunk->count) + { + inline_site_line_table += 1; + } + else + { + chunk = chunk->next; + inline_site_line_table = 0; + if(chunk != 0) + { + inline_site_line_table = chunk->v; + } + } + } + + // rjf: build scope + RDIM_Scope *scope = rdim_scope_chunk_list_push(arena, sym_scopes, sym_scopes_chunk_cap); + scope->inline_site = inline_site; + if(top_scope_node == 0) + { + // TODO(rjf): log + } + if(top_scope_node != 0) + { + RDIM_Scope *top_scope = top_scope_node->scope; + SLLQueuePush_N(top_scope->first_child, top_scope->last_child, scope, next_sibling); + scope->parent_scope = top_scope; + scope->symbol = top_scope->symbol; + } + + // rjf: push this scope to scope stack + { + P2R_ScopeNode *node = free_scope_node; + if(node != 0) { SLLStackPop(free_scope_node); } + else { node = push_array_no_zero(scratch.arena, P2R_ScopeNode, 1); } + node->scope = scope; + SLLStackPush(top_scope_node, node); + } + + // rjf: parse offset ranges of this inline site - attach to scope + { + CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(0, 0, procedure_base_voff); + for(;;) + { + CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, binary_annots); + + if(step.flags & CV_C13InlineSiteDecoderStepFlag_EmitRange) + { + // rjf: build new range & add to scope + RDIM_Rng1U64 voff_range = { step.range.min, step.range.max }; + rdim_scope_push_voff_range(arena, sym_scopes, scope, voff_range); + } + + if(step.flags & CV_C13InlineSiteDecoderStepFlag_ExtendLastRange) + { + if(scope->voff_ranges.last != 0) + { + scope->voff_ranges.last->v.max = step.range.max; + } + } + + if(step.flags == 0) + { + break; + } + } + } + }break; + + //- rjf: INLINESITE_END + case CV_SymKind_INLINESITE_END: + { + P2R_ScopeNode *n = top_scope_node; + if(n != 0) + { + SLLStackPop(top_scope_node); + SLLStackPush(free_scope_node, n); + } + defrange_target = 0; + defrange_target_is_param = 0; + }break; + + //- rjf: CONSTANT + case CV_SymKind_CONSTANT: + if(params->subset_flags & RDIM_SubsetFlag_Constants) + { + // rjf: unpack + CV_SymConstant *sym = (CV_SymConstant *)sym_header_struct_base; + RDIM_Type *type = p2r_type_ptr_from_itype(sym->itype); + U8 *val_ptr = (U8 *)(sym+1); + CV_NumericParsed val = cv_numeric_from_data_range(val_ptr, sym_data_opl); + U64 val64 = cv_u64_from_numeric(&val); + U8 *name_ptr = val_ptr + val.encoded_size; + String8 name = str8_cstring_capped(name_ptr, sym_data_opl); + String8 val_data = str8_struct(&val64); + U64 container_name_opl = 0; + if(type != 0) + { + container_name_opl = p2r_end_of_cplusplus_container_name(type->name); + } + String8 name_qualified = name; + if(container_name_opl != 0) + { + name_qualified = push_str8f(arena, "%S%S", str8_prefix(type->name, container_name_opl), name); + } + + // rjf: build constant symbol + if(name_qualified.size != 0) + { + RDIM_Symbol *cnst = rdim_symbol_chunk_list_push(arena, sym_constants, sym_constants_chunk_cap); + cnst->name = name_qualified; + cnst->type = type; + rdim_symbol_push_value_data(arena, sym_constants, cnst, val_data); + } + }break; + } + } + } + + scratch_end(scratch); + } + } +#undef p2r_type_ptr_from_itype + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: join all lane symbols + // + { + if(lane_idx() == lane_from_task_idx(0)) ProfScope("join locations") + { + for EachIndex(idx, all_syms_count) + { + rdim_location_chunk_list_concat_in_place(&p2r_shared->all_locations, &p2r_shared->syms_locations[idx]); + } + } + if(lane_idx() == lane_from_task_idx(1)) ProfScope("join procedures") + { + for EachIndex(idx, all_syms_count) + { + rdim_symbol_chunk_list_concat_in_place(&p2r_shared->all_procedures, &p2r_shared->syms_procedures[idx]); + } + } + if(lane_idx() == lane_from_task_idx(2)) ProfScope("join global variables") + { + for EachIndex(idx, all_syms_count) + { + rdim_symbol_chunk_list_concat_in_place(&p2r_shared->all_global_variables, &p2r_shared->syms_global_variables[idx]); + } + } + if(lane_idx() == lane_from_task_idx(3)) ProfScope("join thread variables") + { + for EachIndex(idx, all_syms_count) + { + rdim_symbol_chunk_list_concat_in_place(&p2r_shared->all_thread_variables, &p2r_shared->syms_thread_variables[idx]); + } + } + if(lane_idx() == lane_from_task_idx(4)) ProfScope("join constants") + { + for EachIndex(idx, all_syms_count) + { + rdim_symbol_chunk_list_concat_in_place(&p2r_shared->all_constants, &p2r_shared->syms_constants[idx]); + } + } + if(lane_idx() == lane_from_task_idx(5)) ProfScope("join scopes") + { + for EachIndex(idx, all_syms_count) + { + rdim_scope_chunk_list_concat_in_place(&p2r_shared->all_scopes, &p2r_shared->syms_scopes[idx]); + } + } + if(lane_idx() == lane_from_task_idx(6)) ProfScope("join inline sites") + { + for EachIndex(idx, all_syms_count) + { + rdim_inline_site_chunk_list_concat_in_place(&p2r_shared->all_inline_sites, &p2r_shared->syms_inline_sites[idx]); + } + } + if(lane_idx() == lane_from_task_idx(7)) ProfScope("join typedefs") + { + for EachIndex(idx, all_syms_count) + { + rdim_type_chunk_list_concat_in_place(&p2r_shared->all_types__pre_typedefs, &p2r_shared->syms_typedefs[idx]); + } + p2r_shared->all_types = p2r_shared->all_types__pre_typedefs; + } + } + lane_sync(); + RDIM_LocationChunkList all_locations = p2r_shared->all_locations; + RDIM_SymbolChunkList all_procedures = p2r_shared->all_procedures; + RDIM_SymbolChunkList all_global_variables = p2r_shared->all_global_variables; + RDIM_SymbolChunkList all_thread_variables = p2r_shared->all_thread_variables; + RDIM_SymbolChunkList all_constants = p2r_shared->all_constants; + RDIM_ScopeChunkList all_scopes = p2r_shared->all_scopes; + RDIM_InlineSiteChunkList all_inline_sites = p2r_shared->all_inline_sites; + RDIM_TypeChunkList all_types = p2r_shared->all_types; + + ////////////////////////////////////////////////////////////// + //- rjf: bundle all outputs + // + RDIM_BakeParams result = {0}; + { + //- rjf: produce top-level-info + RDIM_TopLevelInfo top_level_info = {0}; + { + top_level_info.arch = arch; + top_level_info.exe_name = str8_skip_last_slash(params->input_exe_name); + top_level_info.exe_hash = exe_hash; + top_level_info.voff_max = exe_voff_max; + if(!params->deterministic) + { + top_level_info.producer_name = str8_lit(BUILD_TITLE_STRING_LITERAL); } } - //////////////////////////// - //- rjf: join tasks, merge with top-level collections - // - ProfScope("join tasks, merge with top-level collections") + //- rjf: build binary sections list + RDIM_BinarySectionList binary_sections = {0}; + if(params->subset_flags & RDIM_SubsetFlag_BinarySections) ProfScope("build binary section list") { - for(U64 idx = 0; idx < tasks_count; idx += 1) + COFF_SectionHeader *coff_ptr = coff_sections.v; + COFF_SectionHeader *coff_opl = coff_ptr + coff_sections.count; + for(;coff_ptr < coff_opl; coff_ptr += 1) { - P2R_SymbolStreamConvertOut *out = async_task_join_struct(tasks[idx], P2R_SymbolStreamConvertOut); - rdim_symbol_chunk_list_concat_in_place(&all_procedures, &out->procedures); - rdim_symbol_chunk_list_concat_in_place(&all_global_variables, &out->global_variables); - rdim_symbol_chunk_list_concat_in_place(&all_thread_variables, &out->thread_variables); - rdim_symbol_chunk_list_concat_in_place(&all_constants, &out->constants); - rdim_scope_chunk_list_concat_in_place(&all_scopes, &out->scopes); - rdim_inline_site_chunk_list_concat_in_place(&all_inline_sites,&out->inline_sites); - rdim_type_chunk_list_concat_in_place(&all_types, &out->typedefs); + char *name_first = (char *)coff_ptr->name; + char *name_opl = name_first + sizeof(coff_ptr->name); + RDIM_BinarySection *sec = rdim_binary_section_list_push(arena, &binary_sections); + sec->name = str8_cstring_capped(name_first, name_opl); + sec->flags = p2r_rdi_binary_section_flags_from_coff_section_flags(coff_ptr->flags); + sec->voff_first = coff_ptr->voff; + sec->voff_opl = coff_ptr->voff+coff_ptr->vsize; + sec->foff_first = coff_ptr->foff; + sec->foff_opl = coff_ptr->foff+coff_ptr->fsize; } } + + //- rjf: fill + result.top_level_info = top_level_info; + result.binary_sections = binary_sections; + result.units = all_units; + result.types = all_types; + result.udts = all_udts; + result.src_files = all_src_files; + result.line_tables = all_line_tables; + result.locations = all_locations; + result.global_variables = all_global_variables; + result.thread_variables = all_thread_variables; + result.constants = all_constants; + result.procedures = all_procedures; + result.scopes = all_scopes; + result.inline_sites = all_inline_sites; } - ////////////////////////////////////////////////////////////// - //- rjf: types pass 5: join UDT build tasks - // - RDIM_UDTChunkList all_udts = {0}; - for(U64 idx = 0; idx < udt_tasks_count; idx += 1) - { - RDIM_UDTChunkList *udts = async_task_join_struct(udt_tasks[idx], RDIM_UDTChunkList); - rdim_udt_chunk_list_concat_in_place(&all_udts, udts); - } - - ////////////////////////////////////////////////////////////// - //- rjf: fill output - // - RDIM_BakeParams out = {0}; - { - out.top_level_info = top_level_info; - out.binary_sections = binary_sections; - out.units = all_units; - out.types = all_types; - out.udts = all_udts; - out.src_files = all_src_files; - out.line_tables = all_line_tables; - out.global_variables = all_global_variables; - out.thread_variables = all_thread_variables; - out.constants = all_constants; - out.procedures = all_procedures; - out.scopes = all_scopes; - out.inline_sites = all_inline_sites; - } - - scratch_end(scratch); - return out; + return result; } - -//////////////////////////////// - diff --git a/src/rdi_from_pdb/rdi_from_pdb.h b/src/rdi_from_pdb/rdi_from_pdb.h index 90c61514..bb16e8ee 100644 --- a/src/rdi_from_pdb/rdi_from_pdb.h +++ b/src/rdi_from_pdb/rdi_from_pdb.h @@ -19,88 +19,7 @@ struct P2R_ConvertParams }; //////////////////////////////// -//~ rjf: Initial PDB Information Extraction & Conversion Preparation Task Types - -//- rjf: tpi hash parsing - -typedef struct P2R_TPIHashParseIn P2R_TPIHashParseIn; -struct P2R_TPIHashParseIn -{ - PDB_Strtbl *strtbl; - PDB_TpiParsed *tpi; - String8 hash_data; - String8 aux_data; -}; - -//- rjf: tpi leaves parsing - -typedef struct P2R_TPILeafParseIn P2R_TPILeafParseIn; -struct P2R_TPILeafParseIn -{ - String8 leaf_data; - CV_TypeId itype_first; -}; - -//- rjf: exe hashing - -typedef struct P2R_EXEHashIn P2R_EXEHashIn; -struct P2R_EXEHashIn -{ - String8 exe_data; -}; - -//- rjf: symbol stream parsing - -typedef struct P2R_SymbolStreamParseIn P2R_SymbolStreamParseIn; -struct P2R_SymbolStreamParseIn -{ - String8 data; -}; - -//- rjf: c13 line info stream parsing - -typedef struct P2R_C13StreamParseIn P2R_C13StreamParseIn; -struct P2R_C13StreamParseIn -{ - String8 data; - String8 strtbl; - COFF_SectionHeaderArray coff_sections; -}; - -//- rjf: comp unit parsing - -typedef struct P2R_CompUnitParseIn P2R_CompUnitParseIn; -struct P2R_CompUnitParseIn -{ - String8 data; -}; - -//- rjf: comp unit contribution table parsing - -typedef struct P2R_CompUnitContributionsParseIn P2R_CompUnitContributionsParseIn; -struct P2R_CompUnitContributionsParseIn -{ - String8 data; - COFF_SectionHeaderArray coff_sections; -}; - -//- rjf: comp unit contribution table bucketing by unit - -typedef struct P2R_CompUnitContributionsBucketIn P2R_CompUnitContributionsBucketIn; -struct P2R_CompUnitContributionsBucketIn -{ - U64 comp_unit_count; - PDB_CompUnitContributionArray contributions; -}; - -typedef struct P2R_CompUnitContributionsBucketOut P2R_CompUnitContributionsBucketOut; -struct P2R_CompUnitContributionsBucketOut -{ - RDIM_Rng1U64ChunkList *unit_ranges; -}; - -//////////////////////////////// -//~ rjf: Conversion Data Structure & Task Types +//~ rjf: Shared Conversion State //- rjf: link name map (voff -> string) @@ -137,79 +56,7 @@ struct P2R_SrcFileMap U64 slots_count; }; -//- rjf: per-unit source files conversion tasks - -typedef struct P2R_GatherUnitSrcFilesIn P2R_GatherUnitSrcFilesIn; -struct P2R_GatherUnitSrcFilesIn -{ - PDB_Strtbl *pdb_strtbl; - COFF_SectionHeaderArray coff_sections; - PDB_CompUnit *comp_unit; - CV_SymParsed *comp_unit_syms; - CV_C13Parsed *comp_unit_c13s; -}; - -typedef struct P2R_GatherUnitSrcFilesOut P2R_GatherUnitSrcFilesOut; -struct P2R_GatherUnitSrcFilesOut -{ - String8Array src_file_paths; -}; - -//- rjf: unit conversion tasks - -typedef struct P2R_UnitConvertIn P2R_UnitConvertIn; -struct P2R_UnitConvertIn -{ - U64 comp_unit_idx; - PDB_Strtbl *pdb_strtbl; - COFF_SectionHeaderArray coff_sections; - PDB_CompUnit *comp_unit; - RDIM_Rng1U64ChunkList comp_unit_ranges; - CV_SymParsed *comp_unit_syms; - CV_C13Parsed *comp_unit_c13s; - P2R_SrcFileMap *src_file_map; -}; - -typedef struct P2R_UnitConvertOut P2R_UnitConvertOut; -struct P2R_UnitConvertOut -{ - RDIM_UnitChunkList units; - RDIM_LineTableChunkList line_tables; - RDIM_LineTable *unit_first_inline_site_line_table; -}; - -//- rjf: src file sequence equipping task - -typedef struct P2R_SrcFileSeqEquipIn P2R_SrcFileSeqEquipIn; -struct P2R_SrcFileSeqEquipIn -{ - RDIM_SrcFileChunkList src_files; - RDIM_LineTableChunkList line_tables; -}; - -//- rjf: link name map building tasks - -typedef struct P2R_LinkNameMapBuildIn P2R_LinkNameMapBuildIn; -struct P2R_LinkNameMapBuildIn -{ - CV_SymParsed *sym; - COFF_SectionHeaderArray coff_sections; - P2R_LinkNameMap *link_name_map; -}; - -//- rjf: type forward resolution map build - -typedef struct P2R_ITypeFwdMapFillIn P2R_ITypeFwdMapFillIn; -struct P2R_ITypeFwdMapFillIn -{ - PDB_TpiHashParsed *tpi_hash; - CV_LeafParsed *tpi_leaf; - CV_TypeId itype_first; - CV_TypeId itype_opl; - CV_TypeId *itype_fwd_map; -}; - -//- rjf: itype chain build +//- rjf: itype chains typedef struct P2R_TypeIdChain P2R_TypeIdChain; struct P2R_TypeIdChain @@ -218,64 +65,101 @@ struct P2R_TypeIdChain CV_TypeId itype; }; -typedef struct P2R_ITypeChainBuildIn P2R_ITypeChainBuildIn; -struct P2R_ITypeChainBuildIn +//- rjf: main state bundle + +typedef struct P2R_Shared P2R_Shared; +struct P2R_Shared { - CV_LeafParsed *tpi_leaf; - CV_TypeId itype_first; - CV_TypeId itype_opl; - CV_TypeId *itype_fwd_map; - P2R_TypeIdChain **itype_chains; -}; - -//- rjf: udt conversion - -typedef struct P2R_UDTConvertIn P2R_UDTConvertIn; -struct P2R_UDTConvertIn -{ - CV_LeafParsed *tpi_leaf; - CV_TypeId itype_first; - CV_TypeId itype_opl; - CV_TypeId *itype_fwd_map; - RDIM_Type **itype_type_ptrs; -}; - -//- rjf: symbol stream conversion - -typedef struct P2R_SymbolStreamConvertIn P2R_SymbolStreamConvertIn; -struct P2R_SymbolStreamConvertIn -{ - B32 parsing_global_stream; - RDI_Arch arch; + MSF_RawStreamTable *msf_raw_stream_table; + U64 msf_stream_lane_counter; + MSF_Parsed *msf; + + PDB_Info *pdb_info; + PDB_NamedStreamTable *named_streams; + + PDB_Strtbl *strtbl; + String8 raw_strtbl; + PDB_DbiParsed *dbi; + PDB_TpiParsed *tpi; + PDB_TpiParsed *ipi; + COFF_SectionHeaderArray coff_sections; + PDB_GsiParsed *gsi; + PDB_GsiParsed *psi_gsi_part; + + U64 exe_hash; PDB_TpiHashParsed *tpi_hash; CV_LeafParsed *tpi_leaf; + PDB_TpiHashParsed *ipi_hash; CV_LeafParsed *ipi_leaf; - CV_SymParsed *sym; - U64 sym_ranges_first; - U64 sym_ranges_opl; + PDB_CompUnitArray *comp_units; + PDB_CompUnitContributionArray comp_unit_contributions; + RDIM_Rng1U64ChunkList *unit_ranges; + + U64 sym_c13_unit_lane_counter; + U64 all_syms_count; + CV_SymParsed **all_syms; // [0] -> global; rest are unit nums + CV_C13Parsed **all_c13s; // [0] -> blank (global); rest are unit nums + + U64 exe_voff_max; + RDI_Arch arch; + U64 symbol_count_prediction; + + P2R_LinkNameMap link_name_map; + + U64 sym_lane_take_counter; + + String8Array *unit_file_paths; + U64Array *unit_file_paths_hashes; + + U64 total_path_count; + + RDIM_SrcFileChunkList all_src_files__sequenceless; + P2R_SrcFileMap src_file_map; + + RDIM_UnitChunkList all_units; + RDIM_LineTableChunkList *units_line_tables; + RDIM_LineTable **units_first_inline_site_line_tables; + + RDIM_LineTableChunkList all_line_tables; + CV_TypeId *itype_fwd_map; + CV_TypeId itype_first; + CV_TypeId itype_opl; + + P2R_TypeIdChain **itype_chains; + RDIM_Type **itype_type_ptrs; - P2R_LinkNameMap *link_name_map; - RDIM_LineTable *first_inline_site_line_table; -}; - -typedef struct P2R_SymbolStreamConvertOut P2R_SymbolStreamConvertOut; -struct P2R_SymbolStreamConvertOut -{ - RDIM_SymbolChunkList procedures; - RDIM_SymbolChunkList global_variables; - RDIM_SymbolChunkList thread_variables; - RDIM_SymbolChunkList constants; - RDIM_ScopeChunkList scopes; - RDIM_InlineSiteChunkList inline_sites; - RDIM_TypeChunkList typedefs; + RDIM_Type **basic_type_ptrs; + RDIM_TypeChunkList all_types__pre_typedefs; + + RDIM_UDTChunkList *lanes_udts; + + RDIM_UDTChunkList all_udts; + + RDIM_LocationChunkList *syms_locations; + RDIM_SymbolChunkList *syms_procedures; + RDIM_SymbolChunkList *syms_global_variables; + RDIM_SymbolChunkList *syms_thread_variables; + RDIM_SymbolChunkList *syms_constants; + RDIM_ScopeChunkList *syms_scopes; + RDIM_InlineSiteChunkList *syms_inline_sites; + RDIM_TypeChunkList *syms_typedefs; + + RDIM_LocationChunkList all_locations; + RDIM_SymbolChunkList all_procedures; + RDIM_SymbolChunkList all_global_variables; + RDIM_SymbolChunkList all_thread_variables; + RDIM_SymbolChunkList all_constants; + RDIM_ScopeChunkList all_scopes; + RDIM_InlineSiteChunkList all_inline_sites; + RDIM_TypeChunkList all_types; }; //////////////////////////////// //~ rjf: Globals -global ASYNC_Root *p2r_async_root = 0; +global P2R_Shared *p2r_shared = 0; //////////////////////////////// //~ rjf: Basic Helpers @@ -299,61 +183,13 @@ internal RDI_TypeKind p2r_rdi_type_kind_from_cv_basic_type(CV_BasicType basic_ty //////////////////////////////// //~ rjf: Location Info Building Helpers -internal RDIM_Location *p2r_location_from_addr_reg_off(Arena *arena, RDI_Arch arch, RDI_RegCode reg_code, U32 reg_byte_size, U32 reg_byte_pos, S64 offset, B32 extra_indirection); internal RDI_RegCode p2r_reg_code_from_arch_encoded_fp_reg(RDI_Arch arch, CV_EncodedFramePtrReg encoded_reg); -internal void p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_LocationSet *locset, RDIM_Location *location, CV_LvarAddrRange *range, COFF_SectionHeader *section, CV_LvarAddrGap *gaps, U64 gap_count); - -//////////////////////////////// -//~ rjf: Initial Parsing & Preparation Pass Tasks - -ASYNC_WORK_DEF(p2r_exe_hash_work); -ASYNC_WORK_DEF(p2r_tpi_hash_parse_work); -ASYNC_WORK_DEF(p2r_tpi_leaf_work); -ASYNC_WORK_DEF(p2r_symbol_stream_parse_work); -ASYNC_WORK_DEF(p2r_c13_stream_parse_work); -ASYNC_WORK_DEF(p2r_comp_unit_parse_work); -ASYNC_WORK_DEF(p2r_comp_unit_contributions_parse_work); -ASYNC_WORK_DEF(p2r_comp_unit_contributions_bucket_work); - -//////////////////////////////// -//~ rjf: Unit Source File Gathering Tasks - -ASYNC_WORK_DEF(p2r_gather_unit_src_file_work); - -//////////////////////////////// -//~ rjf: Unit Conversion Tasks - -ASYNC_WORK_DEF(p2r_unit_convert_work); - -//////////////////////////////// -//~ rjf: Source File Sequence Equipping Task - -ASYNC_WORK_DEF(p2r_src_file_seq_equip_work); - -//////////////////////////////// -//~ rjf: Link Name Map Building Tasks - -ASYNC_WORK_DEF(p2r_link_name_map_build_work); - -//////////////////////////////// -//~ rjf: Type Parsing/Conversion Tasks - -ASYNC_WORK_DEF(p2r_itype_fwd_map_fill_work); -ASYNC_WORK_DEF(p2r_itype_chain_build_work); - -//////////////////////////////// -//~ rjf: UDT Conversion Tasks - -ASYNC_WORK_DEF(p2r_udt_convert_work); - -//////////////////////////////// -//~ rjf: Symbol Stream Conversion Tasks - -ASYNC_WORK_DEF(p2r_symbol_stream_convert_work); +internal RDIM_LocationInfo p2r_location_info_from_addr_reg_off(Arena *arena, RDI_Arch arch, RDI_RegCode reg_code, U32 reg_byte_size, U32 reg_byte_pos, S64 offset, B32 extra_indirection); +internal void p2r_local_push_location_cases_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_Local *local, RDIM_Location *loc, CV_LvarAddrRange *range, COFF_SectionHeader *section, CV_LvarAddrGap *gaps, U64 gap_count); //////////////////////////////// //~ rjf: Top-Level Conversion Entry Point -internal RDIM_BakeParams p2r_convert(Arena *arena, ASYNC_Root *async_root, P2R_ConvertParams *in); +internal RDIM_BakeParams p2r_convert(Arena *arena, P2R_ConvertParams *params); #endif // RDI_FROM_PDB_H diff --git a/src/rdi_make/rdi_make_local.c b/src/rdi_make/rdi_make_local.c index a751530b..e886f697 100644 --- a/src/rdi_make/rdi_make_local.c +++ b/src/rdi_make/rdi_make_local.c @@ -3,8 +3,6 @@ #include "lib_rdi_make/rdi_make.c" -//////////////////////////////// - internal RDIM_DataModel rdim_data_model_from_os_arch(OperatingSystem os, RDI_Arch arch) { @@ -19,8 +17,6 @@ rdim_data_model_from_os_arch(OperatingSystem os, RDI_Arch arch) return data_model; } -//////////////////////////////// - internal RDIM_TopLevelInfo rdim_make_top_level_info(String8 image_name, Arch arch, U64 exe_hash, RDIM_BinarySectionList sections) { @@ -52,1085 +48,2828 @@ rdim_make_top_level_info(String8 image_name, Arch arch, U64 exe_hash, RDIM_Binar return top_level_info; } -//////////////////////////////// -//~ rjf: Baking Stage Tasks - -//- rjf: bake string map building - -#define rdim_make_string_map_if_needed() do {if(in->maps[thread_idx] == 0) ProfScope("make map") {in->maps[thread_idx] = rdim_bake_string_map_loose_make(arena, in->top);}} while(0) - -ASYNC_WORK_DEF(rdim_bake_src_files_strings_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeSrcFilesStringsIn *in = (RDIM_BakeSrcFilesStringsIn *)input; - rdim_make_string_map_if_needed(); - ProfScope("bake src file strings") rdim_bake_string_map_loose_push_src_files(arena, in->top, in->maps[thread_idx], in->list); - ProfEnd(); - return 0; -} - -ASYNC_WORK_DEF(rdim_bake_units_strings_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeUnitsStringsIn *in = (RDIM_BakeUnitsStringsIn *)input; - rdim_make_string_map_if_needed(); - ProfScope("bake unit strings") rdim_bake_string_map_loose_push_units(arena, in->top, in->maps[thread_idx], in->list); - ProfEnd(); - return 0; -} - -ASYNC_WORK_DEF(rdim_bake_types_strings_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeTypesStringsIn *in = (RDIM_BakeTypesStringsIn *)input; - rdim_make_string_map_if_needed(); - ProfScope("bake type strings") - { - for(RDIM_BakeTypesStringsInNode *n = in->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_type_slice(arena, in->top, in->maps[thread_idx], n->v, n->count); - } - } - ProfEnd(); - return 0; -} - -ASYNC_WORK_DEF(rdim_bake_udts_strings_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeUDTsStringsIn *in = (RDIM_BakeUDTsStringsIn *)input; - rdim_make_string_map_if_needed(); - ProfScope("bake udt strings") - { - for(RDIM_BakeUDTsStringsInNode *n = in->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_udt_slice(arena, in->top, in->maps[thread_idx], n->v, n->count); - } - } - ProfEnd(); - return 0; -} - -ASYNC_WORK_DEF(rdim_bake_symbols_strings_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeSymbolsStringsIn *in = (RDIM_BakeSymbolsStringsIn *)input; - rdim_make_string_map_if_needed(); - ProfScope("bake symbol strings") - { - for(RDIM_BakeSymbolsStringsInNode *n = in->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_symbol_slice(arena, in->top, in->maps[thread_idx], n->v, n->count); - } - } - ProfEnd(); - return 0; -} - -ASYNC_WORK_DEF(rdim_bake_inline_site_strings_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeInlineSiteStringsIn *in = input; - rdim_make_string_map_if_needed(); - ProfScope("bake inline site strings") - { - for(RDIM_BakeInlineSiteStringsInNode *n = in->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_inline_site_slice(arena, in->top, in->maps[thread_idx], n->v, n->count); - } - } - ProfEnd(); - return 0; -} - -ASYNC_WORK_DEF(rdim_bake_scopes_strings_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeScopesStringsIn *in = (RDIM_BakeScopesStringsIn *)input; - rdim_make_string_map_if_needed(); - ProfScope("bake scope strings") - { - for(RDIM_BakeScopesStringsInNode *n = in->first; n != 0; n = n->next) - { - rdim_bake_string_map_loose_push_scope_slice(arena, in->top, in->maps[thread_idx], n->v, n->count); - } - } - ProfEnd(); - return 0; -} - -ASYNC_WORK_DEF(rdim_bake_line_tables_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeLineTablesIn *in = (RDIM_BakeLineTablesIn *)input; - RDIM_LineTableBakeResult *out = push_array(arena, RDIM_LineTableBakeResult, 1); - ProfScope("bake line tables") *out = rdim_bake_line_tables(arena, in->line_tables); - ProfEnd(); - return out; -} - -#undef rdim_make_string_map_if_needed - -//- rjf: bake string map joining - -ASYNC_WORK_DEF(rdim_bake_string_map_join_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_JoinBakeStringMapSlotsIn *in = (RDIM_JoinBakeStringMapSlotsIn *)input; - ProfScope("join bake string maps") - { - for(U64 src_map_idx = 0; src_map_idx < in->src_maps_count; src_map_idx += 1) - { - for(U64 slot_idx = in->slot_idx_range.min; slot_idx < in->slot_idx_range.max; slot_idx += 1) - { - B32 src_slots_good = (in->src_maps[src_map_idx] != 0 && in->src_maps[src_map_idx]->slots != 0); - B32 dst_slot_is_zero = (in->dst_map->slots[slot_idx] == 0); - if(src_slots_good && dst_slot_is_zero) - { - in->dst_map->slots[slot_idx] = in->src_maps[src_map_idx]->slots[slot_idx]; - } - else if(src_slots_good && in->src_maps[src_map_idx]->slots[slot_idx] != 0) - { - rdim_bake_string_chunk_list_concat_in_place(in->dst_map->slots[slot_idx], in->src_maps[src_map_idx]->slots[slot_idx]); - } - } - } - } - ProfEnd(); - return 0; -} - -//- rjf: bake string map sorting - -ASYNC_WORK_DEF(rdim_bake_string_map_sort_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_SortBakeStringMapSlotsIn *in = (RDIM_SortBakeStringMapSlotsIn *)input; - ProfScope("sort bake string chunk list map range") - { - for(U64 slot_idx = in->slot_idx; - slot_idx < in->slot_idx+in->slot_count; - slot_idx += 1) - { - if(in->src_map->slots[slot_idx] != 0) - { - if(in->src_map->slots[slot_idx]->total_count > 1) - { - in->dst_map->slots[slot_idx] = push_array(arena, RDIM_BakeStringChunkList, 1); - *in->dst_map->slots[slot_idx] = rdim_bake_string_chunk_list_sorted_from_unsorted(arena, in->src_map->slots[slot_idx]); - } - else - { - in->dst_map->slots[slot_idx] = in->src_map->slots[slot_idx]; - } - } - } - } - ProfEnd(); - return 0; -} - -//- rjf: pass 1: interner/deduper map builds - -ASYNC_WORK_DEF(rdim_build_bake_name_map_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BuildBakeNameMapIn *in = (RDIM_BuildBakeNameMapIn *)input; - RDIM_BakeNameMap *name_map = 0; - ProfScope("build name map %i", in->k) name_map = rdim_bake_name_map_from_kind_params(arena, in->k, in->params); - ProfEnd(); - return name_map; -} - -//- rjf: pass 2: string-map-dependent debug info stream builds - -ASYNC_WORK_DEF(rdim_bake_units_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeUnitsIn *in = (RDIM_BakeUnitsIn *)input; - RDIM_UnitBakeResult *out = push_array(arena, RDIM_UnitBakeResult, 1); - ProfScope("bake units") *out = rdim_bake_units(arena, in->strings, in->path_tree, in->units); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_unit_vmap_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeUnitVMapIn *in = (RDIM_BakeUnitVMapIn *)input; - RDIM_UnitVMapBakeResult *out = push_array(arena, RDIM_UnitVMapBakeResult, 1); - ProfScope("bake unit vmap") *out = rdim_bake_unit_vmap(arena, in->units); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_src_files_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeSrcFilesIn *in = (RDIM_BakeSrcFilesIn *)input; - RDIM_SrcFileBakeResult *out = push_array(arena, RDIM_SrcFileBakeResult, 1); - ProfScope("bake src files") *out = rdim_bake_src_files(arena, in->strings, in->path_tree, in->src_files); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_udts_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeUDTsIn *in = (RDIM_BakeUDTsIn *)input; - RDIM_UDTBakeResult *out = push_array(arena, RDIM_UDTBakeResult, 1); - ProfScope("bake udts") *out = rdim_bake_udts(arena, in->strings, in->udts); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_global_variables_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeGlobalVariablesIn *in = (RDIM_BakeGlobalVariablesIn *)input; - RDIM_GlobalVariableBakeResult *out = push_array(arena, RDIM_GlobalVariableBakeResult, 1); - ProfScope("bake global variables") *out = rdim_bake_global_variables(arena, in->strings, in->global_variables); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_global_vmap_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeGlobalVMapIn *in = (RDIM_BakeGlobalVMapIn *)input; - RDIM_GlobalVMapBakeResult *out = push_array(arena, RDIM_GlobalVMapBakeResult, 1); - ProfScope("bake global vmap") *out = rdim_bake_global_vmap(arena, in->global_variables); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_thread_variables_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeThreadVariablesIn *in = (RDIM_BakeThreadVariablesIn *)input; - RDIM_ThreadVariableBakeResult *out = push_array(arena, RDIM_ThreadVariableBakeResult, 1); - ProfScope("bake thread variables") *out = rdim_bake_thread_variables(arena, in->strings, in->thread_variables); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_constants_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeConstantsIn *in = (RDIM_BakeConstantsIn *)input; - RDIM_ConstantsBakeResult *out = push_array(arena, RDIM_ConstantsBakeResult, 1); - ProfScope("bake constants") *out = rdim_bake_constants(arena, in->strings, in->constants); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_procedures_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeProceduresIn *in = (RDIM_BakeProceduresIn *)input; - RDIM_ProcedureBakeResult *out = push_array(arena, RDIM_ProcedureBakeResult, 1); - ProfScope("bake procedures") *out = rdim_bake_procedures(arena, in->strings, in->location_blocks, in->location_data_blobs, in->procedures); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_scopes_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeScopesIn *in = (RDIM_BakeScopesIn *)input; - RDIM_ScopeBakeResult *out = push_array(arena, RDIM_ScopeBakeResult, 1); - ProfScope("bake scopes") *out = rdim_bake_scopes(arena, in->strings, in->location_blocks, in->location_data_blobs, in->scopes); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_scope_vmap_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeScopeVMapIn *in = (RDIM_BakeScopeVMapIn *)input; - RDIM_ScopeVMapBakeResult *out = push_array(arena, RDIM_ScopeVMapBakeResult, 1); - ProfScope("bake scope vmap") *out = rdim_bake_scope_vmap(arena, in->scopes); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_inline_sites_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeInlineSitesIn *in = (RDIM_BakeInlineSitesIn *)input; - RDIM_InlineSiteBakeResult *out = push_array(arena, RDIM_InlineSiteBakeResult, 1); - ProfScope("bake inline sites") *out = rdim_bake_inline_sites(arena, in->strings, in->inline_sites); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_file_paths_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeFilePathsIn *in = (RDIM_BakeFilePathsIn *)input; - RDIM_FilePathBakeResult *out = push_array(arena, RDIM_FilePathBakeResult, 1); - ProfScope("bake file paths") *out = rdim_bake_file_paths(arena, in->strings, in->path_tree); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_strings_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeStringsIn *in = (RDIM_BakeStringsIn *)input; - RDIM_StringBakeResult *out = push_array(arena, RDIM_StringBakeResult, 1); - ProfScope("bake strings") *out = rdim_bake_strings(arena, in->strings); - ProfEnd(); - return out; -} - -//- rjf: pass 3: idx-run-map-dependent debug info stream builds - -ASYNC_WORK_DEF(rdim_bake_type_nodes_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeTypeNodesIn *in = (RDIM_BakeTypeNodesIn *)input; - RDIM_TypeNodeBakeResult *out = push_array(arena, RDIM_TypeNodeBakeResult, 1); - ProfScope("bake type nodes") *out = rdim_bake_types(arena, in->strings, in->idx_runs, in->types); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_name_map_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeNameMapIn *in = (RDIM_BakeNameMapIn *)input; - RDIM_NameMapBakeResult *out = push_array(arena, RDIM_NameMapBakeResult, 1); - ProfScope("bake name map %i", in->kind) *out = rdim_bake_name_map(arena, in->strings, in->idx_runs, in->map); - ProfEnd(); - return out; -} - -ASYNC_WORK_DEF(rdim_bake_idx_runs_work) -{ - ProfBeginFunction(); - Arena *arena = async_root_thread_arena(rdim_local_async_root); - RDIM_BakeIdxRunsIn *in = (RDIM_BakeIdxRunsIn *)input; - RDIM_IndexRunBakeResult *out = push_array(arena, RDIM_IndexRunBakeResult, 1); - ProfScope("bake idx runs") *out = rdim_bake_index_runs(arena, in->idx_runs); - ProfEnd(); - return out; -} - -internal U64 -rdim_local_hash(RDIM_String8 string) -{ - U64 hash = 5381; - U8 *ptr = string.str; - U8 *opl = string.str + string.size; - for (;ptr < opl; ++ptr) { - hash = ((hash << 5) + hash) + (*ptr); - } - return hash; -} - -internal void -rdim_local_resolve_incomplete_types(RDIM_TypeChunkList *types, RDIM_UDTChunkList *udts) -{ - ProfBeginFunction(); - - Temp scratch = scratch_begin(0,0); - - U64 total_type_count = types->total_count + 1; - - ProfBegin("Build Hash Table"); - RDIM_Type **name_ht = rdim_push_array(scratch.arena, RDIM_Type *, total_type_count); - for(RDIM_TypeChunkNode *chunk = types->first; chunk != 0; chunk = chunk->next) - { - for(RDI_U64 i = 0; i < chunk->count; i += 1) - { - RDIM_Type *type = &chunk->v[i]; - if(RDI_TypeKind_FirstUserDefined <= type->kind && type->kind <= RDI_TypeKind_LastRecord) - { - RDIM_String8 name = type->link_name.size ? type->link_name : type->name; - RDI_U64 hash = rdim_local_hash(name); - - RDI_U64 best_slot = hash % types->total_count; - RDI_U64 slot = best_slot; - do - { - RDIM_Type *s = name_ht[slot]; - if(s == 0) - { - break; - } - - if(s->link_name.size) - { - if(str8_match(s->link_name, name, 0)) - { - break; - } - } - else if(s->name.size) - { - if(str8_match(s->name, type->name, 0)) - { - break; - } - } - - slot = (slot + 1) % total_type_count; - } while (slot != best_slot); - - if(name_ht[slot] == 0) - { - name_ht[slot] = type; - } - } - } - } - ProfEnd(); - - ProfBegin("Make Fwd Map"); - RDIM_Type **fwd_map = rdim_push_array(scratch.arena, RDIM_Type *, total_type_count); - for(RDIM_TypeChunkNode *chunk = types->first; chunk != 0; chunk = chunk->next) - { - for(RDI_U64 i = 0; i < chunk->count; i += 1) - { - RDIM_Type *type = &chunk->v[i]; - - if(RDI_TypeKind_FirstIncomplete <= type->kind && type->kind <= RDI_TypeKind_LastIncomplete) - { - RDIM_String8 name = type->link_name.size ? type->link_name : type->name; - RDI_U64 hash = rdim_local_hash(name); - RDI_U64 best_slot = hash % types->total_count; - RDI_U64 slot = best_slot; - - RDIM_Type *match = 0; - do - { - if(name_ht[slot] == 0) - { - break; - } - RDIM_Type *s = name_ht[slot]; - if(s->link_name.size) - { - if(str8_match(s->link_name, type->link_name, 0)) - { - match = s; - break; - } - } - else - { - if(str8_match(s->name, type->name, 0)) - { - match = s; - break; - } - } - - slot = (slot + 1) % total_type_count; - } while(slot != best_slot); - - if(match) - { - type->kind = RDI_TypeKind_NULL; - - RDI_U64 type_idx = rdim_idx_from_type(type); - fwd_map[type_idx] = match; - } - } - } - } - ProfEnd(); - - ProfBegin("Resolve Types"); - for(RDIM_TypeChunkNode *chunk = types->first; chunk != 0; chunk = chunk->next) - { - for(RDI_U64 i = 0; i < chunk->count; ++i) - { - RDIM_Type *t = &chunk->v[i]; - if(t->direct_type) - { - RDI_U64 direct_idx = rdim_idx_from_type(t->direct_type); - if(fwd_map[direct_idx]) - { - t->direct_type = fwd_map[direct_idx]; - } - } - if(t->param_types) - { - for(RDI_U64 param_idx = 0; param_idx < t->count; param_idx += 1) - { - RDI_U64 type_idx = rdim_idx_from_type(t->param_types[param_idx]); - if(fwd_map[type_idx]) - { - t->param_types[param_idx] = fwd_map[type_idx]; - } - } - } - } - } - for(RDIM_UDTChunkNode *chunk = udts->first; chunk != 0; chunk = chunk->next) - { - for(RDI_U64 i = 0; i < chunk->count; ++i) - { - RDIM_UDT *udt = &chunk->v[i]; - RDI_U64 self_idx = rdim_idx_from_type(udt->self_type); - if(fwd_map[self_idx]) - { - udt->self_type = fwd_map[self_idx]; - } - - for(RDIM_UDTMember *member = udt->first_member; member != 0; member = member->next) - { - RDI_U64 member_idx = rdim_idx_from_type(member->type); - if(fwd_map[member_idx]) - { - member->type = fwd_map[member_idx]; - } - } - } - } - ProfEnd(); - - scratch_end(scratch); - ProfEnd(); -} - internal RDIM_BakeResults -rdim_bake(Arena *arena, ASYNC_Root *async_root, RDIM_BakeParams *in_params) +rdim_bake(Arena *arena, RDIM_BakeParams *params) { - Temp scratch = scratch_begin(0,0); - RDIM_BakeResults out = {0}; - rdim_local_async_root = async_root; - - ////////////////////////////// - //- rjf: kick off line tables baking + ////////////////////////////////////////////////////////////// + //- rjf: set up shared state // - ASYNC_Task *bake_line_tables_task = 0; + if(lane_idx() == 0) { - RDIM_BakeLineTablesIn *in = push_array(scratch.arena, RDIM_BakeLineTablesIn, 1); - in->line_tables = &in_params->line_tables; - bake_line_tables_task = async_task_launch(scratch.arena, rdim_bake_line_tables_work, .input = in); + rdim_shared = push_array(arena, RDIM_Shared, 1); } + lane_sync(); - ////////////////////////////// - //- rjf: build interned path tree + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage gather unsorted vmap keys/markers // - RDIM_BakePathTree *path_tree = 0; - ProfScope("build interned path tree") + ProfScope("gather unsorted vmap keys/markers") { - path_tree = rdim_bake_path_tree_from_params(arena, in_params); - } - - ////////////////////////////// - //- rjf: kick off string map building tasks - // - RDIM_BakeStringMapTopology bake_string_map_topology = {(64 + - in_params->procedures.total_count*1 + - in_params->global_variables.total_count*1 + - in_params->thread_variables.total_count*1 + - in_params->types.total_count/2)}; - RDIM_BakeStringMapLoose **bake_string_maps__in_progress = push_array(scratch.arena, RDIM_BakeStringMapLoose *, async_thread_count()); - ASYNC_TaskList bake_string_map_build_tasks = {0}; - { - // rjf: src files - ProfScope("kick off src files string map build task") + //- rjf: gather scope vmap keys/markers + if(lane_idx() == lane_from_task_idx(0)) ProfScope("gather scope vmap keys/markers") { - RDIM_BakeSrcFilesStringsIn *in = push_array(scratch.arena, RDIM_BakeSrcFilesStringsIn, 1); - in->top = &bake_string_map_topology; - in->maps = bake_string_maps__in_progress; - in->list = &in_params->src_files; - async_task_list_push(scratch.arena, &bake_string_map_build_tasks, async_task_launch(scratch.arena, rdim_bake_src_files_strings_work, .input = in)); - } - - // rjf: units - ProfScope("kick off units string map build task") - { - RDIM_BakeUnitsStringsIn *in = push_array(scratch.arena, RDIM_BakeUnitsStringsIn, 1); - in->top = &bake_string_map_topology; - in->maps = bake_string_maps__in_progress; - in->list = &in_params->units; - async_task_list_push(scratch.arena, &bake_string_map_build_tasks, async_task_launch(scratch.arena, rdim_bake_units_strings_work, .input = in)); - } - - // rjf: types - ProfScope("kick off types string map build tasks") - { - U64 items_per_task = 4096; - U64 num_tasks = (in_params->types.total_count+items_per_task-1)/items_per_task; - RDIM_TypeChunkNode *chunk = in_params->types.first; - U64 chunk_off = 0; - for(U64 task_idx = 0; task_idx < num_tasks; task_idx += 1) + rdim_shared->scope_vmap_count = params->scopes.scope_voff_count; + rdim_shared->scope_vmap_keys = push_array_no_zero(arena, RDIM_SortKey, rdim_shared->scope_vmap_count); + rdim_shared->scope_vmap_keys__swap = push_array_no_zero(arena, RDIM_SortKey, rdim_shared->scope_vmap_count); + rdim_shared->scope_vmap_markers = push_array_no_zero(arena, RDIM_VMapMarker, rdim_shared->scope_vmap_count); + ProfScope("fill keys/markers") { - RDIM_BakeTypesStringsIn *in = push_array(scratch.arena, RDIM_BakeTypesStringsIn, 1); - in->top = &bake_string_map_topology; - in->maps = bake_string_maps__in_progress; - U64 items_left = items_per_task; - for(;chunk != 0 && items_left > 0;) + RDIM_SortKey *key_ptr = rdim_shared->scope_vmap_keys; + RDIM_VMapMarker *marker_ptr = rdim_shared->scope_vmap_markers; + for(RDIM_ScopeChunkNode *chunk_n = params->scopes.first; chunk_n != 0; chunk_n = chunk_n->next) { - U64 items_in_this_chunk = Min(items_per_task, chunk->count-chunk_off); - RDIM_BakeTypesStringsInNode *n = push_array(scratch.arena, RDIM_BakeTypesStringsInNode, 1); - SLLQueuePush(in->first, in->last, n); - n->v = chunk->v + chunk_off; - n->count = items_in_this_chunk; - chunk_off += items_in_this_chunk; - items_left -= items_in_this_chunk; - if(chunk_off >= chunk->count) + for(RDI_U64 chunk_idx = 0; chunk_idx < chunk_n->count; chunk_idx += 1) { - chunk = chunk->next; - chunk_off = 0; + RDIM_Scope *src_scope = &chunk_n->v[chunk_idx]; + RDI_U32 scope_idx = (RDI_U32)rdim_idx_from_scope(src_scope); // TODO(rjf): @u64_to_u32 + for(RDIM_Rng1U64Node *n = src_scope->voff_ranges.first; n != 0; n = n->next) + { + key_ptr->key = n->v.min; + key_ptr->val = marker_ptr; + marker_ptr->idx = scope_idx; + marker_ptr->begin_range = 1; + key_ptr += 1; + marker_ptr += 1; + + key_ptr->key = n->v.max; + key_ptr->val = marker_ptr; + marker_ptr->idx = scope_idx; + marker_ptr->begin_range = 0; + key_ptr += 1; + marker_ptr += 1; + } } } - async_task_list_push(scratch.arena, &bake_string_map_build_tasks, async_task_launch(scratch.arena, rdim_bake_types_strings_work, .input = in)); } } - // rjf: UDTs - ProfScope("kick off udts string map build tasks") + //- rjf: gather unit vmap keys/markers + if(lane_idx() == lane_from_task_idx(1)) ProfScope("gather unit vmap keys/markers") { - U64 items_per_task = 4096; - U64 num_tasks = (in_params->udts.total_count+items_per_task-1)/items_per_task; - RDIM_UDTChunkNode *chunk = in_params->udts.first; - U64 chunk_off = 0; - for(U64 task_idx = 0; task_idx < num_tasks; task_idx += 1) + // rjf: count voff ranges + RDI_U64 voff_range_count = 0; + for(RDIM_UnitChunkNode *n = params->units.first; n != 0; n = n->next) { - RDIM_BakeUDTsStringsIn *in = push_array(scratch.arena, RDIM_BakeUDTsStringsIn, 1); - in->top = &bake_string_map_topology; - in->maps = bake_string_maps__in_progress; - U64 items_left = items_per_task; - for(;chunk != 0 && items_left > 0;) + for(RDI_U64 idx = 0; idx < n->count; idx += 1) { - U64 items_in_this_chunk = Min(items_per_task, chunk->count-chunk_off); - RDIM_BakeUDTsStringsInNode *n = push_array(scratch.arena, RDIM_BakeUDTsStringsInNode, 1); - SLLQueuePush(in->first, in->last, n); - n->v = chunk->v + chunk_off; - n->count = items_in_this_chunk; - chunk_off += items_in_this_chunk; - items_left -= items_in_this_chunk; - if(chunk_off >= chunk->count) + RDIM_Unit *unit = &n->v[idx]; + voff_range_count += unit->voff_ranges.total_count; + } + } + + // rjf: count necessary markers + RDI_U64 marker_count = voff_range_count*2; + + // rjf: build keys/markers arrays + RDIM_SortKey *keys = rdim_push_array_no_zero(arena, RDIM_SortKey, marker_count); + RDIM_VMapMarker *markers = rdim_push_array_no_zero(arena, RDIM_VMapMarker, marker_count); + { + RDIM_SortKey *key_ptr = keys; + RDIM_VMapMarker *marker_ptr = markers; + RDI_U32 unit_idx = 1; + for(RDIM_UnitChunkNode *unit_chunk_n = params->units.first; + unit_chunk_n != 0; + unit_chunk_n = unit_chunk_n->next) + { + for(RDI_U64 idx = 0; idx < unit_chunk_n->count; idx += 1) { - chunk = chunk->next; - chunk_off = 0; + RDIM_Unit *unit = &unit_chunk_n->v[idx]; + for(RDIM_Rng1U64ChunkNode *n = unit->voff_ranges.first; n != 0; n = n->next) + { + for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) + { + RDIM_Rng1U64 range = n->v[chunk_idx]; + if(range.min < range.max) + { + key_ptr->key = range.min; + key_ptr->val = marker_ptr; + marker_ptr->idx = unit_idx; + marker_ptr->begin_range = 1; + key_ptr += 1; + marker_ptr += 1; + + key_ptr->key = range.max; + key_ptr->val = marker_ptr; + marker_ptr->idx = unit_idx; + marker_ptr->begin_range = 0; + key_ptr += 1; + marker_ptr += 1; + } + } + } + unit_idx += 1; } } - async_task_list_push(scratch.arena, &bake_string_map_build_tasks, async_task_launch(scratch.arena, rdim_bake_udts_strings_work, .input = in)); + } + + // rjf: store + rdim_shared->unit_vmap_count = marker_count; + rdim_shared->unit_vmap_keys = keys; + rdim_shared->unit_vmap_keys__swap = push_array_no_zero(arena, RDIM_SortKey, marker_count); + rdim_shared->unit_vmap_markers = markers; + } + + //- rjf: gather global vmap keys/markers + if(lane_idx() == lane_from_task_idx(2)) ProfScope("gather global vmap keys/markers") + { + //- rjf: allocate keys/markers + RDI_U64 marker_count = params->global_variables.total_count*2 + 2; + RDIM_SortKey *keys = rdim_push_array_no_zero(arena, RDIM_SortKey, marker_count); + RDIM_VMapMarker *markers = rdim_push_array_no_zero(arena, RDIM_VMapMarker, marker_count); + + //- rjf: fill + { + RDIM_SortKey *key_ptr = keys; + RDIM_VMapMarker *marker_ptr = markers; + + // rjf: fill actual globals + for(RDIM_SymbolChunkNode *n = params->global_variables.first; n != 0; n = n->next) + { + for(RDI_U64 chunk_idx = 0; chunk_idx < n->count; chunk_idx += 1) + { + RDIM_Symbol *global_var = &n->v[chunk_idx]; + RDI_U32 global_var_idx = (RDI_U32)rdim_idx_from_symbol(global_var); // TODO(rjf): @u64_to_u32 + RDI_U64 global_var_size = global_var->type ? global_var->type->byte_size : 1; + + RDI_U64 first = global_var->offset; + RDI_U64 opl = first + global_var_size; + + key_ptr->key = first; + key_ptr->val = marker_ptr; + marker_ptr->idx = global_var_idx; + marker_ptr->begin_range = 1; + key_ptr += 1; + marker_ptr += 1; + + key_ptr->key = opl; + key_ptr->val = marker_ptr; + marker_ptr->idx = global_var_idx; + marker_ptr->begin_range = 0; + key_ptr += 1; + marker_ptr += 1; + } + } + + // rjf: fill nil global + { + RDI_U32 global_idx = 0; + RDI_U64 first = 0; + RDI_U64 opl = 0xffffffffffffffffull; + key_ptr->key = first; + key_ptr->val = marker_ptr; + marker_ptr->idx = global_idx; + marker_ptr->begin_range = 1; + key_ptr += 1; + marker_ptr += 1; + key_ptr->key = opl; + key_ptr->val = marker_ptr; + marker_ptr->idx = global_idx; + marker_ptr->begin_range = 0; + key_ptr += 1; + marker_ptr += 1; + } + } + + //- rjf: store + rdim_shared->global_vmap_count = marker_count; + rdim_shared->global_vmap_keys = keys; + rdim_shared->global_vmap_keys__swap = push_array_no_zero(arena, RDIM_SortKey, marker_count); + rdim_shared->global_vmap_markers = markers; + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage sort all vmap keys + // + ProfScope("sort all vmap keys") + { + // rjf: set up + if(lane_idx() == 0) + { + rdim_shared->lane_digit_counts = push_array(arena, U32 *, lane_count()); + rdim_shared->lane_digit_offsets = push_array(arena, U32 *, lane_count()); + } + lane_sync(); + + // rjf: sort + struct + { + RDI_U64 vmap_count; + RDIM_SortKey *keys; + RDIM_SortKey *keys__swap; + } + sort_tasks[] = + { + {rdim_shared->scope_vmap_count, rdim_shared->scope_vmap_keys, rdim_shared->scope_vmap_keys__swap}, + {rdim_shared->unit_vmap_count, rdim_shared->unit_vmap_keys, rdim_shared->unit_vmap_keys__swap}, + {rdim_shared->global_vmap_count, rdim_shared->global_vmap_keys, rdim_shared->global_vmap_keys__swap}, + }; + for EachElement(sort_task_idx, sort_tasks) ProfScope("sort %I64u", sort_task_idx) + { + RDI_U64 vmap_count = sort_tasks[sort_task_idx].vmap_count; + RDIM_SortKey *keys = sort_tasks[sort_task_idx].keys; + RDIM_SortKey *keys__swap = sort_tasks[sort_task_idx].keys__swap; + U64 bits_per_digit = 8; + U64 digits_count = 64 / bits_per_digit; + U64 num_possible_values_per_digit = 1 << bits_per_digit; + rdim_shared->lane_digit_counts[lane_idx()] = push_array_no_zero(arena, U32, num_possible_values_per_digit); + rdim_shared->lane_digit_offsets[lane_idx()] = push_array_no_zero(arena, U32, num_possible_values_per_digit); + RDIM_SortKey *src = keys; + RDIM_SortKey *dst = keys__swap; + U64 element_count = vmap_count; + for EachIndex(digit_idx, digits_count) + { + // rjf: count digit value occurrences per-lane + { + U32 *digit_counts = rdim_shared->lane_digit_counts[lane_idx()]; + MemoryZero(digit_counts, sizeof(digit_counts[0])*num_possible_values_per_digit); + Rng1U64 range = lane_range(element_count); + for EachInRange(idx, range) + { + RDIM_SortKey *sort_key = &src[idx]; + U16 digit_value = (U16)(U8)(sort_key->key >> (digit_idx*bits_per_digit)); + digit_counts[digit_value] += 1; + } + } + lane_sync(); + + // rjf: compute thread * digit value *relative* offset table + { + Rng1U64 range = lane_range(num_possible_values_per_digit); + for EachInRange(value_idx, range) + { + U64 layout_off = 0; + for EachIndex(lane_idx, lane_count()) + { + rdim_shared->lane_digit_offsets[lane_idx][value_idx] = layout_off; + layout_off += rdim_shared->lane_digit_counts[lane_idx][value_idx]; + } + } + } + lane_sync(); + + // rjf: convert relative offsets -> absolute offsets + if(lane_idx() == 0) + { + U64 last_off = 0; + U64 num_of_nonzero_digit = 0; + for EachIndex(value_idx, num_possible_values_per_digit) + { + for EachIndex(lane_idx, lane_count()) + { + rdim_shared->lane_digit_offsets[lane_idx][value_idx] += last_off; + } + last_off = rdim_shared->lane_digit_offsets[lane_count()-1][value_idx] + rdim_shared->lane_digit_counts[lane_count()-1][value_idx]; + } + // NOTE(rjf): required that: (last_off == element_count) + } + lane_sync(); + + // rjf: move + { + U32 *lane_digit_offsets = rdim_shared->lane_digit_offsets[lane_idx()]; + Rng1U64 range = lane_range(element_count); + for EachInRange(idx, range) + { + RDIM_SortKey *src_key = &src[idx]; + U16 digit_value = (U16)(U8)(src_key->key >> (digit_idx*bits_per_digit)); + U64 dst_off = lane_digit_offsets[digit_value]; + lane_digit_offsets[digit_value] += 1; + MemoryCopyStruct(&dst[dst_off], src_key); + } + } + lane_sync(); + + // rjf: swap + { + RDIM_SortKey *swap = src; + src = dst; + dst = swap; + } + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake all vmaps + // + ProfScope("bake all vmaps") + { + Temp scratch = scratch_begin(&arena, 1); + typedef struct VMapBakeTask VMapBakeTask; + struct VMapBakeTask + { + VMapBakeTask *next; + String8 name; + RDI_U64 count; + RDIM_SortKey *keys; + RDIM_VMapMarker *markers; + RDIM_BakeVMap *bake_vmap_out; + }; + VMapBakeTask *first_task = 0; + VMapBakeTask *last_task = 0; + if(lane_idx() == lane_from_task_idx(0)) + { + VMapBakeTask *task = push_array(scratch.arena, VMapBakeTask, 1); + task->name = str8_lit("scopes"); + task->count = rdim_shared->scope_vmap_count; + task->keys = rdim_shared->scope_vmap_keys; + task->markers = rdim_shared->scope_vmap_markers; + task->bake_vmap_out = &rdim_shared->baked_scope_vmap.vmap; + SLLQueuePush(first_task, last_task, task); + } + if(lane_idx() == lane_from_task_idx(1)) + { + VMapBakeTask *task = push_array(scratch.arena, VMapBakeTask, 1); + task->name = str8_lit("units"); + task->count = rdim_shared->unit_vmap_count; + task->keys = rdim_shared->unit_vmap_keys; + task->markers = rdim_shared->unit_vmap_markers; + task->bake_vmap_out = &rdim_shared->baked_unit_vmap.vmap; + SLLQueuePush(first_task, last_task, task); + } + if(lane_idx() == lane_from_task_idx(2)) + { + VMapBakeTask *task = push_array(scratch.arena, VMapBakeTask, 1); + task->name = str8_lit("globals"); + task->count = rdim_shared->global_vmap_count; + task->keys = rdim_shared->global_vmap_keys; + task->markers = rdim_shared->global_vmap_markers; + task->bake_vmap_out = &rdim_shared->baked_global_vmap.vmap; + SLLQueuePush(first_task, last_task, task); + } + for(VMapBakeTask *task = first_task; task != 0; task = task->next) ProfScope("vmap bake for %.*s", str8_varg(task->name)) + { + //- rjf: determine if an extra vmap entry for zero is needed + RDI_U32 extra_vmap_entry = 0; + if(task->count > 0 && task->keys[0].key != 0) + { + extra_vmap_entry = 1; + } + + //- rjf: fill output vmap entries + RDI_U32 vmap_count_raw = extra_vmap_entry + task->count; + RDI_VMapEntry *vmap = rdim_push_array(arena, RDI_VMapEntry, vmap_count_raw); + RDI_U32 vmap_entry_count_pass_1 = 0; + ProfScope("fill output vmap entries") + { + typedef struct RDIM_VMapRangeTracker RDIM_VMapRangeTracker; + struct RDIM_VMapRangeTracker + { + RDIM_VMapRangeTracker *next; + RDI_U32 idx; + }; + RDI_VMapEntry *vmap_ptr = vmap; + if(extra_vmap_entry) + { + vmap_ptr->voff = 0; + vmap_ptr->idx = 0; + vmap_ptr += 1; + } + RDIM_VMapRangeTracker *tracker_stack = 0; + RDIM_VMapRangeTracker *tracker_free = 0; + RDIM_SortKey *key_ptr = task->keys; + RDIM_SortKey *key_opl = task->keys + task->count; + for(;key_ptr < key_opl;) + { + // rjf: get initial map state from tracker stack + RDI_U32 initial_idx = (RDI_U32)0xffffffff; + if(tracker_stack != 0) + { + initial_idx = tracker_stack->idx; + } + + // rjf: update tracker stack + // + // * we must process _all_ of the changes that apply at this voff before moving on + // + RDI_U64 voff = key_ptr->key; + + for(;key_ptr < key_opl && key_ptr->key == voff; key_ptr += 1) + { + RDIM_VMapMarker *marker = (RDIM_VMapMarker*)key_ptr->val; + RDI_U32 idx = marker->idx; + + // rjf: range begin -> push to stack + if(marker->begin_range) + { + RDIM_VMapRangeTracker *new_tracker = tracker_free; + if(new_tracker != 0) + { + RDIM_SLLStackPop(tracker_free); + } + else + { + new_tracker = rdim_push_array(scratch.arena, RDIM_VMapRangeTracker, 1); + } + RDIM_SLLStackPush(tracker_stack, new_tracker); + new_tracker->idx = idx; + } + + // rjf: range ending -> pop matching node from stack (not always the top) + else + { + RDIM_VMapRangeTracker **ptr_in = &tracker_stack; + RDIM_VMapRangeTracker *match = 0; + for(RDIM_VMapRangeTracker *node = tracker_stack; node != 0;) + { + if(node->idx == idx) + { + match = node; + break; + } + ptr_in = &node->next; + node = node->next; + } + if(match != 0) + { + *ptr_in = match->next; + RDIM_SLLStackPush(tracker_free, match); + } + } + } + + // rjf: get final map state from tracker stack + RDI_U32 final_idx = 0; + if(tracker_stack != 0) + { + final_idx = tracker_stack->idx; + } + + // rjf: if final is different from initial - emit new vmap entry + if(final_idx != initial_idx) + { + vmap_ptr->voff = voff; + vmap_ptr->idx = final_idx; + vmap_ptr += 1; + } + } + + vmap_entry_count_pass_1 = (RDI_U32)(vmap_ptr - vmap); // TODO(rjf): @u64_to_u32 + } + + //- rjf: combine duplicate neighbors + RDI_U32 vmap_entry_count = 0; + ProfScope("combine duplicate neighbors") + { + RDI_VMapEntry *vmap_ptr = vmap; + RDI_VMapEntry *vmap_opl = vmap + vmap_entry_count_pass_1; + RDI_VMapEntry *vmap_out = vmap; + for(;vmap_ptr < vmap_opl;) + { + RDI_VMapEntry *vmap_range_first = vmap_ptr; + RDI_U64 idx = vmap_ptr->idx; + vmap_ptr += 1; + for(;vmap_ptr < vmap_opl && vmap_ptr->idx == idx;) vmap_ptr += 1; + rdim_memcpy_struct(vmap_out, vmap_range_first); + vmap_out += 1; + } + vmap_entry_count = (RDI_U32)(vmap_out - vmap); // TODO(rjf): @u64_to_u32 + } + + //- rjf: fill result + task->bake_vmap_out->vmap = vmap; + task->bake_vmap_out->count = vmap_entry_count; + } + scratch_end(scratch); + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage build interned path tree + // + if(lane_idx() == 0) ProfScope("build interned path tree") + { + //- rjf: set up tree + RDIM_BakePathTree *tree = rdim_push_array(arena, RDIM_BakePathTree, 1); + rdim_bake_path_tree_insert(arena, tree, rdim_str8_lit("")); + + //- rjf: bake unit file paths + RDIM_ProfScope("bake unit file paths") + { + for(RDIM_UnitChunkNode *n = params->units.first; n != 0; n = n->next) + { + for(RDI_U64 idx = 0; idx < n->count; idx += 1) + { + rdim_bake_path_tree_insert(arena, tree, n->v[idx].source_file); + rdim_bake_path_tree_insert(arena, tree, n->v[idx].object_file); + rdim_bake_path_tree_insert(arena, tree, n->v[idx].archive_file); + rdim_bake_path_tree_insert(arena, tree, n->v[idx].build_path); + } } } - // rjf: symbols - ProfScope("kick off symbols string map build tasks") + //- rjf: bake source file paths + RDIM_ProfScope("bake source file paths") { + for(RDIM_SrcFileChunkNode *n = params->src_files.first; n != 0; n = n->next) + { + for(RDI_U64 idx = 0; idx < n->count; idx += 1) + { + RDIM_BakePathNode *node = rdim_bake_path_tree_insert(arena, tree, n->v[idx].path); + node->src_file = &n->v[idx]; + } + } + } + + rdim_shared->path_tree = tree; + } + lane_sync(); + RDIM_BakePathTree *path_tree = rdim_shared->path_tree; + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage gather all unsorted, joined, line table info; & sort + // + ProfScope("gather all unsorted, joined, line table info; & sort") + { + //- rjf: set up outputs + ProfScope("set up outputs") + { + // rjf: calculate header info + if(lane_idx() == 0) + { + rdim_shared->line_tables_count = params->line_tables.total_count; + rdim_shared->src_line_tables = push_array(arena, RDIM_LineTable *, rdim_shared->line_tables_count); + ProfScope("flatten chunk list") + { + U64 joined_idx = 0; + for(RDIM_LineTableChunkNode *n = params->line_tables.first; n != 0; n = n->next) + { + for EachIndex(idx, n->count) + { + rdim_shared->src_line_tables[joined_idx] = &n->v[idx]; + joined_idx += 1; + } + } + } + rdim_shared->baked_line_tables.line_tables_count = params->line_tables.total_count + 1; + rdim_shared->baked_line_tables.line_table_voffs_count = params->line_tables.total_line_count + 2*params->line_tables.total_seq_count; + rdim_shared->baked_line_tables.line_table_lines_count = params->line_tables.total_line_count + params->line_tables.total_seq_count; + rdim_shared->baked_line_tables.line_table_columns_count= 1; + rdim_shared->line_table_block_take_counter = 0; + } + lane_sync(); + + // rjf: allocate outputs + ProfScope("allocate outputs") + { + if(lane_idx() == lane_from_task_idx(0)) + { + rdim_shared->unsorted_joined_line_tables = push_array(arena, RDIM_UnsortedJoinedLineTable, rdim_shared->line_tables_count); + } + if(lane_idx() == lane_from_task_idx(1)) + { + rdim_shared->sorted_line_table_keys = push_array(arena, RDIM_SortKey *, rdim_shared->line_tables_count); + } + if(lane_idx() == lane_from_task_idx(2)) + { + rdim_shared->baked_line_tables.line_tables = push_array(arena, RDI_LineTable, rdim_shared->baked_line_tables.line_tables_count); + ProfScope("lay out line tables") + { + U64 voffs_base_idx = 0; + U64 lines_base_idx = 0; + U64 cols_base_idx = 0; + for EachIndex(idx, rdim_shared->line_tables_count) + { + U64 final_idx = idx+1; // NOTE(rjf): +1, to reserve [0] for nil + RDIM_LineTable *src = rdim_shared->src_line_tables[idx]; + RDI_LineTable *dst = &rdim_shared->baked_line_tables.line_tables[final_idx]; + dst->voffs_base_idx = voffs_base_idx; // TODO(rjf): @u64_to_u32 + dst->lines_base_idx = lines_base_idx; // TODO(rjf): @u64_to_u32 + dst->cols_base_idx = cols_base_idx; // TODO(rjf): @u64_to_u32 + dst->lines_count = src->line_count + src->seq_count; // TODO(rjf): @u64_to_u32 + voffs_base_idx += src->line_count + 2*src->seq_count; + lines_base_idx += src->line_count + 1*src->seq_count; + } + } + } + if(lane_idx() == lane_from_task_idx(3)) + { + rdim_shared->baked_line_tables.line_table_voffs = push_array(arena, RDI_U64, rdim_shared->baked_line_tables.line_table_voffs_count); + } + if(lane_idx() == lane_from_task_idx(4)) + { + rdim_shared->baked_line_tables.line_table_lines = push_array(arena, RDI_Line, rdim_shared->baked_line_tables.line_table_lines_count); + } + if(lane_idx() == lane_from_task_idx(5)) + { + rdim_shared->baked_line_tables.line_table_columns = push_array(arena, RDI_Column, rdim_shared->baked_line_tables.line_table_columns_count); + } + } + } + lane_sync(); + + //- rjf: wide bake + ProfScope("wide bake") + { + U64 line_table_block_size = 4096; + U64 line_table_block_count = (rdim_shared->line_tables_count + line_table_block_size - 1) / line_table_block_size; + for(;;) + { + U64 line_table_block_num = ins_atomic_u64_inc_eval(&rdim_shared->line_table_block_take_counter); + if(0 == line_table_block_num || line_table_block_count < line_table_block_num) + { + break; + } + U64 line_table_block_idx = line_table_block_num-1; + Rng1U64 line_table_range = r1u64(line_table_block_idx*line_table_block_size, (line_table_block_idx+1)*line_table_block_size); + line_table_range.max = Min(rdim_shared->line_tables_count, line_table_range.max); + for EachInRange(line_table_idx, line_table_range) + { + RDIM_LineTable *src = rdim_shared->src_line_tables[line_table_idx]; + RDIM_UnsortedJoinedLineTable *dst = &rdim_shared->unsorted_joined_line_tables[line_table_idx]; + + //- rjf: gather + dst->line_count = src->line_count; + dst->seq_count = src->seq_count; + dst->key_count = dst->line_count + dst->seq_count; + dst->line_keys = rdim_push_array_no_zero(arena, RDIM_SortKey, dst->key_count); + dst->line_recs = rdim_push_array_no_zero(arena, RDIM_LineRec, dst->line_count); + { + RDIM_SortKey *key_ptr = dst->line_keys; + RDIM_LineRec *rec_ptr = dst->line_recs; + for(RDIM_LineSequenceNode *seq_n = src->first_seq; seq_n != 0; seq_n = seq_n->next) + { + RDIM_LineSequence *seq = &seq_n->v; + for(RDI_U64 line_idx = 0; line_idx < seq->line_count; line_idx += 1) + { + key_ptr->key = seq->voffs[line_idx]; + key_ptr->val = rec_ptr; + key_ptr += 1; + rec_ptr->file_id = (RDI_U32)rdim_idx_from_src_file(seq->src_file); // TODO(rjf): @u64_to_u32 + rec_ptr->line_num = seq->line_nums[line_idx]; + if(seq->col_nums != 0) + { + rec_ptr->col_first = seq->col_nums[line_idx*2]; + rec_ptr->col_opl = seq->col_nums[line_idx*2 + 1]; + } + rec_ptr += 1; + } + key_ptr->key = seq->voffs[seq->line_count]; + key_ptr->val = 0; + key_ptr += 1; + } + } + + //- rjf: sort + rdim_shared->sorted_line_table_keys[line_table_idx] = rdim_sort_key_array(arena, + rdim_shared->unsorted_joined_line_tables[line_table_idx].line_keys, + rdim_shared->unsorted_joined_line_tables[line_table_idx].key_count); + + //- rjf: fill + RDIM_SortKey *sorted_line_keys = rdim_shared->sorted_line_table_keys[line_table_idx]; + U64 sorted_line_keys_count = rdim_shared->unsorted_joined_line_tables[line_table_idx].key_count; + RDI_LineTable *dst_line_table = &rdim_shared->baked_line_tables.line_tables[line_table_idx+1]; + U64 *arranged_voffs = rdim_shared->baked_line_tables.line_table_voffs + dst_line_table->voffs_base_idx; + RDI_Line *arranged_lines = rdim_shared->baked_line_tables.line_table_lines + dst_line_table->lines_base_idx; + RDI_Column *arranged_cols = rdim_shared->baked_line_tables.line_table_columns + dst_line_table->cols_base_idx; + { + for EachIndex(idx, sorted_line_keys_count) + { + arranged_voffs[idx] = sorted_line_keys[idx].key; + } + arranged_voffs[sorted_line_keys_count] = ~0ull; + for EachIndex(idx, sorted_line_keys_count) + { + RDIM_LineRec *rec = (RDIM_LineRec*)sorted_line_keys[idx].val; + if(rec != 0) + { + arranged_lines[idx].file_idx = rec->file_id; + arranged_lines[idx].line_num = rec->line_num; + } + else + { + arranged_lines[idx].file_idx = 0; + arranged_lines[idx].line_num = 0; + } + } + } + } + } + } + } + lane_sync(); + RDI_U64 line_tables_count = rdim_shared->line_tables_count; + RDIM_LineTable **src_line_tables = rdim_shared->src_line_tables; + RDIM_UnsortedJoinedLineTable *unsorted_joined_line_tables = rdim_shared->unsorted_joined_line_tables; + RDIM_SortKey **sorted_line_table_keys = rdim_shared->sorted_line_table_keys; + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage build string map + // + ProfScope("build string map") + { + //- rjf: set up per-lane outputs + if(lane_idx() == 0) ProfScope("set up per-lane outputs") + { + rdim_shared->bake_string_map_topology.slots_count = (64 + + params->procedures.total_count*1 + + params->global_variables.total_count*1 + + params->thread_variables.total_count*1 + + params->types.total_count/2); + rdim_shared->lane_bake_string_maps__loose = push_array(arena, RDIM_BakeStringMapLoose *, lane_count()); + rdim_shared->bake_string_map__loose = rdim_bake_string_map_loose_make(arena, &rdim_shared->bake_string_map_topology); + } + lane_sync(); + + //- rjf: set up this lane's map + ProfScope("set up this lane's map") + { + rdim_shared->lane_bake_string_maps__loose[lane_idx()] = rdim_bake_string_map_loose_make(arena, &rdim_shared->bake_string_map_topology); + } + RDIM_BakeStringMapTopology *lane_map_top = &rdim_shared->bake_string_map_topology; + RDIM_BakeStringMapLoose *lane_map = rdim_shared->lane_bake_string_maps__loose[lane_idx()]; + + //- rjf: push all strings into this lane's map + ProfScope("push all strings into this lane's map") + { + // rjf: push small top-level strings + if(lane_idx() == 0) ProfScope("push small top-level strings") + { + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 1, params->top_level_info.exe_name); + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 1, params->top_level_info.producer_name); + for(RDIM_BinarySectionNode *n = params->binary_sections.first; n != 0; n = n->next) + { + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 1, n->v.name); + } + for(RDIM_BakePathNode *n = path_tree->first; n != 0; n = n->next_order) + { + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 1, n->name); + } + } + + // rjf: push strings from source files + ProfScope("src files") + { + for EachNode(n, RDIM_SrcFileChunkNode, params->src_files.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_String8 normalized_path = rdim_lower_from_str8(arena, n->v[n_idx].path); + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 1, normalized_path); + } + } + } + + // rjf: push strings from units + ProfScope("units") + { + for EachNode(n, RDIM_UnitChunkNode, params->units.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].unit_name); + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].compiler_name); + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].source_file); + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].object_file); + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].archive_file); + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].build_path); + } + } + } + + // rjf: push strings from types + ProfScope("types") + { + for EachNode(n, RDIM_TypeChunkNode, params->types.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].name); + } + } + } + + // rjf: push strings from udts + ProfScope("udts") + { + for EachNode(n, RDIM_UDTChunkNode, params->udts.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) + { + for EachNode(mem, RDIM_UDTMember, n->v[idx].first_member) + { + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, mem->name); + } + for EachNode(enum_val, RDIM_UDTEnumVal, n->v[idx].first_enum_val) + { + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, enum_val->name); + } + } + } + } + + // rjf: push strings from symbols RDIM_SymbolChunkList *symbol_lists[] = { - &in_params->global_variables, - &in_params->thread_variables, - &in_params->procedures, - &in_params->constants, + ¶ms->global_variables, + ¶ms->thread_variables, + ¶ms->procedures, + ¶ms->constants, }; - for(U64 list_idx = 0; list_idx < ArrayCount(symbol_lists); list_idx += 1) + ProfScope("symbols") { - U64 items_per_task = 4096; - U64 num_tasks = (symbol_lists[list_idx]->total_count+items_per_task-1)/items_per_task; - RDIM_SymbolChunkNode *chunk = symbol_lists[list_idx]->first; - U64 chunk_off = 0; - for(U64 task_idx = 0; task_idx < num_tasks; task_idx += 1) + for EachElement(list_idx, symbol_lists) { - RDIM_BakeSymbolsStringsIn *in = push_array(scratch.arena, RDIM_BakeSymbolsStringsIn, 1); - in->top = &bake_string_map_topology; - in->maps = bake_string_maps__in_progress; - U64 items_left = items_per_task; - for(;chunk != 0 && items_left > 0;) + for EachNode(n, RDIM_SymbolChunkNode, symbol_lists[list_idx]->first) { - U64 items_in_this_chunk = Min(items_per_task, chunk->count-chunk_off); - RDIM_BakeSymbolsStringsInNode *n = push_array(scratch.arena, RDIM_BakeSymbolsStringsInNode, 1); - SLLQueuePush(in->first, in->last, n); - n->v = chunk->v + chunk_off; - n->count = items_in_this_chunk; - chunk_off += items_in_this_chunk; - items_left -= items_in_this_chunk; - if(chunk_off >= chunk->count) + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) { - chunk = chunk->next; - chunk_off = 0; + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].name); + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].link_name); } } - async_task_list_push(scratch.arena, &bake_string_map_build_tasks, async_task_launch(scratch.arena, rdim_bake_symbols_strings_work, .input = in)); } } - } - - // rjf: inline sites - ProfScope("kick off inline site string map build task") - { - U64 items_per_task = 4096; - U64 num_tasks = CeilIntegerDiv(in_params->inline_sites.total_count, items_per_task); - RDIM_InlineSiteChunkNode *chunk = in_params->inline_sites.first; - U64 chunk_off = 0; - for(U64 task_idx = 0; task_idx < num_tasks; task_idx += 1) + + //- rjf: push strings from inline sites + ProfScope("inline sites") { - RDIM_BakeInlineSiteStringsIn *in = push_array(scratch.arena, RDIM_BakeInlineSiteStringsIn, 1); - in->top = &bake_string_map_topology; - in->maps = bake_string_maps__in_progress; - U64 items_left = items_per_task; - for(;chunk != 0 && items_left > 0;) + for EachNode(n, RDIM_InlineSiteChunkNode, params->inline_sites.first) { - U64 items_in_this_chunk = Min(items_per_task, chunk->count-chunk_off); - RDIM_BakeInlineSiteStringsInNode *n = push_array(scratch.arena, RDIM_BakeInlineSiteStringsInNode, 1); - SLLQueuePush(in->first, in->last, n); - n->v = chunk->v + chunk_off; - n->count = items_in_this_chunk; - chunk_off += items_in_this_chunk; - items_left -= items_in_this_chunk; - if(chunk_off >= chunk->count) + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) { - chunk = chunk->next; - chunk_off = 0; + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, n->v[n_idx].name); } } - async_task_list_push(scratch.arena, &bake_string_map_build_tasks, async_task_launch(scratch.arena, rdim_bake_inline_site_strings_work, .input = in)); } - } - - // rjf: scope chunks - ProfScope("kick off scope chunks string map build tasks") - { - U64 items_per_task = 4096; - U64 num_tasks = (in_params->scopes.total_count+items_per_task-1)/items_per_task; - RDIM_ScopeChunkNode *chunk = in_params->scopes.first; - U64 chunk_off = 0; - for(U64 task_idx = 0; task_idx < num_tasks; task_idx += 1) + + //- rjf: push strings from scopes + ProfScope("scopes") { - RDIM_BakeScopesStringsIn *in = push_array(scratch.arena, RDIM_BakeScopesStringsIn, 1); - in->top = &bake_string_map_topology; - in->maps = bake_string_maps__in_progress; - U64 items_left = items_per_task; - for(;chunk != 0 && items_left > 0;) + for EachNode(n, RDIM_ScopeChunkNode, params->scopes.first) { - U64 items_in_this_chunk = Min(items_per_task, chunk->count-chunk_off); - RDIM_BakeScopesStringsInNode *n = push_array(scratch.arena, RDIM_BakeScopesStringsInNode, 1); - SLLQueuePush(in->first, in->last, n); - n->v = chunk->v + chunk_off; - n->count = items_in_this_chunk; - chunk_off += items_in_this_chunk; - items_left -= items_in_this_chunk; - if(chunk_off >= chunk->count) + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) { - chunk = chunk->next; - chunk_off = 0; + for EachNode(local, RDIM_Local, n->v[n_idx].first_local) + { + rdim_bake_string_map_loose_insert(arena, lane_map_top, lane_map, 4, local->name); + } } } - async_task_list_push(scratch.arena, &bake_string_map_build_tasks, async_task_launch(scratch.arena, rdim_bake_scopes_strings_work, .input = in)); } } - } - - ////////////////////////////// - //- rjf: kick off name map building tasks - // - RDIM_BuildBakeNameMapIn build_bake_name_map_in[RDI_NameMapKind_COUNT] = {0}; - ASYNC_Task *build_bake_name_map_task[RDI_NameMapKind_COUNT] = {0}; - for(RDI_NameMapKind k = (RDI_NameMapKind)(RDI_NameMapKind_NULL+1); - k < RDI_NameMapKind_COUNT; - k = (RDI_NameMapKind)(k+1)) - { - build_bake_name_map_in[k].k = k; - build_bake_name_map_in[k].params = in_params; - build_bake_name_map_task[k] = async_task_launch(scratch.arena, rdim_build_bake_name_map_work, .input = &build_bake_name_map_in[k]); - } - - ////////////////////////////// - //- rjf: join string map building tasks - // - ProfScope("join string map building tasks") - { - for(ASYNC_TaskNode *n = bake_string_map_build_tasks.first; n != 0; n = n->next) - { - async_task_join(n->v); - } - } - - ////////////////////////////// - //- rjf: produce joined string map - // - RDIM_BakeStringMapLoose *unsorted_bake_string_map = rdim_bake_string_map_loose_make(arena, &bake_string_map_topology); - ProfScope("produce joined string map") - { - U64 slots_per_task = 16384; - U64 num_tasks = (bake_string_map_topology.slots_count+slots_per_task-1)/slots_per_task; - ASYNC_Task **tasks = push_array(scratch.arena, ASYNC_Task *, num_tasks); + lane_sync(); - // rjf: kickoff tasks - for(U64 task_idx = 0; task_idx < num_tasks; task_idx += 1) + //- rjf: join + ProfScope("join") { - RDIM_JoinBakeStringMapSlotsIn *in = push_array(scratch.arena, RDIM_JoinBakeStringMapSlotsIn, 1); - in->top = &bake_string_map_topology; - in->src_maps = bake_string_maps__in_progress; - in->src_maps_count = async_thread_count(); - in->dst_map = unsorted_bake_string_map; - in->slot_idx_range = r1u64(task_idx*slots_per_task, task_idx*slots_per_task + slots_per_task); - in->slot_idx_range.max = Min(in->slot_idx_range.max, in->top->slots_count); - tasks[task_idx] = async_task_launch(scratch.arena, rdim_bake_string_map_join_work, .input = in); - } - - // rjf: join tasks - for(U64 task_idx = 0; task_idx < num_tasks; task_idx += 1) - { - async_task_join(tasks[task_idx]); - } - - // rjf: insert small top-level stuff - rdim_bake_string_map_loose_push_top_level_info(arena, &bake_string_map_topology, unsorted_bake_string_map, &in_params->top_level_info); - rdim_bake_string_map_loose_push_binary_sections(arena, &bake_string_map_topology, unsorted_bake_string_map, &in_params->binary_sections); - rdim_bake_string_map_loose_push_path_tree(arena, &bake_string_map_topology, unsorted_bake_string_map, path_tree); - } - - ////////////////////////////// - //- rjf: kick off string map sorting tasks - // - ASYNC_TaskList sort_bake_string_map_tasks = {0}; - RDIM_BakeStringMapLoose *sorted_bake_string_map__in_progress = rdim_bake_string_map_loose_make(arena, &bake_string_map_topology); - { - U64 slots_per_task = 256; - U64 num_tasks = (bake_string_map_topology.slots_count+slots_per_task-1)/slots_per_task; - for(U64 task_idx = 0; task_idx < num_tasks; task_idx += 1) - { - RDIM_SortBakeStringMapSlotsIn *in = push_array(scratch.arena, RDIM_SortBakeStringMapSlotsIn, 1); + Rng1U64 slot_range = lane_range(rdim_shared->bake_string_map_topology.slots_count); + for EachInRange(slot_idx, slot_range) { - in->top = &bake_string_map_topology; - in->src_map = unsorted_bake_string_map; - in->dst_map = sorted_bake_string_map__in_progress; - in->slot_idx = task_idx*slots_per_task; - in->slot_count = slots_per_task; - if(in->slot_idx+in->slot_count > bake_string_map_topology.slots_count) + for EachIndex(src_lane_idx, lane_count()) { - in->slot_count = bake_string_map_topology.slots_count - in->slot_idx; + RDIM_BakeStringMapLoose *src_map = rdim_shared->lane_bake_string_maps__loose[src_lane_idx]; + RDIM_BakeStringMapLoose *dst_map = rdim_shared->bake_string_map__loose; + if(dst_map->slots[slot_idx] == 0 && src_map->slots[slot_idx] != 0) + { + dst_map->slots[slot_idx] = src_map->slots[slot_idx]; + } + else if(dst_map->slots[slot_idx] != 0 && src_map->slots[slot_idx] != 0) + { + rdim_bake_string_chunk_list_concat_in_place(dst_map->slots[slot_idx], src_map->slots[slot_idx]); + } } } - async_task_list_push(scratch.arena, &sort_bake_string_map_tasks, async_task_launch(scratch.arena, rdim_bake_string_map_sort_work, .input = in)); } - } - - ////////////////////////////// - //- rjf: join string map sorting tasks - // - ProfScope("join string map sorting tasks") - { - for(ASYNC_TaskNode *n = sort_bake_string_map_tasks.first; n != 0; n = n->next) + lane_sync(); + + //- rjf: sort + ProfScope("sort") { - async_task_join(n->v); - } - } - RDIM_BakeStringMapLoose *sorted_bake_string_map = sorted_bake_string_map__in_progress; - - ////////////////////////////// - //- rjf: build finalized string map - // - ProfBegin("build finalized string map base indices"); - RDIM_BakeStringMapBaseIndices bake_string_map_base_idxes = rdim_bake_string_map_base_indices_from_map_loose(arena, &bake_string_map_topology, sorted_bake_string_map); - ProfEnd(); - ProfBegin("build finalized string map"); - RDIM_BakeStringMapTight bake_strings = rdim_bake_string_map_tight_from_loose(arena, &bake_string_map_topology, &bake_string_map_base_idxes, sorted_bake_string_map); - ProfEnd(); - - ////////////////////////////// - //- rjf: kick off pass 2 tasks - // - RDIM_BakeUnitsIn bake_units_top_level_in = {&bake_strings, path_tree, &in_params->units}; - ASYNC_Task *bake_units_task = async_task_launch(scratch.arena, rdim_bake_units_work, .input = &bake_units_top_level_in); - RDIM_BakeUnitVMapIn bake_unit_vmap_in = {&in_params->units}; - ASYNC_Task *bake_unit_vmap_task = async_task_launch(scratch.arena, rdim_bake_unit_vmap_work, .input = &bake_unit_vmap_in); - RDIM_BakeSrcFilesIn bake_src_files_in = {&bake_strings, path_tree, &in_params->src_files}; - ASYNC_Task *bake_src_files_task = async_task_launch(scratch.arena, rdim_bake_src_files_work, .input = &bake_src_files_in); - RDIM_BakeUDTsIn bake_udts_in = {&bake_strings, &in_params->udts}; - ASYNC_Task *bake_udts_task = async_task_launch(scratch.arena, rdim_bake_udts_work, .input = &bake_udts_in); - RDIM_BakeGlobalVMapIn bake_global_vmap_in = {&in_params->global_variables}; - ASYNC_Task *bake_global_vmap_task = async_task_launch(scratch.arena, rdim_bake_global_vmap_work, .input = &bake_global_vmap_in); - RDIM_BakeScopeVMapIn bake_scope_vmap_in = {&in_params->scopes}; - ASYNC_Task *bake_scope_vmap_task = async_task_launch(scratch.arena, rdim_bake_scope_vmap_work, .input = &bake_scope_vmap_in); - RDIM_BakeInlineSitesIn bake_inline_sites_in = {&bake_strings, &in_params->inline_sites}; - ASYNC_Task *bake_inline_sites_task = async_task_launch(scratch.arena, rdim_bake_inline_sites_work, .input = &bake_inline_sites_in); - RDIM_BakeFilePathsIn bake_file_paths_in = {&bake_strings, path_tree}; - ASYNC_Task *bake_file_paths_task = async_task_launch(scratch.arena, rdim_bake_file_paths_work, .input = &bake_file_paths_in); - RDIM_BakeStringsIn bake_strings_in = {&bake_strings}; - ASYNC_Task *bake_strings_task = async_task_launch(scratch.arena, rdim_bake_strings_work, .input = &bake_strings_in); - RDIM_BakeConstantsIn bake_constants_in = {&bake_strings, &in_params->constants}; - ASYNC_Task *bake_constants_task = async_task_launch(scratch.arena, rdim_bake_constants_work, .input = &bake_constants_in); - - ////////////////////////////// - //- rjf: (GIANT SERIAL DEPENDENCY CHAIN HACK OF LOCATION BLOCK BUILDING) - // - // TODO(rjf): // TODO(rjf): // TODO(rjf): { - // - // This needs to be majorly cleaned up. We are doing this giant - // serial-dependency chain of async tasks (thus removing all async - // properties) because each async task here is secretly mutating - // the same input parameter (something which breaks the rules & - // style used everywhere else in the converter). - // - // Location blocks for each category of symbol should be built - // & arranged in parallel, then joined via a very thin operation - // after the fact. We should not ever be secretly mutating input - // parameters to async tasks, we need to be only returning new - // stuff. - // - RDIM_String8List location_blocks = {0}; - RDIM_String8List location_data_blobs = {0}; - { - // reserve null location block for opl - rdim_location_block_chunk_list_push_array(arena, &location_blocks, 1); - - // TODO: export location instead of VOFF - RDIM_BakeGlobalVariablesIn bake_global_variables_in = {&bake_strings, &in_params->global_variables}; - ASYNC_Task *bake_global_variables_task = async_task_launch(scratch.arena, rdim_bake_global_variables_work, .input = &bake_global_variables_in); - ProfScope("global variables") out.global_variables = *async_task_join_struct(bake_global_variables_task, RDIM_GlobalVariableBakeResult); - - // TODO: export location instead of VOFF - RDIM_BakeThreadVariablesIn bake_thread_variables_in = {&bake_strings, &in_params->thread_variables}; - ASYNC_Task *bake_thread_variables_task = async_task_launch(scratch.arena, rdim_bake_thread_variables_work, .input = &bake_thread_variables_in); - ProfScope("thread variables") out.thread_variables = *async_task_join_struct(bake_thread_variables_task, RDIM_ThreadVariableBakeResult); - - RDIM_BakeScopesIn bake_scopes_in = {&bake_strings, &in_params->scopes, &location_blocks, &location_data_blobs}; - ASYNC_Task *bake_scopes_task = async_task_launch(scratch.arena, rdim_bake_scopes_work, .input = &bake_scopes_in); - ProfScope("scopes") out.scopes = *async_task_join_struct(bake_scopes_task, RDIM_ScopeBakeResult); - - RDIM_BakeProceduresIn bake_procedures_in = {&bake_strings, &in_params->procedures, &location_blocks, &location_data_blobs}; - ASYNC_Task *bake_procedures_task = async_task_launch(scratch.arena, rdim_bake_procedures_work, .input = &bake_procedures_in); - ProfScope("procedures") out.procedures = *async_task_join_struct(bake_procedures_task, RDIM_ProcedureBakeResult); - } - // - //- TODO(rjf): // TODO(rjf): // TODO(rjf): } - ////////////////////////////// - - ////////////////////////////// - //- rjf: join name map building tasks - // - RDIM_BakeNameMap *name_maps[RDI_NameMapKind_COUNT] = {0}; - ProfScope("join name map building tasks") - { - for(RDI_NameMapKind k = (RDI_NameMapKind)(RDI_NameMapKind_NULL+1); - k < RDI_NameMapKind_COUNT; - k = (RDI_NameMapKind)(k+1)) - { - name_maps[k] = async_task_join_struct(build_bake_name_map_task[k], RDIM_BakeNameMap); - } - } - - ////////////////////////////// - //- rjf: build interned idx run map - // - RDIM_BakeIdxRunMap *idx_runs = 0; - ProfScope("build interned idx run map") - { - idx_runs = rdim_bake_idx_run_map_from_params(arena, name_maps, in_params); - } - - ////////////////////////////// - //- rjf: do small top-level bakes - // - ProfScope("top level info") out.top_level_info = rdim_bake_top_level_info(arena, &bake_strings, &in_params->top_level_info); - ProfScope("binary sections") out.binary_sections = rdim_bake_binary_sections(arena, &bake_strings, &in_params->binary_sections); - ProfScope("top level name maps section") out.top_level_name_maps = rdim_bake_name_maps_top_level(arena, &bake_strings, idx_runs, name_maps); - - ////////////////////////////// - //- rjf: kick off pass 3 tasks - // - RDIM_BakeTypeNodesIn bake_type_nodes_in = {&bake_strings, idx_runs, &in_params->types}; - ASYNC_Task *bake_type_nodes_task = async_task_launch(scratch.arena, rdim_bake_type_nodes_work, .input = &bake_type_nodes_in); - ASYNC_Task *bake_name_maps_tasks[RDI_NameMapKind_COUNT] = {0}; - { - for EachNonZeroEnumVal(RDI_NameMapKind, k) - { - if(name_maps[k] == 0 || name_maps[k]->name_count == 0) + RDIM_BakeStringMapLoose *map = rdim_shared->bake_string_map__loose; + Rng1U64 slot_range = lane_range(rdim_shared->bake_string_map_topology.slots_count); + for EachInRange(slot_idx, slot_range) { - continue; + if(map->slots[slot_idx] != 0 && map->slots[slot_idx]->total_count > 1) + { + *map->slots[slot_idx] = rdim_bake_string_chunk_list_sorted_from_unsorted(arena, map->slots[slot_idx]); + } + } + } + lane_sync(); + + //- rjf: tighten string table + ProfScope("tighten string table") + { + RDIM_BakeStringMapLoose *map = rdim_shared->bake_string_map__loose; + RDIM_BakeStringMapTopology *map_top = &rdim_shared->bake_string_map_topology; + if(lane_idx() == 0) ProfScope("calc base indices, set up tight map") + { + RDIM_BakeStringMapBaseIndices bake_string_map_base_indices = rdim_bake_string_map_base_indices_from_map_loose(arena, map_top, map); + rdim_shared->bake_strings.slots_count = map_top->slots_count; + rdim_shared->bake_strings.slots = rdim_push_array(arena, RDIM_BakeStringChunkList, rdim_shared->bake_strings.slots_count); + rdim_shared->bake_strings.slots_base_idxs = bake_string_map_base_indices.slots_base_idxs; + rdim_shared->bake_strings.total_count = rdim_shared->bake_strings.slots_base_idxs[rdim_shared->bake_strings.slots_count]; + } + lane_sync(); + ProfScope("fill tight map") + { + Rng1U64 slot_range = lane_range(rdim_shared->bake_strings.slots_count); + for EachInRange(idx, slot_range) + { + if(map->slots[idx] != 0) + { + rdim_memcpy_struct(&rdim_shared->bake_strings.slots[idx], map->slots[idx]); + } + } } - RDIM_BakeNameMapIn *in = push_array(scratch.arena, RDIM_BakeNameMapIn, 1); - in->strings = &bake_strings; - in->idx_runs = idx_runs; - in->map = name_maps[k]; - in->kind = k; - bake_name_maps_tasks[k] = async_task_launch(scratch.arena, rdim_bake_name_map_work, .input = in); } } - RDIM_BakeIdxRunsIn bake_idx_runs_in = {idx_runs}; - ASYNC_Task *bake_idx_runs_task = async_task_launch(scratch.arena, rdim_bake_idx_runs_work, .input = &bake_idx_runs_in); + lane_sync(); + RDIM_BakeStringMapTight *bake_strings = &rdim_shared->bake_strings; - ////////////////////////////// - //- rjf: join remaining completed bakes + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage build name maps // - ProfScope("top-level units info") out.units = *async_task_join_struct(bake_units_task, RDIM_UnitBakeResult); - ProfScope("unit vmap") out.unit_vmap = *async_task_join_struct(bake_unit_vmap_task, RDIM_UnitVMapBakeResult); - ProfScope("source files") out.src_files = *async_task_join_struct(bake_src_files_task, RDIM_SrcFileBakeResult); - ProfScope("UDTs") out.udts = *async_task_join_struct(bake_udts_task, RDIM_UDTBakeResult); - ProfScope("global vmap") out.global_vmap = *async_task_join_struct(bake_global_vmap_task, RDIM_GlobalVMapBakeResult); - ProfScope("scope vmap") out.scope_vmap = *async_task_join_struct(bake_scope_vmap_task, RDIM_ScopeVMapBakeResult); - ProfScope("inline sites") out.inline_sites = *async_task_join_struct(bake_inline_sites_task, RDIM_InlineSiteBakeResult); - ProfScope("file paths") out.file_paths = *async_task_join_struct(bake_file_paths_task, RDIM_FilePathBakeResult); - ProfScope("strings") out.strings = *async_task_join_struct(bake_strings_task, RDIM_StringBakeResult); - ProfScope("constants") out.constants = *async_task_join_struct(bake_constants_task, RDIM_ConstantsBakeResult); - ProfScope("type nodes") out.type_nodes = *async_task_join_struct(bake_type_nodes_task, RDIM_TypeNodeBakeResult); - ProfScope("idx runs") out.idx_runs = *async_task_join_struct(bake_idx_runs_task, RDIM_IndexRunBakeResult); - ProfScope("line tables") out.line_tables = *async_task_join_struct(bake_line_tables_task, RDIM_LineTableBakeResult); - - ////////////////////////////// - //- rjf: join individual name map bakes - // - RDIM_NameMapBakeResult name_map_bakes[RDI_NameMapKind_COUNT] = {0}; - ProfScope("name maps") + ProfScope("build name maps") { - for EachNonZeroEnumVal(RDI_NameMapKind, k) + //- rjf: set up + if(lane_idx() == 0) { - RDIM_NameMapBakeResult *bake = async_task_join_struct(bake_name_maps_tasks[k], RDIM_NameMapBakeResult); - if(bake != 0) + for EachNonZeroEnumVal(RDI_NameMapKind, k) { - name_map_bakes[k] = *bake; + U64 slot_count = 0; + switch((RDI_NameMapKindEnum)k) + { + case RDI_NameMapKind_NULL: + case RDI_NameMapKind_COUNT: + {}break; +#define Case(name, total_count) case RDI_NameMapKind_##name:{slot_count = ((total_count) + (total_count)/4);}break + Case(GlobalVariables, params->global_variables.total_count); + Case(ThreadVariables, params->thread_variables.total_count); + Case(Constants, params->constants.total_count); + Case(Procedures, params->procedures.total_count); + Case(LinkNameProcedures, params->procedures.total_count); + Case(Types, params->types.total_count); + Case(NormalSourcePaths, params->src_files.total_count); +#undef Case + } + rdim_shared->lane_bake_name_maps[k] = push_array(arena, RDIM_BakeNameMap *, lane_count()); + rdim_shared->bake_name_map_topology[k].slots_count = slot_count; + } + } + lane_sync(); + + //- rjf: wide build + for EachNonZeroEnumVal(RDI_NameMapKind, k) ProfScope("name map build %.*s", str8_varg(rdi_string_from_name_map_kind(k))) + { + RDIM_BakeNameMapTopology *top = &rdim_shared->bake_name_map_topology[k]; + rdim_shared->lane_bake_name_maps[k][lane_idx()] = rdim_bake_name_map_make(arena, top); + RDIM_BakeNameMap *map = rdim_shared->lane_bake_name_maps[k][lane_idx()]; + B32 link_names = 0; + RDIM_SymbolChunkList *symbols = 0; + switch((RDI_NameMapKindEnum)k) + { + case RDI_NameMapKind_NULL: + case RDI_NameMapKind_COUNT: + {}break; + case RDI_NameMapKind_GlobalVariables: {symbols = ¶ms->global_variables;}goto symbol_name_map_build; + case RDI_NameMapKind_ThreadVariables: {symbols = ¶ms->thread_variables;}goto symbol_name_map_build; + case RDI_NameMapKind_Constants: {symbols = ¶ms->constants;}goto symbol_name_map_build; + case RDI_NameMapKind_Procedures: {symbols = ¶ms->procedures;}goto symbol_name_map_build; + case RDI_NameMapKind_LinkNameProcedures:{symbols = ¶ms->procedures; link_names = 1;}goto symbol_name_map_build; + symbol_name_map_build:; + { + for EachNode(n, RDIM_SymbolChunkNode, symbols->first) + { + Rng1U64 n_range = lane_range(n->count); + for EachInRange(n_idx, n_range) + { + RDIM_Symbol *symbol = &n->v[n_idx]; + rdim_bake_name_map_insert(arena, top, map, 4, link_names ? symbol->link_name : symbol->name, rdim_idx_from_symbol(symbol)); + } + } + }break; + case RDI_NameMapKind_Types: + { + RDIM_TypeChunkList *types = ¶ms->types; + for EachNode(n, RDIM_TypeChunkNode, types->first) + { + Rng1U64 n_range = lane_range(n->count); + for EachInRange(n_idx, n_range) + { + RDIM_Type *type = &n->v[n_idx]; + rdim_bake_name_map_insert(arena, top, map, 4, type->name, rdim_idx_from_type(type)); + } + } + }break; + case RDI_NameMapKind_NormalSourcePaths: + { + RDIM_SrcFileChunkList *src_files = ¶ms->src_files; + for EachNode(n, RDIM_SrcFileChunkNode, src_files->first) + { + Rng1U64 n_range = lane_range(n->count); + for EachInRange(n_idx, n_range) + { + RDIM_SrcFile *src_file = &n->v[n_idx]; + RDIM_String8 normalized_path = rdim_lower_from_str8(arena, src_file->path); + rdim_bake_name_map_insert(arena, top, map, 4, normalized_path, rdim_idx_from_src_file(src_file)); + } + } + }break; + } + } + lane_sync(); + + //- rjf: join & sort + if(lane_idx() == 0) + { + for EachNonZeroEnumVal(RDI_NameMapKind, k) + { + rdim_shared->bake_name_maps[k] = rdim_bake_name_map_make(arena, &rdim_shared->bake_name_map_topology[k]); + } + } + lane_sync(); + for EachNonZeroEnumVal(RDI_NameMapKind, k) ProfScope("name map join & sort %.*s", str8_varg(rdi_string_from_name_map_kind(k))) + { + RDIM_BakeNameMapTopology *top = &rdim_shared->bake_name_map_topology[k]; + RDIM_BakeNameMap *map = rdim_shared->bake_name_maps[k]; + + //- rjf: join + ProfScope("join") + { + Rng1U64 slot_range = lane_range(top->slots_count); + for EachInRange(slot_idx, slot_range) + { + for EachIndex(src_lane_idx, lane_count()) + { + RDIM_BakeNameMap *src_map = rdim_shared->lane_bake_name_maps[k][src_lane_idx]; + RDIM_BakeNameMap *dst_map = map; + if(dst_map->slots[slot_idx] == 0 && src_map->slots[slot_idx] != 0) + { + dst_map->slots[slot_idx] = src_map->slots[slot_idx]; + } + else if(dst_map->slots[slot_idx] != 0 && src_map->slots[slot_idx] != 0) + { + rdim_bake_name_chunk_list_concat_in_place(dst_map->slots[slot_idx], src_map->slots[slot_idx]); + } + } + } + } + + //- rjf: sort + ProfScope("sort") + { + Rng1U64 slot_range = lane_range(top->slots_count); + for EachInRange(slot_idx, slot_range) + { + if(map->slots[slot_idx] != 0 && map->slots[slot_idx]->total_count > 1) + { + *map->slots[slot_idx] = rdim_bake_name_chunk_list_sorted_from_unsorted(arena, map->slots[slot_idx]); + } + } + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage build index runs + // + ProfScope("build index runs") + { + //- rjf: set up per-lane outputs + if(lane_idx() == 0) ProfScope("set up per-lane outputs") + { + rdim_shared->bake_idx_run_map_topology.slots_count = (64 + + params->procedures.total_count + + params->global_variables.total_count + + params->thread_variables.total_count + + params->types.total_count); + rdim_shared->lane_bake_idx_run_maps__loose = push_array(arena, RDIM_BakeIdxRunMapLoose *, lane_count()); + rdim_shared->bake_idx_run_map__loose = rdim_bake_idx_run_map_loose_make(arena, &rdim_shared->bake_idx_run_map_topology); + } + lane_sync(); + + //- rjf: set up this lane's map + ProfScope("set up this lane's map") + { + rdim_shared->lane_bake_idx_run_maps__loose[lane_idx()] = rdim_bake_idx_run_map_loose_make(arena, &rdim_shared->bake_idx_run_map_topology); + } + RDIM_BakeIdxRunMapTopology *lane_map_top = &rdim_shared->bake_idx_run_map_topology; + RDIM_BakeIdxRunMapLoose *lane_map = rdim_shared->lane_bake_idx_run_maps__loose[lane_idx()]; + + //- rjf: wide fill of all index runs + ProfScope("fill all lane index run maps") + { + //- rjf: bake runs of function-type parameter lists + ProfScope("bake runs of function-type parameter lists") + { + for EachNode(n, RDIM_TypeChunkNode, params->types.first) + { + Rng1U64 range = lane_range(n->count); + ProfScope("[%I64u, %I64u)", range.min, range.max) for EachInRange(n_idx, range) + { + RDIM_Type *type = &n->v[n_idx]; + if(type->count == 0) + { + continue; + } + if(type->kind == RDI_TypeKind_Function || type->kind == RDI_TypeKind_Method) + { + RDI_U32 param_idx_run_count = type->count; + RDI_U32 *param_idx_run = rdim_push_array_no_zero(arena, RDI_U32, param_idx_run_count); + for(RDI_U32 idx = 0; idx < param_idx_run_count; idx += 1) + { + param_idx_run[idx] = (RDI_U32)rdim_idx_from_type(type->param_types[idx]); // TODO(rjf): @u64_to_u32 + } + rdim_bake_idx_run_map_loose_insert(arena, lane_map_top, lane_map, 4, param_idx_run, param_idx_run_count); + } + } + } + } + + //- rjf: bake runs of name map match lists + for EachNonZeroEnumVal(RDI_NameMapKind, k) ProfScope("bake runs of name map match lists (%.*s)", str8_varg(rdi_string_from_name_map_kind(k))) + { + RDIM_BakeNameMapTopology *top = &rdim_shared->bake_name_map_topology[k]; + RDIM_BakeNameMap *map = rdim_shared->bake_name_maps[k]; + Rng1U64 slot_idx_range = lane_range(top->slots_count); + for EachInRange(slot_idx, slot_idx_range) + { + RDIM_BakeNameChunkList *slot = map->slots[slot_idx]; + if(slot != 0) + { + Temp scratch = scratch_begin(&arena, 1); + typedef struct IdxRunNode IdxRunNode; + struct IdxRunNode + { + IdxRunNode *next; + RDI_U64 idx; + }; + IdxRunNode *first_idx_run_node = 0; + IdxRunNode *last_idx_run_node = 0; + U64 active_idx_count = 0; + String8 active_string = {0}; + RDIM_BakeNameChunkNode *n = slot->first; + U64 n_idx = 0; + for(;;) + { + // rjf: advance chunk + if(n != 0 && n_idx >= n->count) + { + n = n->next; + n_idx = 0; + } + + // rjf: grab next element + String8 string = {0}; + U64 idx = 0; + if(n != 0) + { + string = n->v[n_idx].string; + idx = n->v[n_idx].idx; + } + + // rjf: next element hash doesn't match the active? -> push index run, clear active list, start new list + if(!str8_match(string, active_string, 0)) + { + if(active_idx_count > 1) + { + RDI_U64 idxs_count = active_idx_count; + RDI_U32 *idxs = rdim_push_array(arena, RDI_U32, idxs_count); + { + U64 write_idx = 0; + for EachNode(idx_run_n, IdxRunNode, first_idx_run_node) + { + idxs[write_idx] = (RDI_U32)idx_run_n->idx; // TODO(rjf): @u64_to_u32 + write_idx += 1; + } + } + rdim_bake_idx_run_map_loose_insert(arena, lane_map_top, lane_map, 4, idxs, idxs_count); + } + active_string = string; + first_idx_run_node = 0; + last_idx_run_node = 0; + active_idx_count = 0; + temp_end(scratch); + } + + // rjf: new element matches the active list -> push + if(active_string.size != 0 && str8_match(string, active_string, 0)) + { + IdxRunNode *idx_run_n = push_array(scratch.arena, IdxRunNode, 1); + idx_run_n->idx = idx; + SLLQueuePush(first_idx_run_node, last_idx_run_node, idx_run_n); + active_idx_count += 1; + } + + // rjf: advance index + n_idx += 1; + + // rjf: end on zero node + if(n == 0) + { + break; + } + } + scratch_end(scratch); + } + } + } + } + lane_sync(); + + //- rjf: join + ProfScope("join") + { + Rng1U64 slot_range = lane_range(rdim_shared->bake_idx_run_map_topology.slots_count); + for EachInRange(slot_idx, slot_range) + { + for EachIndex(src_lane_idx, lane_count()) + { + RDIM_BakeIdxRunMapLoose *src_map = rdim_shared->lane_bake_idx_run_maps__loose[src_lane_idx]; + RDIM_BakeIdxRunMapLoose *dst_map = rdim_shared->bake_idx_run_map__loose; + dst_map->slots_idx_counts[slot_idx] += src_map->slots_idx_counts[slot_idx]; + if(dst_map->slots[slot_idx] == 0 && src_map->slots[slot_idx] != 0) + { + dst_map->slots[slot_idx] = src_map->slots[slot_idx]; + } + else if(dst_map->slots[slot_idx] != 0 && src_map->slots[slot_idx] != 0) + { + rdim_bake_idx_run_chunk_list_concat_in_place(dst_map->slots[slot_idx], src_map->slots[slot_idx]); + } + } + } + } + lane_sync(); + + //- rjf: sort + ProfScope("sort") + { + RDIM_BakeIdxRunMapLoose *map = rdim_shared->bake_idx_run_map__loose; + Rng1U64 slot_range = lane_range(rdim_shared->bake_idx_run_map_topology.slots_count); + for EachInRange(slot_idx, slot_range) + { + if(map->slots[slot_idx] != 0 && map->slots[slot_idx]->total_count > 1) + { + *map->slots[slot_idx] = rdim_bake_idx_run_chunk_list_sorted_from_unsorted(arena, map->slots[slot_idx]); + map->slots_idx_counts[slot_idx] = 0; + for EachNode(n, RDIM_BakeIdxRunChunkNode, map->slots[slot_idx]->first) + { + for EachIndex(idx, n->count) + { + map->slots_idx_counts[slot_idx] += n->v[idx].count; + } + } + } + } + } + lane_sync(); + + //- rjf: tighten idx run table + ProfScope("tighten idx run table") + { + RDIM_BakeIdxRunMapLoose *map = rdim_shared->bake_idx_run_map__loose; + RDIM_BakeIdxRunMapTopology *map_top = &rdim_shared->bake_idx_run_map_topology; + if(lane_idx() == 0) ProfScope("calc base indices, set up tight map") + { + rdim_shared->bake_idx_runs.slots_count = map_top->slots_count; + rdim_shared->bake_idx_runs.slots = rdim_push_array(arena, RDIM_BakeIdxRunChunkList, rdim_shared->bake_idx_runs.slots_count); + rdim_shared->bake_idx_runs.slots_base_idxs = rdim_push_array(arena, RDI_U64, rdim_shared->bake_idx_runs.slots_count+1); + RDI_U64 encoding_idx_off = 0; + for(RDI_U64 slot_idx = 0; slot_idx < map_top->slots_count; slot_idx += 1) + { + rdim_shared->bake_idx_runs.slots_base_idxs[slot_idx] = encoding_idx_off; + if(map->slots[slot_idx] != 0) + { + encoding_idx_off += map->slots_idx_counts[slot_idx]; + } + } + rdim_shared->bake_idx_runs.slots_base_idxs[map_top->slots_count] = encoding_idx_off; + } + lane_sync(); + ProfScope("fill tight map") + { + Rng1U64 slot_range = lane_range(rdim_shared->bake_idx_runs.slots_count); + for EachInRange(idx, slot_range) + { + if(map->slots[idx] != 0) + { + rdim_memcpy_struct(&rdim_shared->bake_idx_runs.slots[idx], map->slots[idx]); + } + } + } + } + } + lane_sync(); + RDIM_BakeIdxRunMap *bake_idx_runs = &rdim_shared->bake_idx_runs; + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake strings + // + ProfScope("bake strings") + { + // rjf: set up + if(lane_idx() == 0) ProfScope("set up; lay out strings") + { + rdim_shared->baked_strings.string_offs_count = bake_strings->total_count + 1; + rdim_shared->baked_strings.string_offs = rdim_push_array(arena, RDI_U32, rdim_shared->baked_strings.string_offs_count); + RDI_U64 off_cursor = 0; + for EachIndex(slot_idx, bake_strings->slots_count) + { + for EachNode(n, RDIM_BakeStringChunkNode, bake_strings->slots[slot_idx].first) + { + for EachIndex(n_idx, n->count) + { + RDIM_BakeString *src = &n->v[n_idx]; + U64 dst_idx = bake_strings->slots_base_idxs[slot_idx] + n->base_idx + n_idx + 1; + rdim_shared->baked_strings.string_offs[dst_idx] = off_cursor; + off_cursor += src->string.size; + } + } + } + rdim_shared->baked_strings.string_data_size = off_cursor; + rdim_shared->baked_strings.string_data = rdim_push_array(arena, RDI_U8, rdim_shared->baked_strings.string_data_size); + } + lane_sync(); + + // rjf: wide fill string data + ProfScope("wide fill") + { + Rng1U64 slot_idx_range = lane_range(bake_strings->slots_count); + for EachInRange(slot_idx, slot_idx_range) + { + for EachNode(n, RDIM_BakeStringChunkNode, bake_strings->slots[slot_idx].first) + { + for EachIndex(n_idx, n->count) + { + RDIM_BakeString *src = &n->v[n_idx]; + U64 dst_idx = bake_strings->slots_base_idxs[slot_idx] + n->base_idx + n_idx + 1; + U64 dst_off = rdim_shared->baked_strings.string_offs[dst_idx]; + rdim_memcpy(rdim_shared->baked_strings.string_data + dst_off, src->string.str, src->string.size); + } + } + } + } + } + lane_sync(); + RDIM_StringBakeResult baked_strings = rdim_shared->baked_strings; + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake idx runs + // + ProfScope("bake idx runs") + { + // rjf: set up + if(lane_idx() == 0) + { + rdim_shared->baked_idx_runs.idx_count = bake_idx_runs->slots_base_idxs[bake_idx_runs->slots_count]; + rdim_shared->baked_idx_runs.idx_runs = push_array(arena, RDI_U32, rdim_shared->baked_idx_runs.idx_count); + } + lane_sync(); + + // rjf: wide fill + { + Rng1U64 range = lane_range(bake_idx_runs->slots_count); + for EachInRange(slot_idx, range) + { + RDI_U64 off = bake_idx_runs->slots_base_idxs[slot_idx]; + for EachNode(n, RDIM_BakeIdxRunChunkNode, bake_idx_runs->slots[slot_idx].first) + { + StaticAssert(sizeof(rdim_shared->baked_idx_runs.idx_runs[0]) == sizeof(n->v[0].idxes[0]), idx_run_size_check); + for EachIndex(n_idx, n->count) + { + rdim_memcpy(rdim_shared->baked_idx_runs.idx_runs + off, n->v[n_idx].idxes, sizeof(n->v[n_idx].idxes[0]) * n->v[n_idx].count); + off += n->v[n_idx].count; + } + } + } + } + } + lane_sync(); + RDIM_IndexRunBakeResult baked_idx_runs = rdim_shared->baked_idx_runs; + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake name maps + // + ProfScope("bake name maps") + { + // rjf: count unique names in all name maps; lay out baked nodes + ProfScope("count unique names in all name maps; lay out baked nodes") + { + if(lane_idx() == 0) + { + for EachNonZeroEnumVal(RDI_NameMapKind, k) + { + rdim_shared->lane_name_map_node_counts[k] = push_array(arena, U64, lane_count()); + rdim_shared->lane_name_map_node_offs[k] = push_array(arena, U64, lane_count()); + } + } + lane_sync(); + for EachNonZeroEnumVal(RDI_NameMapKind, k) + { + RDIM_BakeNameMapTopology *top = &rdim_shared->bake_name_map_topology[k]; + RDIM_BakeNameMap *map = rdim_shared->bake_name_maps[k]; + Rng1U64 range = lane_range(top->slots_count); + for EachInRange(idx, range) + { + if(map->slots[idx] != 0) + { + U64 total_unique_name_count = 0; + U64 last_hash = 0; + for EachNode(n, RDIM_BakeNameChunkNode, map->slots[idx]->first) + { + for EachIndex(n_idx, n->count) + { + if(n->v[n_idx].hash != last_hash) + { + total_unique_name_count += 1; + last_hash = n->v[n_idx].hash; + } + } + } + rdim_shared->lane_name_map_node_counts[k][lane_idx()] += total_unique_name_count; + } + } + } + lane_sync(); + if(lane_idx() == 0) + { + for EachNonZeroEnumVal(RDI_NameMapKind, k) + { + RDI_U64 node_off = 0; + for EachIndex(l_idx, lane_count()) + { + rdim_shared->name_map_node_counts[k] += rdim_shared->lane_name_map_node_counts[k][l_idx]; + rdim_shared->lane_name_map_node_offs[k][l_idx] = node_off; + node_off += rdim_shared->lane_name_map_node_counts[k][l_idx]; + } + rdim_shared->total_name_map_node_count += rdim_shared->name_map_node_counts[k]; + } + } + } + lane_sync(); + + // rjf: setup + ProfScope("setup") + { + if(lane_idx() == lane_from_task_idx(0)) + { + rdim_shared->baked_top_level_name_maps.name_maps_count = RDI_NameMapKind_COUNT; + rdim_shared->baked_top_level_name_maps.name_maps = push_array(arena, RDI_NameMap, rdim_shared->baked_top_level_name_maps.name_maps_count); + RDI_U32 bucket_off = 0; + RDI_U32 node_off = 0; + for EachNonZeroEnumVal(RDI_NameMapKind, k) + { + rdim_shared->baked_top_level_name_maps.name_maps[k].bucket_base_idx = bucket_off; + rdim_shared->baked_top_level_name_maps.name_maps[k].node_base_idx = node_off; + rdim_shared->baked_top_level_name_maps.name_maps[k].bucket_count = (RDI_U32)rdim_shared->bake_name_map_topology[k].slots_count; // TODO(rjf): @u64_to_u32 + rdim_shared->baked_top_level_name_maps.name_maps[k].node_count = (RDI_U32)rdim_shared->name_map_node_counts[k]; // TODO(rjf): @u64_to_u32 + bucket_off += rdim_shared->baked_top_level_name_maps.name_maps[k].bucket_count; + node_off += rdim_shared->baked_top_level_name_maps.name_maps[k].node_count; + } + rdim_shared->baked_name_maps.buckets_count = bucket_off; + rdim_shared->baked_name_maps.buckets = push_array(arena, RDI_NameMapBucket, rdim_shared->baked_name_maps.buckets_count); + } + if(lane_idx() == lane_from_task_idx(1)) + { + rdim_shared->baked_name_maps.nodes_count = rdim_shared->total_name_map_node_count; + rdim_shared->baked_name_maps.nodes = push_array(arena, RDI_NameMapNode, rdim_shared->baked_name_maps.nodes_count); + } + } + lane_sync(); + + // rjf: wide fill baked name maps + ProfScope("wide fill baked name maps") + { + for EachNonZeroEnumVal(RDI_NameMapKind, k) ProfScope("wide fill (%.*s)", str8_varg(rdi_string_from_name_map_kind(k))) + { + RDI_U64 write_node_off = rdim_shared->lane_name_map_node_offs[k][lane_idx()]; + RDIM_BakeNameMapTopology *top = &rdim_shared->bake_name_map_topology[k]; + U64 slots_count = top->slots_count; + RDIM_BakeNameMap *src_map = rdim_shared->bake_name_maps[k]; + RDI_NameMap *dst_map = &rdim_shared->baked_top_level_name_maps.name_maps[k]; + RDI_NameMapBucket *dst_buckets = rdim_shared->baked_name_maps.buckets + dst_map->bucket_base_idx; + RDI_NameMapNode *dst_nodes = rdim_shared->baked_name_maps.nodes + dst_map->node_base_idx; + Rng1U64 slot_range = lane_range(slots_count); + for EachInRange(slot_idx, slot_range) + { + RDIM_BakeNameChunkList *src_slot = src_map->slots[slot_idx]; + if(src_slot == 0) { continue; } + RDI_NameMapBucket *dst_bucket = &dst_buckets[slot_idx]; + dst_bucket->first_node = write_node_off; + { + Temp scratch = scratch_begin(&arena, 1); + typedef struct IdxRunNode IdxRunNode; + struct IdxRunNode + { + IdxRunNode *next; + RDI_U64 idx; + }; + IdxRunNode *first_idx_run_node = 0; + IdxRunNode *last_idx_run_node = 0; + U64 active_idx_count = 0; + String8 active_string = {0}; + RDIM_BakeNameChunkNode *n = src_slot->first; + U64 n_idx = 0; + for(;;) + { + // rjf: advance chunk + if(n != 0 && n_idx >= n->count) + { + n = n->next; + n_idx = 0; + } + + // rjf: grab next element + U64 idx = 0; + String8 string = {0}; + if(n != 0) + { + idx = n->v[n_idx].idx; + string = n->v[n_idx].string; + } + + // rjf: next element doesn't match the active list? -> push index run, clear active list, start new list + if(!str8_match(active_string, string, 0)) + { + // rjf: has active run -> flatten & serialize + if(active_string.size != 0) + { + // rjf: flatten idxes + RDI_U64 idxs_count = active_idx_count; + RDI_U32 *idxs = rdim_push_array(scratch.arena, RDI_U32, idxs_count); + { + U64 write_idx = 0; + for EachNode(idx_run_n, IdxRunNode, first_idx_run_node) + { + idxs[write_idx] = (RDI_U32)idx_run_n->idx; // TODO(rjf): @u64_to_u32 + write_idx += 1; + } + } + + // rjf: serialize node + RDI_NameMapNode *dst_node = &dst_nodes[write_node_off]; + dst_node->string_idx = rdim_bake_idx_from_string(bake_strings, active_string); + dst_node->match_count = idxs_count; + if(dst_node->match_count == 1) + { + dst_node->match_idx_or_idx_run_first = idxs[0]; + } + else if(dst_node->match_count > 1) + { + dst_node->match_idx_or_idx_run_first = rdim_bake_idx_from_idx_run(bake_idx_runs, idxs, idxs_count); + } + dst_bucket->node_count += 1; + write_node_off += 1; + } + + // rjf: start new list + active_string = string; + first_idx_run_node = 0; + last_idx_run_node = 0; + active_idx_count = 0; + temp_end(scratch); + } + + // rjf: hash matches the active list -> push + if(active_string.size != 0 && str8_match(active_string, string, 0)) + { + IdxRunNode *idx_run_n = push_array(scratch.arena, IdxRunNode, 1); + idx_run_n->idx = idx; + SLLQueuePush(first_idx_run_node, last_idx_run_node, idx_run_n); + active_idx_count += 1; + } + + // rjf: advance index + n_idx += 1; + + // rjf: end on zero node + if(n == 0) + { + break; + } + } + scratch_end(scratch); + } + } + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage gather line-bucketed src line map data + // + ProfScope("gather line-bucketed src line map data") + { + if(lane_idx() == 0) + { + rdim_shared->bake_src_line_maps = push_array(arena, RDIM_BakeSrcLineMap, params->src_files.total_count); + } + lane_sync(); + { + for EachNode(n, RDIM_SrcFileChunkNode, params->src_files.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + U64 file_idx = n->base_idx + n_idx; + RDIM_BakeSrcLineMap *map = &rdim_shared->bake_src_line_maps[file_idx]; + + // rjf: set up map + map->slots_count = n->v[n_idx].total_line_count; + map->slots = push_array(arena, RDIM_BakeSrcLineMapSlot, map->slots_count); + + // rjf: gather line-bucketed info + for EachNode(frag, RDIM_SrcFileLineMapFragment, n->v[n_idx].first_line_map_fragment) + { + RDIM_LineSequence *seq = frag->seq; + for EachIndex(idx, seq->line_count) + { + RDI_U32 line_num = seq->line_nums[idx]; + RDI_U64 voff_first = seq->voffs[idx]; + RDI_U64 voff_opl = seq->voffs[idx+1]; + RDI_U64 slot_idx = line_num%map->slots_count; + + // rjf: find existing line node + RDIM_BakeSrcLineMapNode *line_node = 0; + { + for EachNode(line_n, RDIM_BakeSrcLineMapNode, map->slots[slot_idx].first) + { + if(line_n->line_num == line_num) + { + line_node = line_n; + break; + } + } + } + + // rjf: construct new node if unseen + if(line_node == 0) + { + line_node = push_array(arena, RDIM_BakeSrcLineMapNode, 1); + SLLQueuePush(map->slots[slot_idx].first, map->slots[slot_idx].last, line_node); + line_node->line_num = line_num; + map->line_count += 1; + } + + // rjf: push this voff range + RDIM_Rng1U64 voff_range = {voff_first, voff_opl}; + rdim_rng1u64_list_push(arena, &line_node->voff_ranges, voff_range); + map->voff_range_count += 1; + } + } + } + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage sort line-bucketed src line map data + // + ProfScope("sort line-bucketed src line map data") + { + U64 map_count = params->src_files.total_count; + if(lane_idx() == 0) + { + rdim_shared->bake_src_line_map_keys = push_array(arena, RDIM_SortKey *, map_count); + } + lane_sync(); + for(;;) + { + U64 map_num = ins_atomic_u64_inc_eval(&rdim_shared->bake_src_line_map_take_counter); + if(map_num < 1 || map_count < map_num) + { + break; + } + U64 map_idx = map_num-1; + RDIM_BakeSrcLineMap *map = &rdim_shared->bake_src_line_maps[map_idx]; + + // rjf: gather keys + rdim_shared->bake_src_line_map_keys[map_idx] = push_array_no_zero(arena, RDIM_SortKey, map->line_count); + RDIM_SortKey *keys = rdim_shared->bake_src_line_map_keys[map_idx]; + { + U64 key_idx = 0; + for EachIndex(slot_idx, map->slots_count) + { + for EachNode(n, RDIM_BakeSrcLineMapNode, map->slots[slot_idx].first) + { + keys[key_idx].key = n->line_num; + keys[key_idx].val = n; + key_idx += 1; + } + } + } + + // rjf: sort keys + { + radsort(keys, map->line_count, rdim_sort_key_is_before); } } } - ////////////////////////////// - //- rjf: join all individual name map bakes + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage compute src file / src file line map layout // - ProfScope("join all name map bakes into final name map bake") + ProfScope("compute src file / src file line map layout") { - out.name_maps = rdim_name_map_bake_results_combine(arena, name_map_bakes, ArrayCount(name_map_bakes)); + // rjf: set up + if(lane_idx() == 0) + { + rdim_shared->lane_chunk_src_file_num_counts = push_array(arena, U64, lane_count()*params->src_files.chunk_count); + rdim_shared->lane_chunk_src_file_voff_counts = push_array(arena, U64, lane_count()*params->src_files.chunk_count); + rdim_shared->lane_chunk_src_file_map_counts = push_array(arena, U64, lane_count()*params->src_files.chunk_count); + rdim_shared->lane_chunk_src_file_num_offs = push_array(arena, U64, lane_count()*params->src_files.chunk_count); + rdim_shared->lane_chunk_src_file_voff_offs = push_array(arena, U64, lane_count()*params->src_files.chunk_count); + rdim_shared->lane_chunk_src_file_map_offs = push_array(arena, U64, lane_count()*params->src_files.chunk_count); + } + lane_sync(); + + // rjf: wide count + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_SrcFileChunkNode, params->src_files.first) + { + Rng1U64 range = lane_range(n->count); + U64 slot_idx = lane_idx()*params->src_files.chunk_count + chunk_idx; + for EachInRange(idx, range) + { + RDIM_BakeSrcLineMap *map = &rdim_shared->bake_src_line_maps[n->base_idx + idx]; + rdim_shared->lane_chunk_src_file_num_counts[slot_idx] += map->line_count; + rdim_shared->lane_chunk_src_file_voff_counts[slot_idx] += map->voff_range_count; + rdim_shared->lane_chunk_src_file_map_counts[slot_idx] += !!map->line_count; + } + chunk_idx += 1; + } + } + lane_sync(); + + // rjf: layout + if(lane_idx() == 0) + { + U64 chunk_idx = 0; + U64 num_layout_off = 0; + U64 voff_layout_off = 0; + U64 map_layout_off = 1; + for EachNode(n, RDIM_SrcFileChunkNode, params->src_files.first) + { + for EachIndex(l_idx, lane_count()) + { + U64 slot_idx = l_idx*params->src_files.chunk_count + chunk_idx; + rdim_shared->lane_chunk_src_file_num_offs[slot_idx] = num_layout_off; + rdim_shared->lane_chunk_src_file_voff_offs[slot_idx] = voff_layout_off; + rdim_shared->lane_chunk_src_file_map_offs[slot_idx] = map_layout_off; + num_layout_off += rdim_shared->lane_chunk_src_file_num_counts[slot_idx]; + voff_layout_off += rdim_shared->lane_chunk_src_file_voff_counts[slot_idx]; + map_layout_off += rdim_shared->lane_chunk_src_file_map_counts[slot_idx]; + } + chunk_idx += 1; + } + rdim_shared->total_src_map_line_count = num_layout_off; + rdim_shared->total_src_map_voff_count = voff_layout_off; + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake src files + // + ProfScope("bake src files") + { + //- rjf: set up + if(lane_idx() == 0) + { + rdim_shared->baked_src_files.source_files_count = params->src_files.total_count+1; + rdim_shared->baked_src_files.source_files = push_array(arena, RDI_SourceFile, rdim_shared->baked_src_files.source_files_count); + rdim_shared->baked_src_files.source_line_maps_count = params->src_files.source_line_map_count+1; + rdim_shared->baked_src_files.source_line_maps = push_array(arena, RDI_SourceLineMap, rdim_shared->baked_src_files.source_line_maps_count); + rdim_shared->baked_src_files.source_line_map_nums_count = rdim_shared->total_src_map_line_count; + rdim_shared->baked_src_files.source_line_map_nums = push_array(arena, RDI_U32, rdim_shared->baked_src_files.source_line_map_nums_count); + rdim_shared->baked_src_files.source_line_map_rngs_count = rdim_shared->total_src_map_line_count + rdim_shared->baked_src_files.source_line_maps_count; + rdim_shared->baked_src_files.source_line_map_rngs = push_array(arena, RDI_U32, rdim_shared->baked_src_files.source_line_map_rngs_count); + rdim_shared->baked_src_files.source_line_map_voffs_count = rdim_shared->total_src_map_voff_count; + rdim_shared->baked_src_files.source_line_map_voffs = push_array(arena, RDI_U64, rdim_shared->baked_src_files.source_line_map_voffs_count); + } + lane_sync(); + + //- rjf: bake + U64 chunk_idx = 0; + for EachNode(n, RDIM_SrcFileChunkNode, params->src_files.first) + { + Rng1U64 range = lane_range(n->count); + U64 slot_idx = lane_idx()*params->src_files.chunk_count + chunk_idx; + U64 dst_num_off = rdim_shared->lane_chunk_src_file_num_offs[slot_idx]; + U64 dst_map_off = rdim_shared->lane_chunk_src_file_map_offs[slot_idx]; + U64 dst_voff_off = rdim_shared->lane_chunk_src_file_voff_offs[slot_idx]; + U64 dst_rng_off = dst_num_off + dst_map_off; + for EachInRange(idx, range) + { + RDIM_BakeSrcLineMap *map = &rdim_shared->bake_src_line_maps[n->base_idx + idx]; + RDIM_SortKey *sorted_map_keys = rdim_shared->bake_src_line_map_keys[n->base_idx + idx]; + RDIM_SrcFile *src = &n->v[idx]; + RDI_SourceFile *dst = &rdim_shared->baked_src_files.source_files[n->base_idx + idx + 1]; + RDI_SourceLineMap *dst_map = &rdim_shared->baked_src_files.source_line_maps[dst_map_off]; + RDI_U32 *dst_nums = &rdim_shared->baked_src_files.source_line_map_nums[dst_num_off]; + RDI_U32 *dst_rngs = &rdim_shared->baked_src_files.source_line_map_rngs[dst_rng_off]; + RDI_U64 *dst_voffs = &rdim_shared->baked_src_files.source_line_map_voffs[dst_voff_off]; + + //- rjf: fill file info + Temp scratch = scratch_begin(&arena, 1); + String8 normalized_path = rdim_lower_from_str8(scratch.arena, src->path); + dst->file_path_node_idx = rdim_bake_path_node_idx_from_string(path_tree, src->path); + dst->normal_full_path_string_idx = rdim_bake_idx_from_string(bake_strings, normalized_path); + dst->source_line_map_idx = src->total_line_count ? dst_map_off : 0; + scratch_end(scratch); + + //- rjf: fill map info + if(src->total_line_count != 0) + { + dst_map->line_count = (RDI_U32)map->line_count; // TODO(rjf): @u64_to_u32 + dst_map->voff_count = (RDI_U32)map->voff_range_count; // TODO(rjf): @u64_to_u32 + dst_map->line_map_nums_base_idx = (RDI_U32)dst_num_off; // TODO(rjf): @u64_to_u32 + dst_map->line_map_range_base_idx = (RDI_U32)dst_rng_off; // TODO(rjf): @u64_to_u32 + dst_map->line_map_voff_base_idx = (RDI_U32)dst_voff_off; // TODO(rjf): @u64_to_u32 + dst_map_off += 1; + } + + //- rjf: fill nums/ranges/voffs info + if(src->total_line_count != 0) + { + U64 *dst_voff_ptr = dst_voffs; + for EachIndex(line_num_idx, map->line_count) + { + dst_nums[line_num_idx] = (RDI_U32)sorted_map_keys[line_num_idx].key; // TODO(rjf): @u64_to_u32 + dst_rngs[line_num_idx] = (RDI_U32)(dst_voff_ptr - dst_voffs); // TODO(rjf): @u64_to_u32 + RDIM_BakeSrcLineMapNode *node = (RDIM_BakeSrcLineMapNode *)sorted_map_keys[line_num_idx].val; + for EachNode(rng_n, RDIM_Rng1U64Node, node->voff_ranges.first) + { + dst_voff_ptr[0] = rng_n->v.min; + dst_voff_ptr += 1; + } + } + dst_rngs[map->line_count] = (RDI_U32)map->voff_range_count; // TODO(rjf): @u64_to_u32 + dst_num_off += map->line_count; + dst_rng_off += map->line_count+1; + dst_voff_off += map->voff_range_count; + } + } + chunk_idx += 1; + } } - //////////////////////////////// + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage compute lane UDT member/enum-val layouts + // + ProfScope("compute lane UDT member/enum-val layouts") + { + // rjf: allocate + if(lane_idx() == 0) + { + rdim_shared->member_chunk_lane_counts = push_array(arena, U64, lane_count() * params->udts.chunk_count); + rdim_shared->member_chunk_lane_offs = push_array(arena, U64, lane_count() * params->udts.chunk_count); + rdim_shared->enum_val_chunk_lane_counts = push_array(arena, U64, lane_count() * params->udts.chunk_count); + rdim_shared->enum_val_chunk_lane_offs = push_array(arena, U64, lane_count() * params->udts.chunk_count); + } + lane_sync(); + + // rjf: count + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_UDTChunkNode, params->udts.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) + { + U64 slot_idx = lane_idx()*params->udts.chunk_count + chunk_idx; + rdim_shared->member_chunk_lane_counts[slot_idx] += n->v[idx].member_count; + rdim_shared->enum_val_chunk_lane_counts[slot_idx] += n->v[idx].enum_val_count; + } + chunk_idx += 1; + } + } + lane_sync(); + + // rjf: layout + if(lane_idx() == 0) + { + U64 member_layout_off = 1; + U64 enum_val_layout_off = 1; + U64 chunk_idx = 0; + for EachNode(n, RDIM_UDTChunkNode, params->udts.first) + { + for EachIndex(l_idx, lane_count()) + { + U64 slot_idx = l_idx*params->udts.chunk_count + chunk_idx; + rdim_shared->member_chunk_lane_offs[slot_idx] = member_layout_off; + rdim_shared->enum_val_chunk_lane_offs[slot_idx] = enum_val_layout_off; + member_layout_off += rdim_shared->member_chunk_lane_counts[slot_idx]; + enum_val_layout_off += rdim_shared->enum_val_chunk_lane_counts[slot_idx]; + } + chunk_idx += 1; + } + } + } + lane_sync(); - out.location_blocks = rdim_str8_list_join(arena, &location_blocks, rdim_str8(0,0)); - out.location_data = rdim_str8_list_join(arena, &location_data_blobs, rdim_str8(0,0)); + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake UDTs + // + ProfScope("bake UDTs") + { + //- rjf: set up + ProfScope("set up") + { + if(lane_idx() == lane_from_task_idx(0)) + { + rdim_shared->baked_udts.udts_count = params->udts.total_count+1; + rdim_shared->baked_udts.udts = push_array(arena, RDI_UDT, rdim_shared->baked_udts.udts_count); + } + if(lane_idx() == lane_from_task_idx(1)) + { + rdim_shared->baked_udts.members_count = params->udts.total_member_count+1; + rdim_shared->baked_udts.members = push_array(arena, RDI_Member, rdim_shared->baked_udts.members_count); + } + if(lane_idx() == lane_from_task_idx(2)) + { + rdim_shared->baked_udts.enum_members_count = params->udts.total_enum_val_count+1; + rdim_shared->baked_udts.enum_members = push_array(arena, RDI_EnumMember, rdim_shared->baked_udts.enum_members_count); + } + } + lane_sync(); + + //- rjf: bake UDTs + ProfScope("bake UDTs") + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_UDTChunkNode, params->udts.first) + { + Rng1U64 range = lane_range(n->count); + U64 layout_slot_idx = lane_idx()*params->udts.chunk_count + chunk_idx; + U64 member_layout_off = rdim_shared->member_chunk_lane_offs[layout_slot_idx]; + U64 enum_val_layout_off = rdim_shared->enum_val_chunk_lane_offs[layout_slot_idx]; + for EachInRange(n_idx, range) + { + RDIM_UDT *src_udt = &n->v[n_idx]; + RDI_UDT *dst_udt = &rdim_shared->baked_udts.udts[n->base_idx + n_idx + 1]; + + //- rjf: fill basics + dst_udt->self_type_idx = (RDI_U32)rdim_idx_from_type(src_udt->self_type); // TODO(rjf): @u64_to_u32 + dst_udt->file_idx = (RDI_U32)rdim_idx_from_src_file(src_udt->src_file); // TODO(rjf): @u64_to_u32 + dst_udt->line = src_udt->line; + dst_udt->col = src_udt->col; + + //- rjf: fill member info + if(src_udt->first_member != 0) + { + U64 member_off_first = member_layout_off; + for EachNode(src_member, RDIM_UDTMember, src_udt->first_member) + { + RDI_Member *dst_member = &rdim_shared->baked_udts.members[member_layout_off]; + dst_member->kind = src_member->kind; + dst_member->name_string_idx = rdim_bake_idx_from_string(bake_strings, src_member->name); + dst_member->type_idx = (RDI_U32)rdim_idx_from_type(src_member->type); // TODO(rjf): @u64_to_u32 + dst_member->off = src_member->off; + member_layout_off += 1; + } + U64 member_off_opl = member_layout_off; + dst_udt->member_first = (RDI_U32)member_off_first; // TODO(rjf): @u64_to_u32 + dst_udt->member_count = (RDI_U32)(member_off_opl - member_off_first); // TODO(rjf): @u64_to_u32 + } + + //- rjf: fill enum val info + else if(src_udt->first_enum_val != 0) + { + U64 enum_val_off_first = enum_val_layout_off; + for EachNode(src_enum_val, RDIM_UDTEnumVal, src_udt->first_enum_val) + { + RDI_EnumMember *dst_member = &rdim_shared->baked_udts.enum_members[enum_val_layout_off]; + dst_member->name_string_idx = rdim_bake_idx_from_string(bake_strings, src_enum_val->name); + dst_member->val = src_enum_val->val; + enum_val_layout_off += 1; + } + U64 enum_val_off_opl = enum_val_layout_off; + dst_udt->flags |= RDI_UDTFlag_EnumMembers; + dst_udt->member_first = (RDI_U32)enum_val_off_first; // TODO(rjf): @u64_to_u32 + dst_udt->member_count = (RDI_U32)(enum_val_off_opl - enum_val_off_first); // TODO(rjf): @u64_to_u32 + } + } + chunk_idx += 1; + } + } + } + lane_sync(); - rdim_local_async_root = 0; - scratch_end(scratch); - return out; + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage compute lane location block layout + // + U64 total_location_case_chunk_count = (params->scopes.chunk_count + params->procedures.chunk_count); + ProfScope("compute lane location block layout") + { + // rjf: set up + if(lane_idx() == 0) + { + rdim_shared->location_case_chunk_lane_counts = push_array(arena, RDI_U64, lane_count() * total_location_case_chunk_count); + rdim_shared->location_case_chunk_lane_offs = push_array(arena, RDI_U64, lane_count() * total_location_case_chunk_count); + } + lane_sync(); + + // rjf: per-chunk-lane count of location cases + { + // rjf: count location cases in scopes + U64 chunk_idx = 0; + for EachNode(n, RDIM_ScopeChunkNode, params->scopes.first) + { + U64 slot_idx = lane_idx() * total_location_case_chunk_count + chunk_idx; + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) + { + for EachNode(local, RDIM_Local, n->v[idx].first_local) + { + rdim_shared->location_case_chunk_lane_counts[slot_idx] += local->location_cases.count; + } + } + chunk_idx += 1; + } + + // rjf: count location cases in procedures + for EachNode(n, RDIM_SymbolChunkNode, params->procedures.first) + { + U64 slot_idx = lane_idx() * total_location_case_chunk_count + chunk_idx; + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) + { + rdim_shared->location_case_chunk_lane_counts[slot_idx] += n->v[idx].location_cases.count; + } + chunk_idx += 1; + } + } + lane_sync(); + + // rjf: lay out location case offsets + if(lane_idx() == 0) + { + U64 chunk_idx = 0; + U64 location_case_layout_off = 1; + for EachNode(n, RDIM_ScopeChunkNode, params->scopes.first) + { + for EachIndex(l_idx, lane_count()) + { + U64 slot_idx = l_idx * total_location_case_chunk_count + chunk_idx; + rdim_shared->location_case_chunk_lane_offs[slot_idx] = location_case_layout_off; + location_case_layout_off += rdim_shared->location_case_chunk_lane_counts[slot_idx]; + } + chunk_idx += 1; + } + for EachNode(n, RDIM_SymbolChunkNode, params->procedures.first) + { + for EachIndex(l_idx, lane_count()) + { + U64 slot_idx = l_idx * total_location_case_chunk_count + chunk_idx; + rdim_shared->location_case_chunk_lane_offs[slot_idx] = location_case_layout_off; + location_case_layout_off += rdim_shared->location_case_chunk_lane_counts[slot_idx]; + } + chunk_idx += 1; + } + rdim_shared->total_location_case_count = location_case_layout_off; + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake location blocks + // + ProfScope("bake location blocks") + { + // rjf: set up + if(lane_idx() == 0) + { + rdim_shared->baked_location_blocks.location_blocks_count = rdim_shared->total_location_case_count; + rdim_shared->baked_location_blocks.location_blocks = push_array(arena, RDI_LocationBlock, rdim_shared->baked_location_blocks.location_blocks_count); + } + lane_sync(); + + // rjf: wide fill from scopes + U64 chunk_idx = 0; + ProfScope("wide fill from scopes") + { + for EachNode(n, RDIM_ScopeChunkNode, params->scopes.first) + { + U64 layout_slot_idx = lane_idx() * total_location_case_chunk_count + chunk_idx; + U64 layout_off = rdim_shared->location_case_chunk_lane_offs[layout_slot_idx]; + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) + { + for EachNode(local, RDIM_Local, n->v[idx].first_local) + { + for EachNode(src, RDIM_LocationCase, local->location_cases.first) + { + RDI_LocationBlock *dst = &rdim_shared->baked_location_blocks.location_blocks[layout_off]; + dst->scope_off_first = (RDI_U32)src->voff_range.min; // TODO(rjf): @u64_to_u32 + dst->scope_off_opl = (RDI_U32)src->voff_range.max; // TODO(rjf): @u64_to_u32 + dst->location_data_off = (RDI_U32)rdim_off_from_location(src->location); // TODO(rjf): @u64_to_u32 + layout_off += 1; + } + } + } + chunk_idx += 1; + } + } + + // rjf: wide fill from procedures + ProfScope("wide fill from procedures") + { + for EachNode(n, RDIM_SymbolChunkNode, params->procedures.first) + { + U64 layout_slot_idx = lane_idx() * total_location_case_chunk_count + chunk_idx; + U64 layout_off = rdim_shared->location_case_chunk_lane_offs[layout_slot_idx]; + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) + { + for EachNode(src, RDIM_LocationCase, n->v[idx].location_cases.first) + { + RDI_LocationBlock *dst = &rdim_shared->baked_location_blocks.location_blocks[layout_off]; + dst->scope_off_first = (RDI_U32)src->voff_range.min; // TODO(rjf): @u64_to_u32 + dst->scope_off_opl = (RDI_U32)src->voff_range.max; // TODO(rjf): @u64_to_u32 + dst->location_data_off = (RDI_U32)rdim_off_from_location(src->location); // TODO(rjf): @u64_to_u32 + layout_off += 1; + } + } + chunk_idx += 1; + } + } + } + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake locations + // + ProfScope("bake locations") + { + if(lane_idx() == 0) + { + rdim_shared->baked_locations.location_data_size = params->locations.total_encoded_size+1; + rdim_shared->baked_locations.location_data = push_array(arena, RDI_U8, rdim_shared->baked_locations.location_data_size); + } + lane_sync(); + for EachNode(n, RDIM_LocationChunkNode, params->locations.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_Location *loc = &n->v[n_idx]; + RDI_U8 *dst = &rdim_shared->baked_locations.location_data[n->base_encoding_off + loc->relative_encoding_off + 1]; + switch((RDI_LocationKindEnum)loc->info.kind) + { + case RDI_LocationKind_NULL:{}break; + case RDI_LocationKind_AddrBytecodeStream: + case RDI_LocationKind_ValBytecodeStream: + { + MemoryCopy(dst+0, &loc->info.kind, sizeof(loc->info.kind)); + RDI_U64 write_off = sizeof(loc->info.kind); + for EachNode(op_node, RDIM_EvalBytecodeOp, loc->info.bytecode.first_op) + { + MemoryCopy(dst + write_off, &op_node->op, 1); + write_off += 1; + MemoryCopy(dst + write_off, &op_node->p, op_node->p_size); + write_off += op_node->p_size; + } + dst[write_off] = 0; + }break; + case RDI_LocationKind_AddrRegPlusU16: + case RDI_LocationKind_AddrAddrRegPlusU16: + { + RDI_LocationRegPlusU16 baked = {loc->info.kind, loc->info.reg_code, loc->info.offset}; + MemoryCopy(dst, &baked, sizeof(baked)); + }break; + case RDI_LocationKind_ValReg: + { + RDI_LocationReg baked = {loc->info.kind, loc->info.reg_code}; + MemoryCopy(dst, &baked, sizeof(baked)); + }break; + } + } + } + } + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage compute layout for scope sub-lists (locals / voffs) + // + ProfScope("compute layout for scope sub-lists (locals / voffs)") + { + // rjf: set up + if(lane_idx() == 0) + { + rdim_shared->scope_local_chunk_lane_counts = push_array(arena, RDI_U64, lane_count() * params->scopes.chunk_count); + rdim_shared->scope_local_chunk_lane_offs = push_array(arena, RDI_U64, lane_count() * params->scopes.chunk_count); + rdim_shared->scope_voff_chunk_lane_counts = push_array(arena, RDI_U64, lane_count() * params->scopes.chunk_count); + rdim_shared->scope_voff_chunk_lane_offs = push_array(arena, RDI_U64, lane_count() * params->scopes.chunk_count); + } + lane_sync(); + + // rjf: count per-lane-chunk + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_ScopeChunkNode, params->scopes.first) + { + U64 num_locals_in_this_lane_and_node = 0; + U64 num_voffs_in_this_lane_and_node = 0; + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + num_locals_in_this_lane_and_node += n->v[n_idx].local_count; + num_voffs_in_this_lane_and_node += n->v[n_idx].voff_ranges.count*2; + } + rdim_shared->scope_local_chunk_lane_counts[lane_idx()*params->scopes.chunk_count + chunk_idx] = num_locals_in_this_lane_and_node; + rdim_shared->scope_voff_chunk_lane_counts[lane_idx()*params->scopes.chunk_count + chunk_idx] = num_voffs_in_this_lane_and_node; + chunk_idx += 1; + } + } + lane_sync(); + + // rjf: lay out each lane's range + if(lane_idx() == 0) + { + U64 local_layout_off = 1; + U64 voff_layout_off = 1; + U64 chunk_idx = 0; + for EachNode(n, RDIM_ScopeChunkNode, params->scopes.first) + { + for EachIndex(l_idx, lane_count()) + { + U64 slot_idx = l_idx*params->scopes.chunk_count + chunk_idx; + rdim_shared->scope_local_chunk_lane_offs[slot_idx] = local_layout_off; + rdim_shared->scope_voff_chunk_lane_offs[slot_idx] = voff_layout_off; + local_layout_off += rdim_shared->scope_local_chunk_lane_counts[slot_idx]; + voff_layout_off += rdim_shared->scope_voff_chunk_lane_counts[slot_idx]; + } + chunk_idx += 1; + } + } + lane_sync(); + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake scopes + // + ProfScope("bake scopes") + { + //- rjf: setup outputs + if(lane_idx() == lane_from_task_idx(0)) + { + rdim_shared->baked_scopes.scopes_count = params->scopes.total_count+1; + rdim_shared->baked_scopes.scopes = push_array(arena, RDI_Scope, rdim_shared->baked_scopes.scopes_count); + } + if(lane_idx() == lane_from_task_idx(1)) + { + rdim_shared->baked_scopes.scope_voffs_count = params->scopes.scope_voff_count+1; + rdim_shared->baked_scopes.scope_voffs = push_array(arena, RDI_U64, rdim_shared->baked_scopes.scope_voffs_count); + } + if(lane_idx() == lane_from_task_idx(2)) + { + rdim_shared->baked_scopes.locals_count = params->scopes.local_count+1; + rdim_shared->baked_scopes.locals = push_array(arena, RDI_Local, rdim_shared->baked_scopes.locals_count); + } + lane_sync(); + + //- rjf: wide fill + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_ScopeChunkNode, params->scopes.first) + { + Rng1U64 range = lane_range(n->count); + U64 scope_chunk_lane_slot_idx = lane_idx()*params->scopes.chunk_count + chunk_idx; + U64 chunk_local_off = rdim_shared->scope_local_chunk_lane_offs[scope_chunk_lane_slot_idx]; + U64 chunk_voff_off = rdim_shared->scope_voff_chunk_lane_offs[scope_chunk_lane_slot_idx]; + U64 location_block_chunk_lane_slot_idx = lane_idx() * total_location_case_chunk_count + chunk_idx; + U64 chunk_location_block_off = rdim_shared->location_case_chunk_lane_offs[location_block_chunk_lane_slot_idx]; + for EachInRange(n_idx, range) + { + U64 dst_idx = 1 + n->base_idx + n_idx; + RDIM_Scope *src_scope = &n->v[n_idx]; + RDI_Scope *dst_scope = &rdim_shared->baked_scopes.scopes[dst_idx]; + + //- rjf: fill voff ranges + U64 voff_idx_first = chunk_voff_off; + for EachNode(rng_n, RDIM_Rng1U64Node, src_scope->voff_ranges.first) + { + rdim_shared->baked_scopes.scope_voffs[chunk_voff_off+0] = rng_n->v.min; + rdim_shared->baked_scopes.scope_voffs[chunk_voff_off+1] = rng_n->v.max; + chunk_voff_off += 2; + } + U64 voff_idx_opl = chunk_voff_off; + + //- rjf: fill locals + U64 local_idx_first = chunk_local_off; + for EachNode(src_local, RDIM_Local, src_scope->first_local) + { + RDI_Local *dst_local = &rdim_shared->baked_scopes.locals[chunk_local_off]; + dst_local->kind = src_local->kind; + dst_local->name_string_idx = rdim_bake_idx_from_string(bake_strings, src_local->name); + dst_local->type_idx = (RDI_U32)rdim_idx_from_type(src_local->type); // TODO(rjf): @u64_to_u32 + if(src_local->location_cases.count != 0) + { + dst_local->location_first = chunk_location_block_off; + dst_local->location_opl = chunk_location_block_off + src_local->location_cases.count; + chunk_location_block_off += src_local->location_cases.count; + } + chunk_local_off += 1; + } + U64 local_idx_opl = chunk_local_off; + + //- rjf: fill scope + dst_scope->proc_idx = (RDI_U32)rdim_idx_from_symbol(src_scope->symbol); // TODO(rjf): @u64_to_u32 + dst_scope->parent_scope_idx = (RDI_U32)rdim_idx_from_scope(src_scope->parent_scope); // TODO(rjf): @u64_to_u32 + dst_scope->first_child_scope_idx = (RDI_U32)rdim_idx_from_scope(src_scope->first_child); // TODO(rjf): @u64_to_u32 + dst_scope->next_sibling_scope_idx = (RDI_U32)rdim_idx_from_scope(src_scope->next_sibling); // TODO(rjf): @u64_to_u32 + dst_scope->voff_range_first = (RDI_U32)voff_idx_first; // TODO(rjf): @u64_to_u32 + dst_scope->voff_range_opl = (RDI_U32)voff_idx_opl; // TODO(rjf): @u64_to_u32 + dst_scope->local_first = (RDI_U32)local_idx_first; // TODO(rjf): @u64_to_u32 + dst_scope->local_count = (RDI_U32)(local_idx_opl - local_idx_first); // TODO(rjf): @u64_to_u32 + dst_scope->inline_site_idx = (RDI_U32)rdim_idx_from_inline_site(src_scope->inline_site); // TODO(rjf): @u64_to_u32 + } + chunk_idx += 1; + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake procedures + // + ProfScope("bake procedures") + { + if(lane_idx() == 0) + { + rdim_shared->baked_procedures.procedures_count = params->procedures.total_count+1; + rdim_shared->baked_procedures.procedures = push_array(arena, RDI_Procedure, rdim_shared->baked_procedures.procedures_count); + } + lane_sync(); + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_SymbolChunkNode, params->procedures.first) + { + U64 location_block_layout_slot_idx = lane_idx()*total_location_case_chunk_count + params->scopes.chunk_count + chunk_idx; + U64 location_block_off = rdim_shared->location_case_chunk_lane_offs[location_block_layout_slot_idx]; + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_Symbol *src = &n->v[n_idx]; + RDI_Procedure *dst = &rdim_shared->baked_procedures.procedures[n->base_idx + n_idx + 1]; + dst->name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + dst->link_name_string_idx = rdim_bake_idx_from_string(bake_strings, src->link_name); + if(src->is_extern) + { + dst->link_flags |= RDI_LinkFlag_External; + } + if(src->container_type != 0) + { + dst->link_flags |= RDI_LinkFlag_TypeScoped; + dst->container_idx = src->container_type ? (RDI_U32)rdim_idx_from_udt(src->container_type->udt) : 0; // TODO(rjf): @u64_to_u32 + } + else if(src->container_symbol != 0) + { + dst->link_flags |= RDI_LinkFlag_ProcScoped; + dst->container_idx = (RDI_U32)rdim_idx_from_symbol(src->container_symbol); // TODO(rjf): @u64_to_u32 + } + dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); // TODO(rjf): @u64_to_u32 + dst->root_scope_idx = (RDI_U32)rdim_idx_from_scope(src->root_scope); // TODO(rjf): @u64_to_u32 + if(src->location_cases.count != 0) + { + dst->frame_base_location_first = location_block_off; + dst->frame_base_location_opl = location_block_off + src->location_cases.count; + location_block_off += src->location_cases.count; + } + } + chunk_idx += 1; + } + } + } + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage compute layout for constant data + // + ProfScope("compute layout for constant data") + { + // rjf: set up + if(lane_idx() == 0) + { + rdim_shared->constant_data_chunk_lane_counts = push_array(arena, U64, lane_count() * params->constants.chunk_count); + rdim_shared->constant_data_chunk_lane_offs = push_array(arena, U64, lane_count() * params->constants.chunk_count); + } + lane_sync(); + + // rjf: count + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_SymbolChunkNode, params->constants.first) + { + U64 slot_idx = lane_idx()*params->constants.chunk_count + chunk_idx; + Rng1U64 range = lane_range(n->count); + for EachInRange(idx, range) + { + rdim_shared->constant_data_chunk_lane_counts[slot_idx] += n->v[idx].value_data.size; + } + chunk_idx += 1; + } + } + lane_sync(); + + // rjf: layout + if(lane_idx() == 0) + { + U64 chunk_idx = 0; + U64 layout_off = 0; + for EachNode(n, RDIM_SymbolChunkNode, params->constants.first) + { + for EachIndex(l_idx, lane_count()) + { + U64 slot_idx = l_idx*params->constants.chunk_count + chunk_idx; + rdim_shared->constant_data_chunk_lane_offs[slot_idx] = layout_off; + layout_off += rdim_shared->constant_data_chunk_lane_counts[slot_idx]; + } + chunk_idx += 1; + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake constants + // + ProfScope("bake constants") + { + // rjf: set up + if(lane_idx() == lane_from_task_idx(0)) + { + rdim_shared->baked_constants.constant_values_count = params->constants.total_count+1; + rdim_shared->baked_constants.constant_values = push_array(arena, RDI_U32, rdim_shared->baked_constants.constant_values_count); + } + if(lane_idx() == lane_from_task_idx(1)) + { + rdim_shared->baked_constants.constant_value_data_size = params->constants.total_value_data_size; + rdim_shared->baked_constants.constant_value_data = push_array(arena, RDI_U8, rdim_shared->baked_constants.constant_value_data_size); + } + if(lane_idx() == lane_from_task_idx(2)) + { + rdim_shared->baked_constants.constants_count = params->constants.total_count+1; + rdim_shared->baked_constants.constants = push_array(arena, RDI_Constant, rdim_shared->baked_constants.constants_count); + } + lane_sync(); + + // rjf: wide bake + { + U64 chunk_idx = 0; + for EachNode(n, RDIM_SymbolChunkNode, params->constants.first) + { + U64 slot_idx = lane_idx()*params->constants.chunk_count + chunk_idx; + U64 value_data_off = rdim_shared->constant_data_chunk_lane_offs[slot_idx]; + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_Symbol *src = &n->v[n_idx]; + RDI_Constant *dst = &rdim_shared->baked_constants.constants[1 + n->base_idx + n_idx]; + RDI_U32 *dst_value_off = &rdim_shared->baked_constants.constant_values[1 + n->base_idx + n_idx]; + RDI_U8 *dst_value_data = rdim_shared->baked_constants.constant_value_data + value_data_off; + dst->name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); // TODO(rjf): @u64_to_u32 + dst->constant_value_idx = 1 + n->base_idx + n_idx; + dst_value_off[0] = (RDI_U32)value_data_off; // TODO(rjf): @u64_to_u32 + rdim_memcpy(dst_value_data, src->value_data.str, src->value_data.size); + value_data_off += src->value_data.size; + } + chunk_idx += 1; + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake units, symbols, types, UDTs + // + { + //- rjf: setup outputs + if(lane_idx() == lane_from_task_idx(0)) + { + rdim_shared->baked_units.units_count = params->units.total_count+1; + rdim_shared->baked_units.units = push_array(arena, RDI_Unit, rdim_shared->baked_units.units_count); + } + if(lane_idx() == lane_from_task_idx(1)) + { + rdim_shared->baked_type_nodes.type_nodes_count = params->types.total_count+1; + rdim_shared->baked_type_nodes.type_nodes = push_array(arena, RDI_TypeNode, rdim_shared->baked_type_nodes.type_nodes_count); + } + if(lane_idx() == lane_from_task_idx(2)) + { + rdim_shared->baked_global_variables.global_variables_count = params->global_variables.total_count+1; + rdim_shared->baked_global_variables.global_variables = push_array(arena, RDI_GlobalVariable, rdim_shared->baked_global_variables.global_variables_count); + } + if(lane_idx() == lane_from_task_idx(3)) + { + rdim_shared->baked_thread_variables.thread_variables_count = params->thread_variables.total_count+1; + rdim_shared->baked_thread_variables.thread_variables = push_array(arena, RDI_ThreadVariable, rdim_shared->baked_thread_variables.thread_variables_count); + } + if(lane_idx() == lane_from_task_idx(4)) + { + rdim_shared->baked_inline_sites.inline_sites_count = params->inline_sites.total_count+1; + rdim_shared->baked_inline_sites.inline_sites = push_array(arena, RDI_InlineSite, rdim_shared->baked_inline_sites.inline_sites_count); + } + lane_sync(); + + //- rjf: bake units + ProfScope("bake units") + { + for EachNode(n, RDIM_UnitChunkNode, params->units.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_Unit *src = &n->v[n_idx]; + RDI_Unit *dst = &rdim_shared->baked_units.units[n->base_idx + n_idx + 1]; + dst->unit_name_string_idx = rdim_bake_idx_from_string(bake_strings, src->unit_name); + dst->compiler_name_string_idx = rdim_bake_idx_from_string(bake_strings, src->compiler_name); + dst->source_file_path_node = rdim_bake_path_node_idx_from_string(path_tree, src->source_file); + dst->object_file_path_node = rdim_bake_path_node_idx_from_string(path_tree, src->object_file); + dst->archive_file_path_node = rdim_bake_path_node_idx_from_string(path_tree, src->archive_file); + dst->build_path_node = rdim_bake_path_node_idx_from_string(path_tree, src->build_path); + dst->language = src->language; + dst->line_table_idx = (RDI_U32)rdim_idx_from_line_table(src->line_table); // TODO(rjf): @u64_to_u32 + } + } + } + + //- rjf: bake type nodes + ProfScope("bake type nodes") + { + for EachNode(n, RDIM_TypeChunkNode, params->types.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_Type *src = &n->v[n_idx]; + RDI_TypeNode *dst = &rdim_shared->baked_type_nodes.type_nodes[n->base_idx + n_idx + 1]; + + //- rjf: fill shared type node info + dst->kind = src->kind; + dst->flags = (RDI_U16)src->flags; // TODO(rjf): @u32_to_u16 + dst->byte_size = src->byte_size; + + //- rjf: fill built-in-only type node info + if(RDI_TypeKind_FirstBuiltIn <= dst->kind && dst->kind <= RDI_TypeKind_LastBuiltIn) + { + dst->built_in.name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + } + + //- rjf: fill array sizes + else if(dst->kind == RDI_TypeKind_Array) + { + U64 direct_byte_size = 1; + if(src->direct_type && src->direct_type->byte_size > 0) + { + direct_byte_size = src->direct_type->byte_size; + } + dst->constructed.direct_type_idx = (RDI_U32)rdim_idx_from_type(src->direct_type); + dst->constructed.count = src->byte_size / direct_byte_size; + } + + //- rjf: fill constructed type node info + else if(RDI_TypeKind_FirstConstructed <= dst->kind && dst->kind <= RDI_TypeKind_LastConstructed) + { + dst->constructed.direct_type_idx = (RDI_U32)rdim_idx_from_type(src->direct_type); // TODO(rjf): @u64_to_u32 + dst->constructed.count = src->count; + if(dst->kind == RDI_TypeKind_Function || dst->kind == RDI_TypeKind_Method) + { + RDI_U32 param_idx_run_count = src->count; + RDI_U32 *param_idx_run = rdim_push_array_no_zero(arena, RDI_U32, param_idx_run_count); + for(RDI_U32 idx = 0; idx < param_idx_run_count; idx += 1) + { + param_idx_run[idx] = (RDI_U32)rdim_idx_from_type(src->param_types[idx]); // TODO(rjf): @u64_to_u32 + } + dst->constructed.param_idx_run_first = rdim_bake_idx_from_idx_run(bake_idx_runs, param_idx_run, param_idx_run_count); + } + else if(dst->kind == RDI_TypeKind_MemberPtr) + { + // TODO(rjf): member pointers not currently supported. + } + } + + //- rjf: fill user-defined-type info + else if(RDI_TypeKind_FirstUserDefined <= dst->kind && dst->kind <= RDI_TypeKind_LastUserDefined) + { + dst->user_defined.name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + dst->user_defined.udt_idx = (RDI_U32)rdim_idx_from_udt(src->udt); // TODO(rjf): @u64_to_u32 + dst->user_defined.direct_type_idx = (RDI_U32)rdim_idx_from_type(src->direct_type); // TODO(rjf): @u64_to_u32 + } + + //- rjf: fill bitfield info + else if(dst->kind == RDI_TypeKind_Bitfield) + { + dst->bitfield.direct_type_idx = (RDI_U32)rdim_idx_from_type(src->direct_type); // TODO(rjf): @u64_to_u32 + dst->bitfield.off = src->off; + dst->bitfield.size = src->count; + } + } + } + } + + //- rjf: bake global variables + ProfScope("bake global variables") + { + for EachNode(n, RDIM_SymbolChunkNode, params->global_variables.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_Symbol *src = &n->v[n_idx]; + RDI_GlobalVariable *dst = &rdim_shared->baked_global_variables.global_variables[n->base_idx + n_idx + 1]; + dst->name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + dst->voff = src->offset; + dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); // TODO(rjf): @u64_to_u32 + if(src->is_extern) + { + dst->link_flags |= RDI_LinkFlag_External; + } + if(src->container_type != 0) + { + dst->link_flags |= RDI_LinkFlag_TypeScoped; + dst->container_idx = src->container_type ? (RDI_U32)rdim_idx_from_udt(src->container_type->udt) : 0; // TODO(rjf): @u64_to_u32 + } + else if(src->container_symbol != 0) + { + dst->link_flags |= RDI_LinkFlag_ProcScoped; + dst->container_idx = (RDI_U32)rdim_idx_from_symbol(src->container_symbol); // TODO(rjf): @u64_to_u32 + } + } + } + } + + //- rjf: bake thread variables + ProfScope("bake thread variables") + { + for EachNode(n, RDIM_SymbolChunkNode, params->thread_variables.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDIM_Symbol *src = &n->v[n_idx]; + RDI_ThreadVariable *dst = &rdim_shared->baked_thread_variables.thread_variables[n->base_idx + n_idx + 1]; + dst->name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + dst->tls_off = (RDI_U32)src->offset; // TODO(rjf): @u64_to_u32 + dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); + if(src->is_extern) + { + dst->link_flags |= RDI_LinkFlag_External; + } + if(src->container_type != 0) + { + dst->link_flags |= RDI_LinkFlag_TypeScoped; + dst->container_idx = src->container_type ? (RDI_U32)rdim_idx_from_udt(src->container_type->udt) : 0; // TODO(rjf): @u64_to_u32 + } + else if(src->container_symbol != 0) + { + dst->link_flags |= RDI_LinkFlag_ProcScoped; + dst->container_idx = (RDI_U32)rdim_idx_from_symbol(src->container_symbol); // TODO(rjf): @u64_to_u32 + } + } + } + } + + //- rjf: bake inline sites + ProfScope("bake inline sites") + { + for EachNode(n, RDIM_InlineSiteChunkNode, params->inline_sites.first) + { + Rng1U64 range = lane_range(n->count); + for EachInRange(n_idx, range) + { + RDI_InlineSite *dst = &rdim_shared->baked_inline_sites.inline_sites[n->base_idx + n_idx + 1]; + RDIM_InlineSite *src = &n->v[n_idx]; + dst->name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + dst->type_idx = (RDI_U32)rdim_idx_from_type(src->type); // TODO(rjf): @u64_to_u32 + dst->owner_type_idx = (RDI_U32)rdim_idx_from_type(src->owner); // TODO(rjf): @u64_to_u32 + dst->line_table_idx = (RDI_U32)rdim_idx_from_line_table(src->line_table); // TODO(rjf): @u64_to_u32 + } + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage bake file paths + // + ProfScope("bake file paths") + { + // rjf: set up + if(lane_idx() == 0) + { + rdim_shared->baked_file_paths.nodes_count = path_tree->count; + rdim_shared->baked_file_paths.nodes = push_array(arena, RDI_FilePathNode, rdim_shared->baked_file_paths.nodes_count); + rdim_shared->baked_file_path_src_nodes = push_array(arena, RDIM_BakePathNode *, rdim_shared->baked_file_paths.nodes_count); + { + U64 idx = 0; + for(RDIM_BakePathNode *n = path_tree->first; n != 0; n = n->next_order) + { + rdim_shared->baked_file_path_src_nodes[idx] = n; + idx += 1; + } + } + } + lane_sync(); + + // rjf: fill + { + Rng1U64 range = lane_range(rdim_shared->baked_file_paths.nodes_count); + for EachInRange(idx, range) + { + RDIM_BakePathNode *src = rdim_shared->baked_file_path_src_nodes[idx]; + RDI_FilePathNode *dst = &rdim_shared->baked_file_paths.nodes[idx]; + dst->name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + dst->source_file_idx = rdim_idx_from_src_file(src->src_file); + if(src->parent != 0) + { + dst->parent_path_node = src->parent->idx; + } + if(src->first_child != 0) + { + dst->first_child = src->first_child->idx; + } + if(src->next_sibling != 0) + { + dst->next_sibling = src->next_sibling->idx; + } + } + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage do small final baking tasks + // + ProfScope("do small final baking tasks") + { + if(lane_idx() == lane_from_task_idx(0)) ProfScope("bake top level info") + { + rdim_shared->baked_top_level_info.top_level_info.arch = params->top_level_info.arch; + rdim_shared->baked_top_level_info.top_level_info.exe_name_string_idx = rdim_bake_idx_from_string(bake_strings, params->top_level_info.exe_name); + rdim_shared->baked_top_level_info.top_level_info.exe_hash = params->top_level_info.exe_hash; + rdim_shared->baked_top_level_info.top_level_info.voff_max = params->top_level_info.voff_max; + rdim_shared->baked_top_level_info.top_level_info.producer_name_string_idx = rdim_bake_idx_from_string(bake_strings, params->top_level_info.producer_name); + } + if(lane_idx() == lane_from_task_idx(1)) ProfScope("bake binary sections") + { + RDIM_BinarySectionList *src = ¶ms->binary_sections; + RDI_BinarySection *dst_base = rdim_push_array(arena, RDI_BinarySection, src->count+1); + U64 dst_idx = 1; + for(RDIM_BinarySectionNode *src_n = src->first; src_n != 0; src_n = src_n->next, dst_idx += 1) + { + RDIM_BinarySection *src = &src_n->v; + RDI_BinarySection *dst = &dst_base[dst_idx]; + dst->name_string_idx = rdim_bake_idx_from_string(bake_strings, src->name); + dst->flags = src->flags; + dst->voff_first = src->voff_first; + dst->voff_opl = src->voff_opl; + dst->foff_first = src->foff_first; + dst->foff_opl = src->foff_opl; + } + rdim_shared->baked_binary_sections.binary_sections = dst_base; + rdim_shared->baked_binary_sections.binary_sections_count = dst_idx; + } + } + lane_sync(); + + ////////////////////////////////////////////////////////////// + //- rjf: @rdim_bake_stage package results + // + RDIM_BakeResults result = {0}; + { + result.top_level_info = rdim_shared->baked_top_level_info; + result.binary_sections = rdim_shared->baked_binary_sections; + result.units = rdim_shared->baked_units; + result.unit_vmap = rdim_shared->baked_unit_vmap; + result.src_files = rdim_shared->baked_src_files; + result.line_tables = rdim_shared->baked_line_tables; + result.type_nodes = rdim_shared->baked_type_nodes; + result.udts = rdim_shared->baked_udts; + result.global_variables = rdim_shared->baked_global_variables; + result.global_vmap = rdim_shared->baked_global_vmap; + result.thread_variables = rdim_shared->baked_thread_variables; + result.constants = rdim_shared->baked_constants; + result.procedures = rdim_shared->baked_procedures; + result.scopes = rdim_shared->baked_scopes; + result.inline_sites = rdim_shared->baked_inline_sites; + result.scope_vmap = rdim_shared->baked_scope_vmap; + result.top_level_name_maps = rdim_shared->baked_top_level_name_maps; + result.name_maps = rdim_shared->baked_name_maps; + result.file_paths = rdim_shared->baked_file_paths; + result.strings = rdim_shared->baked_strings; + result.idx_runs = rdim_shared->baked_idx_runs; + result.locations = rdim_shared->baked_locations; + result.location_blocks2 = rdim_shared->baked_location_blocks; + } + + return result; } internal RDIM_SerializedSectionBundle @@ -1166,4 +2905,3 @@ rdim_compress(Arena *arena, RDIM_SerializedSectionBundle *in) return out; } - diff --git a/src/rdi_make/rdi_make_local.h b/src/rdi_make/rdi_make_local.h index 437f7bce..9dfea4e2 100644 --- a/src/rdi_make/rdi_make_local.h +++ b/src/rdi_make/rdi_make_local.h @@ -48,340 +48,141 @@ //- rjf: main library #include "lib_rdi_make/rdi_make.h" -//- rjf: line table baking task types +//- rjf: unsorted joined line table info -typedef struct RDIM_BakeLineTablesIn RDIM_BakeLineTablesIn; -struct RDIM_BakeLineTablesIn +typedef struct RDIM_UnsortedJoinedLineTable RDIM_UnsortedJoinedLineTable; +struct RDIM_UnsortedJoinedLineTable { - RDIM_LineTableChunkList *line_tables; + RDI_U64 line_count; + RDI_U64 seq_count; + RDI_U64 key_count; + RDIM_SortKey *line_keys; + RDIM_LineRec *line_recs; }; -//- rjf: string map baking task types +//- rjf: shared state bundle -typedef struct RDIM_BakeSrcFilesStringsIn RDIM_BakeSrcFilesStringsIn; -struct RDIM_BakeSrcFilesStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_SrcFileChunkList *list; -}; - -typedef struct RDIM_BakeUnitsStringsIn RDIM_BakeUnitsStringsIn; -struct RDIM_BakeUnitsStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_UnitChunkList *list; -}; - -typedef struct RDIM_BakeUDTsStringsInNode RDIM_BakeUDTsStringsInNode; -struct RDIM_BakeUDTsStringsInNode -{ - RDIM_BakeUDTsStringsInNode *next; - RDIM_UDT *v; - RDI_U64 count; -}; - -typedef struct RDIM_BakeTypesStringsInNode RDIM_BakeTypesStringsInNode; -struct RDIM_BakeTypesStringsInNode -{ - RDIM_BakeTypesStringsInNode *next; - RDIM_Type *v; - RDI_U64 count; -}; - -typedef struct RDIM_BakeTypesStringsIn RDIM_BakeTypesStringsIn; -struct RDIM_BakeTypesStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_BakeTypesStringsInNode *first; - RDIM_BakeTypesStringsInNode *last; -}; - -typedef struct RDIM_BakeUDTsStringsIn RDIM_BakeUDTsStringsIn; -struct RDIM_BakeUDTsStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_BakeUDTsStringsInNode *first; - RDIM_BakeUDTsStringsInNode *last; -}; - -typedef struct RDIM_BakeSymbolsStringsInNode RDIM_BakeSymbolsStringsInNode; -struct RDIM_BakeSymbolsStringsInNode -{ - RDIM_BakeSymbolsStringsInNode *next; - RDIM_Symbol *v; - RDI_U64 count; -}; - -typedef struct RDIM_BakeSymbolsStringsIn RDIM_BakeSymbolsStringsIn; -struct RDIM_BakeSymbolsStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_BakeSymbolsStringsInNode *first; - RDIM_BakeSymbolsStringsInNode *last; -}; - -typedef struct RDIM_BakeInlineSiteStringsInNode RDIM_BakeInlineSiteStringsInNode; -struct RDIM_BakeInlineSiteStringsInNode -{ - RDIM_BakeInlineSiteStringsInNode *next; - RDIM_InlineSite *v; - RDI_U64 count; -}; - -typedef struct RDIM_BakeInlineSiteStringsIn RDIM_BakeInlineSiteStringsIn; -struct RDIM_BakeInlineSiteStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_BakeInlineSiteStringsInNode *first; - RDIM_BakeInlineSiteStringsInNode *last; -}; - -typedef struct RDIM_BakeScopesStringsInNode RDIM_BakeScopesStringsInNode; -struct RDIM_BakeScopesStringsInNode -{ - RDIM_BakeScopesStringsInNode *next; - RDIM_Scope *v; - RDI_U64 count; -}; - -typedef struct RDIM_BakeScopesStringsIn RDIM_BakeScopesStringsIn; -struct RDIM_BakeScopesStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_BakeScopesStringsInNode *first; - RDIM_BakeScopesStringsInNode *last; -}; - -//- rjf: OLD string map baking types - -typedef struct RDIM_BuildBakeStringMapIn RDIM_BuildBakeStringMapIn; -struct RDIM_BuildBakeStringMapIn +typedef struct RDIM_Shared RDIM_Shared; +struct RDIM_Shared { + RDI_U64 scope_vmap_count; + RDIM_SortKey *scope_vmap_keys; + RDIM_SortKey *scope_vmap_keys__swap; + RDIM_VMapMarker *scope_vmap_markers; + RDI_U64 unit_vmap_count; + RDIM_SortKey *unit_vmap_keys; + RDIM_SortKey *unit_vmap_keys__swap; + RDIM_VMapMarker *unit_vmap_markers; + RDI_U64 global_vmap_count; + RDIM_SortKey *global_vmap_keys; + RDIM_SortKey *global_vmap_keys__swap; + RDIM_VMapMarker *global_vmap_markers; + U32 **lane_digit_counts; + U32 **lane_digit_offsets; + + RDIM_ScopeVMapBakeResult baked_scope_vmap; + RDIM_UnitVMapBakeResult baked_unit_vmap; + RDIM_GlobalVMapBakeResult baked_global_vmap; + RDIM_BakePathTree *path_tree; - RDIM_BakeParams *params; + + RDI_U64 line_tables_count; + RDI_U64 line_table_block_take_counter; + RDIM_LineTable **src_line_tables; + RDIM_UnsortedJoinedLineTable *unsorted_joined_line_tables; + + RDIM_SortKey **sorted_line_table_keys; + + RDIM_LineTableBakeResult baked_line_tables; + + RDIM_BakeStringMapTopology bake_string_map_topology; + RDIM_BakeStringMapLoose **lane_bake_string_maps__loose; + RDIM_BakeStringMapLoose *bake_string_map__loose; + RDIM_BakeStringMapTight bake_strings; + + RDIM_BakeNameMapTopology bake_name_map_topology[RDI_NameMapKind_COUNT]; + RDIM_BakeNameMap **lane_bake_name_maps[RDI_NameMapKind_COUNT]; + RDIM_BakeNameMap *bake_name_maps[RDI_NameMapKind_COUNT]; + + RDIM_BakeIdxRunMapTopology bake_idx_run_map_topology; + RDIM_BakeIdxRunMapLoose **lane_bake_idx_run_maps__loose; + RDIM_BakeIdxRunMapLoose *bake_idx_run_map__loose; + RDIM_BakeIdxRunMap bake_idx_runs; + + RDIM_StringBakeResult baked_strings; + + RDIM_IndexRunBakeResult baked_idx_runs; + + RDI_U64 *lane_name_map_node_counts[RDI_NameMapKind_COUNT]; + RDI_U64 *lane_name_map_node_offs[RDI_NameMapKind_COUNT]; + RDI_U64 name_map_node_counts[RDI_NameMapKind_COUNT]; + RDI_U64 total_name_map_node_count; + RDIM_TopLevelNameMapBakeResult baked_top_level_name_maps; + RDIM_NameMapBakeResult baked_name_maps; + + RDIM_BakeSrcLineMap *bake_src_line_maps; + + RDI_U64 bake_src_line_map_take_counter; + RDIM_SortKey **bake_src_line_map_keys; + + RDI_U64 *lane_chunk_src_file_num_counts; // [lane_count * src_file_chunk_count] + RDI_U64 *lane_chunk_src_file_voff_counts; // [lane_count * src_file_chunk_count] + RDI_U64 *lane_chunk_src_file_map_counts; // [lane_count * src_file_chunk_count] + RDI_U64 *lane_chunk_src_file_num_offs; // [lane_count * src_file_chunk_count] + RDI_U64 *lane_chunk_src_file_voff_offs; // [lane_count * src_file_chunk_count] + RDI_U64 *lane_chunk_src_file_map_offs; // [lane_count * src_file_chunk_count] + RDI_U64 total_src_map_line_count; + RDI_U64 total_src_map_voff_count; + + RDIM_SrcFileBakeResult baked_src_files; + + RDI_U64 *member_chunk_lane_counts; // [lane_count * udt_chunk_count] + RDI_U64 *member_chunk_lane_offs; // [lane_count * udt_chunk_count] + RDI_U64 *enum_val_chunk_lane_counts; // [lane_count * udt_chunk_count] + RDI_U64 *enum_val_chunk_lane_offs; // [lane_count * udt_chunk_count] + + RDIM_UDTBakeResult baked_udts; + + RDI_U64 *location_case_chunk_lane_counts; // [lane_count * (scope_chunk_count + procedure_chunk_count) + RDI_U64 *location_case_chunk_lane_offs; // [lane_count * (scope_chunk_count + procedure_chunk_count) + RDI_U64 total_location_case_count; + + RDIM_LocationBlockBakeResult baked_location_blocks; + + RDIM_LocationBakeResult baked_locations; + + RDI_U64 *scope_local_chunk_lane_counts; // [lane_count * scope_chunk_count] + RDI_U64 *scope_local_chunk_lane_offs; // [lane_count * scope_chunk_count] + RDI_U64 *scope_voff_chunk_lane_counts; // [lane_count * scope_chunk_count] + RDI_U64 *scope_voff_chunk_lane_offs; // [lane_count * scope_chunk_count] + + RDIM_ScopeBakeResult baked_scopes; + + RDIM_ProcedureBakeResult baked_procedures; + + RDI_U64 *constant_data_chunk_lane_counts; // [lane_count * constant_chunk_count] + RDI_U64 *constant_data_chunk_lane_offs; // [lane_count * constant_chunk_count] + + RDIM_ConstantsBakeResult baked_constants; + + RDIM_UnitBakeResult baked_units; + RDIM_TypeNodeBakeResult baked_type_nodes; + RDIM_GlobalVariableBakeResult baked_global_variables; + RDIM_ThreadVariableBakeResult baked_thread_variables; + RDIM_InlineSiteBakeResult baked_inline_sites; + + RDIM_BakePathNode **baked_file_path_src_nodes; + RDIM_FilePathBakeResult baked_file_paths; + + RDIM_TopLevelInfoBakeResult baked_top_level_info; + RDIM_BinarySectionBakeResult baked_binary_sections; }; -typedef struct RDIM_BuildBakeNameMapIn RDIM_BuildBakeNameMapIn; -struct RDIM_BuildBakeNameMapIn -{ - RDI_NameMapKind k; - RDIM_BakeParams *params; -}; - -//- rjf: string map joining task types - -typedef struct RDIM_JoinBakeStringMapSlotsIn RDIM_JoinBakeStringMapSlotsIn; -struct RDIM_JoinBakeStringMapSlotsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **src_maps; - U64 src_maps_count; - RDIM_BakeStringMapLoose *dst_map; - Rng1U64 slot_idx_range; -}; - -//- rjf: string map sorting task types - -typedef struct RDIM_SortBakeStringMapSlotsIn RDIM_SortBakeStringMapSlotsIn; -struct RDIM_SortBakeStringMapSlotsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose *src_map; - RDIM_BakeStringMapLoose *dst_map; - U64 slot_idx; - U64 slot_count; -}; - -//- rjf: debug info baking task types - -typedef struct RDIM_BakeUnitsIn RDIM_BakeUnitsIn; -struct RDIM_BakeUnitsIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakePathTree *path_tree; - RDIM_UnitChunkList *units; -}; - -typedef struct RDIM_BakeUnitVMapIn RDIM_BakeUnitVMapIn; -struct RDIM_BakeUnitVMapIn -{ - RDIM_UnitChunkList *units; -}; - -typedef struct RDIM_BakeSrcFilesIn RDIM_BakeSrcFilesIn; -struct RDIM_BakeSrcFilesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakePathTree *path_tree; - RDIM_SrcFileChunkList *src_files; -}; - -typedef struct RDIM_BakeUDTsIn RDIM_BakeUDTsIn; -struct RDIM_BakeUDTsIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_UDTChunkList *udts; -}; - -typedef struct RDIM_BakeGlobalVariablesIn RDIM_BakeGlobalVariablesIn; -struct RDIM_BakeGlobalVariablesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_SymbolChunkList *global_variables; -}; - -typedef struct RDIM_BakeConstantsIn RDIM_BakeConstantsIn; -struct RDIM_BakeConstantsIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_SymbolChunkList *constants; -}; - -typedef struct RDIM_BakeGlobalVMapIn RDIM_BakeGlobalVMapIn; -struct RDIM_BakeGlobalVMapIn -{ - RDIM_SymbolChunkList *global_variables; -}; - -typedef struct RDIM_BakeThreadVariablesIn RDIM_BakeThreadVariablesIn; -struct RDIM_BakeThreadVariablesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_SymbolChunkList *thread_variables; -}; - -typedef struct RDIM_BakeProceduresIn RDIM_BakeProceduresIn; -struct RDIM_BakeProceduresIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_SymbolChunkList *procedures; - RDIM_String8List *location_blocks; - RDIM_String8List *location_data_blobs; -}; - -typedef struct RDIM_BakeScopesIn RDIM_BakeScopesIn; -struct RDIM_BakeScopesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_ScopeChunkList *scopes; - RDIM_String8List *location_blocks; - RDIM_String8List *location_data_blobs; -}; - -typedef struct RDIM_BakeScopeVMapIn RDIM_BakeScopeVMapIn; -struct RDIM_BakeScopeVMapIn -{ - RDIM_ScopeChunkList *scopes; -}; - -typedef struct RDIM_BakeInlineSitesIn RDIM_BakeInlineSitesIn; -struct RDIM_BakeInlineSitesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_InlineSiteChunkList *inline_sites; -}; - -typedef struct RDIM_BakeFilePathsIn RDIM_BakeFilePathsIn; -struct RDIM_BakeFilePathsIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakePathTree *path_tree; -}; - -typedef struct RDIM_BakeStringsIn RDIM_BakeStringsIn; -struct RDIM_BakeStringsIn -{ - RDIM_BakeStringMapTight *strings; -}; - -typedef struct RDIM_BakeTypeNodesIn RDIM_BakeTypeNodesIn; -struct RDIM_BakeTypeNodesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakeIdxRunMap *idx_runs; - RDIM_TypeChunkList *types; -}; - -typedef struct RDIM_BakeNameMapIn RDIM_BakeNameMapIn; -struct RDIM_BakeNameMapIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakeIdxRunMap *idx_runs; - RDIM_BakeNameMap *map; - RDI_NameMapKind kind; -}; - -typedef struct RDIM_BakeIdxRunsIn RDIM_BakeIdxRunsIn; -struct RDIM_BakeIdxRunsIn -{ - RDIM_BakeIdxRunMap *idx_runs; -}; - -//////////////////////////////// +global RDIM_Shared *rdim_shared = 0; internal RDIM_DataModel rdim_data_model_from_os_arch(OperatingSystem os, RDI_Arch arch); - -//////////////////////////////// -//~ rjf: Baking Stage Tasks - -//- rjf: unsorted bake string map building -ASYNC_WORK_DEF(rdim_bake_src_files_strings_work); -ASYNC_WORK_DEF(rdim_bake_units_strings_work); -ASYNC_WORK_DEF(rdim_bake_types_strings_work); -ASYNC_WORK_DEF(rdim_bake_udts_strings_work); -ASYNC_WORK_DEF(rdim_bake_symbols_strings_work); -ASYNC_WORK_DEF(rdim_bake_scopes_strings_work); -ASYNC_WORK_DEF(rdim_bake_line_tables_work); - -//- rjf: bake string map joining -ASYNC_WORK_DEF(rdim_bake_string_map_join_work); - -//- rjf: bake string map sorting -ASYNC_WORK_DEF(rdim_bake_string_map_sort_work); - -//- rjf: pass 1: interner/deduper map builds -ASYNC_WORK_DEF(rdim_build_bake_name_map_work); - -//- rjf: pass 2: string-map-dependent debug info stream builds -ASYNC_WORK_DEF(rdim_bake_units_work); -ASYNC_WORK_DEF(rdim_bake_unit_vmap_work); -ASYNC_WORK_DEF(rdim_bake_src_files_work); -ASYNC_WORK_DEF(rdim_bake_udts_work); -ASYNC_WORK_DEF(rdim_bake_global_variables_work); -ASYNC_WORK_DEF(rdim_bake_global_vmap_work); -ASYNC_WORK_DEF(rdim_bake_thread_variables_work); -ASYNC_WORK_DEF(rdim_bake_constants_work); -ASYNC_WORK_DEF(rdim_bake_procedures_work); -ASYNC_WORK_DEF(rdim_bake_scopes_work); -ASYNC_WORK_DEF(rdim_bake_scope_vmap_work); -ASYNC_WORK_DEF(rdim_bake_file_paths_work); -ASYNC_WORK_DEF(rdim_bake_strings_work); - -//- rjf: pass 3: idx-run-map-dependent debug info stream builds -ASYNC_WORK_DEF(rdim_bake_type_nodes_work); -ASYNC_WORK_DEF(rdim_bake_name_map_work); -ASYNC_WORK_DEF(rdim_bake_idx_runs_work); - -//////////////////////////////// -//~ rjf: Globals - -global ASYNC_Root *rdim_local_async_root = 0; - -//////////////////////////////// - -internal RDIM_DataModel rdim_data_model_from_os_arch(OperatingSystem os, RDI_Arch arch); internal RDIM_TopLevelInfo rdim_make_top_level_info(String8 image_name, Arch arch, U64 exe_hash, RDIM_BinarySectionList sections); - -//////////////////////////////// - -internal RDIM_BakeResults rdim_bake(Arena *arena, ASYNC_Root *async_root, RDIM_BakeParams *in); +internal RDIM_BakeResults rdim_bake(Arena *arena, RDIM_BakeParams *params); internal RDIM_SerializedSectionBundle rdim_compress(Arena *arena, RDIM_SerializedSectionBundle *in); #endif // RDI_MAKE_LOCAL_H diff --git a/src/regs/generated/regs.meta.c b/src/regs/generated/regs.meta.c index 61b2632f..fab1e6c9 100644 --- a/src/regs/generated/regs.meta.c +++ b/src/regs/generated/regs.meta.c @@ -103,7 +103,7 @@ case Arch_x86:{result = regs_g_alias_code_x86_usage_kind_table;}break; return result; } C_LINKAGE_BEGIN -REGS_UsageKind regs_g_reg_code_x64_usage_kind_table[101] = +REGS_UsageKind regs_g_reg_code_x64_usage_kind_table[103] = { REGS_UsageKind_Normal, REGS_UsageKind_Normal, @@ -206,6 +206,8 @@ REGS_UsageKind_Normal, REGS_UsageKind_Normal, REGS_UsageKind_Normal, REGS_UsageKind_Normal, +REGS_UsageKind_Normal, +REGS_UsageKind_Normal, }; REGS_UsageKind regs_g_alias_code_x64_usage_kind_table[96] = @@ -308,7 +310,7 @@ REGS_UsageKind_Normal, REGS_UsageKind_Normal, }; -String8 regs_g_reg_code_x64_string_table[101] = +String8 regs_g_reg_code_x64_string_table[103] = { str8_lit_comp(""), str8_lit_comp("rax"), @@ -411,6 +413,8 @@ str8_lit_comp("k4"), str8_lit_comp("k5"), str8_lit_comp("k6"), str8_lit_comp("k7"), +str8_lit_comp("cetmsr"), +str8_lit_comp("cetssp"), }; String8 regs_g_alias_code_x64_string_table[96] = @@ -513,7 +517,7 @@ str8_lit_comp("mm6"), str8_lit_comp("mm7"), }; -REGS_Rng regs_g_reg_code_x64_rng_table[101] = +REGS_Rng regs_g_reg_code_x64_rng_table[103] = { {0}, {(U16)OffsetOf(REGS_RegBlockX64, rax), 8}, @@ -616,6 +620,8 @@ REGS_Rng regs_g_reg_code_x64_rng_table[101] = {(U16)OffsetOf(REGS_RegBlockX64, k5), 8}, {(U16)OffsetOf(REGS_RegBlockX64, k6), 8}, {(U16)OffsetOf(REGS_RegBlockX64, k7), 8}, +{(U16)OffsetOf(REGS_RegBlockX64, cetmsr), 8}, +{(U16)OffsetOf(REGS_RegBlockX64, cetssp), 8}, }; REGS_Slice regs_g_alias_code_x64_slice_table[96] = diff --git a/src/regs/generated/regs.meta.h b/src/regs/generated/regs.meta.h index 4adc08d4..1f1316dc 100644 --- a/src/regs/generated/regs.meta.h +++ b/src/regs/generated/regs.meta.h @@ -109,6 +109,8 @@ REGS_RegCodeX64_k4, REGS_RegCodeX64_k5, REGS_RegCodeX64_k6, REGS_RegCodeX64_k7, +REGS_RegCodeX64_cetmsr, +REGS_RegCodeX64_cetssp, REGS_RegCodeX64_COUNT, } REGS_RegCodeX64; @@ -423,6 +425,8 @@ REGS_Reg64 k4; REGS_Reg64 k5; REGS_Reg64 k6; REGS_Reg64 k7; +REGS_Reg64 cetmsr; +REGS_Reg64 cetssp; }; typedef struct REGS_RegBlockX86 REGS_RegBlockX86; @@ -491,11 +495,11 @@ REGS_Reg256 ymm7; }; C_LINKAGE_BEGIN -extern REGS_UsageKind regs_g_reg_code_x64_usage_kind_table[101]; +extern REGS_UsageKind regs_g_reg_code_x64_usage_kind_table[103]; extern REGS_UsageKind regs_g_alias_code_x64_usage_kind_table[96]; -extern String8 regs_g_reg_code_x64_string_table[101]; +extern String8 regs_g_reg_code_x64_string_table[103]; extern String8 regs_g_alias_code_x64_string_table[96]; -extern REGS_Rng regs_g_reg_code_x64_rng_table[101]; +extern REGS_Rng regs_g_reg_code_x64_rng_table[103]; extern REGS_Slice regs_g_alias_code_x64_slice_table[96]; extern REGS_UsageKind regs_g_reg_code_x86_usage_kind_table[61]; extern REGS_UsageKind regs_g_alias_code_x86_usage_kind_table[36]; diff --git a/src/regs/rdi/generated/regs_rdi.meta.c b/src/regs/rdi/generated/regs_rdi.meta.c index de284856..686b8049 100644 --- a/src/regs/rdi/generated/regs_rdi.meta.c +++ b/src/regs/rdi/generated/regs_rdi.meta.c @@ -114,6 +114,8 @@ case REGS_RegCodeX64_k4:{result = RDI_RegCodeX64_k4;}break; case REGS_RegCodeX64_k5:{result = RDI_RegCodeX64_k5;}break; case REGS_RegCodeX64_k6:{result = RDI_RegCodeX64_k6;}break; case REGS_RegCodeX64_k7:{result = RDI_RegCodeX64_k7;}break; +case REGS_RegCodeX64_cetmsr:{result = RDI_RegCodeX64_cetmsr;}break; +case REGS_RegCodeX64_cetssp:{result = RDI_RegCodeX64_cetssp;}break; } }break; case Arch_x86: @@ -297,6 +299,8 @@ case RDI_RegCodeX64_k4:{result = REGS_RegCodeX64_k4;}break; case RDI_RegCodeX64_k5:{result = REGS_RegCodeX64_k5;}break; case RDI_RegCodeX64_k6:{result = REGS_RegCodeX64_k6;}break; case RDI_RegCodeX64_k7:{result = REGS_RegCodeX64_k7;}break; +case RDI_RegCodeX64_cetmsr:{result = REGS_RegCodeX64_cetmsr;}break; +case RDI_RegCodeX64_cetssp:{result = REGS_RegCodeX64_cetssp;}break; } }break; case Arch_x86: diff --git a/src/regs/regs.mdesk b/src/regs/regs.mdesk index 56076b33..25d35691 100644 --- a/src/regs/regs.mdesk +++ b/src/regs/regs.mdesk @@ -107,6 +107,8 @@ REGS_RegTableX64: {k5 64 Normal} {k6 64 Normal} {k7 64 Normal} + {cetmsr 64 Normal} + {cetssp 64 Normal} } @table(name base off size usage) diff --git a/src/render/d3d11/render_d3d11.c b/src/render/d3d11/render_d3d11.c index 4a4e979c..444199cf 100644 --- a/src/render/d3d11/render_d3d11.c +++ b/src/render/d3d11/render_d3d11.c @@ -159,7 +159,7 @@ r_init(CmdLine *cmdln) Arena *arena = arena_alloc(); r_d3d11_state = push_array(arena, R_D3D11_State, 1); r_d3d11_state->arena = arena; - r_d3d11_state->device_rw_mutex = os_rw_mutex_alloc(); + r_d3d11_state->device_rw_mutex = rw_mutex_alloc(); //- rjf: create base device ProfBegin("create base device"); @@ -487,7 +487,7 @@ r_window_equip(OS_Handle handle) { ProfBeginFunction(); R_Handle result = {0}; - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { //- rjf: allocate per-window-state R_D3D11_Window *window = r_d3d11_state->first_free_window; @@ -559,7 +559,7 @@ r_hook void r_window_unequip(OS_Handle handle, R_Handle equip_handle) { ProfBeginFunction(); - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { R_D3D11_Window *window = r_d3d11_window_from_handle(equip_handle); window->stage_color_srv->lpVtbl->Release(window->stage_color_srv); @@ -588,7 +588,7 @@ r_tex2d_alloc(R_ResourceKind kind, Vec2S32 size, R_Tex2DFormat format, void *dat //- rjf: allocate R_D3D11_Tex2D *texture = 0; - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { texture = r_d3d11_state->first_free_tex2d; if(texture == 0) @@ -675,7 +675,7 @@ r_hook void r_tex2d_release(R_Handle handle) { ProfBeginFunction(); - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { R_D3D11_Tex2D *texture = r_d3d11_tex2d_from_handle(handle); if(texture != &r_d3d11_tex2d_nil) @@ -711,7 +711,7 @@ r_hook void r_fill_tex2d_region(R_Handle handle, Rng2S32 subrect, void *data) { ProfBeginFunction(); - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { R_D3D11_Tex2D *texture = r_d3d11_tex2d_from_handle(handle); if(texture != &r_d3d11_tex2d_nil) @@ -739,7 +739,7 @@ r_buffer_alloc(R_ResourceKind kind, U64 size, void *data) //- rjf: allocate R_D3D11_Buffer *buffer = 0; - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { buffer = r_d3d11_state->first_free_buffer; if(buffer == 0) @@ -798,7 +798,7 @@ r_hook void r_buffer_release(R_Handle handle) { ProfBeginFunction(); - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { R_D3D11_Buffer *buffer = r_d3d11_buffer_from_handle(handle); SLLStackPush(r_d3d11_state->first_to_free_buffer, buffer); @@ -811,7 +811,7 @@ r_buffer_release(R_Handle handle) r_hook void r_begin_frame(void) { - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { // NOTE(rjf): no-op } @@ -820,7 +820,7 @@ r_begin_frame(void) r_hook void r_end_frame(void) { - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { for(R_D3D11_FlushBuffer *buffer = r_d3d11_state->first_buffer_to_flush; buffer != 0; buffer = buffer->next) { @@ -858,7 +858,7 @@ r_hook void r_window_begin_frame(OS_Handle window, R_Handle window_equip) { ProfBeginFunction(); - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { R_D3D11_Window *wnd = r_d3d11_window_from_handle(window_equip); ID3D11DeviceContext1 *d_ctx = r_d3d11_state->device_ctx; @@ -990,7 +990,7 @@ r_hook void r_window_end_frame(OS_Handle window, R_Handle window_equip) { ProfBeginFunction(); - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { R_D3D11_Window *wnd = r_d3d11_window_from_handle(window_equip); ID3D11DeviceContext1 *d_ctx = r_d3d11_state->device_ctx; @@ -1060,7 +1060,7 @@ r_hook void r_window_submit(OS_Handle window, R_Handle window_equip, R_PassList *passes) { ProfBeginFunction(); - OS_MutexScopeW(r_d3d11_state->device_rw_mutex) + MutexScopeW(r_d3d11_state->device_rw_mutex) { //////////////////////////// //- rjf: unpack arguments diff --git a/src/render/d3d11/render_d3d11.h b/src/render/d3d11/render_d3d11.h index 5ad8934b..549c63c8 100644 --- a/src/render/d3d11/render_d3d11.h +++ b/src/render/d3d11/render_d3d11.h @@ -135,7 +135,7 @@ struct R_D3D11_State R_D3D11_Buffer *first_free_buffer; R_D3D11_Tex2D *first_to_free_tex2d; R_D3D11_Buffer *first_to_free_buffer; - OS_Handle device_rw_mutex; + RWMutex device_rw_mutex; // rjf: base d3d11 objects ID3D11Device *base_device; diff --git a/src/scratch/convertperf.c b/src/scratch/convertperf.c deleted file mode 100644 index 3043f1c2..00000000 --- a/src/scratch/convertperf.c +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Build Options - -#define BUILD_TITLE "convertperf" - -//////////////////////////////// -//~ rjf: Includes - -//- rjf: [lib] -#include "third_party/rad_lzb_simple/rad_lzb_simple.h" -#include "third_party/rad_lzb_simple/rad_lzb_simple.c" - -//- rjf: [h] -#include "base/base_inc.h" -#include "os/os_inc.h" -#include "async/async.h" -#include "rdi_format/rdi_format_local.h" -#include "dbgi/dbgi.h" - -//- rjf: [c] -#include "base/base_inc.c" -#include "os/os_inc.c" -#include "async/async.c" -#include "rdi_format/rdi_format_local.c" -#include "dbgi/dbgi.c" - -//////////////////////////////// -//~ rjf: Entry Points - -internal void -entry_point(CmdLine *cmdline) -{ - Arena *arena = arena_alloc(); - String8 list_path = str8_list_first(&cmdline->inputs); - String8 list_data = os_data_from_file_path(arena, list_path); - U8 splits[] = {'\n'}; - String8List lines = str8_split(arena, list_data, splits, ArrayCount(splits), 0); - OS_HandleList processes = {0}; - String8Node *processes_first_path_n = 0; - U64 limit = 64; - U64 idx = 0; - for(String8Node *n = lines.first; n != 0; n = n->next) - { - String8 dll_path = n->string; - ProfScope("kick off %.*s", str8_varg(dll_path)) - { - String8 dll_path_no_ext = str8_chop_last_dot(dll_path); - String8 dll_name = str8_skip_last_slash(dll_path_no_ext); - String8 pdb_path = push_str8f(arena, "%S.pdb", dll_path_no_ext); - String8 rdi_path = push_str8f(arena, "dump/%S.rdi", dll_name); - OS_Handle handle = os_cmd_line_launchf("raddbg --bin %S --out:%S", pdb_path, rdi_path); - os_handle_list_push(arena, &processes, handle); - if(processes_first_path_n == 0) - { - processes_first_path_n = n; - } - idx += 1; - } - if(idx >= limit) - { - String8Node *line_n = processes_first_path_n; - for(OS_HandleNode *n = processes.first; n != 0; n = n->next, line_n = line_n->next) - { - ProfScope("join %.*s", str8_varg(line_n->string)) - { - os_process_join(n->v, max_U64); - } - } - idx = 0; - MemoryZeroStruct(&processes); - processes_first_path_n = 0; - } - } -} diff --git a/src/scratch/parse_inline_sites.c b/src/scratch/parse_inline_sites.c index f6ae9eed..bd2bb656 100644 --- a/src/scratch/parse_inline_sites.c +++ b/src/scratch/parse_inline_sites.c @@ -266,7 +266,7 @@ entry_point(CmdLine *cmdl) // print run info DateTime now_time_universal = os_now_universal_time(); DateTime now_time_local = os_local_time_from_universal_time(&now_time_universal); - String8 now_time_str = push_date_time_string(arena, &now_time_local); + String8 now_time_str = string_from_date_time(arena, &now_time_local); fprintf(stdout, "Time: %.*s\n", str8_varg(now_time_str)); fprintf(stdout, "File: %.*s\n", str8_varg(pdb_name)); fprintf(stdout, "Size: %llu (bytes)\n", pdb_data.size); diff --git a/src/scratch/ryan_scratch.c b/src/scratch/ryan_scratch.c index 219a644e..692a9e04 100644 --- a/src/scratch/ryan_scratch.c +++ b/src/scratch/ryan_scratch.c @@ -5,7 +5,7 @@ //~ rjf: Build Options #define BUILD_TITLE "ryan_scratch" -#define OS_FEATURE_GRAPHICAL 1 +#define BUILD_CONSOLE_INTERFACE 1 //////////////////////////////// //~ rjf: Includes @@ -13,93 +13,37 @@ //- rjf: [h] #include "base/base_inc.h" #include "os/os_inc.h" -#include "render/render_inc.h" -#include "font_provider/font_provider_inc.h" -#include "font_cache/font_cache.h" -#include "draw/draw.h" +#include "content/content.h" +#include "artifact_cache/artifact_cache.h" +#include "file_stream/file_stream.h" +#include "rdi/rdi_local.h" +#include "dbg_info/dbg_info2.h" //- rjf: [c] #include "base/base_inc.c" #include "os/os_inc.c" -#include "render/render_inc.c" -#include "font_provider/font_provider_inc.c" -#include "font_cache/font_cache.c" -#include "draw/draw.c" +#include "content/content.c" +#include "artifact_cache/artifact_cache.c" +#include "file_stream/file_stream.c" +#include "rdi/rdi_local.c" +#include "dbg_info/dbg_info2.c" //////////////////////////////// -//~ rjf: Globals - -global OS_Handle window_os = {0}; -global R_Handle window_r = {0}; - -//////////////////////////////// -//~ rjf: Entry Points - -internal B32 -frame(void) -{ - B32 quit = 0; - Temp scratch = scratch_begin(0, 0); - - //- rjf: events test - OS_EventList events = os_get_events(scratch.arena, 0); - for(OS_Event *ev = events.first; ev != 0; ev = ev->next) - { - if(ev->kind != OS_EventKind_MouseMove) - { - String8 string = push_str8f(scratch.arena, "%S (%S)\n", os_string_from_event_kind(ev->kind), os_g_key_display_string_table[ev->key]); - printf("%.*s", str8_varg(string)); - raddbg_log((char *)string.str); - fflush(stdout); - } - if(ev->kind == OS_EventKind_Press && ev->key == OS_Key_X) - { - *(volatile int *)0 = 0; - } - } - for(OS_Event *ev = events.first; ev != 0; ev = ev->next) - { - if(ev->kind == OS_EventKind_WindowClose) - { - quit = 1; - break; - } - } - - //- rjf: drawing test - r_begin_frame(); - r_window_begin_frame(window_os, window_r); - { - R_PassList passes = {0}; - R_Pass *pass = r_pass_from_kind(scratch.arena, &passes, R_PassKind_UI); - R_PassParams_UI *pass_ui = pass->params_ui; - R_BatchGroup2DNode group = {0}; - pass_ui->rects.first = pass_ui->rects.last = &group; - pass_ui->rects.count = 1; - group.batches = r_batch_list_make(sizeof(R_Rect2DInst)); - group.params.xform = mat_3x3f32(1.f); - group.params.clip = os_client_rect_from_window(window_os); - Vec2F32 mouse = os_mouse_from_window(window_os); - R_Rect2DInst *inst = r_batch_list_push_inst(scratch.arena, &group.batches, 256); - MemoryZeroStruct(inst); - inst->dst = r2f32p(mouse.x+30, mouse.y+30, mouse.x+100, mouse.y+100); - inst->src = r2f32p(0, 0, 1, 1); - inst->colors[Corner_00] = inst->colors[Corner_01] = inst->colors[Corner_10] = inst->colors[Corner_11] = v4f32(1, 0, 0, 1); - inst->corner_radii[Corner_00] = inst->corner_radii[Corner_01] = inst->corner_radii[Corner_10] = inst->corner_radii[Corner_11] = 8.f; - r_window_submit(window_os, window_r, &passes); - } - r_window_end_frame(window_os, window_r); - r_end_frame(); - - scratch_end(scratch); - return quit; -} +//~ rjf: Entry Point internal void entry_point(CmdLine *cmdline) { - window_os = os_window_open(r2f32p(0, 0, 1280, 720), OS_WindowFlag_UseDefaultPosition, str8_lit("Window")); - os_window_first_paint(window_os); - window_r = r_window_equip(window_os); - for(;!update();); + DI2_Key key = di2_key_from_path_timestamp(str8_lit("C:/devel/raddebugger/build/raddbg.pdb"), 0); + di2_open(key); + for(;;) + { + Access *access = access_open(); + RDI_Parsed *rdi = di2_rdi_from_key(access, key, 1, 0); + if(rdi != &rdi_parsed_nil) + { + int x = 0; + } + access_close(access); + } } diff --git a/src/strip_lib_debug/strip_lib_debug.c b/src/strip_lib_debug/strip_lib_debug.c index 6cae4820..23c15b1c 100644 --- a/src/strip_lib_debug/strip_lib_debug.c +++ b/src/strip_lib_debug/strip_lib_debug.c @@ -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/) #define BUILD_TITLE "Epic Games Tools (R) Lib Strip Debug" diff --git a/src/tester/tester_main.c b/src/tester/tester_main.c index 604ca440..4ee85062 100644 --- a/src/tester/tester_main.c +++ b/src/tester/tester_main.c @@ -14,7 +14,7 @@ //- rjf: [h] #include "base/base_inc.h" #include "os/os_inc.h" -#include "hash_store/hash_store.h" +#include "content/content.h" #include "rdi_format/rdi_format_local.h" #include "regs/regs.h" #include "regs/rdi/regs_rdi.h" @@ -23,7 +23,7 @@ //- rjf: [c] #include "base/base_inc.c" #include "os/os_inc.c" -#include "hash_store/hash_store.c" +#include "content/content.c" #include "rdi_format/rdi_format_local.c" #include "regs/regs.c" #include "regs/rdi/regs_rdi.c" @@ -114,7 +114,7 @@ for(Test *test = test_##name_identifier; test != 0; test = 0) } for(OS_HandleNode *n = processes.first; n != 0; n = n->next) { - os_process_join(n->v, max_U64); + os_process_join(n->v, max_U64, 0); } } @@ -130,7 +130,7 @@ for(Test *test = test_##name_identifier; test != 0; test = 0) } for(OS_HandleNode *n = processes.first; n != 0; n = n->next) { - os_process_join(n->v, max_U64); + os_process_join(n->v, max_U64, 0); } } @@ -148,7 +148,7 @@ for(Test *test = test_##name_identifier; test != 0; test = 0) Temp scratch = scratch_begin(0, 0); String8 path = n->string; String8 data = os_data_from_file_path(scratch.arena, path); - rdi_hashes[idx] = hs_hash_from_data(data); + rdi_hashes[idx] = c_hash_from_data(data); rdi_paths_array[idx] = path; scratch_end(scratch); } @@ -160,7 +160,7 @@ for(Test *test = test_##name_identifier; test != 0; test = 0) Temp scratch = scratch_begin(0, 0); String8 path = n->string; String8 data = os_data_from_file_path(scratch.arena, path); - dump_hashes[idx] = hs_hash_from_data(data); + dump_hashes[idx] = c_hash_from_data(data); dump_paths_array[idx] = path; scratch_end(scratch); } diff --git a/src/text_cache/text_cache.c b/src/text/text.c similarity index 78% rename from src/text_cache/text_cache.c rename to src/text/text.c index c142a844..3ee0c191 100644 --- a/src/text_cache/text_cache.c +++ b/src/text/text.c @@ -1590,206 +1590,6 @@ txt_token_array_from_string__disasm_x64_intel(Arena *arena, U64 *bytes_processed return result; } -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void -txt_init(void) -{ - Arena *arena = arena_alloc(); - txt_shared = push_array(arena, TXT_Shared, 1); - txt_shared->arena = arena; - txt_shared->slots_count = 1024; - txt_shared->slots = push_array(arena, TXT_Slot, txt_shared->slots_count); - txt_shared->stripes_count = Min(txt_shared->slots_count, os_get_system_info()->logical_processor_count); - txt_shared->stripes = push_array(arena, TXT_Stripe, txt_shared->stripes_count); - txt_shared->stripes_free_nodes = push_array(arena, TXT_Node *, txt_shared->stripes_count); - for(U64 idx = 0; idx < txt_shared->stripes_count; idx += 1) - { - txt_shared->stripes[idx].arena = arena_alloc(); - txt_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc(); - txt_shared->stripes[idx].cv = os_condition_variable_alloc(); - } - txt_shared->u2p_ring_size = KB(64); - txt_shared->u2p_ring_base = push_array_no_zero(arena, U8, txt_shared->u2p_ring_size); - txt_shared->u2p_ring_cv = os_condition_variable_alloc(); - txt_shared->u2p_ring_mutex = os_mutex_alloc(); - txt_shared->evictor_thread = os_thread_launch(txt_evictor_thread__entry_point, 0, 0); -} - -//////////////////////////////// -//~ rjf: Thread Context Initialization - -internal void -txt_tctx_ensure_inited(void) -{ - if(txt_tctx == 0) - { - Arena *arena = arena_alloc(); - txt_tctx = push_array(arena, TXT_TCTX, 1); - txt_tctx->arena = arena; - } -} - -//////////////////////////////// -//~ rjf: Scoped Access - -internal TXT_Scope * -txt_scope_open(void) -{ - txt_tctx_ensure_inited(); - TXT_Scope *scope = txt_tctx->free_scope; - if(scope) - { - SLLStackPop(txt_tctx->free_scope); - } - else - { - scope = push_array_no_zero(txt_tctx->arena, TXT_Scope, 1); - } - MemoryZeroStruct(scope); - return scope; -} - -internal void -txt_scope_close(TXT_Scope *scope) -{ - for(TXT_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next) - { - U128 hash = touch->hash; - next = touch->next; - U64 slot_idx = hash.u64[1]%txt_shared->slots_count; - U64 stripe_idx = slot_idx%txt_shared->stripes_count; - TXT_Slot *slot = &txt_shared->slots[slot_idx]; - TXT_Stripe *stripe = &txt_shared->stripes[stripe_idx]; - OS_MutexScopeR(stripe->rw_mutex) - { - for(TXT_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(hash, n->hash) && touch->lang == n->lang) - { - ins_atomic_u64_dec_eval(&n->scope_ref_count); - break; - } - } - } - SLLStackPush(txt_tctx->free_touch, touch); - } - SLLStackPush(txt_tctx->free_scope, scope); -} - -internal void -txt_scope_touch_node__stripe_r_guarded(TXT_Scope *scope, TXT_Node *node) -{ - TXT_Touch *touch = txt_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(txt_tctx->free_touch); - } - else - { - touch = push_array_no_zero(txt_tctx->arena, TXT_Touch, 1); - } - MemoryZeroStruct(touch); - touch->hash = node->hash; - touch->lang = node->lang; - SLLStackPush(scope->top_touch, touch); -} - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal TXT_TextInfo -txt_text_info_from_hash_lang(TXT_Scope *scope, U128 hash, TXT_LangKind lang) -{ - TXT_TextInfo info = {0}; - if(!u128_match(hash, u128_zero())) - { - U64 slot_idx = hash.u64[1]%txt_shared->slots_count; - U64 stripe_idx = slot_idx%txt_shared->stripes_count; - TXT_Slot *slot = &txt_shared->slots[slot_idx]; - TXT_Stripe *stripe = &txt_shared->stripes[stripe_idx]; - B32 found = 0; - OS_MutexScopeR(stripe->rw_mutex) - { - for(TXT_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(hash, n->hash) && n->lang == lang) - { - MemoryCopyStruct(&info, &n->info); - info.bytes_processed = ins_atomic_u64_eval(&n->info.bytes_processed); - info.bytes_to_process = ins_atomic_u64_eval(&n->info.bytes_to_process); - found = 1; - txt_scope_touch_node__stripe_r_guarded(scope, n); - break; - } - } - } - B32 node_is_new = 0; - if(!found) - { - OS_MutexScopeW(stripe->rw_mutex) - { - TXT_Node *node = 0; - for(TXT_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(hash, n->hash) && n->lang == lang) - { - node = n; - break; - } - } - if(node == 0) - { - node = txt_shared->stripes_free_nodes[stripe_idx]; - if(node) - { - SLLStackPop(txt_shared->stripes_free_nodes[stripe_idx]); - } - else - { - node = push_array_no_zero(stripe->arena, TXT_Node, 1); - } - MemoryZeroStruct(node); - DLLPushBack(slot->first, slot->last, node); - node->hash = hash; - node->lang = lang; - node_is_new = 1; - } - } - } - if(node_is_new) - { - txt_u2p_enqueue_req(hash, lang, max_U64); - async_push_work(txt_parse_work); - } - } - return info; -} - -internal TXT_TextInfo -txt_text_info_from_key_lang(TXT_Scope *scope, HS_Key key, TXT_LangKind lang, U128 *hash_out) -{ - TXT_TextInfo 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 = txt_text_info_from_hash_lang(scope, hash, lang); - if(result.lines_count != 0) - { - if(hash_out) - { - *hash_out = hash; - } - break; - } - } - return result; -} - //////////////////////////////// //~ rjf: Text Info Extractor Helpers @@ -2132,7 +1932,7 @@ txt_scope_node_from_info_off(TXT_TextInfo *info, U64 off) scope_n != &txt_scope_node_nil; scope_n = txt_scope_node_from_info_num(info, scope_n->parent_num)) { - if(off < info->tokens.v[scope_n->token_idx_range.max].range.max) + if(info->tokens.v[scope_n->token_idx_range.min].range.min <= off && off < info->tokens.v[scope_n->token_idx_range.max].range.max) { result = scope_n; break; @@ -2151,121 +1951,62 @@ txt_scope_node_from_info_pt(TXT_TextInfo *info, TxtPt pt) } //////////////////////////////// -//~ rjf: Transfer Threads +//~ rjf: Artifact Cache Hooks / Lookups -internal B32 -txt_u2p_enqueue_req(U128 hash, TXT_LangKind lang, U64 endt_us) +typedef struct TXT_Artifact TXT_Artifact; +struct TXT_Artifact { - B32 good = 0; - OS_MutexScope(txt_shared->u2p_ring_mutex) for(;;) - { - U64 unconsumed_size = txt_shared->u2p_ring_write_pos - txt_shared->u2p_ring_read_pos; - U64 available_size = txt_shared->u2p_ring_size - unconsumed_size; - if(available_size >= sizeof(hash)+sizeof(lang)) - { - good = 1; - txt_shared->u2p_ring_write_pos += ring_write_struct(txt_shared->u2p_ring_base, txt_shared->u2p_ring_size, txt_shared->u2p_ring_write_pos, &hash); - txt_shared->u2p_ring_write_pos += ring_write_struct(txt_shared->u2p_ring_base, txt_shared->u2p_ring_size, txt_shared->u2p_ring_write_pos, &lang); - break; - } - if(os_now_microseconds() >= endt_us) - { - break; - } - os_condition_variable_wait(txt_shared->u2p_ring_cv, txt_shared->u2p_ring_mutex, endt_us); - } - if(good) - { - os_condition_variable_broadcast(txt_shared->u2p_ring_cv); - } - return good; -} + Arena *arena; + U128 data_hash; + TXT_TextInfo info; +}; -internal void -txt_u2p_dequeue_req(U128 *hash_out, TXT_LangKind *lang_out) +typedef struct TXT_ArtifactCreateShared TXT_ArtifactCreateShared; +struct TXT_ArtifactCreateShared { - OS_MutexScope(txt_shared->u2p_ring_mutex) for(;;) - { - U64 unconsumed_size = txt_shared->u2p_ring_write_pos - txt_shared->u2p_ring_read_pos; - if(unconsumed_size >= sizeof(*hash_out) + sizeof(*lang_out)) - { - txt_shared->u2p_ring_read_pos += ring_read_struct(txt_shared->u2p_ring_base, txt_shared->u2p_ring_size, txt_shared->u2p_ring_read_pos, hash_out); - txt_shared->u2p_ring_read_pos += ring_read_struct(txt_shared->u2p_ring_base, txt_shared->u2p_ring_size, txt_shared->u2p_ring_read_pos, lang_out); - break; - } - os_condition_variable_wait(txt_shared->u2p_ring_cv, txt_shared->u2p_ring_mutex, max_U64); - } - os_condition_variable_broadcast(txt_shared->u2p_ring_cv); -} + Arena *arena; + TXT_TextInfo info; + TXT_Artifact *artifact; +}; -ASYNC_WORK_DEF(txt_parse_work) +internal AC_Artifact +txt_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out) { ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + Access *access = access_open(); - //- rjf: get next key + //- rjf: get shared state + TXT_ArtifactCreateShared *shared = 0; + if(lane_idx() == 0) + { + shared = push_array(scratch.arena, TXT_ArtifactCreateShared, 1); + } + lane_sync_u64(&shared, 0); + + //- rjf: unpack key U128 hash = {0}; TXT_LangKind lang = TXT_LangKind_Null; - txt_u2p_dequeue_req(&hash, &lang); - HS_Scope *scope = hs_scope_open(); - - //- rjf: unpack hash - U64 slot_idx = hash.u64[1]%txt_shared->slots_count; - U64 stripe_idx = slot_idx%txt_shared->stripes_count; - TXT_Slot *slot = &txt_shared->slots[slot_idx]; - TXT_Stripe *stripe = &txt_shared->stripes[stripe_idx]; - - //- rjf: take task - B32 got_task = 0; - OS_MutexScopeR(stripe->rw_mutex) - { - for(TXT_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(n->hash, hash) && n->lang == lang) - { - 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); - } + str8_deserial_read_struct(key, 0, &hash); + str8_deserial_read_struct(key, sizeof(hash), &lang); + String8 data = c_data_from_hash(access, hash); + TXT_LangLexFunctionType *lex_function = txt_lex_function_from_lang_kind(lang); //- rjf: data -> text info - Arena *info_arena = 0; - TXT_TextInfo info = {0}; - if(got_task && !u128_match(hash, u128_zero())) + if(!u128_match(hash, u128_zero())) { - info_arena = arena_alloc(); - - //- rjf: grab pointers to working counters - U64 *bytes_processed_ptr = 0; - U64 *bytes_to_process_ptr = 0; - OS_MutexScopeR(stripe->rw_mutex) + if(lane_idx() == 0) { - for(TXT_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(n->hash, hash) && n->lang == lang) - { - bytes_processed_ptr = &n->info.bytes_processed; - bytes_to_process_ptr = &n->info.bytes_to_process; - } - } + shared->arena = arena_alloc(); } //- rjf: set # of bytes to process - if(bytes_to_process_ptr) - { - // (line ending calc) (line counting) (line measuring) (lexing) - ins_atomic_u64_eval_assign(bytes_to_process_ptr, Min(data.size, 1024) + data.size + data.size + data.size*(lang != TXT_LangKind_Null)); - } + // (line ending calc) (line counting) (line measuring) (lexing) + set_progress_target(Min(data.size, 1024) + data.size + data.size + data.size*(lang != TXT_LangKind_Null)); //- rjf: detect line end kind TXT_LineEndKind line_end_kind = TXT_LineEndKind_Null; + if(lane_idx() == 0) { U64 lf_count = 0; U64 cr_count = 0; @@ -2288,279 +2029,259 @@ ASYNC_WORK_DEF(txt_parse_work) { line_end_kind = TXT_LineEndKind_LF; } - info.line_end_kind = line_end_kind; - } - - //- rjf: bump progress - if(bytes_processed_ptr) - { - ins_atomic_u64_eval_assign(bytes_processed_ptr, Min(data.size, 1024)); + shared->info.line_end_kind = line_end_kind; } + lane_sync(); + set_progress(Min(data.size, 1024)); //- rjf: count # of lines - U64 line_count = 1; - U64 byte_process_start_idx = 0; - for(U64 idx = 0; idx < data.size; idx += 1) + U64 lane_line_count = 0; + if(lane_idx() == 0) { - if(data.str[idx] == '\n' || data.str[idx] == '\r') + lane_line_count = 1; + } + { + Rng1U64 range = lane_range(data.size); + for EachInRange(idx, range) { - line_count += 1; - if(data.str[idx] == '\r') + if(data.str[idx] == '\n') { - idx += 1; + lane_line_count += 1; + } + if(idx && idx%1000 == 0) + { + add_progress(1000); } } - if(idx && idx%1000 == 0) - { - ins_atomic_u64_add_eval(bytes_processed_ptr, 1000); - } - } - - //- rjf: bump progress - if(bytes_processed_ptr) - { - ins_atomic_u64_eval_assign(bytes_processed_ptr, Min(data.size, 1024) + data.size); } + ins_atomic_u64_add_eval(&shared->info.lines_count, lane_line_count); + lane_sync(); + set_progress(Min(data.size, 1024) + data.size); //- rjf: allocate & store line ranges - info.lines_count = line_count; - info.lines_ranges = push_array_no_zero(info_arena, Rng1U64, info.lines_count); - U64 line_idx = 0; - U64 line_start_idx = 0; - for(U64 idx = 0; idx <= data.size; idx += 1) + if(lane_idx() == 0) { - if(idx == data.size || data.str[idx] == '\n' || data.str[idx] == '\r') + shared->info.lines_ranges = push_array_no_zero(shared->arena, Rng1U64, shared->info.lines_count); + U64 line_idx = 0; + U64 line_start_idx = 0; + for(U64 idx = 0; idx <= data.size; idx += 1) { - Rng1U64 line_range = r1u64(line_start_idx, idx); - U64 line_size = dim_1u64(line_range); - info.lines_ranges[line_idx] = line_range; - info.lines_max_size = Max(info.lines_max_size, line_size); - line_idx += 1; - line_start_idx = idx+1; - if(idx < data.size && data.str[idx] == '\r') + if(idx == data.size || data.str[idx] == '\n' || data.str[idx] == '\r') { - line_start_idx += 1; - idx += 1; + Rng1U64 line_range = r1u64(line_start_idx, idx); + U64 line_size = dim_1u64(line_range); + shared->info.lines_ranges[line_idx] = line_range; + shared->info.lines_max_size = Max(shared->info.lines_max_size, line_size); + line_idx += 1; + line_start_idx = idx+1; + if(idx < data.size && data.str[idx] == '\r') + { + line_start_idx += 1; + idx += 1; + } + } + if(idx && idx%1000 == 0) + { + add_progress(1000); } } - if(idx && idx%1000 == 0) - { - ins_atomic_u64_add_eval(bytes_processed_ptr, 1000); - } - } - - //- rjf: bump progress - if(bytes_processed_ptr) - { - ins_atomic_u64_eval_assign(bytes_processed_ptr, Min(data.size, 1024) + data.size + data.size); } + lane_sync(); + set_progress(Min(data.size, 1024) + data.size + data.size); //- rjf: lex function * data -> tokens - TXT_TokenArray tokens = {0}; - TXT_LangLexFunctionType *lex_function = txt_lex_function_from_lang_kind(lang); - if(lex_function != 0) + if(lane_idx() == 0 && lex_function != 0) { - tokens = lex_function(info_arena, bytes_processed_ptr, data); - } - info.tokens = tokens; - - //- rjf: bump progress - if(bytes_processed_ptr) - { - ins_atomic_u64_eval_assign(bytes_processed_ptr, Min(data.size, 1024) + data.size + data.size + data.size*(lex_function != 0)); + shared->info.tokens = lex_function(shared->arena, 0, data); } + lane_sync(); + set_progress(Min(data.size, 1024) + data.size + data.size + data.size*(lex_function != 0)); + TXT_TokenArray tokens = shared->info.tokens; //- rjf: count scope points - U64 scope_pt_opener_count = 0; - U64 scope_pt_count = 0; - for EachIndex(idx, tokens.count) { - if(tokens.v[idx].kind == TXT_TokenKind_Symbol) + U64 lane_scope_pt_opener_count = 0; + U64 lane_scope_pt_count = 0; + Rng1U64 range = lane_range(tokens.count); + for EachInRange(idx, range) { - String8 token_string = str8_substr(data, tokens.v[idx].range); - B32 is_opener = (token_string.str[0] == '{' || - token_string.str[0] == '(' || - token_string.str[0] == '['); - B32 is_closer = (token_string.str[0] == '}' || - token_string.str[0] == ')' || - token_string.str[0] == ']'); - if(token_string.size == 1 && (is_opener || is_closer)) + if(tokens.v[idx].kind == TXT_TokenKind_Symbol) { - scope_pt_count += 1; - scope_pt_opener_count += !!is_opener; - } - } - } - - //- rjf: allocate & fill scope data - info.scope_pts.count = scope_pt_count; - info.scope_pts.v = push_array_no_zero(info_arena, TXT_ScopePt, info.scope_pts.count); - info.scope_nodes.count = scope_pt_opener_count; - info.scope_nodes.v = push_array_no_zero(info_arena, TXT_ScopeNode, info.scope_nodes.count); - { - typedef struct ScopeTask ScopeTask; - struct ScopeTask - { - ScopeTask *next; - U64 scope_idx; - }; - Temp scratch = scratch_begin(0, 0); - ScopeTask *top_scope_task = 0; - ScopeTask *free_scope_task = 0; - U64 pt_idx = 0; - U64 scope_idx = 0; - for EachIndex(token_idx, tokens.count) - { - if(tokens.v[token_idx].kind == TXT_TokenKind_Symbol) - { - String8 token_string = str8_substr(data, tokens.v[token_idx].range); + String8 token_string = str8_substr(data, tokens.v[idx].range); B32 is_opener = (token_string.str[0] == '{' || token_string.str[0] == '(' || token_string.str[0] == '['); B32 is_closer = (token_string.str[0] == '}' || token_string.str[0] == ')' || token_string.str[0] == ']'); - - // rjf: opener symbols -> push scope - if(is_opener) + if(token_string.size == 1 && (is_opener || is_closer)) { - // rjf: insert into scope tree - TXT_ScopeNode *new_scope = &info.scope_nodes.v[scope_idx]; - new_scope->token_idx_range.min = token_idx; - if(top_scope_task) + lane_scope_pt_count += 1; + lane_scope_pt_opener_count += !!is_opener; + } + } + } + ins_atomic_u64_add_eval(&shared->info.scope_pts.count, lane_scope_pt_count); + ins_atomic_u64_add_eval(&shared->info.scope_nodes.count, lane_scope_pt_opener_count); + } + lane_sync(); + + //- rjf: allocate & fill scope data + if(lane_idx() == 0) + { + shared->info.scope_pts.v = push_array_no_zero(shared->arena, TXT_ScopePt, shared->info.scope_pts.count); + shared->info.scope_nodes.v = push_array_no_zero(shared->arena, TXT_ScopeNode, shared->info.scope_nodes.count); + { + typedef struct ScopeTask ScopeTask; + struct ScopeTask + { + ScopeTask *next; + U64 scope_idx; + }; + Temp scratch = scratch_begin(0, 0); + ScopeTask *top_scope_task = 0; + ScopeTask *free_scope_task = 0; + U64 pt_idx = 0; + U64 scope_idx = 0; + for EachIndex(token_idx, tokens.count) + { + if(tokens.v[token_idx].kind == TXT_TokenKind_Symbol) + { + String8 token_string = str8_substr(data, tokens.v[token_idx].range); + B32 is_opener = (token_string.str[0] == '{' || + token_string.str[0] == '(' || + token_string.str[0] == '['); + B32 is_closer = (token_string.str[0] == '}' || + token_string.str[0] == ')' || + token_string.str[0] == ']'); + + // rjf: opener symbols -> push scope + if(is_opener) { - U64 new_scope_num = scope_idx+1; - TXT_ScopeNode *parent = &info.scope_nodes.v[top_scope_task->scope_idx]; - if(parent->first_num == 0) + // rjf: insert into scope tree + TXT_ScopeNode *new_scope = &shared->info.scope_nodes.v[scope_idx]; + new_scope->token_idx_range.min = token_idx; + if(top_scope_task) { - parent->first_num = new_scope_num; + U64 new_scope_num = scope_idx+1; + TXT_ScopeNode *parent = &shared->info.scope_nodes.v[top_scope_task->scope_idx]; + if(parent->first_num == 0) + { + parent->first_num = new_scope_num; + } + if(parent->last_num != 0) + { + TXT_ScopeNode *prev_scope = &shared->info.scope_nodes.v[parent->last_num-1]; + prev_scope->next_num = new_scope_num; + } + parent->last_num = new_scope_num; + new_scope->parent_num = top_scope_task->scope_idx+1; } - if(parent->last_num != 0) + + // rjf: push onto scope stack + ScopeTask *scope_task = free_scope_task; + if(scope_task) { - TXT_ScopeNode *prev_scope = &info.scope_nodes.v[parent->last_num-1]; - prev_scope->next_num = new_scope_num; + SLLStackPop(free_scope_task); } - parent->last_num = new_scope_num; - new_scope->parent_num = top_scope_task->scope_idx+1; + else + { + scope_task = push_array(scratch.arena, ScopeTask, 1); + } + scope_task->scope_idx = scope_idx; + scope_idx += 1; + SLLStackPush(top_scope_task, scope_task); } - // rjf: push onto scope stack - ScopeTask *scope_task = free_scope_task; - if(scope_task) + // rjf: opener or closer -> fill endpoint + if(top_scope_task && (is_opener || is_closer)) { - SLLStackPop(free_scope_task); + shared->info.scope_pts.v[pt_idx].token_idx = token_idx; + shared->info.scope_pts.v[pt_idx].scope_idx = top_scope_task->scope_idx; + pt_idx += 1; } - else + + // rjf: closer symbols -> pop + if(is_closer && top_scope_task != 0) { - scope_task = push_array(scratch.arena, ScopeTask, 1); + ScopeTask *popped = top_scope_task; + shared->info.scope_nodes.v[popped->scope_idx].token_idx_range.max = token_idx; + SLLStackPop(top_scope_task); + SLLStackPush(free_scope_task, popped); } - scope_task->scope_idx = scope_idx; - scope_idx += 1; - SLLStackPush(top_scope_task, scope_task); - } - - // rjf: opener or closer -> fill endpoint - if(top_scope_task && (is_opener || is_closer)) - { - info.scope_pts.v[pt_idx].token_idx = token_idx; - info.scope_pts.v[pt_idx].scope_idx = top_scope_task->scope_idx; - pt_idx += 1; - } - - // rjf: closer symbols -> pop - if(is_closer && top_scope_task != 0) - { - ScopeTask *popped = top_scope_task; - info.scope_nodes.v[popped->scope_idx].token_idx_range.max = token_idx; - SLLStackPop(top_scope_task); - SLLStackPush(free_scope_task, popped); } } + scratch_end(scratch); } - scratch_end(scratch); } + lane_sync(); } - //- rjf: commit results to cache - if(got_task) OS_MutexScopeW(stripe->rw_mutex) + //- rjf: mark dependency on hash + c_hash_downstream_inc(hash); + + //- rjf: package as artifact + if(lane_idx() == 0 && shared->arena != 0) { - for(TXT_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(n->hash, hash) && n->lang == lang) - { - hs_hash_downstream_inc(n->hash); - n->arena = info_arena; - info.bytes_processed = n->info.bytes_processed; - info.bytes_to_process = n->info.bytes_to_process; - MemoryCopyStruct(&n->info, &info); - ins_atomic_u32_eval_assign(&n->is_working, 0); - ins_atomic_u64_inc_eval(&n->load_count); - break; - } - } + shared->artifact = push_array(shared->arena, TXT_Artifact, 1); + shared->artifact->arena = shared->arena; + shared->artifact->data_hash = hash; + shared->artifact->info = shared->info; } + lane_sync(); - hs_scope_close(scope); + access_close(access); + scratch_end(scratch); ProfEnd(); - return 0; + AC_Artifact result = {0}; + result.u64[0] = (U64)shared->artifact; + return result; } -//////////////////////////////// -//~ rjf: Evictor Threads - internal void -txt_evictor_thread__entry_point(void *p) +txt_artifact_destroy(AC_Artifact artifact) { - ThreadNameF("[txt] evictor thread"); - for(;;) - { - U64 check_time_us = os_now_microseconds(); - U64 check_time_user_clocks = update_tick_idx(); - U64 evict_threshold_us = 2*1000000; - U64 evict_threshold_user_clocks = 10; - for(U64 slot_idx = 0; slot_idx < txt_shared->slots_count; slot_idx += 1) - { - U64 stripe_idx = slot_idx%txt_shared->stripes_count; - TXT_Slot *slot = &txt_shared->slots[slot_idx]; - TXT_Stripe *stripe = &txt_shared->stripes[stripe_idx]; - B32 slot_has_work = 0; - OS_MutexScopeR(stripe->rw_mutex) - { - for(TXT_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(TXT_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); - hs_hash_downstream_dec(n->hash); - if(n->arena != 0) - { - arena_release(n->arena); - } - SLLStackPush(txt_shared->stripes_free_nodes[stripe_idx], n); - } - } - } - } - os_sleep_milliseconds(500); - } + TXT_Artifact *txt_artifact = (TXT_Artifact *)artifact.u64[0]; + if(txt_artifact == 0) { return; } + c_hash_downstream_dec(txt_artifact->data_hash); + arena_release(txt_artifact->arena); +} + +internal TXT_TextInfo +txt_text_info_from_hash_lang(Access *access, U128 hash, TXT_LangKind lang) +{ + struct + { + U128 hash; + TXT_LangKind lang; + } key = {hash, lang}; + String8 key_string = str8_struct(&key); + AC_Artifact artifact = ac_artifact_from_key(access, key_string, txt_artifact_create, txt_artifact_destroy, 0, .flags = AC_Flag_Wide); + TXT_Artifact *txt_artifact = (TXT_Artifact *)artifact.u64[0]; + TXT_TextInfo info = {0}; + if(txt_artifact != 0) + { + info = txt_artifact->info; + } + return info; +} + +internal TXT_TextInfo +txt_text_info_from_key_lang(Access *access, C_Key key, TXT_LangKind lang, U128 *hash_out) +{ + TXT_TextInfo 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 = txt_text_info_from_hash_lang(access, hash, lang); + if(result.lines_count != 0) + { + if(hash_out) + { + *hash_out = hash; + } + break; + } + } + return result; } diff --git a/src/text_cache/text_cache.h b/src/text/text.h similarity index 63% rename from src/text_cache/text_cache.h rename to src/text/text.h index f7f062f8..1c31aa52 100644 --- a/src/text_cache/text_cache.h +++ b/src/text/text.h @@ -1,8 +1,8 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -#ifndef TEXT_CACHE_H -#define TEXT_CACHE_H +#ifndef TEXT_H +#define TEXT_H //////////////////////////////// //~ rjf: Value Types @@ -155,112 +155,10 @@ TXT_LangKind; typedef TXT_TokenArray TXT_LangLexFunctionType(Arena *arena, U64 *bytes_processed_counter, String8 string); -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct TXT_Node TXT_Node; -struct TXT_Node -{ - // rjf: links - TXT_Node *next; - TXT_Node *prev; - - // rjf: key - U128 hash; - TXT_LangKind lang; - - // rjf: artifacts - Arena *arena; - TXT_TextInfo info; - - // rjf: metadata - B32 is_working; - U64 scope_ref_count; - U64 last_time_touched_us; - U64 last_user_clock_idx_touched; - U64 load_count; -}; - -typedef struct TXT_Slot TXT_Slot; -struct TXT_Slot -{ - TXT_Node *first; - TXT_Node *last; -}; - -typedef struct TXT_Stripe TXT_Stripe; -struct TXT_Stripe -{ - Arena *arena; - OS_Handle rw_mutex; - OS_Handle cv; -}; - -//////////////////////////////// -//~ rjf: Scoped Access - -typedef struct TXT_Touch TXT_Touch; -struct TXT_Touch -{ - TXT_Touch *next; - U128 hash; - TXT_LangKind lang; -}; - -typedef struct TXT_Scope TXT_Scope; -struct TXT_Scope -{ - TXT_Scope *next; - TXT_Touch *top_touch; -}; - -//////////////////////////////// -//~ rjf: Thread Context - -typedef struct TXT_TCTX TXT_TCTX; -struct TXT_TCTX -{ - Arena *arena; - TXT_Scope *free_scope; - TXT_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State - -typedef struct TXT_Shared TXT_Shared; -struct TXT_Shared -{ - Arena *arena; - - // rjf: user clock - U64 user_clock_idx; - - // rjf: cache - U64 slots_count; - U64 stripes_count; - TXT_Slot *slots; - TXT_Stripe *stripes; - TXT_Node **stripes_free_nodes; - - // 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 thread - OS_Handle evictor_thread; -}; - //////////////////////////////// //~ rjf: Globals read_only global TXT_ScopeNode txt_scope_node_nil = {0}; -thread_static TXT_TCTX *txt_tctx = 0; -global TXT_Shared *txt_shared = 0; //////////////////////////////// //~ rjf: Basic Helpers @@ -287,29 +185,6 @@ internal TXT_TokenArray txt_token_array_from_string__jai(Arena *arena, U64 *byte internal TXT_TokenArray txt_token_array_from_string__zig(Arena *arena, U64 *bytes_processed_counter, String8 string); internal TXT_TokenArray txt_token_array_from_string__disasm_x64_intel(Arena *arena, U64 *bytes_processed_counter, String8 string); -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void txt_init(void); - -//////////////////////////////// -//~ rjf: Thread Context Initialization - -internal void txt_tctx_ensure_inited(void); - -//////////////////////////////// -//~ rjf: Scoped Access - -internal TXT_Scope *txt_scope_open(void); -internal void txt_scope_close(TXT_Scope *scope); -internal void txt_scope_touch_node__stripe_r_guarded(TXT_Scope *scope, TXT_Node *node); - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal TXT_TextInfo txt_text_info_from_hash_lang(TXT_Scope *scope, U128 hash, TXT_LangKind lang); -internal TXT_TextInfo txt_text_info_from_key_lang(TXT_Scope *scope, HS_Key key, TXT_LangKind lang, U128 *hash_out); - //////////////////////////////// //~ rjf: Text Info Extractor Helpers @@ -326,15 +201,11 @@ internal TXT_ScopeNode *txt_scope_node_from_info_off(TXT_TextInfo *info, U64 off internal TXT_ScopeNode *txt_scope_node_from_info_pt(TXT_TextInfo *info, TxtPt pt); //////////////////////////////// -//~ rjf: Parse Threads +//~ rjf: Artifact Cache Hooks / Lookups -internal B32 txt_u2p_enqueue_req(U128 hash, TXT_LangKind lang, U64 endt_us); -internal void txt_u2p_dequeue_req(U128 *hash_out, TXT_LangKind *lang_out); -ASYNC_WORK_DEF(txt_parse_work); +internal AC_Artifact txt_artifact_create(String8 key, U64 gen, U64 *requested_gen, B32 *retry_out); +internal void txt_artifact_destroy(AC_Artifact artifact); +internal TXT_TextInfo txt_text_info_from_hash_lang(Access *access, U128 hash, TXT_LangKind lang); +internal TXT_TextInfo txt_text_info_from_key_lang(Access *access, C_Key key, TXT_LangKind lang, U128 *hash_out); -//////////////////////////////// -//~ rjf: Evictor Threads - -internal void txt_evictor_thread__entry_point(void *p); - -#endif // TEXT_CACHE_H +#endif // TEXT_H diff --git a/src/texture_cache/texture_cache.c b/src/texture_cache/texture_cache.c deleted file mode 100644 index 245b69f6..00000000 --- a/src/texture_cache/texture_cache.c +++ /dev/null @@ -1,388 +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: Basic Helpers - -internal TEX_Topology -tex_topology_make(Vec2S32 dim, R_Tex2DFormat fmt) -{ - TEX_Topology top = {0}; - top.dim.x = (S16)Clamp(0, dim.x, max_S32); - top.dim.y = (S16)Clamp(0, dim.y, max_S32); - top.fmt = fmt; - return top; -} - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void -tex_init(void) -{ - Arena *arena = arena_alloc(); - tex_shared = push_array(arena, TEX_Shared, 1); - tex_shared->arena = arena; - tex_shared->slots_count = 1024; - tex_shared->stripes_count = Min(tex_shared->slots_count, os_get_system_info()->logical_processor_count); - tex_shared->slots = push_array(arena, TEX_Slot, tex_shared->slots_count); - tex_shared->stripes = push_array(arena, TEX_Stripe, tex_shared->stripes_count); - tex_shared->stripes_free_nodes = push_array(arena, TEX_Node *, tex_shared->stripes_count); - for(U64 idx = 0; idx < tex_shared->stripes_count; idx += 1) - { - tex_shared->stripes[idx].arena = arena_alloc(); - tex_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc(); - tex_shared->stripes[idx].cv = os_condition_variable_alloc(); - } - tex_shared->u2x_ring_size = KB(64); - tex_shared->u2x_ring_base = push_array_no_zero(arena, U8, tex_shared->u2x_ring_size); - tex_shared->u2x_ring_cv = os_condition_variable_alloc(); - tex_shared->u2x_ring_mutex = os_mutex_alloc(); - tex_shared->evictor_thread = os_thread_launch(tex_evictor_thread__entry_point, 0, 0); -} - -//////////////////////////////// -//~ rjf: Thread Context Initialization - -internal void -tex_tctx_ensure_inited(void) -{ - if(tex_tctx == 0) - { - Arena *arena = arena_alloc(); - tex_tctx = push_array(arena, TEX_TCTX, 1); - tex_tctx->arena = arena; - } -} - -//////////////////////////////// -//~ rjf: Scoped Access - -internal TEX_Scope * -tex_scope_open(void) -{ - tex_tctx_ensure_inited(); - TEX_Scope *scope = tex_tctx->free_scope; - if(scope) - { - SLLStackPop(tex_tctx->free_scope); - } - else - { - scope = push_array_no_zero(tex_tctx->arena, TEX_Scope, 1); - } - MemoryZeroStruct(scope); - return scope; -} - -internal void -tex_scope_close(TEX_Scope *scope) -{ - for(TEX_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next) - { - U128 hash = touch->hash; - next = touch->next; - U64 slot_idx = hash.u64[1]%tex_shared->slots_count; - U64 stripe_idx = slot_idx%tex_shared->stripes_count; - TEX_Slot *slot = &tex_shared->slots[slot_idx]; - TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx]; - OS_MutexScopeR(stripe->rw_mutex) - { - for(TEX_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(hash, n->hash) && MemoryMatchStruct(&touch->topology, &n->topology)) - { - ins_atomic_u64_dec_eval(&n->scope_ref_count); - break; - } - } - } - SLLStackPush(tex_tctx->free_touch, touch); - } - SLLStackPush(tex_tctx->free_scope, scope); -} - -internal void -tex_scope_touch_node__stripe_r_guarded(TEX_Scope *scope, TEX_Node *node) -{ - TEX_Touch *touch = tex_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(tex_tctx->free_touch); - } - else - { - touch = push_array_no_zero(tex_tctx->arena, TEX_Touch, 1); - } - MemoryZeroStruct(touch); - touch->hash = node->hash; - touch->topology = node->topology; - SLLStackPush(scope->top_touch, touch); -} - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal R_Handle -tex_texture_from_hash_topology(TEX_Scope *scope, U128 hash, TEX_Topology topology) -{ - R_Handle handle = {0}; - { - U64 slot_idx = hash.u64[1]%tex_shared->slots_count; - U64 stripe_idx = slot_idx%tex_shared->stripes_count; - TEX_Slot *slot = &tex_shared->slots[slot_idx]; - TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx]; - B32 found = 0; - B32 stale = 0; - OS_MutexScopeR(stripe->rw_mutex) - { - for(TEX_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(hash, n->hash) && MemoryMatchStruct(&topology, &n->topology)) - { - handle = n->texture; - found = !r_handle_match(r_handle_zero(), handle); - tex_scope_touch_node__stripe_r_guarded(scope, n); - break; - } - } - } - B32 node_is_new = 0; - if(!found) - { - OS_MutexScopeW(stripe->rw_mutex) - { - TEX_Node *node = 0; - for(TEX_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(hash, n->hash) && MemoryMatchStruct(&topology, &n->topology)) - { - node = n; - break; - } - } - if(node == 0) - { - node = tex_shared->stripes_free_nodes[stripe_idx]; - if(node) - { - SLLStackPop(tex_shared->stripes_free_nodes[stripe_idx]); - } - else - { - node = push_array_no_zero(stripe->arena, TEX_Node, 1); - } - MemoryZeroStruct(node); - DLLPushBack(slot->first, slot->last, node); - node->hash = hash; - MemoryCopyStruct(&node->topology, &topology); - node_is_new = 1; - } - } - } - if(node_is_new) - { - tex_u2x_enqueue_req(hash, topology, max_U64); - async_push_work(tex_xfer_work); - } - } - return handle; -} - -internal R_Handle -tex_texture_from_key_topology(TEX_Scope *scope, HS_Key key, TEX_Topology topology, U128 *hash_out) -{ - 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 = tex_texture_from_hash_topology(scope, hash, topology); - if(!r_handle_match(handle, r_handle_zero())) - { - if(hash_out) - { - *hash_out = hash; - } - break; - } - } - return handle; -} - -//////////////////////////////// -//~ rjf: Transfer Threads - -internal B32 -tex_u2x_enqueue_req(U128 hash, TEX_Topology top, U64 endt_us) -{ - B32 good = 0; - OS_MutexScope(tex_shared->u2x_ring_mutex) for(;;) - { - U64 unconsumed_size = tex_shared->u2x_ring_write_pos-tex_shared->u2x_ring_read_pos; - U64 available_size = tex_shared->u2x_ring_size-unconsumed_size; - if(available_size >= sizeof(hash)+sizeof(top)) - { - good = 1; - tex_shared->u2x_ring_write_pos += ring_write_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_write_pos, &hash); - tex_shared->u2x_ring_write_pos += ring_write_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_write_pos, &top); - break; - } - if(os_now_microseconds() >= endt_us) - { - break; - } - os_condition_variable_wait(tex_shared->u2x_ring_cv, tex_shared->u2x_ring_mutex, endt_us); - } - if(good) - { - os_condition_variable_broadcast(tex_shared->u2x_ring_cv); - } - return good; -} - -internal void -tex_u2x_dequeue_req(U128 *hash_out, TEX_Topology *top_out) -{ - OS_MutexScope(tex_shared->u2x_ring_mutex) for(;;) - { - U64 unconsumed_size = tex_shared->u2x_ring_write_pos-tex_shared->u2x_ring_read_pos; - if(unconsumed_size >= sizeof(*hash_out)+sizeof(*top_out)) - { - tex_shared->u2x_ring_read_pos += ring_read_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_read_pos, hash_out); - tex_shared->u2x_ring_read_pos += ring_read_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_read_pos, top_out); - break; - } - os_condition_variable_wait(tex_shared->u2x_ring_cv, tex_shared->u2x_ring_mutex, max_U64); - } - os_condition_variable_broadcast(tex_shared->u2x_ring_cv); -} - -ASYNC_WORK_DEF(tex_xfer_work) -{ - ProfBeginFunction(); - HS_Scope *scope = hs_scope_open(); - - //- rjf: decode - U128 hash = {0}; - TEX_Topology top = {0}; - tex_u2x_dequeue_req(&hash, &top); - - //- rjf: unpack hash - U64 slot_idx = hash.u64[1]%tex_shared->slots_count; - U64 stripe_idx = slot_idx%tex_shared->stripes_count; - TEX_Slot *slot = &tex_shared->slots[slot_idx]; - TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx]; - - //- rjf: take task - B32 got_task = 0; - OS_MutexScopeR(stripe->rw_mutex) - { - for(TEX_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(n->hash, hash) && MemoryMatchStruct(&top, &n->topology)) - { - 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 * topology -> texture - R_Handle texture = {0}; - if(got_task && top.dim.x > 0 && top.dim.y > 0 && data.size >= (U64)top.dim.x*(U64)top.dim.y*(U64)r_tex2d_format_bytes_per_pixel_table[top.fmt]) - { - texture = r_tex2d_alloc(R_ResourceKind_Static, v2s32(top.dim.x, top.dim.y), top.fmt, data.str); - } - - //- rjf: commit results to cache - if(got_task) OS_MutexScopeW(stripe->rw_mutex) - { - for(TEX_Node *n = slot->first; n != 0; n = n->next) - { - if(u128_match(n->hash, hash) && MemoryMatchStruct(&top, &n->topology)) - { - n->texture = texture; - 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 -tex_evictor_thread__entry_point(void *p) -{ - ThreadNameF("[tex] 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 < tex_shared->slots_count; slot_idx += 1) - { - U64 stripe_idx = slot_idx%tex_shared->stripes_count; - TEX_Slot *slot = &tex_shared->slots[slot_idx]; - TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx]; - B32 slot_has_work = 0; - OS_MutexScopeR(stripe->rw_mutex) - { - for(TEX_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(TEX_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->texture, r_handle_zero())) - { - r_tex2d_release(n->texture); - } - SLLStackPush(tex_shared->stripes_free_nodes[stripe_idx], n); - } - } - } - os_sleep_milliseconds(5); - } - os_sleep_milliseconds(1000); - } -} diff --git a/src/texture_cache/texture_cache.h b/src/texture_cache/texture_cache.h deleted file mode 100644 index 70b53a85..00000000 --- a/src/texture_cache/texture_cache.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef TEXTURE_CACHE_H -#define TEXTURE_CACHE_H - -//////////////////////////////// -//~ rjf: Texture Topology - -typedef struct TEX_Topology TEX_Topology; -struct TEX_Topology -{ - Vec2S16 dim; - R_Tex2DFormat fmt; -}; - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct TEX_Node TEX_Node; -struct TEX_Node -{ - TEX_Node *next; - TEX_Node *prev; - U128 hash; - TEX_Topology topology; - R_Handle texture; - B32 is_working; - U64 scope_ref_count; - U64 last_time_touched_us; - U64 last_user_clock_idx_touched; - U64 load_count; -}; - -typedef struct TEX_Slot TEX_Slot; -struct TEX_Slot -{ - TEX_Node *first; - TEX_Node *last; -}; - -typedef struct TEX_Stripe TEX_Stripe; -struct TEX_Stripe -{ - Arena *arena; - OS_Handle rw_mutex; - OS_Handle cv; -}; - -//////////////////////////////// -//~ rjf: Scoped Access - -typedef struct TEX_Touch TEX_Touch; -struct TEX_Touch -{ - TEX_Touch *next; - U128 hash; - TEX_Topology topology; -}; - -typedef struct TEX_Scope TEX_Scope; -struct TEX_Scope -{ - TEX_Scope *next; - TEX_Touch *top_touch; -}; - -//////////////////////////////// -//~ rjf: Thread Context - -typedef struct TEX_TCTX TEX_TCTX; -struct TEX_TCTX -{ - Arena *arena; - TEX_Scope *free_scope; - TEX_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State - -typedef struct TEX_Shared TEX_Shared; -struct TEX_Shared -{ - Arena *arena; - - // rjf: cache - U64 slots_count; - U64 stripes_count; - TEX_Slot *slots; - TEX_Stripe *stripes; - TEX_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 TEX_TCTX *tex_tctx = 0; -global TEX_Shared *tex_shared = 0; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal TEX_Topology tex_topology_make(Vec2S32 dim, R_Tex2DFormat fmt); - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void tex_init(void); - -//////////////////////////////// -//~ rjf: Thread Context Initialization - -internal void tex_tctx_ensure_inited(void); - -//////////////////////////////// -//~ rjf: Scoped Access - -internal TEX_Scope *tex_scope_open(void); -internal void tex_scope_close(TEX_Scope *scope); -internal void tex_scope_touch_node__stripe_r_guarded(TEX_Scope *scope, TEX_Node *node); - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal R_Handle tex_texture_from_hash_topology(TEX_Scope *scope, U128 hash, TEX_Topology topology); -internal R_Handle tex_texture_from_key_topology(TEX_Scope *scope, HS_Key key, TEX_Topology topology, U128 *hash_out); - -//////////////////////////////// -//~ rjf: Transfer Threads - -internal B32 tex_u2x_enqueue_req(U128 hash, TEX_Topology top, U64 endt_us); -internal void tex_u2x_dequeue_req(U128 *hash_out, TEX_Topology *top_out); -ASYNC_WORK_DEF(tex_xfer_work); - -//////////////////////////////// -//~ rjf: Evictor Threads - -internal void tex_evictor_thread__entry_point(void *p); - -#endif //TEXTURE_CACHE_H diff --git a/src/third_party/blake3/c/blake3_dispatch.c b/src/third_party/blake3/c/blake3_dispatch.c index 78f7ddb6..0082f974 100644 --- a/src/third_party/blake3/c/blake3_dispatch.c +++ b/src/third_party/blake3/c/blake3_dispatch.c @@ -6,7 +6,6 @@ #if defined(IS_X86) #if defined(_MSC_VER) -#include #include #elif defined(__GNUC__) #include @@ -15,6 +14,7 @@ #endif #endif + #if !defined(BLAKE3_ATOMICS) #if defined(__has_include) #if __has_include() && !defined(_MSC_VER) @@ -32,9 +32,9 @@ #define ATOMIC_LOAD(x) x #define ATOMIC_STORE(x, y) x = y #elif defined(_MSC_VER) -#define ATOMIC_INT LONG -#define ATOMIC_LOAD(x) InterlockedOr(&x, 0) -#define ATOMIC_STORE(x, y) InterlockedExchange(&x, y) +#define ATOMIC_INT long +#define ATOMIC_LOAD(x) _InterlockedOr(&x, 0) +#define ATOMIC_STORE(x, y) _InterlockedExchange(&x, y) #else #define ATOMIC_INT int #define ATOMIC_LOAD(x) x diff --git a/src/third_party/md5/md5.c b/src/third_party/md5/md5.c index 57f429d4..c31e1c82 100644 --- a/src/third_party/md5/md5.c +++ b/src/third_party/md5/md5.c @@ -204,7 +204,7 @@ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) return ptr; } -void MD5_Init(MD5_CTX *ctx) +MD5_API void MD5_Init(MD5_CTX *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -215,7 +215,7 @@ void MD5_Init(MD5_CTX *ctx) ctx->hi = 0; } -void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +MD5_API void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) { MD5_u32plus saved_lo; unsigned long used, available; @@ -255,7 +255,7 @@ void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) (dst)[2] = (unsigned char)((src) >> 16); \ (dst)[3] = (unsigned char)((src) >> 24); -void MD5_Final(unsigned char *result, MD5_CTX *ctx) +MD5_API void MD5_Final(unsigned char *result, MD5_CTX *ctx) { unsigned long used, available; diff --git a/src/third_party/md5/md5.h b/src/third_party/md5/md5.h index 2da44bf3..ce5e0c92 100644 --- a/src/third_party/md5/md5.h +++ b/src/third_party/md5/md5.h @@ -23,6 +23,10 @@ * See md5.c for more information. */ +#ifndef MD5_API +# define MD5_API +#endif + #ifdef HAVE_OPENSSL #include #elif !defined(_MD5_H) @@ -38,8 +42,8 @@ typedef struct { MD5_u32plus block[16]; } MD5_CTX; -extern void MD5_Init(MD5_CTX *ctx); -extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); -extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); +MD5_API void MD5_Init(MD5_CTX *ctx); +MD5_API void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +MD5_API void MD5_Final(unsigned char *result, MD5_CTX *ctx); #endif diff --git a/src/third_party/radsort/radsort.h b/src/third_party/radsort/radsort.h index 9b735a41..9850a6ff 100644 --- a/src/third_party/radsort/radsort.h +++ b/src/third_party/radsort/radsort.h @@ -558,7 +558,7 @@ RSFORCEINLINE void radsortinternal( void * start, size_t len, size_t element_siz piv = scan; while( scan <= rend ) { - size_t adv = is_before( scan, tmp ); + int adv = is_before( scan, tmp ); swapper( piv, scan, element_size ); if ( adv ) piv = rsadd_ptr( piv, element_size ); // needs to be a cmov scan = rsadd_ptr( scan, element_size ); diff --git a/src/torture/torture.c b/src/torture/torture.c index 0aa3f83d..ab845134 100644 --- a/src/torture/torture.c +++ b/src/torture/torture.c @@ -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/) //////////////////////////////// @@ -23,6 +23,9 @@ #include "coff/coff_lib_writer.h" #include "pe/pe.h" #include "pe/pe_section_flags.h" +#include "linker/base_ext/base_core.h" +#include "linker/base_ext/base_arena.h" +#include "linker/base_ext/base_arrays.h" #include "linker/hash_table.h" #include "base/base_inc.c" @@ -33,6 +36,9 @@ #include "coff/coff_lib_writer.c" #include "pe/pe.c" #include "linker/hash_table.c" +#include "linker/base_ext/base_core.c" +#include "linker/base_ext/base_arena.c" +#include "linker/base_ext/base_arrays.c" #include "linker/lnk_cmd_line.h" #include "linker/lnk_cmd_line.c" @@ -136,7 +142,9 @@ t_invoke_linker_with_time_out(U64 time_out, String8 cmdline) if (os_handle_match(linker_handle, os_handle_zero())) { fprintf(stderr, "unable to start process: %.*s\n", str8_varg(g_linker)); } else { - B32 was_joined = os_process_join_exit_code(linker_handle, time_out, &exit_code); + U64 exit_code_u64 = 0; + B32 was_joined = os_process_join(linker_handle, time_out, &exit_code_u64); + exit_code = (int)exit_code_u64; if (!was_joined) { os_process_kill(linker_handle); exit_code = T_LINKER_TIME_OUT_EXIT_CODE; @@ -744,31 +752,18 @@ exit:; } internal T_Result -t_weak_vs_weak(void) +t_link_undef(void) { Temp scratch = scratch_begin(0,0); T_Result result = T_Result_Fail; - String8 a_obj; + String8 undef_obj; { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("a")); - COFF_ObjSymbol *sect_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("_a"), 0, sect); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, sect_symbol); - a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); + undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); coff_obj_writer_release(&obj_writer); } - - String8 b_obj; - { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS, str8_lit("b")); - COFF_ObjSymbol *sect_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("_b"), 0, sect); - coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, sect_symbol); - b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); - coff_obj_writer_release(&obj_writer); - } - String8 entry_obj; { @@ -779,19 +774,968 @@ t_weak_vs_weak(void) }; COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); - COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("w")); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); coff_obj_writer_release(&obj_writer); } - if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file(str8_lit("b.obj"), b_obj)) { goto exit; } + if (!t_write_file(str8_lit("undef.obj"), undef_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // try linking unresolved symbol and see if linker picks up on that + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj undef.obj"); + if (linker_exit_code != LNK_Error_UnresolvedSymbol) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_link_unref_undef(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 undef_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("undef")); + undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { 0xc3 }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("undef.obj"), undef_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // try linking unreferenced unresolved symbol, this must link + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj undef.obj"); + if (linker_exit_code == 0) { goto exit; } + + result = T_Result_Pass; + exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_lib_vs_weak_lib(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code; + + // linker must pick weak symbol from a.obj + linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj entry.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x111; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + // linker must pick weak symbol from entry.obj + linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_lib_vs_weak_nolib(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // linker must pick weak symbol from entry.obj + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_lib_vs_weak_alias(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // linker must pick weak symbol from entry.obj + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_lib_vs_weak_antidep(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // linker must pick weak symbol from a.obj + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_alias_vs_weak_alias(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *qwe = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("qwe"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("sym"), COFF_WeakExt_SearchAlias, qwe); + a = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 b; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *ewq = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("ewq"), 0x222, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("sym"), COFF_WeakExt_SearchAlias, ewq); + b = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("sym")); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a)) { goto exit; } + if (!t_write_file(str8_lit("b.obj"), b)) { goto exit; } if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); + if (linker_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_alias_vs_weak_lib(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // linker must pick weak symbol from entry.obj + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); if (linker_exit_code != 0) { goto exit; } + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_alias_vs_weak_nolib(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // linker must pick weak symbol from entry.obj + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_alias_vs_weak_antidep(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + // linker must pick weak symbol from entry.obj + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_nolib_vs_weak_nolib(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_nolib_vs_weak_lib(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_nolib_vs_weak_alias(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != LNK_Error_MultiplyDefinedSymbol) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_nolib_vs_weak_antidep(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_antidep_vs_weak_antidep(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code; + + // linker must pick weak symbol from a.obj + linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj entry.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x111; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + // linker must pick weak symbol from entry.obj + linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_antidep_vs_weak_nolib(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_NoLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_antidep_vs_weak_lib(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x222; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_antidep_vs_weak_alias(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSymbol *q = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("q"), 0x111, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchAlias, q); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *e = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("e"), 0x222, COFF_SymStorageClass_External); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_AntiDependency, e); + coff_obj_writer_section_push_reloc_addr32(obj_writer, sect, 3, sym); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj"); + if (linker_exit_code != 0) { goto exit; } + + { + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *text_sect = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); + if (text_sect == 0) { goto exit; } + String8 text_data = str8_substr(exe, rng_1u64(text_sect->foff, text_sect->foff + text_sect->vsize)); + String8 imm = str8_substr(text_data, rng_1u64(3, 7)); + U32 expected = 0x111; + if (!str8_match(imm, str8_struct(&expected), 0)) { goto exit; } + } + result = T_Result_Pass; exit:; scratch_end(scratch); @@ -1100,77 +2044,100 @@ exit:; } internal T_Result -t_undef_weak(void) +t_undef_weak_lib(void) { Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - String8 entry_symbol_name = str8_lit("my_entry"); - String8 shared_symbol_name = str8_lit("foo"); - - U8 weak_payload[] = { 0xDE, 0xAD, 0xBE, 0xEF }; - String8 weak_obj_name = str8_lit("weak.obj"); + String8 weak; { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSymbol *b = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("b"), 0xc3000000, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("a"), COFF_WeakExt_SearchLibrary, b); + weak = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0,COFF_MachineType_X64); + COFF_ObjSymbol *sym = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("a")); + U8 text[4] = {0}; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, sym); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + entry = coff_obj_writer_serialize(scratch.arena, obj_writer); + } + + if (!t_write_file(str8_lit("weak.obj"), weak)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry)) { goto exit; } + + // undefined symbol must always replace weak symbol with search library + int linker_exit_code = t_invoke_linkerf("/subsystem:console /out:a.exe /entry:entry entry.obj weak.obj"); + if (linker_exit_code != LNK_Error_UnresolvedSymbol) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_undef_weak_search_alias(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 weak_obj; + { + U8 weak_payload[] = { 0xDE, 0xAD, 0xBE, 0xEF }; COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); COFF_ObjSection *weak_sect = t_push_data_section(obj_writer, str8_array_fixed(weak_payload)); COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("ptr")); - coff_obj_writer_push_symbol_weak(obj_writer, shared_symbol_name, COFF_WeakExt_SearchAlias, tag); - String8 weak_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("foo"), COFF_WeakExt_SearchAlias, tag); + weak_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); coff_obj_writer_release(&obj_writer); - if (!t_write_file(weak_obj_name, weak_obj)) { - goto exit; - } } - String8 ptr_obj_name = str8_lit("ptr.obj"); + String8 ptr_obj; { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); - COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, entry_symbol_name); + COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("entry")); coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("ptr"), COFF_WeakExt_SearchAlias, tag); - String8 ptr_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + ptr_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); coff_obj_writer_release(&obj_writer); - if (!t_write_file(ptr_obj_name, ptr_obj)) { - goto exit; - } } - U8 undef_obj_payload[] = { 0x00, 0x00, 0x00, 0x00 }; - String8 undef_obj_name = str8_lit("undef.obj"); + String8 undef_obj; { + U8 undef_obj_payload[] = { 0x00, 0x00, 0x00, 0x00 }; COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); COFF_ObjSection *undef_sect = t_push_data_section(obj_writer, str8_array_fixed(undef_obj_payload)); - COFF_ObjSymbol *undef_symbol = coff_obj_writer_push_symbol_undef(obj_writer, shared_symbol_name); + COFF_ObjSymbol *undef_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("foo")); coff_obj_writer_section_push_reloc(obj_writer, undef_sect, 0, undef_symbol, COFF_Reloc_X64_Addr32Nb); - String8 undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + undef_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); coff_obj_writer_release(&obj_writer); - if (!t_write_file(undef_obj_name, undef_obj)) { - goto exit; - } } - U8 entry_payload[] = {0xC3}; - String8 entry_obj_name = str8_lit("entry.obj"); + String8 entry_obj; { + U8 entry_payload[] = {0xC3}; COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); COFF_ObjSection *text_sect = t_push_text_section(obj_writer, str8_array_fixed(entry_payload)); - coff_obj_writer_push_symbol_extern(obj_writer, entry_symbol_name, 0, text_sect); - String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, text_sect); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } } - int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:%S /out:a.exe %S %S %S %S", entry_symbol_name, weak_obj_name, entry_obj_name, ptr_obj_name, undef_obj_name); + if (!t_write_file(str8_lit("weak.obj"), weak_obj)) { goto exit; } + if (!t_write_file(str8_lit("ptr.obj"), ptr_obj)) { goto exit; } + if (!t_write_file(str8_lit("undef.obj"), undef_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } - T_Linker link_ident = t_ident_linker(); - if (linker_exit_code != 0) { - goto exit; - } + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe weak.obj entry.obj ptr.obj undef.obj"); + if (linker_exit_code != 0) { goto exit; } result = T_Result_Pass; - exit:; scratch_end(scratch); return result; @@ -1929,9 +2896,9 @@ t_simple_lib_test(void) { COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); coff_lib_writer_push_obj(lib_writer, str8_lit("test.obj"), test_obj); - String8List test_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + String8 test_lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); coff_lib_writer_release(&lib_writer); - if (!t_write_file_list(test_lib_name, test_lib)) { + if (!t_write_file(test_lib_name, test_lib)) { goto exit; } } @@ -2151,7 +3118,7 @@ t_import_export(void) } #if OS_WINDOWS - BOOL is_dir_set = SetDllDirectoryA(g_wdir.str); + BOOL is_dir_set = SetDllDirectoryA((LPCSTR)g_wdir.str); AssertAlways(is_dir_set); HANDLE export_dll = LoadLibrary("export.dll"); @@ -3223,9 +4190,9 @@ t_include(void) COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); coff_lib_writer_push_obj(lib_writer, str8_lit("include.obj"), obj); - String8List lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + String8 lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); coff_lib_writer_release(&lib_writer); - if (!t_write_file_list(str8_lit("include.lib"), lib)) { goto exit; } + if (!t_write_file(str8_lit("include.lib"), lib)) { goto exit; } } { @@ -3463,8 +4430,8 @@ t_import_kernel32(void) str8_list_pushf(scratch.arena, &launch_opts.cmd_line, "%S/a.exe", g_wdir); OS_Handle handle = os_process_launch(&launch_opts); AssertAlways(!os_handle_match(handle, os_handle_zero())); - int exit_code = -1; - os_process_join_exit_code(handle, max_U64, &exit_code); + U64 exit_code = max_U64; + os_process_join(handle, max_U64, &exit_code); os_process_detach(handle); if (exit_code != 0) { goto exit; } @@ -3809,6 +4776,90 @@ exit:; return result; } +internal T_Result +t_first_member_header(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("8"), 0x8, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("1"), 0x1, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("9"), 0x9, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("7"), 0x7, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("4"), 0x4, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("5"), 0x5, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("2"), 0x2, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("3"), 0x3, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("6"), 0x6, COFF_SymStorageClass_External); + obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 lib; + { + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_obj(lib_writer, str8_lit("obj.obj"), obj); + lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 0); + coff_lib_writer_release(&lib_writer); + } + + t_write_file(str8_lit("test.lib"), lib); + t_write_entry_obj(); + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe test.lib entry.obj /include:1 /include:2 /include:3 /include:4 /include:5 /include:6 /include:7 /include:8 /include:9"); + if (linker_exit_code != 0) goto exit; + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_second_member_header(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("8"), 0x8, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("1"), 0x1, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("9"), 0x9, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("7"), 0x7, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("4"), 0x4, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("5"), 0x5, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("2"), 0x2, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("3"), 0x3, COFF_SymStorageClass_External); + coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("6"), 0x6, COFF_SymStorageClass_External); + obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 lib; + { + COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); + coff_lib_writer_push_obj(lib_writer, str8_lit("obj.obj"), obj); + lib = coff_lib_writer_serialize(scratch.arena, lib_writer, 0, 0, 1); + coff_lib_writer_release(&lib_writer); + } + + t_write_file(str8_lit("test.lib"), lib); + t_write_entry_obj(); + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe test.lib entry.obj /include:1 /include:2 /include:3 /include:4 /include:5 /include:6 /include:7 /include:8 /include:9"); + if (linker_exit_code != 0) goto exit; + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + internal T_Result t_opt_ref_dangling_section(void) { @@ -3863,7 +4914,7 @@ t_opt_ref_dangling_section(void) coff_obj_writer_release(&obj_writer); } - String8List b_lib; + String8 b_lib; { COFF_LibWriter *lib_writer = coff_lib_writer_alloc(); coff_lib_writer_push_obj(lib_writer, str8_lit("b.obj"), b_obj); @@ -3873,7 +4924,7 @@ t_opt_ref_dangling_section(void) if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } - if (!t_write_file_list(str8_lit("b.lib"), b_lib)) { goto exit; } + if (!t_write_file(str8_lit("b.lib"), b_lib)) { goto exit; } int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj a.obj b.lib"); if (linker_exit_code != 0) { goto exit; } @@ -3904,54 +4955,72 @@ entry_point(CmdLine *cmdline) static struct { char *label; T_Result (*r)(void); } target_array[] = { - { "simple_link_test", t_simple_link_test }, - { "machine_compat_check", t_machine_compat_check }, - { "out_of_bounds_section_number", t_out_of_bounds_section_number }, - { "merge", t_merge }, - { "undef_section", t_undef_section }, - { "undef_reloc_section", t_undef_reloc_section }, - { "weak_vs_weak", t_weak_vs_weak }, - { "weak_vs_common", t_weak_vs_common }, - { "abs_vs_weak", t_abs_vs_weak }, - { "abs_vs_regular", t_abs_vs_regular }, - { "abs_vs_common", t_abs_vs_common }, - { "abs_vs_abs", t_abs_vs_abs }, - { "undef_weak", t_undef_weak }, - { "sect_symbol", t_sect_symbol }, - { "weak_cycle", t_weak_cycle }, - { "weak_tag", t_weak_tag }, - { "find_merged_pdata", t_find_merged_pdata }, - { "section_sort", t_section_sort }, - { "flag_conf", t_flag_conf }, - { "invalid_bss", t_invalid_bss }, - { "common_block", t_common_block }, - { "base_relocs", t_base_relocs }, - { "simple_lib_test", t_simple_lib_test }, - { "sect_align", t_sect_align }, - { "image_base", t_image_base }, - { "comdat_any", t_comdat_any }, - { "comdat_no_duplicates", t_comdat_no_duplicates }, - { "comdat_same_size", t_comdat_same_size }, - { "comdat_exact_match", t_comdat_exact_match }, - { "comdat_largest", t_comdat_largest }, - { "comdat_associative", t_comdat_associative }, - { "comdat_associative_loop", t_comdat_associative_loop }, - { "comdat_associative_non_comdat", t_comdat_associative_non_comdat }, - { "comdat_associative_out_of_bounds", t_comdat_associative_out_of_bounds }, - { "comdat_with_offset", t_comdat_with_offset }, - { "reloc_against_removed_comdat", t_reloc_against_removed_comdat }, - { "alt_name", t_alt_name }, - { "include", t_include }, - { "communal_var_vs_regular_comdat", t_communal_var_vs_regular_comdat }, - { "communal_var_vs_regular", t_communal_var_vs_regular }, - { "import_kernel32", t_import_kernel32 }, - { "delay_import_user32", t_delay_import_user32 }, - { "delay_import", t_delay_import }, - { "empty_section", t_empty_section }, - { "removed_section", t_removed_section }, - { "function_pad_min", t_function_pad_min }, - //{ "opt_ref_dangling_section", t_opt_ref_dangling_section }, - //{ "import_export", t_import_export }, + { "simple_link_test", t_simple_link_test }, + { "machine_compat_check", t_machine_compat_check }, + { "out_of_bounds_section_number", t_out_of_bounds_section_number }, + { "merge", t_merge }, + { "undef_section", t_undef_section }, + { "undef_reloc_section", t_undef_reloc_section }, + { "link_undef", t_link_undef }, + { "link_unref_undef", t_link_unref_undef }, + { "weak_lib_vs_weak_lib", t_weak_lib_vs_weak_lib }, + { "weak_lib_vs_weak_nolib", t_weak_lib_vs_weak_nolib }, + { "weak_lib_vs_weak_alias", t_weak_lib_vs_weak_alias }, + { "weak_lib_vs_weak_antidep", t_weak_lib_vs_weak_antidep }, + { "weak_alias_vs_weak_alias", t_weak_alias_vs_weak_alias }, + { "weak_alias_vs_weak_lib", t_weak_alias_vs_weak_lib }, + { "weak_alias_vs_weak_nolib", t_weak_alias_vs_weak_nolib }, + { "weak_alias_vs_weak_antidep", t_weak_alias_vs_weak_antidep }, + { "weak_nolib_vs_weak_nolib", t_weak_nolib_vs_weak_nolib }, + { "weak_nolib_vs_weak_lib", t_weak_nolib_vs_weak_lib }, + { "weak_nolib_vs_weak_alias", t_weak_nolib_vs_weak_alias }, + { "weak_nolib_vs_weak_antidep", t_weak_nolib_vs_weak_antidep }, + { "weak_antidep_vs_weak_antidep", t_weak_antidep_vs_weak_antidep }, + { "weak_antidep_vs_weak_nolib", t_weak_antidep_vs_weak_nolib }, + { "weak_antidep_vs_weak_lib", t_weak_antidep_vs_weak_lib }, + { "weak_antidep_vs_weak_alias", t_weak_antidep_vs_weak_alias }, + { "weak_vs_common", t_weak_vs_common }, + { "abs_vs_weak", t_abs_vs_weak }, + { "abs_vs_regular", t_abs_vs_regular }, + { "abs_vs_common", t_abs_vs_common }, + { "abs_vs_abs", t_abs_vs_abs }, + { "undef_weak_lib", t_undef_weak_lib }, + { "undef_weak_search_alias", t_undef_weak_search_alias }, + { "sect_symbol", t_sect_symbol }, + { "weak_cycle", t_weak_cycle }, + { "weak_tag", t_weak_tag }, + { "find_merged_pdata", t_find_merged_pdata }, + { "section_sort", t_section_sort }, + { "flag_conf", t_flag_conf }, + { "invalid_bss", t_invalid_bss }, + { "common_block", t_common_block }, + { "base_relocs", t_base_relocs }, + { "simple_lib_test", t_simple_lib_test }, + { "sect_align", t_sect_align }, + { "image_base", t_image_base }, + { "comdat_any", t_comdat_any }, + { "comdat_no_duplicates", t_comdat_no_duplicates }, + { "comdat_same_size", t_comdat_same_size }, + { "comdat_exact_match", t_comdat_exact_match }, + { "comdat_largest", t_comdat_largest }, + { "comdat_associative", t_comdat_associative }, + { "comdat_associative_loop", t_comdat_associative_loop }, + { "comdat_associative_non_comdat", t_comdat_associative_non_comdat }, + { "comdat_associative_out_of_bounds", t_comdat_associative_out_of_bounds }, + { "comdat_with_offset", t_comdat_with_offset }, + { "reloc_against_removed_comdat", t_reloc_against_removed_comdat }, + { "alt_name", t_alt_name }, + { "include", t_include }, + { "communal_var_vs_regular_comdat", t_communal_var_vs_regular_comdat }, + { "communal_var_vs_regular", t_communal_var_vs_regular }, + { "import_kernel32", t_import_kernel32 }, + { "delay_import_user32", t_delay_import_user32 }, + { "delay_import", t_delay_import }, + { "empty_section", t_empty_section }, + { "removed_section", t_removed_section }, + { "function_pad_min", t_function_pad_min }, + { "first_member_header", t_first_member_header }, + { "second_member_header", t_second_member_header }, }; // @@ -4076,7 +5145,7 @@ entry_point(CmdLine *cmdline) // { U64 max_label_size = 0; - for (U64 i = 0; i < ArrayCount(target_array); i += 1) { max_label_size = Max(max_label_size, cstring8_length(target_array[i].label)); } + for (U64 i = 0; i < ArrayCount(target_array); i += 1) { max_label_size = Max(max_label_size, cstring8_length((U8*)target_array[i].label)); } U64 dots_min = 10; U64 dots_size = max_label_size+dots_min; @@ -4124,7 +5193,7 @@ entry_point(CmdLine *cmdline) T_Result result = t_run(target_array[target_idx].r); - U64 dots_count = (max_label_size - cstring8_length(target_array[target_idx].label)) + dots_min; + U64 dots_count = (max_label_size - cstring8_length((U8*)target_array[target_idx].label)) + dots_min; String8 msg = push_str8f(scratch.arena, "%s%.*s%s", target_array[target_idx].label, dots_count, dots, t_string_from_result(result)); // run progress diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 8bd50c8f..333d1b47 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -9,12 +9,6 @@ thread_static UI_State *ui_state = 0; //////////////////////////////// //~ rjf: Basic Type Functions -#if !defined(XXH_IMPLEMENTATION) -# define XXH_IMPLEMENTATION -# define XXH_STATIC_LINKING_ONLY -# include "third_party/xxHash/xxhash.h" -#endif - internal String8 ui_hash_part_from_key_string(String8 string) { @@ -56,14 +50,12 @@ ui_key_make(U64 v) internal UI_Key ui_key_from_string(UI_Key seed_key, String8 string) { - ProfBeginFunction(); UI_Key result = {0}; if(string.size != 0) { String8 hash_part = ui_hash_part_from_key_string(string); result.u64[0] = u64_hash_from_seed_str8(seed_key.u64[0], hash_part); } - ProfEnd(); return result; } @@ -2416,7 +2408,6 @@ ui_color_from_tags_key_name(UI_Key key, String8 name) internal UI_Box * ui_build_box_from_key(UI_BoxFlags flags, UI_Key key) { - ProfBeginFunction(); ui_state->build_box_count += 1; //- rjf: grab active parent @@ -2622,7 +2613,6 @@ ui_build_box_from_key(UI_BoxFlags flags, UI_Key key) } //- rjf: return - ProfEnd(); return box; } @@ -2646,8 +2636,6 @@ ui_active_seed_key(void) internal UI_Box * ui_build_box_from_string(UI_BoxFlags flags, String8 string) { - ProfBeginFunction(); - //- rjf: grab active parent UI_Box *parent = ui_top_parent(); @@ -2662,7 +2650,6 @@ ui_build_box_from_string(UI_BoxFlags flags, String8 string) } //- rjf: return - ProfEnd(); return box; } @@ -2684,7 +2671,6 @@ ui_build_box_from_stringf(UI_BoxFlags flags, char *fmt, ...) internal void ui_box_equip_display_string(UI_Box *box, String8 string) { - ProfBeginFunction(); box->string = push_str8_copy(ui_build_arena(), string); box->flags |= UI_BoxFlag_HasDisplayString; Vec4F32 text_color = box->text_color; @@ -2721,7 +2707,6 @@ ui_box_equip_display_string(UI_Box *box, String8 string) } scratch_end(scratch); } - ProfEnd(); } internal void @@ -2822,7 +2807,6 @@ ui_box_char_pos_from_xy(UI_Box *box, Vec2F32 xy) internal UI_Signal ui_signal_from_box(UI_Box *box) { - ProfBeginFunction(); B32 is_focus_hot = box->flags & UI_BoxFlag_FocusHot && !(box->flags & UI_BoxFlag_FocusHotDisabled); UI_Signal sig = {box}; sig.event_flags |= os_get_modifiers(); @@ -2843,7 +2827,6 @@ ui_signal_from_box(UI_Box *box) //- rjf: determine if we're under the context menu or not // B32 ctx_menu_is_ancestor = 0; - ProfScope("check context menu ancestor") { for(UI_Box *parent = box; !ui_box_is_nil(parent); parent = parent->parent) { @@ -3230,7 +3213,6 @@ ui_signal_from_box(UI_Box *box) } } - ProfEnd(); return sig; }