From c5dc7bee3e243b8b481168b2eac015ea5a972fe4 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Fri, 10 Apr 2026 16:53:01 -0700 Subject: [PATCH] fix info stream layout --- src/linker/base_ext/base_bit_array.c | 231 +------- src/linker/base_ext/base_bit_array.h | 16 +- src/linker/pdb_ext/pdb_builder.c | 281 ++++----- src/linker/pdb_ext/pdb_builder.h | 21 +- src/third_party/martins_bitscan/bitscan.h | 664 ++++++++++++++++++++++ src/torture/torture_base.c | 25 + src/torture/torture_main.c | 8 + 7 files changed, 818 insertions(+), 428 deletions(-) create mode 100644 src/third_party/martins_bitscan/bitscan.h diff --git a/src/linker/base_ext/base_bit_array.c b/src/linker/base_ext/base_bit_array.c index 8c0a8d1e..3cb4f7c4 100644 --- a/src/linker/base_ext/base_bit_array.c +++ b/src/linker/base_ext/base_bit_array.c @@ -1,234 +1,25 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +#include "third_party/martins_bitscan/bitscan.h" + internal U32Array -bit_array_init32(Arena *arena, U64 word_count) +bit_array_init32(Arena *arena, U64 bit_count) { - U32Array result; - result.count = CeilIntegerDiv(word_count, 32); - result.v = push_array(arena, U32, word_count); - return result; + U64 count = CeilIntegerDiv(bit_count, 32); + return (U32Array){ .count = count, .v = push_array(arena, U32, count) }; } internal U64 bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state) { - Assert(lo < bit_array.count*32); - Assert(hi <= bit_array.count*32); - Assert(lo <= hi); - Assert(state == 0 || state == 1); - - U64 word_lo = lo / 32; - U64 word_hi = CeilIntegerDiv(hi, 32) - 1; - - U64 word_idx = word_lo; - U64 bit_idx = 0; - - U64 scan_count = hi - lo; - if (scan_count < 32) { - U64 bit_lo = lo % 32; - U64 bit_hi = hi % 32; - U64 word = bit_array.v[word_idx]; - word ^= state - 1; - word &= (1U << bit_hi) - (1U << bit_lo); - if (word) { - bit_idx = ctz32(word); - goto exit; - } - } else { - U32 first_word = bit_array.v[word_idx]; - first_word ^= state - 1; - first_word &= ~0u << (lo % 32); - if (first_word) { - bit_idx = ctz32(first_word); - goto exit; - } - - for (word_idx += 1; word_idx < word_hi; word_idx += 1) { - U32 word = bit_array.v[word_idx]; - word ^= state - 1; - if (word != 0) { - bit_idx = ctz32(word); - goto exit; - } - } - - U64 bit_hi = hi - (word_idx * 32); - U32 last_word = bit_array.v[word_idx]; - last_word ^= state - 1; - last_word &= (1 << bit_hi) - 1; - if (last_word) { - bit_idx = ctz32(last_word); - goto exit; - } - } - - word_idx = 0; - bit_idx = max_U32; - - exit:; - - U64 result = word_idx * 32 + bit_idx; - return result; + return bitscan_lsb_index32(bit_array.v, lo, hi, state); } internal U64 bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state) { - Assert(lo <= hi); - Assert(state == 0 || state == 1); - - S64 word_lo = lo / 32; - S64 word_hi = CeilIntegerDiv(hi, 32) - 1; - - S64 word_idx = word_hi; - S64 bit_idx = -1; - - U64 scan_count = hi - lo; - if (scan_count < 32) { - S64 bit_lo = lo % 32; - S64 bit_hi = bit_lo + scan_count; - U32 word = bit_array.v[word_idx]; - for (bit_idx = bit_hi; bit_idx >= bit_lo; bit_idx -= 1) { - U32 bit = ExtractBit(word, bit_idx); - if (bit == state) { - goto exit; - } - } - } else { - U32 last_word = bit_array.v[word_idx]; - S64 bit_hi = hi % 32; - for (bit_idx = bit_hi; bit_idx >= 0; bit_idx -= 1) { - U32 bit = ExtractBit(last_word, bit_idx); - if (bit == state) { - goto exit; - } - } - - for (word_idx -= 1; word_idx > word_lo; word_idx -= 1) { - U32 word = bit_array.v[word_idx]; - for (bit_idx = 32 - 1; bit_idx >= 0; bit_idx -= 1) { - U32 bit = ExtractBit(word, bit_idx); - if (bit == state) { - goto exit; - } - } - } - - U32 first_word = bit_array.v[word_idx]; - S64 bit_lo = lo % 32; - for (bit_idx = 32 - 1; bit_idx >= bit_lo; bit_idx -= 1) { - U32 bit = ExtractBit(first_word, bit_idx); - if (bit == state) { - goto exit; - } - } - } - - word_idx = 0; - bit_idx = max_U32; - - exit:; - - S64 result_s64 = word_idx * 32 + bit_idx; - U64 result_u64 = (U64)result_s64; - return result_u64; -} - -internal Rng1U64 -bit_array_scan_left_to_right32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count) -{ - Rng1U64 result = rng_1u64(max_U64, max_U64); - - U64 curr_count = 0, rover = lo; - while (curr_count < in_row_count) { - rover = bit_array_scan_left_to_right32(bit_array, rover, hi, state); - - // no more bits in range - if (rover >= hi) { - break; - } - - // set first match - if (result.v[0] == max_U64) { - result = rng_1u64(rover, rover); - continue; - } - - // reset on non-contiguous range - B32 is_bit_index_not_adjoined = (result.v[0] + 1 < rover); - if (is_bit_index_not_adjoined) { - curr_count = 0; - result = rng_1u64(max_U64, max_U64); - continue; - } - - // advance - result.v[1] = rover; - curr_count -= 1; - } - - // did we allocate enough bits? - if (curr_count != in_row_count) { - result = rng_1u64(max_U64, max_U64); - } - - return result; -} - -internal Rng1U64 -bit_array_scan_right_to_left32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count) -{ - Rng1U64 result = rng_1u64(max_U64, max_U64); - - U64 curr_count = 0, rover = lo; - while (curr_count < in_row_count) { - rover = bit_array_scan_right_to_left32(bit_array, lo, rover, state); - - // no more bits in range - if (rover >= hi) { - break; - } - - // set first match - if (result.v[0] == max_U64) { - result = rng_1u64(rover, rover); - continue; - } - - // reset on non-contiguous range - B32 is_bit_index_not_adjoined = (result.v[0] + 1 < rover); - if (is_bit_index_not_adjoined) { - curr_count = 0; - result = rng_1u64(max_U64, max_U64); - continue; - } - - // advance - result.v[0] = rover; - curr_count -= 1; - } - - // did we allocate enough bits? - if (curr_count != in_row_count) { - result = rng_1u64(max_U64, max_U64); - } - - return result; -} - -internal U64 -bit_array_find_next_unset_bit32(U32Array bit_array) -{ - U64 result = bit_array_scan_left_to_right32(bit_array, 0, bit_array.count*32, 0); - return result; -} - -internal U64 -bit_array_find_next_set_bit32(U32Array bit_array) -{ - U64 result = bit_array_scan_left_to_right32(bit_array, 0, bit_array.count*32, 1); - return result; + return bitscan_msb_index32(bit_array.v, lo, hi, state); } internal void @@ -244,14 +35,6 @@ 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) -{ - for (U64 idx = range.min ; idx < range.max; idx += 1) { - bit_array_set_bit32(bit_array, idx, state); - } -} - internal U32 bit_array_get_bit32(U32Array bit_array, U64 idx) { diff --git a/src/linker/base_ext/base_bit_array.h b/src/linker/base_ext/base_bit_array.h index 995facd7..5c76ef32 100644 --- a/src/linker/base_ext/base_bit_array.h +++ b/src/linker/base_ext/base_bit_array.h @@ -3,16 +3,10 @@ #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 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 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 void bit_array_set_bit32 (U32Array bit_array, U64 idx, B32 state); +internal U32 bit_array_get_bit32 (U32Array bit_array, U64 idx); diff --git a/src/linker/pdb_ext/pdb_builder.c b/src/linker/pdb_ext/pdb_builder.c index 63c8d163..768d3dd6 100644 --- a/src/linker/pdb_ext/pdb_builder.c +++ b/src/linker/pdb_ext/pdb_builder.c @@ -21,7 +21,6 @@ pdb_hash_table_alloc(PDB_HashTable *ht, U32 max) ht->deleted_bits = bit_array_init32(ht->arena, max); ht->max = max; ht->count = 0; - bit_array_set_bit_range32(ht->deleted_bits, rng_1u64(0, max), 1); ProfEnd(); } @@ -148,51 +147,48 @@ pdb_hash_table_from_data(PDB_HashTable *ht, return error; } +internal int +pdb_hash_table_bucket_is_before(void *raw_a, void *raw_b) +{ + PDB_HashTableBucket *a = *(PDB_HashTableBucket **)raw_a, *b = *(PDB_HashTableBucket **)raw_b; + return a->insert_idx < b->insert_idx; +} + internal String8 -pdb_data_from_hash_table(Arena *arena, - PDB_HashTable *ht, - B32 has_local_data, - PDB_HashTablePackFunc *pack_func, - void *pack_ud) +pdb_data_from_hash_table(Arena *arena, PDB_HashTable *ht, PDB_HashTablePackFunc *pack_func, void *pack_ud) { ProfBeginFunction(); Temp scratch = scratch_begin(&arena, 1); - String8Array key_arr = {0}; - String8Array value_arr = {0}; - pdb_hash_table_get_present_keys_and_values(scratch.arena, ht, &key_arr, &value_arr); + String8List kv_srl = {0}; str8_serial_begin(scratch.arena, &kv_srl); + PDB_HashTableBucket **buckets = pdb_hash_table_get_present_buckets(scratch.arena, ht); + for EachIndex(i, ht->count) { pack_func(scratch.arena, &kv_srl, buckets[i], buckets[i]->key, buckets[i]->value, pack_ud); } - String8List local_data_srl = {0}; - String8List key_value_srl = {0}; - str8_serial_begin(scratch.arena, &local_data_srl); - str8_serial_begin(scratch.arena, &key_value_srl); + // compute count of present words that are needed + U64 present_word_count = 0; + U64 present_msb = bit_array_scan_right_to_left32(ht->present_bits, 0, ht->present_bits.count*32, 1); + if (present_msb < ht->present_bits.count*32) { present_word_count = present_msb / 32 + 1; } - for (U64 i = 0; i < ht->count; i += 1) { - String8 key = key_arr.v[i]; - String8 value = value_arr.v[i]; - pack_func(scratch.arena, &local_data_srl, &key_value_srl, key, value, pack_ud); - } + // compute count of deleted words that are needed + U64 deleted_word_count = 0; + U64 deleted_msb = bit_array_scan_right_to_left32(ht->deleted_bits, 0, ht->deleted_bits.count*32, 1); + if (deleted_msb < ht->deleted_bits.count*32) { deleted_word_count = deleted_msb / 32 + 1; } - // serialize hash table - String8List srl = {0}; - str8_serial_begin(scratch.arena, &srl); - if (has_local_data) { - U32 local_data_size32 = safe_cast_u32(local_data_srl.total_size); - str8_serial_push_u32(scratch.arena, &srl, local_data_size32); - str8_list_concat_in_place(&srl, &local_data_srl); - } - str8_serial_push_u32(scratch.arena, &srl, ht->count); - str8_serial_push_u32(scratch.arena, &srl, ht->max); - str8_serial_push_u32(scratch.arena, &srl, ht->present_bits.count); - str8_serial_push_array(scratch.arena, &srl, &ht->present_bits.v[0], ht->present_bits.count); - str8_serial_push_u32(scratch.arena, &srl, ht->deleted_bits.count); - str8_serial_push_array(scratch.arena, &srl, &ht->deleted_bits.v[0], ht->deleted_bits.count); - str8_list_concat_in_place(&srl, &key_value_srl); - String8 data = str8_serial_end(arena, &srl); + // write hash table + String8List ht_srl = {0}; str8_serial_begin(scratch.arena, &ht_srl); + str8_serial_push_u32 (scratch.arena, &ht_srl, ht->count); + str8_serial_push_u32 (scratch.arena, &ht_srl, ht->max); + str8_serial_push_u32 (scratch.arena, &ht_srl, present_word_count); + str8_serial_push_array(scratch.arena, &ht_srl, &ht->present_bits.v[0], present_word_count); + str8_serial_push_u32 (scratch.arena, &ht_srl, deleted_word_count); + str8_serial_push_array(scratch.arena, &ht_srl, &ht->deleted_bits.v[0], deleted_word_count); + str8_list_concat_in_place(&ht_srl, &kv_srl); + + String8 result = str8_serial_end(arena, &ht_srl); scratch_end(scratch); ProfEnd(); - return data; + return result; } internal void @@ -230,8 +226,9 @@ pdb_hash_table_try_set(PDB_HashTable *ht, String8 key, String8 value) B32 is_present = pdb_hash_table_is_present(ht, ibucket); if ( ! is_present) { PDB_HashTableBucket *bucket = &ht->bucket_arr[ibucket]; - bucket->key = push_str8_copy(ht->arena, key); - bucket->value = push_str8_copy(ht->arena, value); + bucket->key = push_str8_copy(ht->arena, key); + bucket->value = push_str8_copy(ht->arena, value); + bucket->insert_idx = ht->count; bit_array_set_bit32(ht->present_bits, ibucket, 1); bit_array_set_bit32(ht->deleted_bits, ibucket, 0); @@ -254,7 +251,7 @@ pdb_hash_table_set(PDB_HashTable *ht, String8 key, String8 value) // should resize? U64 load_factor = pdb_hash_table_compute_load_factor(ht->max); if (ht->count + 1 >= load_factor) { - pdb_hash_table_grow(ht, ht->max * 2); + pdb_hash_table_grow(ht, load_factor * 2); } // set new item @@ -328,6 +325,21 @@ pdb_hash_table_is_deleted(PDB_HashTable *ht, U32 k) return bit_array_is_bit_set(ht->deleted_bits, k); } +internal PDB_HashTableBucket ** +pdb_hash_table_get_present_buckets(Arena *arena, PDB_HashTable *ht) +{ + U64 result_count = 0; + PDB_HashTableBucket **result = push_array(arena, PDB_HashTableBucket *, ht->count); + for EachIndex(bucket_idx, ht->max) { + if (bit_array_is_bit_set(ht->present_bits, bucket_idx)) { + PDB_HashTableBucket *bucket = &ht->bucket_arr[bucket_idx]; + Assert(result_count < ht->count); + result[result_count++] = bucket; + } + } + return result; +} + internal void pdb_hash_table_get_present_keys_and_values(Arena *arena, PDB_HashTable *ht, String8Array *keys_out, String8Array *values_out) { @@ -422,12 +434,7 @@ PDB_HASH_TABLE_PACK_FUNC(pdb_named_stream_ht_pack) { Assert(!ud); Assert(value.size == sizeof(U32)); - - U64 key_data_offset = local_data_srl->total_size; - str8_serial_push_cstr(arena, local_data_srl, key); - - U32 key_data_offset32 = safe_cast_u32(key_data_offset); - str8_serial_push_u32(arena, key_value_srl, key_data_offset32); + str8_serial_push_u32(arena, key_value_srl, bucket->key_offset); str8_serial_push_string(arena, key_value_srl, value); } @@ -436,7 +443,7 @@ PDB_HASH_TABLE_PACK_FUNC(pdb_hash_adj_ht_pack) { Assert(value.size == sizeof(CV_TypeIndex)); - PDB_StringTable *strtab = (PDB_StringTable*)ud; + PDB_StringTable *strtab = ud; PDB_StringIndex string_idx = PDB_INVALID_STRING_INDEX; B32 is_found = pdb_strtab_search(strtab, key, &string_idx); @@ -453,7 +460,7 @@ PDB_HASH_TABLE_PACK_FUNC(pdb_src_header_block_ht_pack) { Assert(value.size == sizeof(PDB_SrcHeaderBlockEntry)); - PDB_StringTable *strtab = (PDB_StringTable*)ud; + PDB_StringTable *strtab = ud; PDB_StringIndex path_idx = 0; B32 is_found = pdb_strtab_search(strtab, key, &path_idx); @@ -488,22 +495,44 @@ pdb_named_stream_ht_from_data(PDB_HashTable *ht, String8 data, U64 *read_bytes_o internal String8 pdb_data_from_hash_adj_hash_table(Arena *arena, PDB_HashTable *ht, PDB_StringTable *strtab) { - String8 data = pdb_data_from_hash_table(arena, ht, 0, pdb_hash_adj_ht_pack, strtab); - return data; + return pdb_data_from_hash_table(arena, ht, pdb_hash_adj_ht_pack, strtab); } internal String8 pdb_data_from_src_header_block_ht(Arena *arena, PDB_HashTable *ht, PDB_StringTable *strtab) { - String8 data = pdb_data_from_hash_table(arena, ht, 0, pdb_src_header_block_ht_pack, strtab); - return data; + return pdb_data_from_hash_table(arena, ht, pdb_src_header_block_ht_pack, strtab); } internal String8 pdb_data_from_named_stream_ht(Arena *arena, PDB_HashTable *ht) { - String8 data = pdb_data_from_hash_table(arena, ht, 1, pdb_named_stream_ht_pack, 0); - return data; + Temp scratch = scratch_begin(&arena, 1); + + // serialize names (layout must be in insert order) + String8List key_data_srl = {0}; str8_serial_begin(scratch.arena, &key_data_srl); + { + PDB_HashTableBucket **buckets = pdb_hash_table_get_present_buckets(scratch.arena, ht); + radsort(buckets, ht->count, pdb_hash_table_bucket_is_before); + for EachIndex(i, ht->count) { + buckets[i]->key_offset = key_data_srl.total_size; + str8_serial_push_cstr(scratch.arena, &key_data_srl, buckets[i]->key); + } + } + + // serialize hash table + String8 ht_data = pdb_data_from_hash_table(arena, ht, pdb_named_stream_ht_pack, 0); + + // put names and hash table together + String8List srl = {0}; str8_serial_begin(scratch.arena, &srl); + str8_serial_push_u32(scratch.arena, &srl, safe_cast_u32(key_data_srl.total_size)); + str8_serial_push_data_list(scratch.arena, &srl, key_data_srl.first); + str8_serial_push_string(scratch.arena, &srl, ht_data); + + String8 result = str8_serial_end(arena, &srl); + + scratch_end(scratch); + return result; } //////////////////////////////// @@ -830,126 +859,6 @@ pdb_type_server_alloc(U64 bucket_cap) return ts; } -internal PDB_TypeServer * -pdb_type_server_open_v80(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable *strtab) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - PDB_TypeServer *ts = NULL; - - PDB_TpiHeader header = {0}; - msf_stream_seek(msf, sn, 0); - MSF_UInt read_header_size = msf_stream_read_struct(msf, sn, &header); - - // have we read enough bytes? - if (read_header_size != sizeof(PDB_TpiHeader)) { - goto exit; - } - // is lowest non-simple type index valid? - if (header.ti_lo < CV_MinComplexTypeIndex) { - goto exit; - } - // is high non-simple type index valid? - if (header.ti_lo > header.ti_hi) { - goto exit; - } - - // validate hash bucket count - if (header.hash_bucket_count == 0) { - goto exit; - } - if (header.hash_bucket_count > PDB_TYPE_SERVER_HASH_BUCKET_COUNT_MAX) { - goto exit; - } - - // are there enough bytes in the stream to read hash values? - U64 hash_stream_size = msf_stream_get_size(msf, header.hash_sn); - if (header.hash_vals.off + header.hash_vals.size > hash_stream_size) { - goto exit; - } - - ts = pdb_type_server_alloc(header.hash_bucket_count); - - // read & parse code view types - String8 types_data = msf_stream_read_block(ts->arena, msf, sn, header.leaf_data_size); - CV_DebugT debug_t = cv_debug_t_from_data(scratch.arena, types_data, PDB_LEAF_ALIGN); - - // read hash data - U8 *hash_buffer = push_array(scratch.arena, U8, header.hash_vals.size); - msf_stream_seek(msf, header.hash_sn, header.hash_vals.off); - MSF_UInt hash_buffer_size = msf_stream_read(msf, header.hash_sn, hash_buffer, header.hash_vals.size); - Assert(hash_buffer_size == header.hash_vals.size); - - // rebuild type buckets - for (U64 cursor = 0, leaf_idx = 0; - cursor + header.hash_key_size <= hash_buffer_size; - cursor += header.hash_key_size, leaf_idx += 1) { - String8 raw_leaf = cv_debug_t_get_raw_leaf(&debug_t, leaf_idx); - - str8_list_push(ts->arena, &ts->leaf_list, raw_leaf); - - // read out bucket hash - U64 hash = 0; - MemoryCopy(&hash, hash_buffer + cursor, header.hash_key_size); - - // push bucket - PDB_TypeBucket *bucket = push_array(ts->arena, PDB_TypeBucket, 1); - bucket->raw_leaf = raw_leaf; - bucket->type_index = header.ti_lo + leaf_idx; - SLLStackPush(ts->buckets[hash], bucket); - } - - // adjust type buckets - msf_stream_seek(msf, header.hash_sn, header.hash_adj.off); - String8 adjust_data = msf_stream_read_block(scratch.arena, msf, header.hash_sn, header.hash_adj.size); - - // open adjust hash table - PDB_HashTableParseError hash_adj_parse_error = pdb_hash_adj_hash_table_from_data(&ts->hash_adj, adjust_data, strtab, 0); - if (hash_adj_parse_error == PDB_HashTableParseError_OUT_OF_BYTES) { - pdb_hash_table_alloc(&ts->hash_adj, 16); - } else { - Assert(hash_adj_parse_error == PDB_HashTableParseError_OK); - } - - // grab keys and values - String8Array key_arr = {0}; - String8Array value_arr = {0}; - pdb_hash_table_get_present_keys_and_values(scratch.arena, &ts->hash_adj, &key_arr, &value_arr); - - // adjust type buckets - for (U64 i = 0; i < ts->hash_adj.count; i += 1) { - String8 type_name = key_arr.v[i]; - CV_TypeIndex type_index = *(CV_TypeIndex*)value_arr.v[i].str; - - // name -> hash - U64 hash = pdb_hash_v1(type_name); - hash %= ts->bucket_cap; - - // search for type bucket - PDB_TypeBucket *curr, *prev; - for (curr = ts->buckets[hash], prev = 0; curr != 0; prev = curr, curr = curr->next) { - if (curr->type_index == type_index) { - break; - } - } - - // move type to the head - if (prev && curr) { - prev->next = curr->next; - curr->next = ts->buckets[hash]; - ts->buckets[hash] = curr; - } - - Assert(curr); - } - -exit: - scratch_end(scratch); - ProfEnd(); - return ts; -} - internal THREAD_POOL_TASK_FUNC(pdb_write_type_to_bucket_map_32_task) { @@ -1509,7 +1418,7 @@ pdb_info_alloc(U32 age, COFF_TimeStamp time_stamp, Guid guid) info->age = age; info->guid = guid; pdb_strtab_alloc(&info->strtab, 0x3fff); - pdb_hash_table_alloc(&info->named_stream_ht, 4); + pdb_hash_table_alloc(&info->named_stream_ht, 1); pdb_hash_table_alloc(&info->src_header_block_ht, 8); ProfEnd(); return info; @@ -1615,9 +1524,13 @@ pdb_info_build(PDB_InfoContext *info, MSF_Context *msf, MSF_StreamNumber sn) Temp scratch = scratch_begin(0,0); // finalize named streams - pdb_info_build_src_header_block(info, msf); + if (info->src_header_block_ht.count) { + pdb_info_build_src_header_block(info, msf); + } pdb_info_build_link_info(info, msf); - pdb_info_build_names(info, msf); + if (info->strtab.bucket_count > 1) { + pdb_info_build_names(info, msf); + } // serialize named streams hash table String8 named_stream_ht_data = pdb_data_from_named_stream_ht(scratch.arena, &info->named_stream_ht); @@ -1634,6 +1547,7 @@ pdb_info_build(PDB_InfoContext *info, MSF_Context *msf, MSF_StreamNumber sn) str8_serial_begin(scratch.arena, &info_srl); str8_serial_push_struct(scratch.arena, &info_srl, &header); str8_serial_push_string(scratch.arena, &info_srl, named_stream_ht_data); + str8_serial_push_u32(scratch.arena, &info_srl, 0); if (info->flags & PDB_FeatureFlag_HAS_ID_STREAM) { str8_serial_push_u32(scratch.arena, &info_srl, PDB_FeatureSig_VC140); } @@ -1666,12 +1580,9 @@ internal MSF_StreamNumber pdb_push_named_stream(PDB_HashTable *named_stream_ht, MSF_Context *msf, String8 name) { ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); MSF_StreamNumber sn = msf_stream_alloc(msf); - String8 name_cstr = push_cstr(scratch.arena, name); U32 sn32 = (U32)sn; - pdb_hash_table_set(named_stream_ht, name_cstr, str8_struct(&sn32)); - scratch_end(scratch); + pdb_hash_table_set(named_stream_ht, name, str8_struct(&sn32)); ProfEnd(); return sn; } @@ -2665,8 +2576,6 @@ dbi_build_module_info(Arena *arena, PDB_DbiContext *dbi, MSF_Context *msf) // TODO: generate EC info header->src_file = 0; header->pdb_file = 0; - - Assert(header->sn != MSF_INVALID_STREAM_NUMBER); // push module info str8_serial_push_struct(arena, &module_info_list, header); @@ -2941,7 +2850,7 @@ dbi_build_dbg_header(Arena *arena, PDB_DbiContext *dbi, MSF_Context *msf) } internal void -dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht) +dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht, B32 is_stripped) { ProfBeginFunction(); Temp scratch = scratch_begin(0, 0); @@ -2976,6 +2885,10 @@ dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumbe header.flags = 0; header.machine = dbi->machine; header.reserved = 0; + + if (is_stripped) { + header.flags |= PDB_DbiHeaderFlag_Stripped; + } ProfBegin("MSF Write"); @@ -3229,7 +3142,7 @@ pdb_get_guid(PDB_Context *pdb) } internal void -pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht, B32 build_gsi) +pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht, B32 build_gsi, B32 is_stripped) { ProfBeginFunction(); @@ -3244,7 +3157,7 @@ pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTa pdb_type_server_build(tp, ipi, strtab, pdb->msf, PDB_FixedStream_Ipi); } - dbi_build(tp, pdb->dbi, pdb->msf, PDB_FixedStream_Dbi, string_ht); + dbi_build(tp, pdb->dbi, pdb->msf, PDB_FixedStream_Dbi, string_ht, is_stripped); pdb_info_build(pdb->info, pdb->msf, PDB_FixedStream_Info); if (build_gsi) { diff --git a/src/linker/pdb_ext/pdb_builder.h b/src/linker/pdb_ext/pdb_builder.h index 9f6f7a70..527d82a2 100644 --- a/src/linker/pdb_ext/pdb_builder.h +++ b/src/linker/pdb_ext/pdb_builder.h @@ -11,16 +11,12 @@ //////////////////////////////// // Hash table -#define PDB_HASH_TABLE_PACK_FUNC(name) void name(Arena *arena, String8List *local_data_srl, String8List *key_value_srl, String8 key, String8 value, void *ud) -typedef PDB_HASH_TABLE_PACK_FUNC(PDB_HashTablePackFunc); - -#define PDB_HASH_TABLE_UNPACK_FUNC(name) B32 name(void *ud, String8 local_data, String8 key_value_data, U64 *key_value_cursor, String8 *key_out, String8 *value_out) -typedef PDB_HASH_TABLE_UNPACK_FUNC(PDB_HashTableUnpackFunc); - typedef struct PDB_HashTableBucket { String8 key; String8 value; + U32 key_offset; + U32 insert_idx; } PDB_HashTableBucket; typedef struct PDB_HashTable @@ -33,6 +29,12 @@ typedef struct PDB_HashTable U32 count; } PDB_HashTable; +#define PDB_HASH_TABLE_PACK_FUNC(name) void name(Arena *arena, String8List *key_value_srl, PDB_HashTableBucket *bucket, String8 key, String8 value, void *ud) +typedef PDB_HASH_TABLE_PACK_FUNC(PDB_HashTablePackFunc); + +#define PDB_HASH_TABLE_UNPACK_FUNC(name) B32 name(void *ud, String8 local_data, String8 key_value_data, U64 *key_value_cursor, String8 *key_out, String8 *value_out) +typedef PDB_HASH_TABLE_UNPACK_FUNC(PDB_HashTableUnpackFunc); + typedef enum { PDB_HashTableParseError_OK, @@ -331,7 +333,7 @@ typedef struct internal PDB_Context * pdb_alloc(U64 page_size, COFF_MachineType machine, COFF_TimeStamp time_stamp, U32 age, Guid guid); internal void pdb_release(PDB_Context **pdb_ptr); -internal void pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht, B32 build_gsi); +internal void pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht, B32 build_gsi, B32 is_stripped); internal void pdb_set_machine(PDB_Context *pdb, COFF_MachineType machine); internal void pdb_set_guid(PDB_Context *pdb, Guid guid); internal void pdb_set_time_stamp(PDB_Context *pdb, COFF_TimeStamp time_stamp); @@ -382,7 +384,7 @@ internal CV_SymbolNode * psi_push(PDB_PsiContext *psi, CV_Pub32Flags flags, U32 // DBI internal PDB_DbiContext * dbi_alloc(COFF_MachineType machine, U32 age); -internal void dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht); +internal void dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht, B32 is_stripped); internal void dbi_release(PDB_DbiContext **dbi_ptr); internal PDB_DbiModule * dbi_push_module(PDB_DbiContext *dbi, String8 obj_path, String8 lib_path); internal String8 dbi_module_read_symbol_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod); @@ -403,7 +405,7 @@ internal void dbi_build_section_header_stream(PDB_DbiContex internal void pdb_hash_table_alloc(PDB_HashTable *ht, U32 max); internal void pdb_hash_table_release(PDB_HashTable *ht); internal PDB_HashTableParseError pdb_hash_table_from_data(PDB_HashTable *ht, String8 data, B32 has_local_data, PDB_HashTableUnpackFunc *unpack_func, void *unpack_ud, U64 *read_bytes_out); -internal String8 pdb_data_from_hash_table(Arena *arena, PDB_HashTable *ht, B32 has_local_data, PDB_HashTablePackFunc *pack_func, void *pack_ud); +internal String8 pdb_data_from_hash_table(Arena *arena, PDB_HashTable *ht, PDB_HashTablePackFunc *pack_func, void *pack_ud); internal void pdb_hash_table_set(PDB_HashTable *ht, String8 key, String8 value); internal B32 pdb_hash_table_get(PDB_HashTable *ht, String8 key, String8 *value_out); internal void pdb_hash_table_delete(PDB_HashTable *ht, String8 key); @@ -412,6 +414,7 @@ internal B32 pdb_hash_table_is_present(PDB_HashTable *ht, U3 internal B32 pdb_hash_table_is_deleted(PDB_HashTable *ht, U32 k); internal U32 pdb_hash_table_hash(String8 key); internal void pdb_hash_table_grow(PDB_HashTable *ht, U64 new_capacity); +internal PDB_HashTableBucket ** pdb_hash_table_get_present_buckets(Arena *arena, PDB_HashTable *ht); internal void pdb_hash_table_get_present_keys_and_values(Arena *arena, PDB_HashTable *ht, String8Array *keys_out, String8Array *values_out); //////////////////////////////// diff --git a/src/third_party/martins_bitscan/bitscan.h b/src/third_party/martins_bitscan/bitscan.h new file mode 100644 index 00000000..1945f627 --- /dev/null +++ b/src/third_party/martins_bitscan/bitscan.h @@ -0,0 +1,664 @@ +#pragma once + +#include +#include + +// returns index of bit that is equal to "bit" value in [lo,hi) interval starting from lo position (lsb->msb) +// returns "hi" value if none found, or interval is empty +// "bit" value must be 0 or 1 +// "bits" array should be large enough to allow indexing it with [hi/32] or [hi/64] index + +static inline size_t bitscan_lsb_index32(const uint32_t* bits, size_t lo, size_t hi, int bit); +static inline size_t bitscan_lsb_index64(const uint64_t* bits, size_t lo, size_t hi, int bit); + +// returns index of bit that is equal to "bit" value in [lo,hi) interval starting from hi position (msb->lsb) +// returns "hi" value if none found, or interval is empty +// "bit" value must be 0 or 1 +// "bits" array should be large enough to allow indexing it with [hi/32] or [hi/64] index + +static inline size_t bitscan_msb_index32(const uint32_t* bits, size_t lo, size_t hi, int bit); +static inline size_t bitscan_msb_index64(const uint64_t* bits, size_t lo, size_t hi, int bit); + +// +// implementation +// + +// to run tests: +// cl.exe -O2 -fsanitize=address -DBITSCAN_TEST=1 -TC bitscan.h && bitscan.exe +// clang.exe -O2 -fsanitize=address,undefined -DBITSCAN_TEST=1 -x c bitscan.h && a.exe + +#if !(defined(__GNUC__) || defined(__clang__)) +# include // _BitScanForward/Reverse +#endif + +#if defined(_M_AMD64) || defined(__x86_64__) +# include // SSE2 +#endif + +// count of zero bits, either starting from lsb (trailing), or from msb (leading) +// input value must be non-zero +// bit index is from 0 (lsb) to 31/63 (msb) + +static inline size_t bitscan__ctz32(uint32_t x) +{ +#if defined(__GNUC__) || defined(__clang__) + return __builtin_ctz(x); +#else + unsigned long index; + _BitScanForward(&index, x); + return index; +#endif +} + +static inline size_t bitscan__ctz64(uint64_t x) +{ +#if defined(__GNUC__) || defined(__clang__) + return __builtin_ctzll(x); +#else + unsigned long index; + _BitScanForward64(&index, x); + return index; +#endif +} + +static inline size_t bitscan__clz32(uint32_t x) +{ +#if defined(__GNUC__) || defined(__clang__) + return __builtin_clz(x); +#else + unsigned long index; + _BitScanReverse(&index, x); + return 31 - index; +#endif +} + +static inline size_t bitscan__clz64(uint64_t x) +{ +#if defined(__GNUC__) || defined(__clang__) + return __builtin_clzll(x); +#else + unsigned long index; + _BitScanReverse64(&index, x); + return 63 - index; +#endif +} + +// returns index of first bit "1" in x when scanning from msb downwards +// bit index is from 0 (lsb) to 31/63 (msb) + +static inline size_t bitscan__lindex32(uint32_t x) +{ + return 31 - bitscan__clz32(x); +} +static inline size_t bitscan__lindex64(uint64_t x) +{ + return 63 - bitscan__clz64(x); +} + +size_t bitscan_lsb_index32(const uint32_t* bits, size_t lo, size_t hi, int bit) +{ +#if 0 + + // reference implementation + for (size_t i = lo; i> (i%32)) & 1) == bit) + { + return i; + } + } + +#else + + if (lo >= hi) + { + return hi; + } + + // to be able to use "ctz" for "find first zero bit" + // do xor with this mask, it will flip all bit values + // thus changing operation to "find first set bit" - which allows to use "ctz" + const uint32_t mask = bit ? 0U : ~0U; + + size_t count = hi - lo; + size_t offset = lo / 32; + size_t first = (unsigned)(-(int)lo) % 32; // 0 if lo%32 == 0, otherwise (32 - lo%32) % 32 + + if (first) // first < 32, how many max bits to use in top of first word + { + uint32_t word = bits[offset] ^ mask; + + // first = lo%32 = 18 + // count = 10 + // in cases count > first, clamp it to first + // + // 3 2 1 0 + // 10987654321098765432109876543210 bit index + // ....xxxxxxxxxx.................. + // ^ ^ ^ + // | | | + // | | +------------------ lo%32 = 18 + // | | | + // | +- count -+ count = 10 bits to process + // | | + // +--- first ---+ first = 32 - lo%32 = 14 + + size_t n = (count < first ? count : first); + word &= (~0U >> (32 - n)) << (lo % 32); + + if (word) + { + return offset * 32 + bitscan__ctz32(word); + } + offset += 1; + count -= n; + } + +#if defined(_M_AMD64) || defined(__x86_64__) + while (count >= 128) + { + __m128i words = _mm_loadu_si128((const __m128i*)&bits[offset]); + __m128i cmp = _mm_cmpeq_epi32(words, _mm_set1_epi32(mask)); + + // if all 16-bytes of "words" are same as mask, then m will be 0 + uint16_t m = 1 + (uint16_t)_mm_movemask_epi8(cmp); + if (m) + { + // find word index to use [0,4) + size_t n = bitscan__ctz32(m) / 4; + + uint32_t word = bits[offset + n] ^ mask; + return offset * 32 + n * 32 + bitscan__ctz32(word); + } + + offset += 4; + count -= 128; + } +#endif + + while (count >= 32) + { + uint32_t word = bits[offset] ^ mask; + if (word) + { + return offset * 32 + bitscan__ctz32(word); + } + offset += 1; + count -= 32; + } + + if (count) // now count < 32, how many bits to process in bottom of last word + { + uint32_t word = bits[offset] ^ mask; + + // use first count bits, rest of bits are masked out to 1 + word |= ~0U << count; + + if (word) + { + return offset * 32 + bitscan__ctz32(word); + } + } + +#endif + + return hi; +} + +size_t bitscan_lsb_index64(const uint64_t* bits, size_t lo, size_t hi, int bit) +{ +#if 0 + + // reference implementation + for (size_t i = lo; i < hi; i++) + { + uint64_t word = bits[i / 64]; + if (((word >> (i % 64)) & 1) == bit) + { + return i; + } + } + +#else + + if (lo >= hi) + { + return hi; + } + + const uint64_t mask = bit ? 0ULL : ~0ULL; + + size_t count = hi - lo; + size_t offset = lo / 64; + size_t first = (unsigned)(-(int)lo) % 64; + + if (first) + { + uint64_t word = bits[offset] ^ mask; + + size_t n = (count < first ? count : first); + word &= (~0ULL >> (64 - n)) << (lo % 64); + + if (word) + { + return offset * 64 + bitscan__ctz64(word); + } + offset += 1; + count -= n; + } + +#if defined(_M_AMD64) || defined(__x86_64__) + while (count >= 128) + { + __m128i words = _mm_loadu_si128((const __m128i*)&bits[offset]); + __m128i cmp = _mm_cmpeq_epi32(words, _mm_set1_epi32((uint32_t)mask)); + uint16_t m = 1 + (uint16_t)_mm_movemask_epi8(cmp); + if (m) + { + size_t n = bitscan__ctz32(m) / 8; + + uint64_t word = bits[offset + n] ^ mask; + return offset * 64 + n * 64 + bitscan__ctz64(word); + } + + offset += 2; + count -= 128; + } +#endif + + while (count >= 64) + { + uint64_t word = bits[offset] ^ mask; + if (word) + { + return offset * 64 + bitscan__ctz64(word); + } + offset += 1; + count -= 64; + } + + if (count) + { + uint64_t word = bits[offset] ^ mask; + word |= ~0ULL << count; + + if (word) + { + return offset * 64 + bitscan__ctz64(word); + } + } + +#endif + + return hi; +} + +size_t bitscan_msb_index32(const uint32_t* bits, size_t lo, size_t hi, int bit) +{ +#if 0 + + // reference implementation + for (size_t i = hi; i-- > lo; ) + { + uint32_t word = bits[i/32]; + if (((word >> (i%32)) & 1) == bit) + { + return i; + } + } + +#else + + if (lo >= hi) + { + return hi; + } + + // to be able to use "clz" for "find last zero bit" + // do xor with this mask, it will flip all bit values + // thus changing operation to "find last set bit" - which allows to use "clz" + const uint32_t mask = bit ? 0U : ~0U; + + size_t count = hi - lo; + size_t offset = (hi - 1) / 32; + size_t first = hi % 32; + + if (first) // first < 32, how many max bits to use in bottom of first word + { + uint32_t word = bits[offset] ^ mask; + + // first = hi%32 = 14 + // count = 10 + // in cases count > first, clamp it to first + // + // 3 2 1 0 + // 10987654321098765432109876543210 bit index + // ..................xxxxxxxxxx.... + // ^ ^ + // | | + // +- count -+ count = 10 bits to process + // | + // +-------------- first = 14 + + size_t n = (count < first ? count : first); + word &= (1U << first) - (1U << (first - n)); + + if (word) + { + return offset * 32 + bitscan__lindex32(word); + } + offset -= 1; + count -= n; + } + +#if defined(_M_AMD64) || defined(__x86_64__) + while (count >= 128) + { + __m128i words = _mm_loadu_si128((const __m128i*)&bits[offset - 3]); + __m128i cmp = _mm_cmpeq_epi32(words, _mm_set1_epi32(mask)); + + // if all 16-bytes of "words" are same as mask, then m will be 0xffff + uint16_t m = (uint16_t)_mm_movemask_epi8(cmp); + + if ((uint16_t)(m + 1)) + { + // find word index to use [0,4) + size_t n = bitscan__lindex32((uint16_t)~m) / 4; + + uint32_t word = bits[offset + n - 3] ^ mask; + return offset * 32 + (n - 3) * 32 + bitscan__lindex32(word); + } + offset -= 4; + count -= 128; + } +#endif + + while (count >= 32) + { + uint32_t word = bits[offset] ^ mask; + if (word) + { + return offset * 32 + bitscan__lindex32(word); + } + offset -= 1; + count -= 32; + } + + if (count) // now count < 32, how many bits to process in top of last word + { + uint32_t word = bits[offset] ^ mask; + + // use last count bits, rest of bits are masked out to 0 + word &= ~0U << (32 - count); + + if (word) + { + return offset * 32 + bitscan__lindex32(word); + } + } + +#endif + + return hi; +} + +size_t bitscan_msb_index64(const uint64_t* bits, size_t lo, size_t hi, int bit) +{ +#if 0 + + // reference implementation + for (size_t i = hi; i-- > lo; ) + { + uint64_t word = bits[i/64]; + if (((word >> (i%64)) & 1) == bit) + { + return i; + } + } + +#else + + if (lo >= hi) + { + return hi; + } + + const uint64_t mask = bit ? 0ULL : ~0ULL; + + size_t count = hi - lo; + size_t offset = (hi - 1) / 64; + size_t first = hi % 64; + + if (first) + { + uint64_t word = bits[offset] ^ mask; + + size_t n = (count < first ? count : first); + word &= (1ULL << first) - (1ULL << (first - n)); + + if (word) + { + return offset * 64 + bitscan__lindex64(word); + } + offset -= 1; + count -= n; + } + +#if defined(_M_AMD64) || defined(__x86_64__) + while (count >= 128) + { + __m128i words = _mm_loadu_si128((const __m128i*)&bits[offset - 1]); + __m128i cmp = _mm_cmpeq_epi32(words, _mm_set1_epi32(mask)); + uint16_t m = (uint16_t)_mm_movemask_epi8(cmp); + if ((uint16_t)(m + 1)) + { + size_t n = bitscan__lindex32((uint16_t)~m) / 8; + + uint64_t word = bits[offset + n - 1] ^ mask; + return offset * 64 + (n - 1) * 64 + bitscan__lindex64(word); + } + offset -= 2; + count -= 128; + } +#endif + + while (count >= 64) + { + uint64_t word = bits[offset] ^ mask; + if (word) + { + return offset * 64 + bitscan__lindex64(word); + } + offset -= 1; + count -= 64; + } + + if (count) + { + uint64_t word = bits[offset] ^ mask; + word &= ~0ULL << (64 - count); + + if (word) + { + return offset * 64 + bitscan__lindex64(word); + } + } + +#endif + + return hi; +} + +#if defined(BITSCAN_TEST) + +#include +#include + +static uint64_t random64() +{ + static uint64_t x = 0, w = 0; + x = x*x + (w += 0xb5ad4eceda1ce2a9); + return x = (x>>32) | (x<<32); +} + +#define BIT32_SET(v,i) v[(i)/32] |= 1U << ((i)%32) +#define BIT32_CLEAR(v,i) v[(i)/32] &= ~(1U << ((i)%32)) + +#define BIT64_SET(v,i) v[(i)/64] |= 1ULL << ((i)%64) +#define BIT64_CLEAR(v,i) v[(i)/64] &= ~(1ULL << ((i)%64)) + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +int main() +{ + enum { kBitCount = 512 }; + enum { kRngCount = 32 }; + + static const size_t offsets[] = + { + 0, 1, 10, 31, 32, 33, 50, 63, 64, 65, 130, kBitCount-65, kBitCount-64, kBitCount-63, kBitCount-33, kBitCount-32, kBitCount-31, kBitCount-1, kBitCount, + }; + size_t offset_count = sizeof(offsets) / sizeof(offsets[0]); + + for (size_t n=0; n<=kBitCount; n++) + { + printf("."); fflush(stdout); + + // bitscan_lsb_index32 + + for (size_t r=0; r= n) continue; + + size_t r; + size_t expected = lo >= hi || hi < n ? hi : n; + + // first n bits are 0, then bit 1 when possible + for (size_t i=0; i= hi || lo >= kBitCount-n ? hi : MIN(hi-1, kBitCount-1-n); + + // last n bits are 0, then bit 1 when possible + for (size_t i=kBitCount-n; i= n) continue; + + size_t r; + size_t expected = lo >= hi || hi < n ? hi : n; + + // first n bits are 0, then bit 1 when possible + for (size_t i = 0; i < n; i++) BIT64_CLEAR(v, i); + if (n < kBitCount) BIT64_SET(v, n); + + r = bitscan_lsb_index64(v, lo, hi, 1); + assert(r == expected); + + // first n bits are 1, then bit 0 when possible + for (size_t i = 0; i < n; i++) BIT64_SET(v, i); + if (n < kBitCount) BIT64_CLEAR(v, n); + + r = bitscan_lsb_index64(v, lo, hi, 0); + assert(r == expected); + } + } + + // bitscan_msb_index64 + + for (size_t r = 0; r < kRngCount; r++) + { + uint64_t v[kBitCount / 64]; + for (size_t i = 0; i < kBitCount / 64; i++) v[i] = random64(); + + for (size_t olo = 0; olo < offset_count; olo++) + for (size_t ohi = 0; ohi < offset_count; ohi++) + { + size_t lo = offsets[olo]; + size_t hi = offsets[ohi]; + if (hi < kBitCount - n) continue; + + size_t r; + size_t expected = lo >= hi || lo >= kBitCount-n ? hi : MIN(hi-1, kBitCount-1-n); + + // last n bits are 0, then bit 1 when possible + for (size_t i = kBitCount - n; i < kBitCount; i++) BIT64_CLEAR(v, i); + if (n < kBitCount) BIT64_SET(v, kBitCount-n-1); + + r = bitscan_msb_index64(v, lo, hi, 1); + assert(r == expected); + + // last n bits are 1, then bit 0 when possible + for (size_t i = kBitCount - n; i < kBitCount; i++) BIT64_SET(v, i); + if (n < kBitCount) BIT64_CLEAR(v, kBitCount-n-1); + + r = bitscan_msb_index64(v, lo, hi, 0); + assert(r == expected); + } + } + } + printf(" OK!\n"); +} + +#endif diff --git a/src/torture/torture_base.c b/src/torture/torture_base.c index 730356ad..ba8e46d4 100644 --- a/src/torture/torture_base.c +++ b/src/torture/torture_base.c @@ -69,4 +69,29 @@ T_BeginTest(str8_list_substr) } } +T_BeginTest(bit_array) +{ + for (U64 start=0; start<32*3; start++) { + for (U64 end=start; end<32*3; end++) { + U32 v[3] = { 0 }; + for (U64 i=start; i= end || lo >= hi || start >= end ? 0 : 1; + U64 idx = bit_array_scan_right_to_left32((U32Array){.v=v, .count=ArrayCount(v)}, lo, hi, 1); + B32 r = idx < hi; + T_Ok(r == expected_r); + if (r) { + T_Ok(idx == expected_idx); + + } + } + } + } + } +} + #undef T_Group diff --git a/src/torture/torture_main.c b/src/torture/torture_main.c index 207c88f8..51e63860 100644 --- a/src/torture/torture_main.c +++ b/src/torture/torture_main.c @@ -76,6 +76,7 @@ #include "ui/ui_inc.h" #include "dbg_engine/dbg_engine_inc.h" #include "raddbg/raddbg_inc.h" +#include "linker/base_ext/base_crc32.h" #include "linker/base_ext/base_core.h" #include "linker/base_ext/base_arena.h" #include "linker/base_ext/base_arrays.h" @@ -83,6 +84,9 @@ #include "linker/thread_pool/thread_pool.h" #include "linker/codeview_ext/codeview.h" #include "linker/pdb_ext/msf_builder.h" +#include "linker/pdb_ext/pdb.h" +#include "linker/pdb_ext/pdb_helpers.h" +#include "linker/pdb_ext/pdb_builder.h" #include "linker/lnk_cmd_line.h" #include "linker/lnk_cmd_line.c" #include "linker/lnk_log.h" @@ -148,9 +152,13 @@ #include "linker/base_ext/base_arena.c" #include "linker/base_ext/base_arrays.c" #include "linker/base_ext/base_bit_array.c" +#include "linker/base_ext/base_crc32.c" #include "linker/thread_pool/thread_pool.c" #include "linker/codeview_ext/codeview.c" #include "linker/pdb_ext/msf_builder.c" +#include "linker/pdb_ext/pdb.c" +#include "linker/pdb_ext/pdb_helpers.c" +#include "linker/pdb_ext/pdb_builder.c" #include "torture.c" #include "torture_base.c"