Skip to content

Commit e1575f8

Browse files
committed
Merge branch 'dev' of https://github.com/EpicGames/raddebugger into dev
2 parents 0b50d87 + bb86072 commit e1575f8

File tree

155 files changed

+78660
-1744
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

155 files changed

+78660
-1744
lines changed

.github/workflows/builds.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ jobs:
2828
call build rdi_from_pdb clang debug || exit /b 1
2929
call build rdi_from_dwarf clang debug || exit /b 1
3030
call build rdi_dump clang debug || exit /b 1
31+
call build radlink msvc debug || exit /b 1
32+
call build radlink clang debug || exit /b 1

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,45 @@ like remote debugging, porting to different architectures, further improving
158158
the debugger's features (like improving the visualization engine), and so on.
159159
But for now, we're mostly focused on those first two phases.
160160

161+
---
162+
163+
# The RAD Linker
164+
165+
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).
166+
167+
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.
168+
169+
The command line syntax is fully compatible with MSVC and you can get a full list of implemented switches from `/help`.
170+
171+
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.
172+
173+
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`.
174+
175+
We also have support for large memory pages, which, when enabled, reduce link time by
176+
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.
177+
178+
## Short Term Roadmap
179+
- Porting linker to Linux (for Windows executables, just running on Linux).
180+
- Debug info features
181+
- Get DWARF debug info converter up-and-running.
182+
- Smooth out rough edges in RADDBGI builder.
183+
- Improve build speed further (especially for tiny and mid sizes projects).
184+
- Other features to come
185+
- Dead-code-elimination via `/opt:ref`.
186+
- Link Time Optimizations with the help of clang (we won't support LTCG from MSVC compiler since it is undocumented).
187+
188+
## To build the RAD Linker
189+
- Setup development environment, [see](#Development-Setup-Instructions)
190+
- Run `build linker release` or if you have clang installed `build linker release clang`. We favor latter option for better code generation.
191+
192+
If build was successful linker executable will be placed in `build` folder under `radlink.exe`.
193+
194+
## Benchmarks
195+
196+
![AMD Ryzen Threadripper PRO 3995WX 64-Cores, 256 GiB RAM (Windows x64)](https://github.com/user-attachments/assets/39d95fdf-9f0b-45d3-9fb8-90d8ac624168)
197+
198+
---
199+
161200
## Top-Level Directory Descriptions
162201

163202
- `data`: Small binary files which are used when building, either to embed

build.bat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ if "%rdi_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_fr
104104
if "%rdi_from_dwarf%"=="1" set didbuild=1 && %compile% ..\src\rdi_from_dwarf\rdi_from_dwarf.c %compile_link% %out%rdi_from_dwarf.exe || exit /b 1
105105
if "%rdi_dump%"=="1" set didbuild=1 && %compile% ..\src\rdi_dump\rdi_dump_main.c %compile_link% %out%rdi_dump.exe || exit /b 1
106106
if "%rdi_breakpad_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_breakpad_from_pdb\rdi_breakpad_from_pdb_main.c %compile_link% %out%rdi_breakpad_from_pdb.exe || exit /b 1
107+
if "%radlink%"=="1" set didbuild=1 && %compile% ..\src\linker\lnk.c %compile_link% %out%radlink.exe || exit /b 1
107108
if "%tester%"=="1" set didbuild=1 && %compile% ..\src\tester\tester_main.c %compile_link% %out%tester.exe || exit /b 1
108109
if "%ryan_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1
109110
if "%mule_main%"=="1" set didbuild=1 && del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1

src/base/base_arena.c

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ arena_alloc_(ArenaParams *params)
5858
arena->pos = ARENA_HEADER_SIZE;
5959
arena->cmt = commit_size;
6060
arena->res = reserve_size;
61+
#if ARENA_FREE_LIST
62+
arena->free_size = 0;
63+
arena->free_last = 0;
64+
#endif
6165
AsanPoisonMemoryRegion(base, commit_size);
6266
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
6367
return arena;
@@ -85,18 +89,45 @@ arena_push(Arena *arena, U64 size, U64 align)
8589
// rjf: chain, if needed
8690
if(current->res < pos_pst && !(arena->flags & ArenaFlag_NoChain))
8791
{
88-
U64 res_size = current->res_size;
89-
U64 cmt_size = current->cmt_size;
90-
if(size + ARENA_HEADER_SIZE > res_size)
92+
Arena *new_block = 0;
93+
94+
#if ARENA_FREE_LIST
95+
Arena *prev_block;
96+
for(new_block = arena->free_last, prev_block = 0; new_block != 0; prev_block = new_block, new_block = new_block->prev)
9197
{
92-
res_size = size + ARENA_HEADER_SIZE;
93-
cmt_size = size + ARENA_HEADER_SIZE;
98+
if(new_block->res >= AlignPow2(size, align))
99+
{
100+
if(prev_block)
101+
{
102+
prev_block->prev = new_block->prev;
103+
}
104+
else
105+
{
106+
arena->free_last = new_block->prev;
107+
}
108+
arena->free_size -= new_block->res_size;
109+
AsanUnpoisonMemoryRegion((U8*)new_block + ARENA_HEADER_SIZE, new_block->res_size - ARENA_HEADER_SIZE);
110+
break;
111+
}
94112
}
95-
Arena *new_block = arena_alloc(.reserve_size = res_size,
96-
.commit_size = cmt_size,
97-
.flags = current->flags);
98-
new_block->base_pos = current->base_pos + current->res;
99-
SLLStackPush_N(arena->current, new_block, prev);
113+
#endif
114+
115+
if(new_block == 0)
116+
{
117+
U64 res_size = current->res_size;
118+
U64 cmt_size = current->cmt_size;
119+
if(size + ARENA_HEADER_SIZE > res_size)
120+
{
121+
res_size = size + ARENA_HEADER_SIZE;
122+
cmt_size = size + ARENA_HEADER_SIZE;
123+
}
124+
new_block = arena_alloc(.reserve_size = res_size,
125+
.commit_size = cmt_size,
126+
.flags = current->flags);
127+
new_block->base_pos = current->base_pos + current->res;
128+
SLLStackPush_N(arena->current, new_block, prev);
129+
}
130+
100131
current = new_block;
101132
pos_pre = AlignPow2(current->pos, align);
102133
pos_pst = pos_pre + size;
@@ -155,11 +186,23 @@ arena_pop_to(Arena *arena, U64 pos)
155186
{
156187
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, pos);
157188
Arena *current = arena->current;
189+
190+
#if ARENA_FREE_LIST
191+
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
192+
{
193+
prev = current->prev;
194+
current->pos = ARENA_HEADER_SIZE;
195+
arena->free_size += current->res_size;
196+
SLLStackPush_N(arena->free_last, current, prev);
197+
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res_size - ARENA_HEADER_SIZE);
198+
}
199+
#else
158200
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
159201
{
160202
prev = current->prev;
161203
os_release(current, current->res);
162204
}
205+
#endif
163206
arena->current = current;
164207
U64 new_pos = big_pos - current->base_pos;
165208
AssertAlways(new_pos <= current->pos);

src/base/base_arena.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
////////////////////////////////
88
//~ rjf: Constants
99

10-
#define ARENA_HEADER_SIZE 64
10+
#if ARENA_FREE_LIST
11+
# define ARENA_HEADER_SIZE 128
12+
#else
13+
# define ARENA_HEADER_SIZE 64
14+
#endif
1115

1216
////////////////////////////////
1317
//~ rjf: Types
@@ -40,6 +44,10 @@ struct Arena
4044
U64 pos;
4145
U64 cmt;
4246
U64 res;
47+
#if ARENA_FREE_LIST
48+
U64 free_size;
49+
Arena *free_last;
50+
#endif
4351
};
4452
StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check);
4553

src/base/base_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ enum
8686

8787
typedef enum PathStyle
8888
{
89+
PathStyle_Null,
8990
PathStyle_Relative,
9091
PathStyle_WindowsAbsolute,
9192
PathStyle_UnixAbsolute,

src/codeview/codeview.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ cv_numeric_from_data_range(U8 *first, U8 *opl)
8080
return result;
8181
}
8282

83+
internal U64
84+
cv_read_numeric(String8 data, U64 offset, CV_NumericParsed *out)
85+
{
86+
*out = cv_numeric_from_data_range(data.str + offset, data.str + data.size);
87+
return out->encoded_size;
88+
}
89+
8390
internal B32
8491
cv_numeric_fits_in_u64(CV_NumericParsed *num)
8592
{
@@ -411,7 +418,7 @@ cv_leaf_from_data(Arena *arena, String8 leaf_data, CV_TypeId itype_first)
411418
//~ CodeView C13 Parser Functions
412419

413420
internal CV_C13Parsed *
414-
cv_c13_parsed_from_data(Arena *arena, String8 c13_data, PDB_Strtbl *strtbl, PDB_CoffSectionArray *sections)
421+
cv_c13_parsed_from_data(Arena *arena, String8 c13_data, String8 strtbl, COFF_SectionHeaderArray sections)
415422
{
416423
ProfBeginFunction();
417424

@@ -488,10 +495,10 @@ cv_c13_parsed_from_data(Arena *arena, String8 c13_data, PDB_Strtbl *strtbl, PDB_
488495
B32 has_cols = !!(hdr->flags & CV_C13SubSecLinesFlag_HasColumns);
489496
U64 secrel_off = hdr->sec_off;
490497
U64 secrel_opl = secrel_off + hdr->len;
491-
U64 sec_base_off = sections->sections[sec_idx - 1].voff;
498+
U64 sec_base_off = sections.v[sec_idx - 1].voff;
492499

493500
// rjf: bad section index -> skip
494-
if(sec_idx < 1 || sections->count < sec_idx)
501+
if(sec_idx < 1 || sections.count < sec_idx)
495502
{
496503
continue;
497504
}
@@ -511,7 +518,8 @@ cv_c13_parsed_from_data(Arena *arena, String8 c13_data, PDB_Strtbl *strtbl, PDB_
511518
{
512519
CV_C13Checksum *checksum = (CV_C13Checksum*)(c13_data.str + file_chksms->off + file_off);
513520
U32 name_off = checksum->name_off;
514-
file_name = pdb_strtbl_string_from_off(strtbl, name_off);
521+
file_name = str8_cstring_capped((char*)(strtbl.str + name_off),
522+
(char*)(strtbl.str + strtbl.size));
515523
}
516524

517525
// array layouts
@@ -587,7 +595,8 @@ cv_c13_parsed_from_data(Arena *arena, String8 c13_data, PDB_Strtbl *strtbl, PDB_
587595
{
588596
CV_C13Checksum *checksum = (CV_C13Checksum*)(c13_data.str + file_chksms->off + hdr->file_off);
589597
U32 name_off = checksum->name_off;
590-
file_name = pdb_strtbl_string_from_off(strtbl, name_off);
598+
file_name = str8_cstring_capped((char*)(strtbl.str + name_off),
599+
(char*)(strtbl.str + strtbl.size));
591600
}
592601

593602
// rjf: parse extra files
@@ -608,6 +617,7 @@ cv_c13_parsed_from_data(Arena *arena, String8 c13_data, PDB_Strtbl *strtbl, PDB_
608617
SLLQueuePush(node->inlinee_lines_first, node->inlinee_lines_last, n);
609618
n->v.inlinee = hdr->inlinee;
610619
n->v.file_name = file_name;
620+
n->v.file_off = hdr->file_off;
611621
n->v.first_source_ln = hdr->first_source_ln;
612622
n->v.extra_file_count = extra_file_count;
613623
n->v.extra_files = extra_files;

0 commit comments

Comments
 (0)